Convert the subs edit box buttons to commands
This makes they hotkeyable and extracts a large chunk of logic from the giant mess that is SubsEditBox.
This commit is contained in:
parent
44f0fcce07
commit
1b68790c4b
6 changed files with 369 additions and 284 deletions
|
@ -49,10 +49,14 @@
|
|||
#include "../ass_dialogue.h"
|
||||
#include "../ass_file.h"
|
||||
#include "../ass_karaoke.h"
|
||||
#include "../ass_override.h"
|
||||
#include "../ass_style.h"
|
||||
#include "../dialog_colorpicker.h"
|
||||
#include "../dialog_search_replace.h"
|
||||
#include "../include/aegisub/context.h"
|
||||
#include "../subs_edit_ctrl.h"
|
||||
#include "../subs_grid.h"
|
||||
#include "../text_selection_controller.h"
|
||||
#include "../video_context.h"
|
||||
|
||||
namespace {
|
||||
|
@ -74,16 +78,319 @@ struct validate_sel_multiple : public Command {
|
|||
}
|
||||
};
|
||||
|
||||
template<class T>
|
||||
T get_value(AssDialogue const& line, int blockn, T initial, wxString const& tag, wxString alt = wxString()) {
|
||||
for (int i = blockn; i >= 0; i--) {
|
||||
AssDialogueBlockOverride *ovr = dynamic_cast<AssDialogueBlockOverride*>(line.Blocks[i]);
|
||||
if (!ovr) continue;
|
||||
|
||||
for (int j = (int)ovr->Tags.size() - 1; j >= 0; j--) {
|
||||
if (ovr->Tags[j]->Name == tag || ovr->Tags[j]->Name == alt) {
|
||||
return ovr->Tags[j]->Params[0]->Get<T>(initial);
|
||||
}
|
||||
}
|
||||
}
|
||||
return initial;
|
||||
}
|
||||
|
||||
/// Get the block index in the text of the position
|
||||
int block_at_pos(wxString const& text, int pos) {
|
||||
int n = 0;
|
||||
int max = text.size() - 1;
|
||||
for (int i = 0; i <= pos && i <= max; ++i) {
|
||||
if (i > 0 && text[i] == '{')
|
||||
n++;
|
||||
if (text[i] == '}' && i != max && i != pos && i != pos -1 && (i+1 == max || text[i+1] != '{'))
|
||||
n++;
|
||||
}
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
void set_tag(const agi::Context *c, wxString const& tag, wxString const& value, bool at_end = false) {
|
||||
AssDialogue * const line = c->selectionController->GetActiveLine();
|
||||
if (line->Blocks.empty())
|
||||
line->ParseASSTags();
|
||||
|
||||
int sel_start = c->textSelectionController->GetSelectionStart();
|
||||
int sel_end = c->textSelectionController->GetSelectionEnd();
|
||||
int start = at_end ? sel_end : sel_start;
|
||||
int blockn = block_at_pos(line->Text, start);
|
||||
|
||||
AssDialogueBlockPlain *plain = 0;
|
||||
AssDialogueBlockOverride *ovr = 0;
|
||||
while (blockn >= 0) {
|
||||
AssDialogueBlock *block = line->Blocks[blockn];
|
||||
if (dynamic_cast<AssDialogueBlockDrawing*>(block))
|
||||
--blockn;
|
||||
else if ((plain = dynamic_cast<AssDialogueBlockPlain*>(block))) {
|
||||
// Cursor is in a comment block, so try the previous block instead
|
||||
if (plain->GetText().StartsWith("{")) {
|
||||
--blockn;
|
||||
start = line->Text.rfind('{', start);
|
||||
}
|
||||
else
|
||||
break;
|
||||
}
|
||||
else {
|
||||
ovr = dynamic_cast<AssDialogueBlockOverride*>(block);
|
||||
assert(ovr);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// If we didn't hit a suitable block for inserting the override just put
|
||||
// it at the beginning of the line
|
||||
if (blockn < 0)
|
||||
start = 0;
|
||||
|
||||
wxString insert = tag + value;
|
||||
int shift = insert.size();
|
||||
if (plain || blockn < 0) {
|
||||
line->Text = line->Text.Left(start) + "{" + insert + "}" + line->Text.Mid(start);
|
||||
shift += 2;
|
||||
line->ParseASSTags();
|
||||
}
|
||||
else if(ovr) {
|
||||
wxString alt;
|
||||
if (tag == "\\c") alt = "\\1c";
|
||||
// Remove old of same
|
||||
bool found = false;
|
||||
for (size_t i = 0; i < ovr->Tags.size(); i++) {
|
||||
wxString name = ovr->Tags[i]->Name;
|
||||
if (tag == name || alt == name) {
|
||||
shift -= ((wxString)*ovr->Tags[i]).size();
|
||||
if (found) {
|
||||
delete ovr->Tags[i];
|
||||
ovr->Tags.erase(ovr->Tags.begin() + i);
|
||||
i--;
|
||||
}
|
||||
else {
|
||||
ovr->Tags[i]->Params[0]->Set(value);
|
||||
ovr->Tags[i]->Params[0]->omitted = false;
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!found)
|
||||
ovr->AddTag(insert);
|
||||
|
||||
line->UpdateText();
|
||||
}
|
||||
else
|
||||
assert(false);
|
||||
|
||||
if (!at_end)
|
||||
c->textSelectionController->SetSelection(sel_start + shift, sel_end + shift);
|
||||
}
|
||||
|
||||
void set_text(AssDialogue *line, wxString const& value) {
|
||||
line->Text = value;
|
||||
}
|
||||
|
||||
void commit_text(agi::Context const * const c, wxString const& desc, int *commit_id = 0) {
|
||||
SubtitleSelection const& sel = c->selectionController->GetSelectedSet();
|
||||
for_each(sel.begin(), sel.end(),
|
||||
bind(set_text, std::tr1::placeholders::_1, c->selectionController->GetActiveLine()->Text));
|
||||
int new_commit_id = c->ass->Commit(desc, AssFile::COMMIT_DIAG_TEXT, commit_id ? *commit_id : -1, sel.size() == 1 ? *sel.begin() : 0);
|
||||
if (commit_id)
|
||||
*commit_id = new_commit_id;
|
||||
}
|
||||
|
||||
void toggle_override_tag(const agi::Context *c, bool (AssStyle::*field), const char *tag, wxString const& undo_msg) {
|
||||
AssDialogue *const line = c->selectionController->GetActiveLine();
|
||||
AssStyle const* const style = c->ass->GetStyle(line->Style);
|
||||
bool state = style ? style->*field : AssStyle().*field;
|
||||
|
||||
line->ParseASSTags();
|
||||
int sel_start = c->textSelectionController->GetSelectionStart();
|
||||
int sel_end = c->textSelectionController->GetSelectionEnd();
|
||||
int blockn = block_at_pos(line->Text, sel_start);
|
||||
|
||||
state = get_value(*line, blockn, state, tag);
|
||||
|
||||
set_tag(c, tag, state ? "0" : "1");
|
||||
if (sel_start != sel_end)
|
||||
set_tag(c, tag, state ? "1" : "0", true);
|
||||
|
||||
line->ClearBlocks();
|
||||
commit_text(c, undo_msg);
|
||||
}
|
||||
|
||||
void got_color(const agi::Context *c, const char *tag, int *commit_id, wxColour new_color) {
|
||||
if (new_color.Ok()) {
|
||||
set_tag(c, tag, AssColor(new_color).GetASSFormatted(false));
|
||||
commit_text(c, _("set color"), commit_id);
|
||||
}
|
||||
}
|
||||
|
||||
void show_color_picker(const agi::Context *c, AssColor (AssStyle::*field), const char *tag, const char *alt) {
|
||||
AssDialogue *const line = c->selectionController->GetActiveLine();
|
||||
AssStyle const* const style = c->ass->GetStyle(line->Style);
|
||||
wxColor color = (style ? style->*field : AssStyle().*field).GetWXColor();
|
||||
|
||||
line->ParseASSTags();
|
||||
|
||||
int sel_start = c->textSelectionController->GetSelectionStart();
|
||||
int sel_end = c->textSelectionController->GetSelectionEnd();
|
||||
int blockn = block_at_pos(line->Text, sel_start);
|
||||
|
||||
color = get_value(*line, blockn, color, tag, alt);
|
||||
int commit_id = -1;
|
||||
const wxColor newColor = GetColorFromUser(c->parent, color, bind(got_color, c, tag, &commit_id, std::tr1::placeholders::_1));
|
||||
line->ClearBlocks();
|
||||
commit_text(c, _("set color"), &commit_id);
|
||||
|
||||
if (!newColor.IsOk()) {
|
||||
c->ass->Undo();
|
||||
c->textSelectionController->SetSelection(sel_start, sel_end);
|
||||
}
|
||||
}
|
||||
|
||||
struct edit_color_primary : public Command {
|
||||
CMD_NAME("edit/color/primary")
|
||||
STR_MENU("Primary Color...")
|
||||
STR_DISP("Primary Color")
|
||||
STR_HELP("Primary Color")
|
||||
|
||||
void operator()(agi::Context *c) {
|
||||
show_color_picker(c, &AssStyle::primary, "\\c", "\\1c");
|
||||
}
|
||||
};
|
||||
|
||||
struct edit_color_secondary : public Command {
|
||||
CMD_NAME("edit/color/secondary")
|
||||
STR_MENU("Secondary Color...")
|
||||
STR_DISP("Secondary Color")
|
||||
STR_HELP("Secondary Color")
|
||||
|
||||
void operator()(agi::Context *c) {
|
||||
show_color_picker(c, &AssStyle::secondary, "\\c", "\\1c");
|
||||
}
|
||||
};
|
||||
|
||||
struct edit_color_outline : public Command {
|
||||
CMD_NAME("edit/color/outline")
|
||||
STR_MENU("Outline Color...")
|
||||
STR_DISP("Outline Color")
|
||||
STR_HELP("Outline Color")
|
||||
|
||||
void operator()(agi::Context *c) {
|
||||
show_color_picker(c, &AssStyle::outline, "\\c", "\\1c");
|
||||
}
|
||||
};
|
||||
|
||||
struct edit_color_shadow : public Command {
|
||||
CMD_NAME("edit/color/shadow")
|
||||
STR_MENU("Shadow Color...")
|
||||
STR_DISP("Shadow Color")
|
||||
STR_HELP("Shadow Color")
|
||||
|
||||
void operator()(agi::Context *c) {
|
||||
show_color_picker(c, &AssStyle::shadow, "\\c", "\\1c");
|
||||
}
|
||||
};
|
||||
|
||||
struct edit_style_bold : public Command {
|
||||
CMD_NAME("edit/style/bold")
|
||||
STR_MENU("Bold")
|
||||
STR_DISP("Bold")
|
||||
STR_HELP("Bold")
|
||||
|
||||
void operator()(agi::Context *c) {
|
||||
toggle_override_tag(c, &AssStyle::bold, "\\b", _("toggle bold"));
|
||||
}
|
||||
};
|
||||
|
||||
struct edit_style_italic : public Command {
|
||||
CMD_NAME("edit/style/italic")
|
||||
STR_MENU("Italics")
|
||||
STR_DISP("Italics")
|
||||
STR_HELP("Italics")
|
||||
|
||||
void operator()(agi::Context *c) {
|
||||
toggle_override_tag(c, &AssStyle::italic, "\\i", _("toggle italic"));
|
||||
}
|
||||
};
|
||||
|
||||
struct edit_style_underline : public Command {
|
||||
CMD_NAME("edit/style/underline")
|
||||
STR_MENU("Underline")
|
||||
STR_DISP("Underline")
|
||||
STR_HELP("Underline")
|
||||
|
||||
void operator()(agi::Context *c) {
|
||||
toggle_override_tag(c, &AssStyle::underline, "\\u", _("toggle underline"));
|
||||
}
|
||||
};
|
||||
|
||||
struct edit_style_strikeout : public Command {
|
||||
CMD_NAME("edit/style/strikeout")
|
||||
STR_MENU("Strikeout")
|
||||
STR_DISP("Strikeout")
|
||||
STR_HELP("Strikeout")
|
||||
|
||||
void operator()(agi::Context *c) {
|
||||
toggle_override_tag(c, &AssStyle::strikeout, "\\s", _("toggle strikeout"));
|
||||
}
|
||||
};
|
||||
|
||||
struct edit_font : public Command {
|
||||
CMD_NAME("edit/font")
|
||||
STR_MENU("Font Face...")
|
||||
STR_DISP("Font Face")
|
||||
STR_HELP("Font Face")
|
||||
|
||||
void operator()(agi::Context *c) {
|
||||
AssDialogue *const line = c->selectionController->GetActiveLine();
|
||||
line->ParseASSTags();
|
||||
const int blockn = block_at_pos(line->Text, c->textSelectionController->GetInsertionPoint());
|
||||
|
||||
const AssStyle *style = c->ass->GetStyle(line->Style);
|
||||
const AssStyle default_style;
|
||||
if (!style)
|
||||
style = &default_style;
|
||||
|
||||
const wxFont startfont(
|
||||
get_value(*line, blockn, (int)style->fontsize, "\\fs"),
|
||||
wxFONTFAMILY_DEFAULT,
|
||||
get_value(*line, blockn, style->italic, "\\i") ? wxFONTSTYLE_ITALIC : wxFONTSTYLE_NORMAL,
|
||||
get_value(*line, blockn, style->bold, "\\b") ? wxFONTWEIGHT_BOLD : wxFONTWEIGHT_NORMAL,
|
||||
get_value(*line, blockn, style->underline, "\\u"),
|
||||
get_value(*line, blockn, style->font, "\\fn"));
|
||||
|
||||
const wxFont font = wxGetFontFromUser(c->parent, startfont);
|
||||
if (!font.Ok() || font == startfont) {
|
||||
line->ClearBlocks();
|
||||
return;
|
||||
}
|
||||
|
||||
if (font.GetFaceName() != startfont.GetFaceName())
|
||||
set_tag(c, "\\fn", font.GetFaceName());
|
||||
if (font.GetPointSize() != startfont.GetPointSize())
|
||||
set_tag(c, "\\fs", wxString::Format("%d", font.GetPointSize()));
|
||||
if (font.GetWeight() != startfont.GetWeight())
|
||||
set_tag(c, "\\b", wxString::Format("%d", font.GetWeight() == wxFONTWEIGHT_BOLD));
|
||||
if (font.GetStyle() != startfont.GetStyle())
|
||||
set_tag(c, "\\i", wxString::Format("%d", font.GetStyle() == wxFONTSTYLE_ITALIC));
|
||||
if (font.GetUnderlined() != startfont.GetUnderlined())
|
||||
set_tag(c, "\\i", wxString::Format("%d", font.GetUnderlined()));
|
||||
|
||||
line->ClearBlocks();
|
||||
commit_text(c, _("set font"));
|
||||
}
|
||||
};
|
||||
|
||||
/// Find and replace words in subtitles.
|
||||
struct edit_find_replace : public Command {
|
||||
CMD_NAME("edit/find_replace")
|
||||
STR_MENU("Find and R&eplace...")
|
||||
STR_DISP("Find and Replace")
|
||||
STR_HELP("Find and replace words in subtitles")
|
||||
STR_MENU("Find and R&eplace...")
|
||||
STR_DISP("Find and Replace")
|
||||
STR_HELP("Find and replace words in subtitles")
|
||||
|
||||
void operator()(agi::Context *c) {
|
||||
c->videoController->Stop();
|
||||
Search.OpenDialog(true);
|
||||
void operator()(agi::Context *c) {
|
||||
c->videoController->Stop();
|
||||
Search.OpenDialog(true);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -432,6 +739,11 @@ struct edit_undo : public Command {
|
|||
|
||||
namespace cmd {
|
||||
void init_edit() {
|
||||
reg(new edit_color_primary);
|
||||
reg(new edit_color_secondary);
|
||||
reg(new edit_color_outline);
|
||||
reg(new edit_color_shadow);
|
||||
reg(new edit_font);
|
||||
reg(new edit_find_replace);
|
||||
reg(new edit_line_copy);
|
||||
reg(new edit_line_cut);
|
||||
|
@ -445,6 +757,10 @@ namespace cmd {
|
|||
reg(new edit_line_paste_over);
|
||||
reg(new edit_line_recombine);
|
||||
reg(new edit_line_split_by_karaoke);
|
||||
reg(new edit_style_bold);
|
||||
reg(new edit_style_italic);
|
||||
reg(new edit_style_underline);
|
||||
reg(new edit_style_strikeout);
|
||||
reg(new edit_redo);
|
||||
reg(new edit_undo);
|
||||
}
|
||||
|
|
|
@ -99,14 +99,24 @@ INSERT_ICON("audio/play/selection/begin", button_playfirstfiveh)
|
|||
INSERT_ICON("audio/play/selection/end", button_playlastfiveh)
|
||||
INSERT_ICON("audio/play/to_end", button_playtoend)
|
||||
INSERT_ICON("audio/stop", button_stop)
|
||||
INSERT_ICON("edit/color/primary", button_color_one);
|
||||
INSERT_ICON("edit/color/secondary", button_color_two);
|
||||
INSERT_ICON("edit/color/outline", button_color_three);
|
||||
INSERT_ICON("edit/color/shadow", button_color_four);
|
||||
INSERT_ICON("edit/font", button_fontname);
|
||||
INSERT_ICON("edit/line/copy", copy_button)
|
||||
INSERT_ICON("edit/line/cut", cut_button)
|
||||
INSERT_ICON("edit/line/delete", delete_button)
|
||||
INSERT_ICON("edit/line/paste", paste_button)
|
||||
INSERT_ICON("edit/line/swap", arrow_sort)
|
||||
INSERT_ICON("edit/style/bold", button_bold)
|
||||
INSERT_ICON("edit/style/italic", button_italics)
|
||||
INSERT_ICON("edit/style/strikeout", button_strikeout)
|
||||
INSERT_ICON("edit/style/underline", button_underline)
|
||||
INSERT_ICON("edit/redo", redo_button)
|
||||
INSERT_ICON("edit/search_replace", find_replace_menu)
|
||||
INSERT_ICON("edit/undo", undo_button)
|
||||
INSERT_ICON("grid/line/next/create", button_audio_commit)
|
||||
INSERT_ICON("grid/tag/cycle_hiding", toggle_tag_hiding)
|
||||
INSERT_ICON("help/bugs", bugtracker_button)
|
||||
INSERT_ICON("help/contents", contents_button)
|
||||
|
|
|
@ -267,11 +267,10 @@ class DialogColorPicker : public wxDialog {
|
|||
void OnMouse(wxMouseEvent &evt);
|
||||
void OnCaptureLost(wxMouseCaptureLostEvent&);
|
||||
|
||||
ColorCallback callback;
|
||||
void *callbackUserdata;
|
||||
std::tr1::function<void (wxColour)> callback;
|
||||
|
||||
public:
|
||||
DialogColorPicker(wxWindow *parent, wxColour initial_color, ColorCallback callback = NULL, void *userdata = NULL);
|
||||
DialogColorPicker(wxWindow *parent, wxColour initial_color, std::tr1::function<void (wxColour)> callback);
|
||||
~DialogColorPicker();
|
||||
|
||||
void SetColor(wxColour new_color);
|
||||
|
@ -625,15 +624,15 @@ void ColorPickerScreenDropper::DropFromScreenXY(int x, int y)
|
|||
Refresh(false);
|
||||
}
|
||||
|
||||
wxColour GetColorFromUser(wxWindow *parent, wxColour original, ColorCallback callback, void* userdata)
|
||||
wxColour GetColorFromUser(wxWindow* parent, wxColour original, std::tr1::function<void (wxColour)> callback)
|
||||
{
|
||||
DialogColorPicker dialog(parent, original, callback, userdata);
|
||||
DialogColorPicker dialog(parent, original, callback);
|
||||
if (dialog.ShowModal() == wxID_OK)
|
||||
original = dialog.GetColor();
|
||||
else
|
||||
original = wxNullColour;
|
||||
if (callback)
|
||||
callback(userdata, original);
|
||||
|
||||
callback(original);
|
||||
return original;
|
||||
}
|
||||
|
||||
|
@ -650,10 +649,9 @@ static wxBitmap *make_rgb_image(int width, int offset) {
|
|||
return new wxBitmap(img);
|
||||
}
|
||||
|
||||
DialogColorPicker::DialogColorPicker(wxWindow *parent, wxColour initial_color, ColorCallback callback, void* userdata)
|
||||
DialogColorPicker::DialogColorPicker(wxWindow *parent, wxColour initial_color, std::tr1::function<void (wxColour)> callback)
|
||||
: wxDialog(parent, -1, _("Select Color"))
|
||||
, callback(callback)
|
||||
, callbackUserdata(userdata)
|
||||
{
|
||||
memset(rgb_spectrum, 0, sizeof rgb_spectrum);
|
||||
hsl_spectrum = 0;
|
||||
|
@ -1035,7 +1033,7 @@ void DialogColorPicker::UpdateSpectrumDisplay()
|
|||
}
|
||||
preview_box->SetBitmap(tempBmp);
|
||||
|
||||
if (callback) callback(callbackUserdata, cur_color);
|
||||
callback(cur_color);
|
||||
}
|
||||
|
||||
wxBitmap *DialogColorPicker::MakeGBSpectrum()
|
||||
|
|
|
@ -35,25 +35,17 @@
|
|||
///
|
||||
|
||||
#ifndef AGI_PRE
|
||||
#include <tr1/functional>
|
||||
|
||||
#include <wx/colour.h>
|
||||
#endif
|
||||
|
||||
/// Callback function for GetColorFromUser
|
||||
typedef void (*ColorCallback)(void* userdata, wxColor color);
|
||||
|
||||
/// Wrapper used by templated version of GetColorFromUser
|
||||
template<class T, void (T::*method)(wxColor)>
|
||||
void ColorCallbackWrapper(void* obj, wxColor color) {
|
||||
(static_cast<T*>(obj)->*method)(color);
|
||||
}
|
||||
|
||||
/// @brief Get a color from the user via a color picker dialog
|
||||
/// @param parent Parent window
|
||||
/// @param original Initial color to select
|
||||
/// @param callback Function called whenever the selected color changes if not NULL
|
||||
/// @param userdata Passed to callback function
|
||||
/// @return Last selected color when dialog is closed, or wxNullColour if the dialog was cancelled
|
||||
wxColor GetColorFromUser(wxWindow* parent, wxColor original, ColorCallback callback = NULL, void* userdata = NULL);
|
||||
/// @param callback Function called whenever the selected color changes
|
||||
/// @return Last selected color when dialog is closed, or wxNullColour if the dialog was canceled
|
||||
wxColour GetColorFromUser(wxWindow* parent, wxColour original, std::tr1::function<void (wxColour)> callback);
|
||||
|
||||
/// @brief Get a color from the user via a color picker dialog
|
||||
/// @param T Class which the callback method belongs to
|
||||
|
@ -61,8 +53,8 @@ wxColor GetColorFromUser(wxWindow* parent, wxColor original, ColorCallback callb
|
|||
/// @param parent Parent window
|
||||
/// @param original Initial color to select
|
||||
/// @param callbackObj Object to call callback method on. Must be of type T.
|
||||
/// @return Last selected color when dialog is closed, or wxNullColour if the dialog was cancelled
|
||||
template<class T, void (T::*method)(wxColor)>
|
||||
wxColor GetColorFromUser(wxWindow* parent, wxColor original, T* callbackObj) {
|
||||
return GetColorFromUser(parent, original, &ColorCallbackWrapper<T, method>, callbackObj);
|
||||
/// @return Last selected color when dialog is closed, or wxNullColour if the dialog was canceled
|
||||
template<class T, void (T::*method)(wxColour)>
|
||||
wxColour GetColorFromUser(wxWindow* parent, wxColour original, T* callbackObj) {
|
||||
return GetColorFromUser(parent, original, bind(method, callbackObj, std::tr1::placeholders::_1));
|
||||
}
|
||||
|
|
|
@ -54,10 +54,7 @@
|
|||
|
||||
#include "ass_dialogue.h"
|
||||
#include "ass_file.h"
|
||||
#include "ass_override.h"
|
||||
#include "ass_style.h"
|
||||
#include "command/command.h"
|
||||
#include "dialog_colorpicker.h"
|
||||
#include "dialog_search_replace.h"
|
||||
#include "include/aegisub/context.h"
|
||||
#include "include/aegisub/hotkey.h"
|
||||
|
@ -82,41 +79,6 @@ struct field_setter : public std::binary_function<AssDialogue*, T, void> {
|
|||
}
|
||||
};
|
||||
|
||||
/// @brief Get the value of a tag at a specified position in a line
|
||||
/// @param line Line to get the value from
|
||||
/// @param blockn Block number in the line
|
||||
/// @param initial Value from style to use if the tag does not exist
|
||||
/// @param tag Tag to get the value of
|
||||
/// @param alt Alternate name of the tag, if any
|
||||
template<class T>
|
||||
static T get_value(AssDialogue const& line, int blockn, T initial, wxString tag, wxString alt = wxString()) {
|
||||
for (int i = blockn; i >= 0; i--) {
|
||||
AssDialogueBlockOverride *ovr = dynamic_cast<AssDialogueBlockOverride*>(line.Blocks[i]);
|
||||
if (!ovr) continue;
|
||||
|
||||
for (int j = (int)ovr->Tags.size() - 1; j >= 0; j--) {
|
||||
if (ovr->Tags[j]->Name == tag || ovr->Tags[j]->Name == alt) {
|
||||
return ovr->Tags[j]->Params[0]->Get<T>(initial);
|
||||
}
|
||||
}
|
||||
}
|
||||
return initial;
|
||||
}
|
||||
|
||||
/// Get the block index in the text of the position
|
||||
int block_at_pos(wxString const& text, int pos) {
|
||||
int n = 0;
|
||||
int max = text.size() - 1;
|
||||
for (int i = 0; i <= pos && i <= max; ++i) {
|
||||
if (i > 0 && text[i] == '{')
|
||||
n++;
|
||||
if (text[i] == '}' && i != max && i != pos && i != pos -1 && (i+1 == max || text[i+1] != '{'))
|
||||
n++;
|
||||
}
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
/// Work around wxGTK's fondness for generating events from ChangeValue
|
||||
void change_value(wxTextCtrl *ctrl, wxString const& value) {
|
||||
if (value != ctrl->GetValue())
|
||||
|
@ -189,18 +151,18 @@ SubsEditBox::SubsEditBox(wxWindow *parent, agi::Context *context)
|
|||
|
||||
// Middle-bottom controls
|
||||
MiddleBotSizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
MakeButton(GETIMAGE(button_bold_16), _("Bold"), bind(&SubsEditBox::OnFlagButton, this, &AssStyle::bold, "\\b", _("toggle bold")));
|
||||
MakeButton(GETIMAGE(button_italics_16), _("Italics"), bind(&SubsEditBox::OnFlagButton, this, &AssStyle::italic, "\\i", _("toggle italic")));
|
||||
MakeButton(GETIMAGE(button_underline_16), _("Underline"), bind(&SubsEditBox::OnFlagButton, this, &AssStyle::underline, "\\u", _("toggle underline")));
|
||||
MakeButton(GETIMAGE(button_strikeout_16), _("Strikeout"), bind(&SubsEditBox::OnFlagButton, this, &AssStyle::strikeout, "\\s", _("toggle strikeout")));
|
||||
MakeButton(GETIMAGE(button_fontname_16), _("Font Face"), bind(&SubsEditBox::OnFontButton, this));
|
||||
MakeButton("edit/style/bold");
|
||||
MakeButton("edit/style/italic");
|
||||
MakeButton("edit/style/underline");
|
||||
MakeButton("edit/style/strikeout");
|
||||
MakeButton("edit/font");
|
||||
MiddleBotSizer->AddSpacer(5);
|
||||
MakeButton(GETIMAGE(button_color_one_16), _("Primary color"), bind(&SubsEditBox::OnColorButton, this, &AssStyle::primary, "\\c", "\\1c"));
|
||||
MakeButton(GETIMAGE(button_color_two_16), _("Secondary color"), bind(&SubsEditBox::OnColorButton, this, &AssStyle::secondary, "\\2c", ""));
|
||||
MakeButton(GETIMAGE(button_color_three_16), _("Outline color"), bind(&SubsEditBox::OnColorButton, this, &AssStyle::outline, "\\3c", ""));
|
||||
MakeButton(GETIMAGE(button_color_four_16), _("Shadow color"), bind(&SubsEditBox::OnColorButton, this, &AssStyle::shadow, "\\4c", ""));
|
||||
MakeButton("edit/color/primary");
|
||||
MakeButton("edit/color/secondary");
|
||||
MakeButton("edit/color/outline");
|
||||
MakeButton("edit/color/shadow");
|
||||
MiddleBotSizer->AddSpacer(5);
|
||||
MakeButton(GETIMAGE(button_audio_commit_16), _("Commits the text (Enter)"), bind(&cmd::call, "grid/line/next/create", c));
|
||||
MakeButton("grid/line/next/create");
|
||||
MiddleBotSizer->AddSpacer(10);
|
||||
|
||||
ByTime = MakeRadio(_("T&ime"), true, _("Time by h:mm:ss.cs"));
|
||||
|
@ -227,7 +189,7 @@ SubsEditBox::SubsEditBox(wxWindow *parent, agi::Context *context)
|
|||
TextEdit->SetModEventMask(wxSTC_MOD_INSERTTEXT | wxSTC_MOD_DELETETEXT | wxSTC_STARTACTION);
|
||||
|
||||
Bind(wxEVT_COMMAND_TEXT_UPDATED, &SubsEditBox::OnLayerEnter, this, Layer->GetId());
|
||||
Bind(wxEVT_COMMAND_SPINCTRL_UPDATED, &SubsEditBox::OnLayerChange, this, Layer->GetId());
|
||||
Bind(wxEVT_COMMAND_SPINCTRL_UPDATED, &SubsEditBox::OnLayerEnter, this, Layer->GetId());
|
||||
Bind(wxEVT_COMMAND_CHECKBOX_CLICKED, &SubsEditBox::OnCommentChange, this, CommentBox->GetId());
|
||||
|
||||
Bind(wxEVT_SIZE, &SubsEditBox::OnSize, this);
|
||||
|
@ -267,13 +229,13 @@ TimeEdit *SubsEditBox::MakeTimeCtrl(bool end, wxString const& tooltip, void (Sub
|
|||
return ctrl;
|
||||
}
|
||||
|
||||
template<class Handler>
|
||||
void SubsEditBox::MakeButton(wxBitmap const& icon, wxString const& tooltip, Handler const& handler) {
|
||||
wxBitmapButton *btn = new wxBitmapButton(this, -1, icon);
|
||||
btn->SetToolTip(tooltip);
|
||||
void SubsEditBox::MakeButton(const char *cmd_name) {
|
||||
cmd::Command *command = cmd::get(cmd_name);
|
||||
wxBitmapButton *btn = new wxBitmapButton(this, -1, command->Icon(16));
|
||||
btn->SetToolTip(command->StrHelp());
|
||||
|
||||
MiddleBotSizer->Add(btn, wxSizerFlags().Center().Expand());
|
||||
Bind(wxEVT_COMMAND_BUTTON_CLICKED, handler, btn->GetId());
|
||||
btn->Bind(wxEVT_COMMAND_BUTTON_CLICKED, std::tr1::bind(cmd::call, cmd_name, c));
|
||||
}
|
||||
|
||||
wxComboBox *SubsEditBox::MakeComboBox(wxString const& initial_text, int style, void (SubsEditBox::*handler)(wxCommandEvent&), wxString const& tooltip) {
|
||||
|
@ -445,7 +407,7 @@ void SubsEditBox::SetSelectedRows(T AssDialogue::*field, T value, wxString desc,
|
|||
SetSelectedRows(field_setter<T>(field), value, desc, type, amend);
|
||||
}
|
||||
|
||||
void SubsEditBox::CommitText(wxString desc) {
|
||||
void SubsEditBox::CommitText(wxString const& desc) {
|
||||
SetSelectedRows(&AssDialogue::Text, TextEdit->GetText(), desc, AssFile::COMMIT_DIAG_TEXT, true);
|
||||
}
|
||||
|
||||
|
@ -537,7 +499,6 @@ void SubsEditBox::SetControlsState(bool state) {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
void SubsEditBox::OnStyleChange(wxCommandEvent &) {
|
||||
SetSelectedRows(&AssDialogue::Style, StyleBox->GetValue(), _("style change"), AssFile::COMMIT_DIAG_META);
|
||||
}
|
||||
|
@ -548,10 +509,6 @@ void SubsEditBox::OnActorChange(wxCommandEvent &evt) {
|
|||
PopulateActorList();
|
||||
}
|
||||
|
||||
void SubsEditBox::OnLayerChange(wxSpinEvent &event) {
|
||||
OnLayerEnter(event);
|
||||
}
|
||||
|
||||
void SubsEditBox::OnLayerEnter(wxCommandEvent &) {
|
||||
SetSelectedRows(&AssDialogue::Layer, Layer->GetValue(), _("layer change"), AssFile::COMMIT_DIAG_META);
|
||||
}
|
||||
|
@ -567,6 +524,7 @@ void SubsEditBox::OnEndTimeChange(wxCommandEvent &) {
|
|||
void SubsEditBox::OnDurationChange(wxCommandEvent &) {
|
||||
CommitTimes(TIME_DURATION);
|
||||
}
|
||||
|
||||
void SubsEditBox::OnMarginLChange(wxCommandEvent &) {
|
||||
SetSelectedRows(std::mem_fun(&AssDialogue::SetMarginString<0>), MarginL->GetValue(), _("MarginL change"), AssFile::COMMIT_DIAG_META);
|
||||
if (line) change_value(MarginL, line->GetMarginString(0, false));
|
||||
|
@ -594,171 +552,3 @@ void SubsEditBox::OnEffectChange(wxCommandEvent &) {
|
|||
void SubsEditBox::OnCommentChange(wxCommandEvent &) {
|
||||
SetSelectedRows(&AssDialogue::Comment, CommentBox->GetValue(), _("comment change"), AssFile::COMMIT_DIAG_META);
|
||||
}
|
||||
|
||||
void SubsEditBox::SetTag(wxString tag, wxString value, bool atEnd) {
|
||||
assert(line);
|
||||
if (line->Blocks.empty())
|
||||
line->ParseASSTags();
|
||||
|
||||
int sel_start = c->textSelectionController->GetSelectionStart();
|
||||
int sel_end = c->textSelectionController->GetSelectionEnd();
|
||||
int start = atEnd ? sel_end : sel_start;
|
||||
int blockn = block_at_pos(line->Text, start);
|
||||
|
||||
AssDialogueBlockPlain *plain = 0;
|
||||
AssDialogueBlockOverride *ovr = 0;
|
||||
while (blockn >= 0) {
|
||||
AssDialogueBlock *block = line->Blocks[blockn];
|
||||
if (dynamic_cast<AssDialogueBlockDrawing*>(block))
|
||||
--blockn;
|
||||
else if ((plain = dynamic_cast<AssDialogueBlockPlain*>(block))) {
|
||||
// Cursor is in a comment block, so try the previous block instead
|
||||
if (plain->GetText().StartsWith("{")) {
|
||||
--blockn;
|
||||
start = line->Text.rfind('{', start);
|
||||
}
|
||||
else
|
||||
break;
|
||||
}
|
||||
else {
|
||||
ovr = dynamic_cast<AssDialogueBlockOverride*>(block);
|
||||
assert(ovr);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// If we didn't hit a suitable block for inserting the override just put
|
||||
// it at the beginning of the line
|
||||
if (blockn < 0)
|
||||
start = 0;
|
||||
|
||||
wxString insert = tag + value;
|
||||
int shift = insert.size();
|
||||
if (plain || blockn < 0) {
|
||||
line->Text = line->Text.Left(start) + "{" + insert + "}" + line->Text.Mid(start);
|
||||
shift += 2;
|
||||
line->ParseASSTags();
|
||||
}
|
||||
else if(ovr) {
|
||||
wxString alt;
|
||||
if (tag == "\\c") alt = "\\1c";
|
||||
// Remove old of same
|
||||
bool found = false;
|
||||
for (size_t i = 0; i < ovr->Tags.size(); i++) {
|
||||
wxString name = ovr->Tags[i]->Name;
|
||||
if (tag == name || alt == name) {
|
||||
shift -= ((wxString)*ovr->Tags[i]).size();
|
||||
if (found) {
|
||||
delete ovr->Tags[i];
|
||||
ovr->Tags.erase(ovr->Tags.begin() + i);
|
||||
i--;
|
||||
}
|
||||
else {
|
||||
ovr->Tags[i]->Params[0]->Set(value);
|
||||
ovr->Tags[i]->Params[0]->omitted = false;
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
ovr->AddTag(insert);
|
||||
}
|
||||
|
||||
line->UpdateText();
|
||||
}
|
||||
else
|
||||
assert(false);
|
||||
|
||||
TextEdit->SetTextTo(line->Text);
|
||||
if (!atEnd) c->textSelectionController->SetSelection(sel_start + shift, sel_end + shift);
|
||||
TextEdit->SetFocus();
|
||||
}
|
||||
|
||||
void SubsEditBox::OnFlagButton(bool (AssStyle::*field), const char *tag, wxString const& undo_msg) {
|
||||
AssStyle *style = c->ass->GetStyle(line->Style);
|
||||
bool state = style ? style->*field : AssStyle().*field;
|
||||
|
||||
line->ParseASSTags();
|
||||
int sel_start = c->textSelectionController->GetSelectionStart();
|
||||
int sel_end = c->textSelectionController->GetSelectionEnd();
|
||||
int blockn = block_at_pos(line->Text, sel_start);
|
||||
|
||||
state = get_value(*line, blockn, state, tag);
|
||||
|
||||
SetTag(tag, state ? "0" : "1");
|
||||
if (sel_start != sel_end)
|
||||
SetTag(tag, state ? "1" : "0", true);
|
||||
|
||||
line->ClearBlocks();
|
||||
commitId = -1;
|
||||
CommitText(undo_msg);
|
||||
}
|
||||
|
||||
void SubsEditBox::OnFontButton() {
|
||||
line->ParseASSTags();
|
||||
int blockn = block_at_pos(line->Text, c->textSelectionController->GetInsertionPoint());
|
||||
|
||||
AssStyle *style = c->ass->GetStyle(line->Style);
|
||||
AssStyle defStyle;
|
||||
if (!style) style = &defStyle;
|
||||
|
||||
wxFont startfont(
|
||||
get_value(*line, blockn, (int)style->fontsize, "\\fs"),
|
||||
wxFONTFAMILY_DEFAULT,
|
||||
get_value(*line, blockn, style->italic, "\\i") ? wxFONTSTYLE_ITALIC : wxFONTSTYLE_NORMAL,
|
||||
get_value(*line, blockn, style->bold, "\\b") ? wxFONTWEIGHT_BOLD : wxFONTWEIGHT_NORMAL,
|
||||
get_value(*line, blockn, style->underline, "\\u"),
|
||||
get_value(*line, blockn, style->font, "\\fn"));
|
||||
|
||||
wxFont font = wxGetFontFromUser(this, startfont);
|
||||
if (!font.Ok() || font == startfont) {
|
||||
line->ClearBlocks();
|
||||
return;
|
||||
}
|
||||
|
||||
if (font.GetFaceName() != startfont.GetFaceName())
|
||||
SetTag("\\fn", font.GetFaceName());
|
||||
if (font.GetPointSize() != startfont.GetPointSize())
|
||||
SetTag("\\fs", wxString::Format("%d", font.GetPointSize()));
|
||||
if (font.GetWeight() != startfont.GetWeight())
|
||||
SetTag("\\b", wxString::Format("%d", font.GetWeight() == wxFONTWEIGHT_BOLD));
|
||||
if (font.GetStyle() != startfont.GetStyle())
|
||||
SetTag("\\i", wxString::Format("%d", font.GetStyle() == wxFONTSTYLE_ITALIC));
|
||||
if (font.GetUnderlined() != startfont.GetUnderlined())
|
||||
SetTag("\\i", wxString::Format("%d", font.GetUnderlined()));
|
||||
|
||||
line->ClearBlocks();
|
||||
commitId = -1;
|
||||
CommitText(_("set font"));
|
||||
}
|
||||
|
||||
void SubsEditBox::OnColorButton(AssColor (AssStyle::*field), const char *tag, const char *alt) {
|
||||
AssStyle *style = c->ass->GetStyle(line->Style);
|
||||
wxColor color = (style ? style->*field : AssStyle().*field).GetWXColor();
|
||||
|
||||
colorTag = tag;
|
||||
commitId = -1;
|
||||
|
||||
line->ParseASSTags();
|
||||
|
||||
int sel_start = c->textSelectionController->GetSelectionStart();
|
||||
int sel_end = c->textSelectionController->GetSelectionEnd();
|
||||
int blockn = block_at_pos(line->Text, sel_start);
|
||||
|
||||
color = get_value(*line, blockn, color, colorTag, alt);
|
||||
wxColor newColor = GetColorFromUser<SubsEditBox, &SubsEditBox::SetColorCallback>(c->parent, color, this);
|
||||
line->ClearBlocks();
|
||||
CommitText(_("set color"));
|
||||
|
||||
if (!newColor.IsOk()) {
|
||||
c->ass->Undo();
|
||||
c->textSelectionController->SetSelection(sel_start, sel_end);
|
||||
}
|
||||
}
|
||||
|
||||
void SubsEditBox::SetColorCallback(wxColor newColor) {
|
||||
if (newColor.Ok()) {
|
||||
SetTag(colorTag, AssColor(newColor).GetASSFormatted(false));
|
||||
CommitText(_("set color"));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -50,9 +50,7 @@
|
|||
|
||||
namespace agi { namespace vfr { class Framerate; } }
|
||||
namespace agi { struct Context; }
|
||||
struct AssColor;
|
||||
class AssDialogue;
|
||||
class AssStyle;
|
||||
class AssTime;
|
||||
class SubsTextEditCtrl;
|
||||
class TextSelectionController;
|
||||
|
@ -123,7 +121,7 @@ class SubsEditBox : public wxPanel {
|
|||
void CommitTimes(TimeField field);
|
||||
/// @brief Commits the current edit box contents
|
||||
/// @param desc Undo description to use
|
||||
void CommitText(wxString desc);
|
||||
void CommitText(wxString const& desc);
|
||||
|
||||
/// Last commit ID for undo coalescing
|
||||
int commitId;
|
||||
|
@ -145,8 +143,7 @@ class SubsEditBox : public wxPanel {
|
|||
// Constructor helpers
|
||||
wxTextCtrl *MakeMarginCtrl(wxString const& tooltip, void (SubsEditBox::*handler)(wxCommandEvent&));
|
||||
TimeEdit *MakeTimeCtrl(bool end, wxString const& tooltip, void (SubsEditBox::*handler)(wxCommandEvent&));
|
||||
template<class Handler>
|
||||
void MakeButton(wxBitmap const& icon, wxString const& tooltip, Handler const& handler);
|
||||
void MakeButton(const char *cmd_name);
|
||||
wxComboBox *MakeComboBox(wxString const& initial_text, int style, void (SubsEditBox::*handler)(wxCommandEvent&), wxString const& tooltip);
|
||||
wxRadioButton *MakeRadio(wxString const& text, bool start, wxString const& tooltip);
|
||||
|
||||
|
@ -160,7 +157,6 @@ class SubsEditBox : public wxPanel {
|
|||
void OnStyleChange(wxCommandEvent &event);
|
||||
void OnActorChange(wxCommandEvent &event);
|
||||
void OnLayerEnter(wxCommandEvent &event);
|
||||
void OnLayerChange(wxSpinEvent &event);
|
||||
void OnStartTimeChange(wxCommandEvent &);
|
||||
void OnEndTimeChange(wxCommandEvent &);
|
||||
void OnDurationChange(wxCommandEvent &);
|
||||
|
@ -172,25 +168,8 @@ class SubsEditBox : public wxPanel {
|
|||
void OnSize(wxSizeEvent &event);
|
||||
void OnUndoTimer(wxTimerEvent&);
|
||||
|
||||
void OnFlagButton(bool (AssStyle::*field), const char *tag, wxString const& undo_msg);
|
||||
void OnColorButton(AssColor (AssStyle::*field), const char *tag, const char *alt);
|
||||
void OnFontButton();
|
||||
|
||||
void SetPlaceholderCtrl(wxControl *ctrl, wxString const& value);
|
||||
|
||||
/// @brief Set the value of a tag for the currently selected text
|
||||
/// @param tag Tag to set
|
||||
/// @param value New value of tag
|
||||
/// @param atEnd Set the value at the end of the selection rather than beginning
|
||||
void SetTag(wxString tag, wxString value, bool atEnd = false);
|
||||
|
||||
/// @brief Callback function for the color picker
|
||||
/// @param newColor New color selected in the picker
|
||||
void SetColorCallback(wxColor newColor);
|
||||
|
||||
/// Which color is currently being set
|
||||
wxString colorTag;
|
||||
|
||||
/// @brief Set a field in each selected line to a specified value
|
||||
/// @param set Callable which does the setting
|
||||
/// @param value Value to pass to set
|
||||
|
|
Loading…
Reference in a new issue