Aegisub/src/subs_edit_box.cpp
Ryan Lucia c487dd2bdb Parially revert fffb138b81
I haven't checked whether reverting this breaks IME input, and if it doesn't what changed on wx's end. However, this is the commit that uses private symbols, and so reverting it lets us build against upstream wx. Even if this is a loss in functionality, for now it's fine.
2021-01-10 03:14:12 -05:00

631 lines
23 KiB
C++

// Copyright (c) 2005, Rodrigo Braz Monteiro
// Copyright (c) 2010, Thomas Goyne <plorkyeran@aegisub.org>
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// * Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
// * Neither the name of the Aegisub Group nor the names of its contributors
// may be used to endorse or promote products derived from this software
// without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
//
// Aegisub Project http://www.aegisub.org/
/// @file subs_edit_box.cpp
/// @brief Main subtitle editing area, including toolbars around the text control
/// @ingroup main_ui
#include "subs_edit_box.h"
#include "ass_dialogue.h"
#include "ass_file.h"
#include "base_grid.h"
#include "command/command.h"
#include "compat.h"
#include "dialog_style_editor.h"
#include "flyweight_hash.h"
#include "include/aegisub/context.h"
#include "include/aegisub/hotkey.h"
#include "initial_line_state.h"
#include "options.h"
#include "project.h"
#include "placeholder_ctrl.h"
#include "selection_controller.h"
#include "subs_edit_ctrl.h"
#include "text_selection_controller.h"
#include "timeedit_ctrl.h"
#include "tooltip_manager.h"
#include "validators.h"
#include <libaegisub/character_count.h>
#include <libaegisub/util.h>
#include <functional>
#include <unordered_set>
#include <wx/bmpbuttn.h>
#include <wx/button.h>
#include <wx/checkbox.h>
#include <wx/fontenum.h>
#include <wx/radiobut.h>
#include <wx/settings.h>
#include <wx/sizer.h>
#include <wx/spinctrl.h>
namespace {
/// Work around wxGTK's fondness for generating events from ChangeValue
void change_value(wxTextCtrl *ctrl, wxString const& value) {
if (value != ctrl->GetValue())
ctrl->ChangeValue(value);
}
wxString new_value(wxComboBox *ctrl, wxCommandEvent &evt) {
#ifdef __WXGTK__
return ctrl->GetValue();
#else
return evt.GetString();
#endif
}
void time_edit_char_hook(wxKeyEvent &event) {
// Force a modified event on Enter
if (event.GetKeyCode() == WXK_RETURN) {
TimeEdit *edit = static_cast<TimeEdit*>(event.GetEventObject());
edit->SetValue(edit->GetValue());
}
else
event.Skip();
}
// Passing a pointer-to-member directly to a function sometimes does not work
// in VC++ 2015 Update 2, with it instead passing a null pointer
const auto AssDialogue_Actor = &AssDialogue::Actor;
const auto AssDialogue_Effect = &AssDialogue::Effect;
}
SubsEditBox::SubsEditBox(wxWindow *parent, agi::Context *context)
: wxPanel(parent, -1, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL | wxRAISED_BORDER, "SubsEditBox")
, c(context)
, undo_timer(GetEventHandler())
{
using std::bind;
// Top controls
top_sizer = new wxBoxSizer(wxHORIZONTAL);
comment_box = new wxCheckBox(this,-1,_("&Comment"));
comment_box->SetToolTip(_("Comment this line out. Commented lines don't show up on screen."));
#ifdef __WXGTK__
// Only supported in wxgtk
comment_box->SetCanFocus(false);
#endif
top_sizer->Add(comment_box, 0, wxRIGHT | wxALIGN_CENTER, 5);
style_box = MakeComboBox("Default", wxCB_READONLY, &SubsEditBox::OnStyleChange, _("Style for this line"));
style_edit_button = new wxButton(this, -1, _("Edit"), wxDefaultPosition,
wxSize(GetTextExtent(_("Edit")).GetWidth() + 20, -1));
style_edit_button->Bind(wxEVT_BUTTON, [=](wxCommandEvent&) {
if (active_style) {
wxArrayString font_list = wxFontEnumerator::GetFacenames();
font_list.Sort();
DialogStyleEditor(this, active_style, c, nullptr, "", font_list).ShowModal();
}
});
top_sizer->Add(style_edit_button, wxSizerFlags().Center().Border(wxRIGHT));
actor_box = new Placeholder<wxComboBox>(this, _("Actor"), wxSize(110, -1), wxCB_DROPDOWN | wxTE_PROCESS_ENTER, _("Actor name for this speech. This is only for reference, and is mainly useless."));
Bind(wxEVT_TEXT, &SubsEditBox::OnActorChange, this, actor_box->GetId());
Bind(wxEVT_COMBOBOX, &SubsEditBox::OnActorChange, this, actor_box->GetId());
top_sizer->Add(actor_box, wxSizerFlags(2).Center().Border(wxRIGHT));
effect_box = new Placeholder<wxComboBox>(this, _("Effect"), wxSize(80,-1), wxCB_DROPDOWN | wxTE_PROCESS_ENTER, _("Effect for this line. This can be used to store extra information for karaoke scripts, or for the effects supported by the renderer."));
Bind(wxEVT_TEXT, &SubsEditBox::OnEffectChange, this, effect_box->GetId());
Bind(wxEVT_COMBOBOX, &SubsEditBox::OnEffectChange, this, effect_box->GetId());
top_sizer->Add(effect_box, 3, wxALIGN_CENTER, 5);
char_count = new wxTextCtrl(this, -1, "0", wxDefaultPosition, wxSize(30, -1), wxTE_READONLY | wxTE_CENTER);
char_count->SetToolTip(_("Number of characters in the longest line of this subtitle."));
top_sizer->Add(char_count, 0, wxALIGN_CENTER, 5);
// Middle controls
middle_left_sizer = new wxBoxSizer(wxHORIZONTAL);
layer = new wxSpinCtrl(this,-1,"",wxDefaultPosition,wxSize(50,-1), wxSP_ARROW_KEYS | wxTE_PROCESS_ENTER,0,0x7FFFFFFF,0);
layer->SetToolTip(_("Layer number"));
middle_left_sizer->Add(layer, wxSizerFlags().Center());
middle_left_sizer->AddSpacer(5);
start_time = MakeTimeCtrl(_("Start time"), TIME_START);
end_time = MakeTimeCtrl(_("End time"), TIME_END);
middle_left_sizer->AddSpacer(5);
duration = MakeTimeCtrl(_("Line duration"), TIME_DURATION);
middle_left_sizer->AddSpacer(5);
margin[0] = MakeMarginCtrl(_("Left Margin (0 = default from style)"), 0, _("left margin change"));
margin[1] = MakeMarginCtrl(_("Right Margin (0 = default from style)"), 1, _("right margin change"));
margin[2] = MakeMarginCtrl(_("Vertical Margin (0 = default from style)"), 2, _("vertical margin change"));
middle_left_sizer->AddSpacer(5);
// Middle-bottom controls
middle_right_sizer = new wxBoxSizer(wxHORIZONTAL);
MakeButton("edit/style/bold");
MakeButton("edit/style/italic");
MakeButton("edit/style/underline");
MakeButton("edit/style/strikeout");
MakeButton("edit/font");
middle_right_sizer->AddSpacer(5);
MakeButton("edit/color/primary");
MakeButton("edit/color/secondary");
MakeButton("edit/color/outline");
MakeButton("edit/color/shadow");
middle_right_sizer->AddSpacer(5);
MakeButton("grid/line/next/create");
middle_right_sizer->AddSpacer(10);
by_time = MakeRadio(_("T&ime"), true, _("Time by h:mm:ss.cs"));
by_frame = MakeRadio(_("F&rame"), false, _("Time by frame number"));
by_frame->Enable(false);
split_box = new wxCheckBox(this,-1,_("Show Original"));
split_box->SetToolTip(_("Show the contents of the subtitle line when it was first selected above the edit box. This is sometimes useful when editing subtitles or translating subtitles into another language."));
split_box->Bind(wxEVT_CHECKBOX, &SubsEditBox::OnSplit, this);
middle_right_sizer->Add(split_box, wxSizerFlags().Center().Left());
// Main sizer
wxSizer *main_sizer = new wxBoxSizer(wxVERTICAL);
main_sizer->Add(top_sizer,0,wxEXPAND | wxALL,3);
main_sizer->Add(middle_left_sizer,0,wxEXPAND | wxLEFT | wxRIGHT | wxBOTTOM,3);
main_sizer->Add(middle_right_sizer,0,wxEXPAND | wxLEFT | wxRIGHT | wxBOTTOM,3);
// Text editor
edit_ctrl = new SubsTextEditCtrl(this, wxSize(300,50), wxBORDER_SUNKEN, c);
edit_ctrl->Bind(wxEVT_CHAR_HOOK, &SubsEditBox::OnKeyDown, this);
secondary_editor = new wxTextCtrl(this, -1, "", wxDefaultPosition, wxSize(300,50), wxBORDER_SUNKEN | wxTE_MULTILINE | wxTE_READONLY);
main_sizer->Add(secondary_editor,1,wxEXPAND | wxLEFT | wxRIGHT | wxBOTTOM,3);
main_sizer->Add(edit_ctrl,1,wxEXPAND | wxLEFT | wxRIGHT | wxBOTTOM,3);
main_sizer->Hide(secondary_editor);
bottom_sizer = new wxBoxSizer(wxHORIZONTAL);
bottom_sizer->Add(MakeBottomButton("edit/revert"), wxSizerFlags().Border(wxRIGHT));
bottom_sizer->Add(MakeBottomButton("edit/clear"), wxSizerFlags().Border(wxRIGHT));
bottom_sizer->Add(MakeBottomButton("edit/clear/text"), wxSizerFlags().Border(wxRIGHT));
bottom_sizer->Add(MakeBottomButton("edit/insert_original"));
main_sizer->Add(bottom_sizer);
main_sizer->Hide(bottom_sizer);
SetSizerAndFit(main_sizer);
edit_ctrl->Bind(wxEVT_STC_MODIFIED, &SubsEditBox::OnChange, this);
edit_ctrl->SetModEventMask(wxSTC_MOD_INSERTTEXT | wxSTC_MOD_DELETETEXT | wxSTC_STARTACTION);
Bind(wxEVT_TEXT, &SubsEditBox::OnLayerEnter, this, layer->GetId());
Bind(wxEVT_SPINCTRL, &SubsEditBox::OnLayerEnter, this, layer->GetId());
Bind(wxEVT_CHECKBOX, &SubsEditBox::OnCommentChange, this, comment_box->GetId());
Bind(wxEVT_CHAR_HOOK, &SubsEditBox::OnKeyDown, this);
Bind(wxEVT_SIZE, &SubsEditBox::OnSize, this);
Bind(wxEVT_TIMER, [=](wxTimerEvent&) { commit_id = -1; });
wxSizeEvent evt;
OnSize(evt);
file_changed_slot = c->ass->AddCommitListener(&SubsEditBox::OnCommit, this);
connections = agi::signal::make_vector({
context->project->AddTimecodesListener(&SubsEditBox::UpdateFrameTiming, this),
context->selectionController->AddActiveLineListener(&SubsEditBox::OnActiveLineChanged, this),
context->selectionController->AddSelectionListener(&SubsEditBox::OnSelectedSetChanged, this),
context->initialLineState->AddChangeListener(&SubsEditBox::OnLineInitialTextChanged, this),
});
context->textSelectionController->SetControl(edit_ctrl);
edit_ctrl->SetFocus();
}
SubsEditBox::~SubsEditBox() {
c->textSelectionController->SetControl(nullptr);
}
wxTextCtrl *SubsEditBox::MakeMarginCtrl(wxString const& tooltip, int margin, wxString const& commit_msg) {
wxTextCtrl *ctrl = new wxTextCtrl(this, -1, "", wxDefaultPosition, wxSize(40,-1), wxTE_CENTRE | wxTE_PROCESS_ENTER, IntValidator());
ctrl->SetMaxLength(4);
ctrl->SetToolTip(tooltip);
middle_left_sizer->Add(ctrl, wxSizerFlags().Center());
Bind(wxEVT_TEXT, [=](wxCommandEvent&) {
int value = agi::util::mid(0, atoi(ctrl->GetValue().utf8_str()), 9999);
SetSelectedRows([&](AssDialogue *d) { d->Margin[margin] = value; },
commit_msg, AssFile::COMMIT_DIAG_META);
}, ctrl->GetId());
return ctrl;
}
TimeEdit *SubsEditBox::MakeTimeCtrl(wxString const& tooltip, TimeField field) {
TimeEdit *ctrl = new TimeEdit(this, -1, c, "", wxSize(GetTextExtent(wxS(" 0:00:00.000 ")).GetWidth(),-1), field == TIME_END);
ctrl->SetToolTip(tooltip);
Bind(wxEVT_TEXT, [=](wxCommandEvent&) { CommitTimes(field); }, ctrl->GetId());
ctrl->Bind(wxEVT_CHAR_HOOK, time_edit_char_hook);
middle_left_sizer->Add(ctrl, wxSizerFlags().Center());
return ctrl;
}
void SubsEditBox::MakeButton(const char *cmd_name) {
cmd::Command *command = cmd::get(cmd_name);
wxBitmapButton *btn = new wxBitmapButton(this, -1, command->Icon(16));
ToolTipManager::Bind(btn, command->StrHelp(), "Subtitle Edit Box", cmd_name);
middle_right_sizer->Add(btn, wxSizerFlags().Expand());
btn->Bind(wxEVT_BUTTON, std::bind(&SubsEditBox::CallCommand, this, cmd_name));
}
wxButton *SubsEditBox::MakeBottomButton(const char *cmd_name) {
cmd::Command *command = cmd::get(cmd_name);
wxButton *btn = new wxButton(this, -1, command->StrDisplay(c));
ToolTipManager::Bind(btn, command->StrHelp(), "Subtitle Edit Box", cmd_name);
btn->Bind(wxEVT_BUTTON, std::bind(&SubsEditBox::CallCommand, this, cmd_name));
return btn;
}
wxComboBox *SubsEditBox::MakeComboBox(wxString const& initial_text, int style, void (SubsEditBox::*handler)(wxCommandEvent&), wxString const& tooltip) {
wxString styles[] = { "Default" };
wxComboBox *ctrl = new wxComboBox(this, -1, initial_text, wxDefaultPosition, wxSize(110,-1), 1, styles, style | wxTE_PROCESS_ENTER);
ctrl->SetToolTip(tooltip);
top_sizer->Add(ctrl, wxSizerFlags(2).Center().Border(wxRIGHT));
Bind(wxEVT_COMBOBOX, handler, this, ctrl->GetId());
return ctrl;
}
wxRadioButton *SubsEditBox::MakeRadio(wxString const& text, bool start, wxString const& tooltip) {
wxRadioButton *ctrl = new wxRadioButton(this, -1, text, wxDefaultPosition, wxDefaultSize, start ? wxRB_GROUP : 0);
ctrl->SetValue(start);
ctrl->SetToolTip(tooltip);
Bind(wxEVT_RADIOBUTTON, &SubsEditBox::OnFrameTimeRadio, this, ctrl->GetId());
middle_right_sizer->Add(ctrl, wxSizerFlags().Expand().Border(wxRIGHT));
return ctrl;
}
void SubsEditBox::OnCommit(int type) {
wxEventBlocker blocker(this);
initial_times.clear();
if (type == AssFile::COMMIT_NEW || type & AssFile::COMMIT_STYLES) {
wxString style = style_box->GetValue();
style_box->Clear();
style_box->Append(to_wx(c->ass->GetStyles()));
style_box->Select(style_box->FindString(style));
active_style = line ? c->ass->GetStyle(line->Style) : nullptr;
}
if (type == AssFile::COMMIT_NEW) {
PopulateList(effect_box, AssDialogue_Effect);
PopulateList(actor_box, AssDialogue_Actor);
return;
}
else if (type & AssFile::COMMIT_STYLES)
style_box->Select(style_box->FindString(to_wx(line->Style)));
if (!(type ^ AssFile::COMMIT_ORDER)) return;
SetControlsState(!!line);
UpdateFields(type, true);
}
void SubsEditBox::UpdateFields(int type, bool repopulate_lists) {
if (!line) return;
if (type & AssFile::COMMIT_DIAG_TIME) {
start_time->SetTime(line->Start);
end_time->SetTime(line->End);
SetDurationField();
}
if (type & AssFile::COMMIT_DIAG_TEXT) {
edit_ctrl->SetTextTo(line->Text);
UpdateCharacterCount(line->Text);
}
if (type & AssFile::COMMIT_DIAG_META) {
layer->SetValue(line->Layer);
for (size_t i = 0; i < margin.size(); ++i)
change_value(margin[i], std::to_wstring(line->Margin[i]));
comment_box->SetValue(line->Comment);
style_box->Select(style_box->FindString(to_wx(line->Style)));
active_style = line ? c->ass->GetStyle(line->Style) : nullptr;
style_edit_button->Enable(active_style != nullptr);
if (repopulate_lists) PopulateList(effect_box, AssDialogue_Effect);
effect_box->ChangeValue(to_wx(line->Effect));
effect_box->SetStringSelection(to_wx(line->Effect));
if (repopulate_lists) PopulateList(actor_box, AssDialogue_Actor);
actor_box->ChangeValue(to_wx(line->Actor));
actor_box->SetStringSelection(to_wx(line->Actor));
}
}
void SubsEditBox::PopulateList(wxComboBox *combo, boost::flyweight<std::string> AssDialogue::*field) {
wxEventBlocker blocker(this);
std::unordered_set<boost::flyweight<std::string>> values;
for (auto const& line : c->ass->Events) {
auto const& value = line.*field;
if (!value.get().empty())
values.insert(value);
}
wxArrayString arrstr;
arrstr.reserve(values.size());
transform(values.begin(), values.end(), std::back_inserter(arrstr),
(wxString (*)(std::string const&))to_wx);
arrstr.Sort();
combo->Freeze();
long pos = combo->GetInsertionPoint();
wxString value = combo->GetValue();
combo->Set(arrstr);
combo->ChangeValue(value);
combo->SetStringSelection(value);
combo->SetInsertionPoint(pos);
combo->Thaw();
}
void SubsEditBox::OnActiveLineChanged(AssDialogue *new_line) {
wxEventBlocker blocker(this);
line = new_line;
commit_id = -1;
UpdateFields(AssFile::COMMIT_DIAG_FULL, false);
}
void SubsEditBox::OnSelectedSetChanged() {
initial_times.clear();
}
void SubsEditBox::OnLineInitialTextChanged(std::string const& new_text) {
if (split_box->IsChecked())
secondary_editor->SetValue(to_wx(new_text));
}
void SubsEditBox::UpdateFrameTiming(agi::vfr::Framerate const& fps) {
if (fps.IsLoaded()) {
by_frame->Enable(true);
}
else {
by_frame->Enable(false);
by_time->SetValue(true);
start_time->SetByFrame(false);
end_time->SetByFrame(false);
duration->SetByFrame(false);
c->subsGrid->SetByFrame(false);
}
}
void SubsEditBox::OnKeyDown(wxKeyEvent &event) {
hotkey::check("Subtitle Edit Box", c, event);
}
void SubsEditBox::OnChange(wxStyledTextEvent &event) {
if (line && edit_ctrl->GetTextRaw().data() != line->Text.get()) {
if (event.GetModificationType() & wxSTC_STARTACTION)
commit_id = -1;
CommitText(_("modify text"));
UpdateCharacterCount(line->Text);
}
}
void SubsEditBox::Commit(wxString const& desc, int type, bool amend, AssDialogue *line) {
file_changed_slot.Block();
commit_id = c->ass->Commit(desc, type, (amend && desc == last_commit_type) ? commit_id : -1, line);
file_changed_slot.Unblock();
last_commit_type = desc;
last_time_commit_type = -1;
initial_times.clear();
undo_timer.Start(30000, wxTIMER_ONE_SHOT);
}
template<class setter>
void SubsEditBox::SetSelectedRows(setter set, wxString const& desc, int type, bool amend) {
auto const& sel = c->selectionController->GetSelectedSet();
for_each(sel.begin(), sel.end(), set);
Commit(desc, type, amend, sel.size() == 1 ? *sel.begin() : nullptr);
}
template<class T>
void SubsEditBox::SetSelectedRows(T AssDialogueBase::*field, T value, wxString const& desc, int type, bool amend) {
SetSelectedRows([&](AssDialogue *d) { d->*field = value; }, desc, type, amend);
}
template<class T>
void SubsEditBox::SetSelectedRows(T AssDialogueBase::*field, wxString const& value, wxString const& desc, int type, bool amend) {
boost::flyweight<std::string> conv_value(from_wx(value));
SetSelectedRows([&](AssDialogue *d) { d->*field = conv_value; }, desc, type, amend);
}
void SubsEditBox::CommitText(wxString const& desc) {
auto data = edit_ctrl->GetTextRaw();
SetSelectedRows(&AssDialogue::Text, boost::flyweight<std::string>(data.data(), data.length()), desc, AssFile::COMMIT_DIAG_TEXT, true);
}
void SubsEditBox::CommitTimes(TimeField field) {
auto const& sel = c->selectionController->GetSelectedSet();
for (AssDialogue *d : sel) {
if (!initial_times.count(d))
initial_times[d] = {d->Start, d->End};
switch (field) {
case TIME_START:
initial_times[d].first = d->Start = start_time->GetTime();
d->End = std::max(d->Start, initial_times[d].second);
break;
case TIME_END:
initial_times[d].second = d->End = end_time->GetTime();
d->Start = std::min(d->End, initial_times[d].first);
break;
case TIME_DURATION:
if (by_frame->GetValue()) {
auto const& fps = c->project->Timecodes();
d->End = fps.TimeAtFrame(fps.FrameAtTime(d->Start, agi::vfr::START) + duration->GetFrame() - 1, agi::vfr::END);
}
else
d->End = d->Start + duration->GetTime();
initial_times[d].second = d->End;
break;
}
}
start_time->SetTime(line->Start);
end_time->SetTime(line->End);
if (field != TIME_DURATION)
SetDurationField();
if (field != last_time_commit_type)
commit_id = -1;
last_time_commit_type = field;
file_changed_slot.Block();
commit_id = c->ass->Commit(_("modify times"), AssFile::COMMIT_DIAG_TIME, commit_id, sel.size() == 1 ? *sel.begin() : nullptr);
file_changed_slot.Unblock();
}
void SubsEditBox::SetDurationField() {
// With VFR, the frame count calculated from the duration in time can be
// completely wrong (since the duration is calculated as if it were a start
// time), so we need to explicitly set it with the correct units.
if (by_frame->GetValue())
duration->SetFrame(end_time->GetFrame() - start_time->GetFrame() + 1);
else
duration->SetTime(end_time->GetTime() - start_time->GetTime());
}
void SubsEditBox::OnSize(wxSizeEvent &evt) {
int availableWidth = GetVirtualSize().GetWidth();
int midMin = middle_left_sizer->GetMinSize().GetWidth();
int botMin = middle_right_sizer->GetMinSize().GetWidth();
if (button_bar_split) {
if (availableWidth > midMin + botMin) {
GetSizer()->Detach(middle_right_sizer);
middle_left_sizer->Add(middle_right_sizer,0,wxALIGN_CENTER_VERTICAL);
button_bar_split = false;
}
}
else {
if (availableWidth < midMin) {
middle_left_sizer->Detach(middle_right_sizer);
GetSizer()->Insert(2,middle_right_sizer,0,wxEXPAND | wxLEFT | wxRIGHT | wxBOTTOM,3);
button_bar_split = true;
}
}
evt.Skip();
}
void SubsEditBox::OnFrameTimeRadio(wxCommandEvent &event) {
event.Skip();
bool byFrame = by_frame->GetValue();
start_time->SetByFrame(byFrame);
end_time->SetByFrame(byFrame);
duration->SetByFrame(byFrame);
c->subsGrid->SetByFrame(byFrame);
SetDurationField();
}
void SubsEditBox::SetControlsState(bool state) {
if (state == controls_enabled) return;
controls_enabled = state;
Enable(state);
if (!state) {
wxEventBlocker blocker(this);
edit_ctrl->SetTextTo("");
}
}
void SubsEditBox::OnSplit(wxCommandEvent&) {
Freeze();
GetSizer()->Show(secondary_editor, split_box->IsChecked());
GetSizer()->Show(bottom_sizer, split_box->IsChecked());
Fit();
SetMinSize(GetSize());
GetParent()->GetSizer()->Layout();
Thaw();
if (split_box->IsChecked())
secondary_editor->SetValue(to_wx(c->initialLineState->GetInitialText()));
}
void SubsEditBox::OnStyleChange(wxCommandEvent &evt) {
SetSelectedRows(&AssDialogue::Style, new_value(style_box, evt), _("style change"), AssFile::COMMIT_DIAG_META);
active_style = c->ass->GetStyle(line->Style);
}
void SubsEditBox::OnActorChange(wxCommandEvent &evt) {
bool amend = evt.GetEventType() == wxEVT_TEXT;
SetSelectedRows(AssDialogue_Actor, new_value(actor_box, evt), _("actor change"), AssFile::COMMIT_DIAG_META, amend);
PopulateList(actor_box, AssDialogue_Actor);
}
void SubsEditBox::OnLayerEnter(wxCommandEvent &evt) {
SetSelectedRows(&AssDialogue::Layer, evt.GetInt(), _("layer change"), AssFile::COMMIT_DIAG_META);
}
void SubsEditBox::OnEffectChange(wxCommandEvent &evt) {
bool amend = evt.GetEventType() == wxEVT_TEXT;
SetSelectedRows(AssDialogue_Effect, new_value(effect_box, evt), _("effect change"), AssFile::COMMIT_DIAG_META, amend);
PopulateList(effect_box, AssDialogue_Effect);
}
void SubsEditBox::OnCommentChange(wxCommandEvent &evt) {
SetSelectedRows(&AssDialogue::Comment, !!evt.GetInt(), _("comment change"), AssFile::COMMIT_DIAG_META);
}
void SubsEditBox::CallCommand(const char *cmd_name) {
cmd::call(cmd_name, c);
edit_ctrl->SetFocus();
}
void SubsEditBox::UpdateCharacterCount(std::string const& text) {
int ignore = agi::IGNORE_BLOCKS;
if (OPT_GET("Subtitle/Character Counter/Ignore Whitespace")->GetBool())
ignore |= agi::IGNORE_WHITESPACE;
if (OPT_GET("Subtitle/Character Counter/Ignore Punctuation")->GetBool())
ignore |= agi::IGNORE_PUNCTUATION;
size_t length = agi::MaxLineLength(text, ignore);
char_count->SetValue(std::to_wstring(length));
size_t limit = (size_t)OPT_GET("Subtitle/Character Limit")->GetInt();
if (limit && length > limit)
char_count->SetBackgroundColour(to_wx(OPT_GET("Colour/Subtitle/Syntax/Background/Error")->GetColor()));
else
char_count->SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW));
}