2006-12-24 22:52:54 +01:00
|
|
|
// Copyright (c) 2005, Rodrigo Braz Monteiro
|
|
|
|
// 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.
|
|
|
|
//
|
2009-07-29 07:43:02 +02:00
|
|
|
// Aegisub Project http://www.aegisub.org/
|
|
|
|
|
2012-01-11 20:19:30 +01:00
|
|
|
#include "subs_edit_ctrl.h"
|
|
|
|
|
2009-09-10 15:06:40 +02:00
|
|
|
#include "ass_dialogue.h"
|
2012-10-30 20:32:47 +01:00
|
|
|
#include "command/command.h"
|
2010-05-21 03:13:36 +02:00
|
|
|
#include "compat.h"
|
2014-05-29 17:28:37 +02:00
|
|
|
#include "format.h"
|
2013-01-07 02:50:09 +01:00
|
|
|
#include "options.h"
|
2012-01-11 20:19:30 +01:00
|
|
|
#include "include/aegisub/context.h"
|
2010-08-02 08:31:38 +02:00
|
|
|
#include "include/aegisub/spellchecker.h"
|
2010-12-08 04:36:10 +01:00
|
|
|
#include "selection_controller.h"
|
2014-07-02 05:10:24 +02:00
|
|
|
#include "text_selection_controller.h"
|
2010-07-20 05:11:11 +02:00
|
|
|
#include "thesaurus.h"
|
2006-12-25 06:43:00 +01:00
|
|
|
#include "utils.h"
|
2006-12-24 22:52:54 +01:00
|
|
|
|
2012-10-30 01:33:16 +01:00
|
|
|
#include <libaegisub/ass/dialogue_parser.h>
|
2012-11-01 03:49:29 +01:00
|
|
|
#include <libaegisub/calltip_provider.h>
|
2014-04-25 17:00:03 +02:00
|
|
|
#include <libaegisub/character_count.h>
|
2014-04-23 22:53:24 +02:00
|
|
|
#include <libaegisub/make_unique.h>
|
2014-04-25 17:00:03 +02:00
|
|
|
#include <libaegisub/spellchecker.h>
|
2012-10-30 16:59:47 +01:00
|
|
|
|
2013-01-04 16:01:50 +01:00
|
|
|
#include <boost/algorithm/string/predicate.hpp>
|
|
|
|
#include <boost/algorithm/string/replace.hpp>
|
|
|
|
#include <functional>
|
|
|
|
|
|
|
|
#include <wx/clipbrd.h>
|
|
|
|
#include <wx/intl.h>
|
|
|
|
#include <wx/menu.h>
|
|
|
|
#include <wx/settings.h>
|
|
|
|
|
2010-07-20 05:11:11 +02:00
|
|
|
/// Event ids
|
|
|
|
enum {
|
|
|
|
EDIT_MENU_SPLIT_PRESERVE = 1400,
|
|
|
|
EDIT_MENU_SPLIT_ESTIMATE,
|
2013-11-18 02:21:23 +01:00
|
|
|
EDIT_MENU_SPLIT_VIDEO,
|
2010-07-20 05:11:11 +02:00
|
|
|
EDIT_MENU_CUT,
|
|
|
|
EDIT_MENU_COPY,
|
|
|
|
EDIT_MENU_PASTE,
|
|
|
|
EDIT_MENU_SELECT_ALL,
|
|
|
|
EDIT_MENU_ADD_TO_DICT,
|
2012-12-22 01:40:51 +01:00
|
|
|
EDIT_MENU_REMOVE_FROM_DICT,
|
2010-07-20 05:11:11 +02:00
|
|
|
EDIT_MENU_SUGGESTION,
|
|
|
|
EDIT_MENU_SUGGESTIONS,
|
|
|
|
EDIT_MENU_THESAURUS = 1450,
|
|
|
|
EDIT_MENU_THESAURUS_SUGS,
|
|
|
|
EDIT_MENU_DIC_LANGUAGE = 1600,
|
|
|
|
EDIT_MENU_DIC_LANGS,
|
|
|
|
EDIT_MENU_THES_LANGUAGE = 1700,
|
|
|
|
EDIT_MENU_THES_LANGS
|
|
|
|
};
|
|
|
|
|
2012-01-11 20:19:30 +01:00
|
|
|
SubsTextEditCtrl::SubsTextEditCtrl(wxWindow* parent, wxSize wsize, long style, agi::Context *context)
|
2014-05-30 21:39:39 +02:00
|
|
|
: wxStyledTextCtrl(parent, -1, wxDefaultPosition, wsize, style)
|
2010-08-02 08:31:38 +02:00
|
|
|
, spellchecker(SpellCheckerFactory::GetSpellChecker())
|
2014-04-23 22:53:24 +02:00
|
|
|
, thesaurus(agi::make_unique<Thesaurus>())
|
2012-01-11 20:19:30 +01:00
|
|
|
, context(context)
|
2006-12-24 22:52:54 +01:00
|
|
|
{
|
|
|
|
// Set properties
|
2007-04-22 01:16:38 +02:00
|
|
|
SetWrapMode(wxSTC_WRAP_WORD);
|
2006-12-24 22:52:54 +01:00
|
|
|
SetMarginWidth(1,0);
|
2006-12-25 22:56:56 +01:00
|
|
|
UsePopUp(false);
|
2007-01-03 22:49:13 +01:00
|
|
|
SetStyles();
|
2006-12-25 04:42:44 +01:00
|
|
|
|
|
|
|
// Set hotkeys
|
2007-04-22 01:16:38 +02:00
|
|
|
CmdKeyClear(wxSTC_KEY_RETURN,wxSTC_SCMOD_CTRL);
|
2012-11-06 00:31:11 +01:00
|
|
|
CmdKeyClear(wxSTC_KEY_RETURN,wxSTC_SCMOD_SHIFT);
|
2007-04-22 01:16:38 +02:00
|
|
|
CmdKeyClear(wxSTC_KEY_RETURN,wxSTC_SCMOD_NORM);
|
|
|
|
CmdKeyClear(wxSTC_KEY_TAB,wxSTC_SCMOD_NORM);
|
|
|
|
CmdKeyClear(wxSTC_KEY_TAB,wxSTC_SCMOD_SHIFT);
|
|
|
|
CmdKeyClear('D',wxSTC_SCMOD_CTRL);
|
|
|
|
CmdKeyClear('L',wxSTC_SCMOD_CTRL);
|
|
|
|
CmdKeyClear('L',wxSTC_SCMOD_CTRL | wxSTC_SCMOD_SHIFT);
|
|
|
|
CmdKeyClear('T',wxSTC_SCMOD_CTRL);
|
|
|
|
CmdKeyClear('T',wxSTC_SCMOD_CTRL | wxSTC_SCMOD_SHIFT);
|
|
|
|
CmdKeyClear('U',wxSTC_SCMOD_CTRL);
|
2006-12-24 22:52:54 +01:00
|
|
|
|
2012-09-25 01:35:27 +02:00
|
|
|
using std::bind;
|
2010-07-20 05:11:11 +02:00
|
|
|
|
2012-05-28 16:18:07 +02:00
|
|
|
Bind(wxEVT_CHAR_HOOK, &SubsTextEditCtrl::OnKeyDown, this);
|
|
|
|
|
2013-12-12 03:25:13 +01:00
|
|
|
Bind(wxEVT_MENU, bind(&SubsTextEditCtrl::Cut, this), EDIT_MENU_CUT);
|
|
|
|
Bind(wxEVT_MENU, bind(&SubsTextEditCtrl::Copy, this), EDIT_MENU_COPY);
|
|
|
|
Bind(wxEVT_MENU, bind(&SubsTextEditCtrl::Paste, this), EDIT_MENU_PASTE);
|
|
|
|
Bind(wxEVT_MENU, bind(&SubsTextEditCtrl::SelectAll, this), EDIT_MENU_SELECT_ALL);
|
2012-03-13 00:34:45 +01:00
|
|
|
|
|
|
|
if (context) {
|
2013-12-12 03:25:13 +01:00
|
|
|
Bind(wxEVT_MENU, bind(&cmd::call, "edit/line/split/preserve", context), EDIT_MENU_SPLIT_PRESERVE);
|
|
|
|
Bind(wxEVT_MENU, bind(&cmd::call, "edit/line/split/estimate", context), EDIT_MENU_SPLIT_ESTIMATE);
|
|
|
|
Bind(wxEVT_MENU, bind(&cmd::call, "edit/line/split/video", context), EDIT_MENU_SPLIT_VIDEO);
|
2012-03-13 00:34:45 +01:00
|
|
|
}
|
2010-07-20 05:11:11 +02:00
|
|
|
|
2011-08-31 06:17:37 +02:00
|
|
|
Bind(wxEVT_CONTEXT_MENU, &SubsTextEditCtrl::OnContextMenu, this);
|
2012-11-06 15:33:44 +01:00
|
|
|
Bind(wxEVT_IDLE, std::bind(&SubsTextEditCtrl::UpdateCallTip, this));
|
2014-04-23 02:01:22 +02:00
|
|
|
Bind(wxEVT_STC_DOUBLECLICK, &SubsTextEditCtrl::OnDoubleClick, this);
|
2012-12-22 01:48:24 +01:00
|
|
|
Bind(wxEVT_STC_STYLENEEDED, [=](wxStyledTextEvent&) {
|
|
|
|
{
|
|
|
|
std::string text = GetTextRaw().data();
|
|
|
|
if (text == line_text) return;
|
|
|
|
line_text = move(text);
|
|
|
|
}
|
|
|
|
|
|
|
|
UpdateStyle();
|
|
|
|
});
|
2011-08-31 06:17:37 +02:00
|
|
|
|
2010-12-07 20:09:15 +01:00
|
|
|
OPT_SUB("Subtitle/Edit Box/Font Face", &SubsTextEditCtrl::SetStyles, this);
|
|
|
|
OPT_SUB("Subtitle/Edit Box/Font Size", &SubsTextEditCtrl::SetStyles, this);
|
2011-09-28 21:44:34 +02:00
|
|
|
Subscribe("Normal");
|
|
|
|
Subscribe("Comment");
|
|
|
|
Subscribe("Drawing");
|
|
|
|
Subscribe("Brackets");
|
|
|
|
Subscribe("Slashes");
|
|
|
|
Subscribe("Tags");
|
|
|
|
Subscribe("Error");
|
|
|
|
Subscribe("Parameters");
|
|
|
|
Subscribe("Line Break");
|
|
|
|
Subscribe("Karaoke Template");
|
|
|
|
Subscribe("Karaoke Variable");
|
|
|
|
|
2014-06-27 20:03:07 +02:00
|
|
|
OPT_SUB("Colour/Subtitle/Background", &SubsTextEditCtrl::SetStyles, this);
|
2011-09-28 21:44:34 +02:00
|
|
|
OPT_SUB("Subtitle/Highlight/Syntax", &SubsTextEditCtrl::UpdateStyle, this);
|
2012-11-06 15:33:44 +01:00
|
|
|
OPT_SUB("App/Call Tips", &SubsTextEditCtrl::UpdateCallTip, this);
|
2012-12-22 01:40:51 +01:00
|
|
|
|
2013-12-12 03:25:13 +01:00
|
|
|
Bind(wxEVT_MENU, [=](wxCommandEvent&) {
|
2012-12-22 01:40:51 +01:00
|
|
|
if (spellchecker) spellchecker->AddWord(currentWord);
|
|
|
|
UpdateStyle();
|
|
|
|
SetFocus();
|
|
|
|
}, EDIT_MENU_ADD_TO_DICT);
|
|
|
|
|
2013-12-12 03:25:13 +01:00
|
|
|
Bind(wxEVT_MENU, [=](wxCommandEvent&) {
|
2012-12-22 01:40:51 +01:00
|
|
|
if (spellchecker) spellchecker->RemoveWord(currentWord);
|
|
|
|
UpdateStyle();
|
|
|
|
SetFocus();
|
|
|
|
}, EDIT_MENU_REMOVE_FROM_DICT);
|
2006-12-24 22:52:54 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
SubsTextEditCtrl::~SubsTextEditCtrl() {
|
|
|
|
}
|
|
|
|
|
2011-09-28 21:44:34 +02:00
|
|
|
void SubsTextEditCtrl::Subscribe(std::string const& name) {
|
|
|
|
OPT_SUB("Colour/Subtitle/Syntax/" + name, &SubsTextEditCtrl::SetStyles, this);
|
|
|
|
OPT_SUB("Colour/Subtitle/Syntax/Background/" + name, &SubsTextEditCtrl::SetStyles, this);
|
|
|
|
OPT_SUB("Colour/Subtitle/Syntax/Bold/" + name, &SubsTextEditCtrl::SetStyles, this);
|
|
|
|
}
|
|
|
|
|
2007-04-22 01:16:38 +02:00
|
|
|
BEGIN_EVENT_TABLE(SubsTextEditCtrl,wxStyledTextCtrl)
|
2007-01-14 00:34:04 +01:00
|
|
|
EVT_KILL_FOCUS(SubsTextEditCtrl::OnLoseFocus)
|
2007-01-02 06:00:55 +01:00
|
|
|
|
|
|
|
EVT_MENU_RANGE(EDIT_MENU_SUGGESTIONS,EDIT_MENU_THESAURUS-1,SubsTextEditCtrl::OnUseSuggestion)
|
2010-07-20 05:11:11 +02:00
|
|
|
EVT_MENU_RANGE(EDIT_MENU_THESAURUS_SUGS,EDIT_MENU_DIC_LANGUAGE-1,SubsTextEditCtrl::OnUseSuggestion)
|
2007-01-02 06:00:55 +01:00
|
|
|
EVT_MENU_RANGE(EDIT_MENU_DIC_LANGS,EDIT_MENU_THES_LANGUAGE-1,SubsTextEditCtrl::OnSetDicLanguage)
|
|
|
|
EVT_MENU_RANGE(EDIT_MENU_THES_LANGS,EDIT_MENU_THES_LANGS+100,SubsTextEditCtrl::OnSetThesLanguage)
|
|
|
|
END_EVENT_TABLE()
|
|
|
|
|
2007-01-14 00:34:04 +01:00
|
|
|
void SubsTextEditCtrl::OnLoseFocus(wxFocusEvent &event) {
|
|
|
|
CallTipCancel();
|
|
|
|
event.Skip();
|
|
|
|
}
|
|
|
|
|
2012-05-28 16:18:07 +02:00
|
|
|
void SubsTextEditCtrl::OnKeyDown(wxKeyEvent &event) {
|
2014-05-18 16:50:23 +02:00
|
|
|
event.Skip();
|
|
|
|
|
2012-05-28 16:18:07 +02:00
|
|
|
// Workaround for wxSTC eating tabs.
|
2014-05-18 16:50:23 +02:00
|
|
|
if (event.GetKeyCode() == WXK_TAB)
|
2012-05-28 16:18:07 +02:00
|
|
|
Navigate(event.ShiftDown() ? wxNavigationKeyEvent::IsBackward : wxNavigationKeyEvent::IsForward);
|
2014-05-18 16:50:23 +02:00
|
|
|
else if (event.GetKeyCode() == WXK_RETURN && event.GetModifiers() == wxMOD_SHIFT) {
|
|
|
|
auto sel_start = GetSelectionStart(), sel_end = GetSelectionEnd();
|
|
|
|
wxCharBuffer old = GetTextRaw();
|
|
|
|
std::string data(old.data(), sel_start);
|
|
|
|
data.append("\\N");
|
|
|
|
data.append(old.data() + sel_end, old.length() - sel_end);
|
|
|
|
SetTextRaw(data.c_str());
|
|
|
|
|
|
|
|
SetSelection(sel_start + 2, sel_start + 2);
|
|
|
|
event.Skip(false);
|
2012-05-28 16:18:07 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-06-27 20:03:07 +02:00
|
|
|
void SubsTextEditCtrl::SetSyntaxStyle(int id, wxFont &font, std::string const& name, wxColor const& default_background) {
|
2011-09-28 21:44:34 +02:00
|
|
|
StyleSetFont(id, font);
|
|
|
|
StyleSetBold(id, OPT_GET("Colour/Subtitle/Syntax/Bold/" + name)->GetBool());
|
2012-10-26 16:09:14 +02:00
|
|
|
StyleSetForeground(id, to_wx(OPT_GET("Colour/Subtitle/Syntax/" + name)->GetColor()));
|
2011-09-28 21:44:34 +02:00
|
|
|
const agi::OptionValue *background = OPT_GET("Colour/Subtitle/Syntax/Background/" + name);
|
2014-03-31 18:24:56 +02:00
|
|
|
if (background->GetType() == agi::OptionType::Color)
|
2012-10-26 16:09:14 +02:00
|
|
|
StyleSetBackground(id, to_wx(background->GetColor()));
|
2014-06-27 20:03:07 +02:00
|
|
|
else
|
|
|
|
StyleSetBackground(id, default_background);
|
2011-09-28 21:44:34 +02:00
|
|
|
}
|
|
|
|
|
2007-01-03 22:49:13 +01:00
|
|
|
void SubsTextEditCtrl::SetStyles() {
|
|
|
|
wxFont font = wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT);
|
2009-12-27 20:49:27 +01:00
|
|
|
font.SetEncoding(wxFONTENCODING_DEFAULT); // this solves problems with some fonts not working properly
|
2013-12-10 18:08:30 +01:00
|
|
|
wxString fontname = FontFace("Subtitle/Edit Box");
|
2010-07-20 05:11:11 +02:00
|
|
|
if (!fontname.empty()) font.SetFaceName(fontname);
|
2011-09-28 21:44:34 +02:00
|
|
|
font.SetPointSize(OPT_GET("Subtitle/Edit Box/Font Size")->GetInt());
|
|
|
|
|
2014-06-27 20:03:07 +02:00
|
|
|
auto default_background = to_wx(OPT_GET("Colour/Subtitle/Background")->GetColor());
|
|
|
|
|
2012-10-30 01:33:16 +01:00
|
|
|
namespace ss = agi::ass::SyntaxStyle;
|
2014-06-27 20:03:07 +02:00
|
|
|
SetSyntaxStyle(ss::NORMAL, font, "Normal", default_background);
|
|
|
|
SetSyntaxStyle(ss::COMMENT, font, "Comment", default_background);
|
|
|
|
SetSyntaxStyle(ss::DRAWING, font, "Drawing", default_background);
|
|
|
|
SetSyntaxStyle(ss::OVERRIDE, font, "Brackets", default_background);
|
|
|
|
SetSyntaxStyle(ss::PUNCTUATION, font, "Slashes", default_background);
|
|
|
|
SetSyntaxStyle(ss::TAG, font, "Tags", default_background);
|
|
|
|
SetSyntaxStyle(ss::ERROR, font, "Error", default_background);
|
|
|
|
SetSyntaxStyle(ss::PARAMETER, font, "Parameters", default_background);
|
|
|
|
SetSyntaxStyle(ss::LINE_BREAK, font, "Line Break", default_background);
|
|
|
|
SetSyntaxStyle(ss::KARAOKE_TEMPLATE, font, "Karaoke Template", default_background);
|
|
|
|
SetSyntaxStyle(ss::KARAOKE_VARIABLE, font, "Karaoke Variable", default_background);
|
|
|
|
|
|
|
|
SetCaretForeground(StyleGetForeground(ss::NORMAL));
|
|
|
|
StyleSetBackground(wxSTC_STYLE_DEFAULT, default_background);
|
2007-08-06 16:29:43 +02:00
|
|
|
|
2007-01-03 22:49:13 +01:00
|
|
|
// Misspelling indicator
|
2007-04-22 01:16:38 +02:00
|
|
|
IndicatorSetStyle(0,wxSTC_INDIC_SQUIGGLE);
|
2007-01-03 22:49:13 +01:00
|
|
|
IndicatorSetForeground(0,wxColour(255,0,0));
|
|
|
|
}
|
|
|
|
|
2011-09-28 21:44:34 +02:00
|
|
|
void SubsTextEditCtrl::UpdateStyle() {
|
2013-11-21 18:13:36 +01:00
|
|
|
AssDialogue *diag = context ? context->selectionController->GetActiveLine() : nullptr;
|
2013-01-04 16:01:50 +01:00
|
|
|
bool template_line = diag && diag->Comment && boost::istarts_with(diag->Effect.get(), "template");
|
2012-11-11 15:54:27 +01:00
|
|
|
|
|
|
|
tokenized_line = agi::ass::TokenizeDialogueBody(line_text, template_line);
|
2012-11-07 01:26:00 +01:00
|
|
|
agi::ass::SplitWords(line_text, tokenized_line);
|
2011-09-28 21:44:34 +02:00
|
|
|
|
2012-11-06 15:33:44 +01:00
|
|
|
cursor_pos = -1;
|
|
|
|
UpdateCallTip();
|
|
|
|
|
2020-11-22 19:17:19 +01:00
|
|
|
#if wxVERSION_NUMBER >= 3100
|
|
|
|
StartStyling(0);
|
|
|
|
#else
|
|
|
|
StartStyling(0, 255);
|
|
|
|
#endif
|
2011-08-27 08:52:35 +02:00
|
|
|
|
2010-08-26 20:38:37 +02:00
|
|
|
if (!OPT_GET("Subtitle/Highlight/Syntax")->GetBool()) {
|
2012-11-06 15:33:44 +01:00
|
|
|
SetStyling(line_text.size(), 0);
|
2010-08-26 20:38:37 +02:00
|
|
|
return;
|
|
|
|
}
|
2006-12-24 22:52:54 +01:00
|
|
|
|
2012-11-06 15:33:44 +01:00
|
|
|
if (line_text.empty()) return;
|
2011-09-28 21:44:34 +02:00
|
|
|
|
2016-04-07 20:24:25 +02:00
|
|
|
SetIndicatorCurrent(0);
|
|
|
|
size_t pos = 0;
|
|
|
|
for (auto const& style_range : agi::ass::SyntaxHighlight(line_text, tokenized_line, spellchecker.get())) {
|
|
|
|
if (style_range.type == agi::ass::SyntaxStyle::SPELLING) {
|
|
|
|
SetStyling(style_range.length, agi::ass::SyntaxStyle::NORMAL);
|
|
|
|
IndicatorFillRange(pos, style_range.length);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
SetStyling(style_range.length, style_range.type);
|
|
|
|
IndicatorClearRange(pos, style_range.length);
|
|
|
|
}
|
|
|
|
pos += style_range.length;
|
|
|
|
}
|
2007-01-02 06:00:55 +01:00
|
|
|
}
|
|
|
|
|
2012-11-06 15:33:44 +01:00
|
|
|
void SubsTextEditCtrl::UpdateCallTip() {
|
2010-05-21 03:13:36 +02:00
|
|
|
if (!OPT_GET("App/Call Tips")->GetBool()) return;
|
2007-01-02 23:10:45 +01:00
|
|
|
|
2012-11-06 15:33:44 +01:00
|
|
|
int pos = GetCurrentPos();
|
2013-09-16 16:58:06 +02:00
|
|
|
if (pos == cursor_pos) return;
|
2012-11-06 15:33:44 +01:00
|
|
|
cursor_pos = pos;
|
|
|
|
|
2014-07-05 01:01:09 +02:00
|
|
|
agi::Calltip new_calltip = agi::GetCalltip(tokenized_line, line_text, pos);
|
2007-01-02 06:00:55 +01:00
|
|
|
|
2014-07-05 01:01:09 +02:00
|
|
|
if (!new_calltip.text) {
|
2007-01-02 06:00:55 +01:00
|
|
|
CallTipCancel();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2012-11-01 03:49:29 +01:00
|
|
|
if (!CallTipActive() || calltip_position != new_calltip.tag_position || calltip_text != new_calltip.text)
|
2014-07-05 01:01:09 +02:00
|
|
|
CallTipShow(new_calltip.tag_position, wxString::FromUTF8Unchecked(new_calltip.text));
|
2007-01-02 06:00:55 +01:00
|
|
|
|
2012-11-01 03:49:29 +01:00
|
|
|
calltip_position = new_calltip.tag_position;
|
|
|
|
calltip_text = new_calltip.text;
|
2007-01-02 06:00:55 +01:00
|
|
|
|
2012-11-01 03:49:29 +01:00
|
|
|
CallTipSetHighlight(new_calltip.highlight_start, new_calltip.highlight_end);
|
2006-12-24 23:46:20 +01:00
|
|
|
}
|
|
|
|
|
2014-04-17 03:27:54 +02:00
|
|
|
void SubsTextEditCtrl::SetTextTo(std::string const& text) {
|
2010-07-20 05:11:11 +02:00
|
|
|
SetEvtHandlerEnabled(false);
|
2006-12-24 22:52:54 +01:00
|
|
|
Freeze();
|
|
|
|
|
2014-04-25 17:00:03 +02:00
|
|
|
auto insertion_point = GetInsertionPoint();
|
2014-04-26 05:42:42 +02:00
|
|
|
if (static_cast<size_t>(insertion_point) > line_text.size())
|
2014-04-25 17:00:03 +02:00
|
|
|
line_text = GetTextRaw().data();
|
|
|
|
auto old_pos = agi::CharacterCount(line_text.begin(), line_text.begin() + insertion_point, 0);
|
2012-11-06 15:33:44 +01:00
|
|
|
line_text.clear();
|
2014-04-25 17:00:03 +02:00
|
|
|
|
2014-07-02 05:10:24 +02:00
|
|
|
if (context) {
|
|
|
|
context->textSelectionController->SetSelection(0, 0);
|
|
|
|
SetTextRaw(text.c_str());
|
|
|
|
auto pos = agi::IndexOfCharacter(text, old_pos);
|
|
|
|
context->textSelectionController->SetSelection(pos, pos);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
SetSelection(0, 0);
|
|
|
|
SetTextRaw(text.c_str());
|
|
|
|
auto pos = agi::IndexOfCharacter(text, old_pos);
|
|
|
|
SetSelection(pos, pos);
|
|
|
|
}
|
2006-12-24 22:52:54 +01:00
|
|
|
|
2010-07-20 05:11:11 +02:00
|
|
|
SetEvtHandlerEnabled(true);
|
2006-12-24 22:52:54 +01:00
|
|
|
Thaw();
|
|
|
|
}
|
|
|
|
|
2011-10-10 22:59:04 +02:00
|
|
|
void SubsTextEditCtrl::Paste() {
|
2013-01-04 16:01:50 +01:00
|
|
|
std::string data = GetClipboard();
|
2011-10-10 22:59:04 +02:00
|
|
|
|
2013-01-04 16:01:50 +01:00
|
|
|
boost::replace_all(data, "\r\n", "\\N");
|
|
|
|
boost::replace_all(data, "\n", "\\N");
|
|
|
|
boost::replace_all(data, "\r", "\\N");
|
2011-10-10 22:59:04 +02:00
|
|
|
|
2013-01-04 16:01:50 +01:00
|
|
|
wxCharBuffer old = GetTextRaw();
|
|
|
|
data.insert(0, old.data(), GetSelectionStart());
|
|
|
|
int sel_start = data.size();
|
|
|
|
data.append(old.data() + GetSelectionEnd());
|
2011-10-10 22:59:04 +02:00
|
|
|
|
2013-01-04 16:01:50 +01:00
|
|
|
SetTextRaw(data.c_str());
|
2011-10-10 22:59:04 +02:00
|
|
|
|
|
|
|
SetSelectionStart(sel_start);
|
|
|
|
SetSelectionEnd(sel_start);
|
|
|
|
}
|
|
|
|
|
2011-08-31 06:17:37 +02:00
|
|
|
void SubsTextEditCtrl::OnContextMenu(wxContextMenuEvent &event) {
|
|
|
|
wxPoint pos = event.GetPosition();
|
|
|
|
int activePos;
|
2012-11-07 01:26:00 +01:00
|
|
|
if (pos == wxDefaultPosition)
|
2011-08-31 06:17:37 +02:00
|
|
|
activePos = GetCurrentPos();
|
2012-11-07 01:26:00 +01:00
|
|
|
else
|
2011-08-31 06:17:37 +02:00
|
|
|
activePos = PositionFromPoint(ScreenToClient(pos));
|
2006-12-25 06:43:00 +01:00
|
|
|
|
2012-11-07 01:26:00 +01:00
|
|
|
currentWordPos = GetBoundsOfWordAtPosition(activePos);
|
|
|
|
currentWord = line_text.substr(currentWordPos.first, currentWordPos.second);
|
2006-12-25 22:56:56 +01:00
|
|
|
|
2012-01-11 20:18:45 +01:00
|
|
|
wxMenu menu;
|
2012-11-08 23:05:49 +01:00
|
|
|
if (spellchecker)
|
|
|
|
AddSpellCheckerEntries(menu);
|
|
|
|
|
|
|
|
// Append language list
|
|
|
|
menu.Append(-1,_("Spell checker language"), GetLanguagesMenu(
|
|
|
|
EDIT_MENU_DIC_LANGS,
|
2012-12-23 00:18:38 +01:00
|
|
|
to_wx(OPT_GET("Tool/Spell Checker/Language")->GetString()),
|
2012-11-08 23:05:49 +01:00
|
|
|
to_wx(spellchecker->GetLanguageList())));
|
|
|
|
menu.AppendSeparator();
|
|
|
|
|
|
|
|
AddThesaurusEntries(menu);
|
2006-12-26 00:10:23 +01:00
|
|
|
|
2012-01-11 20:18:45 +01:00
|
|
|
// Standard actions
|
|
|
|
menu.Append(EDIT_MENU_CUT,_("Cu&t"))->Enable(GetSelectionStart()-GetSelectionEnd() != 0);
|
|
|
|
menu.Append(EDIT_MENU_COPY,_("&Copy"))->Enable(GetSelectionStart()-GetSelectionEnd() != 0);
|
|
|
|
menu.Append(EDIT_MENU_PASTE,_("&Paste"))->Enable(CanPaste());
|
|
|
|
menu.AppendSeparator();
|
|
|
|
menu.Append(EDIT_MENU_SELECT_ALL,_("Select &All"));
|
2006-12-25 06:43:00 +01:00
|
|
|
|
2012-01-11 20:18:45 +01:00
|
|
|
// Split
|
2012-03-13 00:34:45 +01:00
|
|
|
if (context) {
|
|
|
|
menu.AppendSeparator();
|
2013-11-18 02:21:23 +01:00
|
|
|
menu.Append(EDIT_MENU_SPLIT_PRESERVE, _("Split at cursor (preserve times)"));
|
|
|
|
menu.Append(EDIT_MENU_SPLIT_ESTIMATE, _("Split at cursor (estimate times)"));
|
|
|
|
cmd::Command *split_video = cmd::get("edit/line/split/video");
|
|
|
|
menu.Append(EDIT_MENU_SPLIT_VIDEO, split_video->StrMenu(context))->Enable(split_video->Validate(context));
|
2012-03-13 00:34:45 +01:00
|
|
|
}
|
2006-12-25 06:52:29 +01:00
|
|
|
|
2012-01-11 20:18:45 +01:00
|
|
|
PopupMenu(&menu);
|
|
|
|
}
|
2006-12-26 00:10:23 +01:00
|
|
|
|
2014-04-23 02:01:22 +02:00
|
|
|
void SubsTextEditCtrl::OnDoubleClick(wxStyledTextEvent &evt) {
|
2014-06-20 19:44:14 +02:00
|
|
|
int pos = evt.GetPosition();
|
|
|
|
if (pos == -1 && !tokenized_line.empty()) {
|
|
|
|
auto tok = tokenized_line.back();
|
|
|
|
SetSelection(line_text.size() - tok.length, line_text.size());
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
auto bounds = GetBoundsOfWordAtPosition(evt.GetPosition());
|
|
|
|
if (bounds.second != 0)
|
|
|
|
SetSelection(bounds.first, bounds.first + bounds.second);
|
|
|
|
else
|
|
|
|
evt.Skip();
|
|
|
|
}
|
2014-04-23 02:01:22 +02:00
|
|
|
}
|
|
|
|
|
2012-01-11 20:18:45 +01:00
|
|
|
void SubsTextEditCtrl::AddSpellCheckerEntries(wxMenu &menu) {
|
2012-11-08 23:05:49 +01:00
|
|
|
if (currentWord.empty()) return;
|
|
|
|
|
2012-12-22 01:40:51 +01:00
|
|
|
if (spellchecker->CanRemoveWord(currentWord))
|
2014-05-29 17:28:37 +02:00
|
|
|
menu.Append(EDIT_MENU_REMOVE_FROM_DICT, fmt_tl("Remove \"%s\" from dictionary", currentWord));
|
2012-12-22 01:40:51 +01:00
|
|
|
|
2012-01-11 20:18:45 +01:00
|
|
|
sugs = spellchecker->GetSuggestions(currentWord);
|
|
|
|
if (spellchecker->CheckWord(currentWord)) {
|
|
|
|
if (sugs.empty())
|
|
|
|
menu.Append(EDIT_MENU_SUGGESTION,_("No spell checker suggestions"))->Enable(false);
|
2006-12-26 00:10:23 +01:00
|
|
|
else {
|
2013-11-21 18:13:36 +01:00
|
|
|
auto subMenu = new wxMenu;
|
2012-01-11 20:18:45 +01:00
|
|
|
for (size_t i = 0; i < sugs.size(); ++i)
|
2012-10-30 16:59:47 +01:00
|
|
|
subMenu->Append(EDIT_MENU_SUGGESTIONS+i, to_wx(sugs[i]));
|
2007-01-02 00:47:09 +01:00
|
|
|
|
2014-05-29 17:28:37 +02:00
|
|
|
menu.Append(-1, fmt_tl("Spell checker suggestions for \"%s\"", currentWord), subMenu);
|
2007-01-01 05:53:55 +01:00
|
|
|
}
|
2006-12-25 22:56:56 +01:00
|
|
|
}
|
2012-01-11 20:18:45 +01:00
|
|
|
else {
|
|
|
|
if (sugs.empty())
|
|
|
|
menu.Append(EDIT_MENU_SUGGESTION,_("No correction suggestions"))->Enable(false);
|
2006-12-25 22:56:56 +01:00
|
|
|
|
2012-01-11 20:18:45 +01:00
|
|
|
for (size_t i = 0; i < sugs.size(); ++i)
|
2012-10-30 16:59:47 +01:00
|
|
|
menu.Append(EDIT_MENU_SUGGESTIONS+i, to_wx(sugs[i]));
|
2006-12-26 00:10:23 +01:00
|
|
|
|
2012-01-11 20:18:45 +01:00
|
|
|
// Append "add word"
|
2014-05-29 17:28:37 +02:00
|
|
|
menu.Append(EDIT_MENU_ADD_TO_DICT, fmt_tl("Add \"%s\" to dictionary", currentWord))->Enable(spellchecker->CanAddWord(currentWord));
|
2012-01-11 20:18:45 +01:00
|
|
|
}
|
|
|
|
}
|
2006-12-25 22:56:56 +01:00
|
|
|
|
2012-01-11 20:18:45 +01:00
|
|
|
void SubsTextEditCtrl::AddThesaurusEntries(wxMenu &menu) {
|
2012-11-08 23:05:49 +01:00
|
|
|
if (currentWord.empty()) return;
|
|
|
|
|
2013-11-02 15:52:45 +01:00
|
|
|
auto results = thesaurus->Lookup(currentWord);
|
2006-12-25 22:56:56 +01:00
|
|
|
|
2012-01-11 20:18:45 +01:00
|
|
|
thesSugs.clear();
|
2007-01-02 00:47:09 +01:00
|
|
|
|
2012-11-04 04:53:03 +01:00
|
|
|
if (results.size()) {
|
2013-11-21 18:13:36 +01:00
|
|
|
auto thesMenu = new wxMenu;
|
2007-01-02 00:47:09 +01:00
|
|
|
|
2012-01-11 20:18:45 +01:00
|
|
|
int curThesEntry = 0;
|
2012-11-04 04:53:03 +01:00
|
|
|
for (auto const& result : results) {
|
2012-01-11 20:18:45 +01:00
|
|
|
// Single word, insert directly
|
2013-11-02 15:52:45 +01:00
|
|
|
if (result.second.empty()) {
|
2012-12-23 00:18:38 +01:00
|
|
|
thesMenu->Append(EDIT_MENU_THESAURUS_SUGS+curThesEntry, to_wx(result.first));
|
2012-11-04 04:53:03 +01:00
|
|
|
thesSugs.push_back(result.first);
|
2012-01-11 20:18:45 +01:00
|
|
|
++curThesEntry;
|
|
|
|
}
|
|
|
|
// Multiple, create submenu
|
|
|
|
else {
|
2013-11-21 18:13:36 +01:00
|
|
|
auto subMenu = new wxMenu;
|
2012-11-04 04:53:03 +01:00
|
|
|
for (auto const& sug : result.second) {
|
|
|
|
subMenu->Append(EDIT_MENU_THESAURUS_SUGS+curThesEntry, to_wx(sug));
|
|
|
|
thesSugs.push_back(sug);
|
2012-01-11 20:18:45 +01:00
|
|
|
++curThesEntry;
|
|
|
|
}
|
2007-01-02 00:47:09 +01:00
|
|
|
|
2012-11-04 04:53:03 +01:00
|
|
|
thesMenu->Append(-1, to_wx(result.first), subMenu);
|
2012-01-11 20:18:45 +01:00
|
|
|
}
|
2007-01-01 06:15:05 +01:00
|
|
|
}
|
|
|
|
|
2014-05-29 17:28:37 +02:00
|
|
|
menu.Append(-1, fmt_tl("Thesaurus suggestions for \"%s\"", currentWord), thesMenu);
|
2006-12-25 06:43:00 +01:00
|
|
|
}
|
2012-01-11 20:18:45 +01:00
|
|
|
else
|
|
|
|
menu.Append(EDIT_MENU_THESAURUS,_("No thesaurus suggestions"))->Enable(false);
|
|
|
|
|
|
|
|
// Append language list
|
|
|
|
menu.Append(-1,_("Thesaurus language"), GetLanguagesMenu(
|
|
|
|
EDIT_MENU_THES_LANGS,
|
2012-12-23 00:18:38 +01:00
|
|
|
to_wx(OPT_GET("Tool/Thesaurus/Language")->GetString()),
|
2012-10-30 16:59:47 +01:00
|
|
|
to_wx(thesaurus->GetLanguageList())));
|
2006-12-25 06:52:29 +01:00
|
|
|
menu.AppendSeparator();
|
2012-01-11 20:18:45 +01:00
|
|
|
}
|
2006-12-25 06:52:29 +01:00
|
|
|
|
2012-01-11 20:18:45 +01:00
|
|
|
wxMenu *SubsTextEditCtrl::GetLanguagesMenu(int base_id, wxString const& curLang, wxArrayString const& langs) {
|
2013-11-21 18:13:36 +01:00
|
|
|
auto languageMenu = new wxMenu;
|
2012-04-21 17:13:51 +02:00
|
|
|
languageMenu->AppendRadioItem(base_id, _("Disable"))->Check(curLang.empty());
|
2006-12-25 06:52:29 +01:00
|
|
|
|
2014-05-06 19:48:22 +02:00
|
|
|
for (size_t i = 0; i < langs.size(); ++i)
|
|
|
|
languageMenu->AppendRadioItem(base_id + i + 1, LocalizedLanguageName(langs[i]))->Check(langs[i] == curLang);
|
2012-01-11 20:18:45 +01:00
|
|
|
|
|
|
|
return languageMenu;
|
2006-12-25 06:43:00 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void SubsTextEditCtrl::OnUseSuggestion(wxCommandEvent &event) {
|
2012-10-30 16:59:47 +01:00
|
|
|
std::string suggestion;
|
2010-07-20 05:11:11 +02:00
|
|
|
int sugIdx = event.GetId() - EDIT_MENU_THESAURUS_SUGS;
|
2012-11-07 01:26:00 +01:00
|
|
|
if (sugIdx >= 0)
|
2012-12-30 01:53:30 +01:00
|
|
|
suggestion = thesSugs[sugIdx];
|
2012-11-07 01:26:00 +01:00
|
|
|
else
|
2010-07-20 05:11:11 +02:00
|
|
|
suggestion = sugs[event.GetId() - EDIT_MENU_SUGGESTIONS];
|
2006-12-25 23:30:11 +01:00
|
|
|
|
2014-06-24 00:49:30 +02:00
|
|
|
size_t pos;
|
|
|
|
while ((pos = suggestion.rfind('(')) != std::string::npos) {
|
|
|
|
// If there's only one suggestion for a word it'll be in the form "(noun) word",
|
|
|
|
// so we need to trim the "(noun) " part
|
|
|
|
if (pos == 0) {
|
|
|
|
pos = suggestion.find(')');
|
|
|
|
if (pos != std::string::npos) {
|
|
|
|
if (pos + 1< suggestion.size() && suggestion[pos + 1] == ' ') ++pos;
|
|
|
|
suggestion.erase(0, pos + 1);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Some replacements have notes about their usage after the word in the
|
|
|
|
// form "word (generic term)" that we need to remove (plus the leading space)
|
2012-10-30 16:59:47 +01:00
|
|
|
suggestion.resize(pos - 1);
|
2014-06-24 00:49:30 +02:00
|
|
|
}
|
2010-07-20 05:11:11 +02:00
|
|
|
|
2012-11-07 01:26:00 +01:00
|
|
|
// line_text needs to get cleared before SetTextRaw to ensure it gets reparsed
|
|
|
|
std::string new_text;
|
|
|
|
swap(line_text, new_text);
|
|
|
|
SetTextRaw(new_text.replace(currentWordPos.first, currentWordPos.second, suggestion).c_str());
|
2006-12-25 23:21:44 +01:00
|
|
|
|
2012-11-07 01:26:00 +01:00
|
|
|
SetSelection(currentWordPos.first, currentWordPos.first + suggestion.size());
|
2006-12-25 23:21:44 +01:00
|
|
|
SetFocus();
|
|
|
|
}
|
2006-12-26 02:20:58 +01:00
|
|
|
|
2007-01-01 05:53:55 +01:00
|
|
|
void SubsTextEditCtrl::OnSetDicLanguage(wxCommandEvent &event) {
|
2012-10-30 16:59:47 +01:00
|
|
|
std::vector<std::string> langs = spellchecker->GetLanguageList();
|
2007-01-01 05:53:55 +01:00
|
|
|
|
2007-01-02 00:47:09 +01:00
|
|
|
int index = event.GetId() - EDIT_MENU_DIC_LANGS - 1;
|
2012-10-30 16:59:47 +01:00
|
|
|
std::string lang;
|
2012-01-11 20:19:21 +01:00
|
|
|
if (index >= 0)
|
|
|
|
lang = langs[index];
|
|
|
|
|
2012-10-30 16:59:47 +01:00
|
|
|
OPT_SET("Tool/Spell Checker/Language")->SetString(lang);
|
2007-01-01 05:53:55 +01:00
|
|
|
|
|
|
|
UpdateStyle();
|
|
|
|
}
|
|
|
|
|
|
|
|
void SubsTextEditCtrl::OnSetThesLanguage(wxCommandEvent &event) {
|
2012-04-17 01:54:47 +02:00
|
|
|
if (!thesaurus) return;
|
|
|
|
|
2012-10-30 16:59:47 +01:00
|
|
|
std::vector<std::string> langs = thesaurus->GetLanguageList();
|
2007-01-01 05:53:55 +01:00
|
|
|
|
2007-01-02 00:47:09 +01:00
|
|
|
int index = event.GetId() - EDIT_MENU_THES_LANGS - 1;
|
2012-10-30 16:59:47 +01:00
|
|
|
std::string lang;
|
2012-01-08 02:36:50 +01:00
|
|
|
if (index >= 0) lang = langs[index];
|
2012-10-30 16:59:47 +01:00
|
|
|
OPT_SET("Tool/Thesaurus/Language")->SetString(lang);
|
2007-01-01 05:53:55 +01:00
|
|
|
|
|
|
|
UpdateStyle();
|
|
|
|
}
|
2012-11-07 01:26:00 +01:00
|
|
|
|
|
|
|
std::pair<int, int> SubsTextEditCtrl::GetBoundsOfWordAtPosition(int pos) {
|
|
|
|
int len = 0;
|
|
|
|
for (auto const& tok : tokenized_line) {
|
2012-12-22 01:29:59 +01:00
|
|
|
if (len + (int)tok.length > pos) {
|
2012-11-07 01:26:00 +01:00
|
|
|
if (tok.type == agi::ass::DialogueTokenType::WORD)
|
2014-05-25 16:41:21 +02:00
|
|
|
return {len, tok.length};
|
|
|
|
return {0, 0};
|
2012-11-07 01:26:00 +01:00
|
|
|
}
|
|
|
|
len += tok.length;
|
|
|
|
}
|
|
|
|
|
2014-05-25 16:41:21 +02:00
|
|
|
return {0, 0};
|
2012-11-07 01:26:00 +01:00
|
|
|
}
|