1 // Copyright (c) 2005, Rodrigo Braz Monteiro
2 // All rights reserved.
3 //
4 // Redistribution and use in source and binary forms, with or without
5 // modification, are permitted provided that the following conditions are met:
6 //
7 //   * Redistributions of source code must retain the above copyright notice,
8 //     this list of conditions and the following disclaimer.
9 //   * Redistributions in binary form must reproduce the above copyright notice,
10 //     this list of conditions and the following disclaimer in the documentation
11 //     and/or other materials provided with the distribution.
12 //   * Neither the name of the Aegisub Group nor the names of its contributors
13 //     may be used to endorse or promote products derived from this software
14 //     without specific prior written permission.
15 //
16 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17 // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
20 // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21 // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22 // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23 // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24 // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25 // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26 // POSSIBILITY OF SUCH DAMAGE.
27 //
28 // Aegisub Project http://www.aegisub.org/
29 
30 /// @file dialog_style_editor.cpp
31 /// @brief Style Editor dialogue box
32 /// @ingroup style_editor
33 ///
34 
35 #include "ass_dialogue.h"
36 #include "ass_file.h"
37 #include "ass_style.h"
38 #include "ass_style_storage.h"
39 #include "colour_button.h"
40 #include "compat.h"
41 #include "dialog_style_editor.h"
42 #include "help_button.h"
43 #include "include/aegisub/context.h"
44 #include "libresrc/libresrc.h"
45 #include "options.h"
46 #include "persist_location.h"
47 #include "selection_controller.h"
48 #include "subs_preview.h"
49 #include "utils.h"
50 #include "validators.h"
51 
52 #include <libaegisub/of_type_adaptor.h>
53 #include <libaegisub/make_unique.h>
54 
55 #include <algorithm>
56 
57 #include <wx/bmpbuttn.h>
58 #include <wx/checkbox.h>
59 #include <wx/msgdlg.h>
60 #include <wx/sizer.h>
61 #include <wx/spinctrl.h>
62 #include <wx/stattext.h>
63 
64 /// Style rename helper that walks a file searching for a style and optionally
65 /// updating references to it
66 class StyleRenamer {
67 	agi::Context *c;
68 	bool found_any = false;
69 	bool do_replace = false;
70 	std::string source_name;
71 	std::string new_name;
72 
73 	/// Process a single override parameter to check if it's \r with this style name
ProcessTag(std::string const & tag,AssOverrideParameter * param,void * userData)74 	static void ProcessTag(std::string const& tag, AssOverrideParameter* param, void *userData) {
75 		StyleRenamer *self = static_cast<StyleRenamer*>(userData);
76 		if (tag == "\\r" && param->GetType() == VariableDataType::TEXT && param->Get<std::string>() == self->source_name) {
77 			if (self->do_replace)
78 				param->Set(self->new_name);
79 			else
80 				self->found_any = true;
81 		}
82 	}
83 
Walk(bool replace)84 	void Walk(bool replace) {
85 		found_any = false;
86 		do_replace = replace;
87 
88 		for (auto& diag : c->ass->Events) {
89 			if (diag.Style == source_name) {
90 				if (replace)
91 					diag.Style = new_name;
92 				else
93 					found_any = true;
94 			}
95 
96 			auto blocks = diag.ParseTags();
97 			for (auto block : blocks | agi::of_type<AssDialogueBlockOverride>())
98 				block->ProcessParameters(&StyleRenamer::ProcessTag, this);
99 			if (replace)
100 				diag.UpdateText(blocks);
101 
102 			if (found_any) return;
103 		}
104 	}
105 
106 public:
StyleRenamer(agi::Context * c,std::string source_name,std::string new_name)107 	StyleRenamer(agi::Context *c, std::string source_name, std::string new_name)
108 	: c(c)
109 	, source_name(std::move(source_name))
110 	, new_name(std::move(new_name))
111 	{
112 	}
113 
114 	/// Check if there are any uses of the original style name in the file
NeedsReplace()115 	bool NeedsReplace() {
116 		Walk(false);
117 		return found_any;
118 	}
119 
120 	/// Replace all uses of the original style name with the new one
Replace()121 	void Replace() {
122 		Walk(true);
123 	}
124 };
125 
DialogStyleEditor(wxWindow * parent,AssStyle * style,agi::Context * c,AssStyleStorage * store,std::string const & new_name,wxArrayString const & font_list)126 DialogStyleEditor::DialogStyleEditor(wxWindow *parent, AssStyle *style, agi::Context *c, AssStyleStorage *store, std::string const& new_name, wxArrayString const& font_list)
127 : wxDialog (parent, -1, _("Style Editor"), wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER)
128 , c(c)
129 , style(style)
130 , store(store)
131 {
132 	if (new_name.size()) {
133 		is_new = true;
134 		style = this->style = new AssStyle(*style);
135 		style->name = new_name;
136 	}
137 	else if (!style) {
138 		is_new = true;
139 		style = this->style = new AssStyle;
140 	}
141 
142 	work = agi::make_unique<AssStyle>(*style);
143 
144 	SetIcon(GETICON(style_toolbutton_16));
145 
146 	auto add_with_label = [&](wxSizer *sizer, wxString const& label, wxWindow *ctrl) {
147 		sizer->Add(new wxStaticText(this, -1, label), wxSizerFlags().Center().Right().Border(wxLEFT | wxRIGHT));
148 		sizer->Add(ctrl, wxSizerFlags(1).Left().Expand());
149 	};
150 
151 	auto num_text_ctrl = [&](double *value, double min, double max, double step) -> wxSpinCtrlDouble * {
152 		auto scd = new wxSpinCtrlDouble(this, -1, "", wxDefaultPosition,
153 			wxSize(75, -1), wxSP_ARROW_KEYS, min, max, *value, step);
154 		scd->SetValidator(DoubleSpinValidator(value));
155 		scd->Bind(wxEVT_SPINCTRLDOUBLE, [=](wxSpinDoubleEvent &evt) {
156 			evt.Skip();
157 			if (updating) return;
158 
159 			bool old = updating;
160 			updating = true;
161 			scd->GetValidator()->TransferFromWindow();
162 			updating = old;
163 			SubsPreview->SetStyle(*work);
164 		});
165 		return scd;
166 	};
167 
168 	// Prepare control values
169 	wxString EncodingValue = std::to_wstring(style->encoding);
170 	wxString alignValues[9] = { "7", "8", "9", "4", "5", "6", "1", "2", "3" };
171 
172 	// Encoding options
173 	wxArrayString encodingStrings;
174 	AssStyle::GetEncodings(encodingStrings);
175 
176 	// Create sizers
177 	wxSizer *NameSizer = new wxStaticBoxSizer(wxHORIZONTAL, this, _("Style Name"));
178 	wxSizer *FontSizer = new wxStaticBoxSizer(wxVERTICAL, this, _("Font"));
179 	wxSizer *ColorsSizer = new wxStaticBoxSizer(wxHORIZONTAL, this, _("Colors"));
180 	wxSizer *MarginSizer = new wxStaticBoxSizer(wxHORIZONTAL, this, _("Margins"));
181 	wxSizer *OutlineBox = new wxStaticBoxSizer(wxHORIZONTAL, this, _("Outline"));
182 	wxSizer *MiscBox = new wxStaticBoxSizer(wxVERTICAL, this, _("Miscellaneous"));
183 	wxSizer *PreviewBox = new wxStaticBoxSizer(wxVERTICAL, this, _("Preview"));
184 
185 	// Create controls
186 	StyleName = new wxTextCtrl(this, -1, to_wx(style->name));
187 	FontName = new wxComboBox(this, -1, to_wx(style->font), wxDefaultPosition, wxSize(150, -1), 0, nullptr, wxCB_DROPDOWN);
188 	auto FontSize = num_text_ctrl(&work->fontsize, 0, 10000.0, 1.0);
189 	BoxBold = new wxCheckBox(this, -1, _("&Bold"));
190 	BoxItalic = new wxCheckBox(this, -1, _("&Italic"));
191 	BoxUnderline = new wxCheckBox(this, -1, _("&Underline"));
192 	BoxStrikeout = new wxCheckBox(this, -1, _("&Strikeout"));
193 	ColourButton *colorButton[] = {
194 		new ColourButton(this, wxSize(55, 16), true, style->primary, ColorValidator(&work->primary)),
195 		new ColourButton(this, wxSize(55, 16), true, style->secondary, ColorValidator(&work->secondary)),
196 		new ColourButton(this, wxSize(55, 16), true, style->outline, ColorValidator(&work->outline)),
197 		new ColourButton(this, wxSize(55, 16), true, style->shadow, ColorValidator(&work->shadow))
198 	};
199 	for (int i = 0; i < 3; i++)
200 		margin[i] = new wxSpinCtrl(this, -1, std::to_wstring(style->Margin[i]),
201 		                           wxDefaultPosition, wxSize(60, -1),
202 		                           wxSP_ARROW_KEYS, 0, 9999, style->Margin[i]);
203 
204 	Alignment = new wxRadioBox(this, -1, _("Alignment"), wxDefaultPosition, wxDefaultSize, 9, alignValues, 3, wxRA_SPECIFY_COLS);
205 	auto Outline = num_text_ctrl(&work->outline_w, 0.0, 1000.0, 0.1);
206 	auto Shadow = num_text_ctrl(&work->shadow_w, 0.0, 1000.0, 0.1);
207 	OutlineType = new wxCheckBox(this, -1, _("&Opaque box"));
208 	auto ScaleX = num_text_ctrl(&work->scalex, 0.0, 10000.0, 1.0);
209 	auto ScaleY = num_text_ctrl(&work->scaley, 0.0, 10000.0, 1.0);
210 	auto Angle = num_text_ctrl(&work->angle, -180.0, 180.0, 1.0);
211 	auto Spacing = num_text_ctrl(&work->spacing, 0.0, 1000.0, 0.1);
212 	Encoding = new wxComboBox(this, -1, "", wxDefaultPosition, wxDefaultSize, encodingStrings, wxCB_READONLY);
213 
214 	// Set control tooltips
215 	StyleName->SetToolTip(_("Style name"));
216 	FontName->SetToolTip(_("Font face"));
217 	FontSize->SetToolTip(_("Font size"));
218 	colorButton[0]->SetToolTip(_("Choose primary color"));
219 	colorButton[1]->SetToolTip(_("Choose secondary color"));
220 	colorButton[2]->SetToolTip(_("Choose outline color"));
221 	colorButton[3]->SetToolTip(_("Choose shadow color"));
222 	margin[0]->SetToolTip(_("Distance from left edge, in pixels"));
223 	margin[1]->SetToolTip(_("Distance from right edge, in pixels"));
224 	margin[2]->SetToolTip(_("Distance from top/bottom edge, in pixels"));
225 	OutlineType->SetToolTip(_("When selected, display an opaque box behind the subtitles instead of an outline around the text"));
226 	Outline->SetToolTip(_("Outline width, in pixels"));
227 	Shadow->SetToolTip(_("Shadow distance, in pixels"));
228 	ScaleX->SetToolTip(_("Scale X, in percentage"));
229 	ScaleY->SetToolTip(_("Scale Y, in percentage"));
230 	Angle->SetToolTip(_("Angle to rotate in Z axis, in degrees"));
231 	Encoding->SetToolTip(_("Encoding, only useful in unicode if the font doesn't have the proper unicode mapping"));
232 	Spacing->SetToolTip(_("Character spacing, in pixels"));
233 	Alignment->SetToolTip(_("Alignment in screen, in numpad style"));
234 
235 	// Set up controls
236 	BoxBold->SetValue(style->bold);
237 	BoxItalic->SetValue(style->italic);
238 	BoxUnderline->SetValue(style->underline);
239 	BoxStrikeout->SetValue(style->strikeout);
240 	OutlineType->SetValue(style->borderstyle == 3);
241 	Alignment->SetSelection(AlignToControl(style->alignment));
242 	// Fill font face list box
243 	FontName->Freeze();
244 	FontName->Append(font_list);
245 	FontName->SetValue(to_wx(style->font));
246 	FontName->Thaw();
247 
248 	// Set encoding value
249 	bool found = false;
250 	for (size_t i=0;i<encodingStrings.Count();i++) {
251 		if (encodingStrings[i].StartsWith(EncodingValue)) {
252 			Encoding->Select(i);
253 			found = true;
254 			break;
255 		}
256 	}
257 	if (!found) Encoding->Select(0);
258 
259 	// Style name sizer
260 	NameSizer->Add(StyleName, 1, wxALL, 0);
261 
262 	// Font sizer
263 	wxSizer *FontSizerTop = new wxBoxSizer(wxHORIZONTAL);
264 	wxSizer *FontSizerBottom = new wxBoxSizer(wxHORIZONTAL);
265 	FontSizerTop->Add(FontName, 1, wxALL, 0);
266 	FontSizerTop->Add(FontSize, 0, wxLEFT, 5);
267 	FontSizerBottom->AddStretchSpacer(1);
268 	FontSizerBottom->Add(BoxBold, 0, 0, 0);
269 	FontSizerBottom->Add(BoxItalic, 0, wxLEFT, 5);
270 	FontSizerBottom->Add(BoxUnderline, 0, wxLEFT, 5);
271 	FontSizerBottom->Add(BoxStrikeout, 0, wxLEFT, 5);
272 	FontSizerBottom->AddStretchSpacer(1);
273 	FontSizer->Add(FontSizerTop, 1, wxALL | wxEXPAND, 0);
274 	FontSizer->Add(FontSizerBottom, 1, wxTOP | wxEXPAND, 5);
275 
276 	// Colors sizer
277 	wxString colorLabels[] = { _("Primary"), _("Secondary"), _("Outline"), _("Shadow") };
278 	ColorsSizer->AddStretchSpacer(1);
279 	for (int i = 0; i < 4; ++i) {
280 		auto sizer = new wxBoxSizer(wxVERTICAL);
281 		sizer->Add(new wxStaticText(this, -1, colorLabels[i]), 0, wxBOTTOM | wxALIGN_CENTER, 5);
282 		sizer->Add(colorButton[i], 0, wxBOTTOM | wxALIGN_CENTER, 5);
283 		ColorsSizer->Add(sizer, 0, wxLEFT, i?5:0);
284 	}
285 	ColorsSizer->AddStretchSpacer(1);
286 
287 	// Margins
288 	wxString marginLabels[] = { _("Left"), _("Right"), _("Vert") };
289 	MarginSizer->AddStretchSpacer(1);
290 	for (int i=0;i<3;i++) {
291 		auto sizer = new wxBoxSizer(wxVERTICAL);
292 		sizer->AddStretchSpacer(1);
293 		sizer->Add(new wxStaticText(this, -1, marginLabels[i]), 0, wxCENTER, 0);
294 		sizer->Add(margin[i], 0, wxTOP | wxCENTER, 5);
295 		sizer->AddStretchSpacer(1);
296 		MarginSizer->Add(sizer, 0, wxEXPAND | wxLEFT, i?5:0);
297 	}
298 	MarginSizer->AddStretchSpacer(1);
299 
300 	// Margins+Alignment
301 	wxSizer *MarginAlign = new wxBoxSizer(wxHORIZONTAL);
302 	MarginAlign->Add(MarginSizer, 1, wxLEFT | wxEXPAND, 0);
303 	MarginAlign->Add(Alignment, 0, wxLEFT | wxEXPAND, 5);
304 
305 	// Outline
306 	add_with_label(OutlineBox, _("Outline:"), Outline);
307 	add_with_label(OutlineBox, _("Shadow:"), Shadow);
308 	OutlineBox->Add(OutlineType, 0, wxLEFT | wxALIGN_CENTER, 5);
309 
310 	// Misc
311 	auto MiscBoxTop = new wxFlexGridSizer(2, 4, 5, 5);
312 	add_with_label(MiscBoxTop, _("Scale X%:"), ScaleX);
313 	add_with_label(MiscBoxTop, _("Scale Y%:"), ScaleY);
314 	add_with_label(MiscBoxTop, _("Rotation:"), Angle);
315 	add_with_label(MiscBoxTop, _("Spacing:"), Spacing);
316 
317 	wxSizer *MiscBoxBottom = new wxBoxSizer(wxHORIZONTAL);
318 	add_with_label(MiscBoxBottom, _("Encoding:"), Encoding);
319 
320 	MiscBox->Add(MiscBoxTop, wxSizerFlags().Expand().Center());
321 	MiscBox->Add(MiscBoxBottom, wxSizerFlags().Expand().Center().Border(wxTOP));
322 
323 	// Preview
324 	auto previewButton = new ColourButton(this, wxSize(45, 16), false, OPT_GET("Colour/Style Editor/Background/Preview")->GetColor());
325 	PreviewText = new wxTextCtrl(this, -1, to_wx(OPT_GET("Tool/Style Editor/Preview Text")->GetString()));
326 	SubsPreview = new SubtitlesPreview(this, wxSize(100, 60), wxSUNKEN_BORDER, OPT_GET("Colour/Style Editor/Background/Preview")->GetColor());
327 
328 	SubsPreview->SetToolTip(_("Preview of current style"));
329 	SubsPreview->SetStyle(*style);
330 	SubsPreview->SetText(from_wx(PreviewText->GetValue()));
331 	PreviewText->SetToolTip(_("Text to be used for the preview"));
332 	previewButton->SetToolTip(_("Color of preview background"));
333 
334 	wxSizer *PreviewBottomSizer = new wxBoxSizer(wxHORIZONTAL);
335 	PreviewBottomSizer->Add(PreviewText, 1, wxEXPAND | wxRIGHT, 5);
336 	PreviewBottomSizer->Add(previewButton, 0, wxEXPAND, 0);
337 	PreviewBox->Add(SubsPreview, 1, wxEXPAND | wxBOTTOM, 5);
338 	PreviewBox->Add(PreviewBottomSizer, 0, wxEXPAND | wxBOTTOM, 0);
339 
340 	// Buttons
341 	auto ButtonSizer = CreateStdDialogButtonSizer(wxOK | wxCANCEL | wxAPPLY | wxHELP);
342 
343 	// Left side sizer
344 	wxSizer *LeftSizer = new wxBoxSizer(wxVERTICAL);
345 	LeftSizer->Add(NameSizer, 0, wxBOTTOM | wxEXPAND, 5);
346 	LeftSizer->Add(FontSizer, 0, wxBOTTOM | wxEXPAND, 5);
347 	LeftSizer->Add(ColorsSizer, 0, wxBOTTOM | wxEXPAND, 5);
348 	LeftSizer->Add(MarginAlign, 0, wxBOTTOM | wxEXPAND, 0);
349 
350 	// Right side sizer
351 	wxSizer *RightSizer = new wxBoxSizer(wxVERTICAL);
352 	RightSizer->Add(OutlineBox, wxSizerFlags().Expand().Border(wxBOTTOM));
353 	RightSizer->Add(MiscBox, wxSizerFlags().Expand().Border(wxBOTTOM));
354 	RightSizer->Add(PreviewBox, wxSizerFlags(1).Expand());
355 
356 	// Controls Sizer
357 	wxSizer *ControlSizer = new wxBoxSizer(wxHORIZONTAL);
358 	ControlSizer->Add(LeftSizer, 0, wxEXPAND, 0);
359 	ControlSizer->Add(RightSizer, 1, wxLEFT | wxEXPAND, 5);
360 
361 	// General Layout
362 	wxSizer *MainSizer = new wxBoxSizer(wxVERTICAL);
363 	MainSizer->Add(ControlSizer, 1, wxALL | wxALIGN_CENTER | wxEXPAND, 5);
364 	MainSizer->Add(ButtonSizer, 0, wxBOTTOM | wxEXPAND, 5);
365 
366 	SetSizerAndFit(MainSizer);
367 
368 	// Force the style name text field to scroll based on its final size, rather
369 	// than its initial size
370 	StyleName->SetInsertionPoint(0);
371 	StyleName->SetInsertionPoint(-1);
372 
373 	persist = agi::make_unique<PersistLocation>(this, "Tool/Style Editor", true);
374 
375 	Bind(wxEVT_CHILD_FOCUS, &DialogStyleEditor::OnChildFocus, this);
376 
377 	Bind(wxEVT_CHECKBOX, &DialogStyleEditor::OnCommandPreviewUpdate, this);
378 	Bind(wxEVT_COMBOBOX, &DialogStyleEditor::OnCommandPreviewUpdate, this);
379 	Bind(wxEVT_SPINCTRL, &DialogStyleEditor::OnCommandPreviewUpdate, this);
380 
381 	previewButton->Bind(EVT_COLOR, &DialogStyleEditor::OnPreviewColourChange, this);
382 	FontName->Bind(wxEVT_TEXT_ENTER, &DialogStyleEditor::OnCommandPreviewUpdate, this);
383 	PreviewText->Bind(wxEVT_TEXT, &DialogStyleEditor::OnPreviewTextChange, this);
384 
385 	Bind(wxEVT_BUTTON, std::bind(&DialogStyleEditor::Apply, this, true, true), wxID_OK);
386 	Bind(wxEVT_BUTTON, std::bind(&DialogStyleEditor::Apply, this, true, false), wxID_APPLY);
387 	Bind(wxEVT_BUTTON, std::bind(&DialogStyleEditor::Apply, this, false, true), wxID_CANCEL);
388 	Bind(wxEVT_BUTTON, std::bind(&HelpButton::OpenPage, "Style Editor"), wxID_HELP);
389 
390 	for (auto const& elem : colorButton)
391 		elem->Bind(EVT_COLOR, &DialogStyleEditor::OnSetColor, this);
392 }
393 
~DialogStyleEditor()394 DialogStyleEditor::~DialogStyleEditor() {
395 	if (is_new)
396 		delete style;
397 }
398 
GetStyleName() const399 std::string DialogStyleEditor::GetStyleName() const {
400 	return style->name;
401 }
402 
Apply(bool apply,bool close)403 void DialogStyleEditor::Apply(bool apply, bool close) {
404 	if (apply) {
405 		std::string new_name = from_wx(StyleName->GetValue());
406 
407 		// Get list of existing styles
408 		std::vector<std::string> styles = store ? store->GetNames() : c->ass->GetStyles();
409 
410 		// Check if style name is unique
411 		AssStyle *existing = store ? store->GetStyle(new_name) : c->ass->GetStyle(new_name);
412 		if (existing && existing != style) {
413 			wxMessageBox(_("There is already a style with this name. Please choose another name."), _("Style name conflict"), wxOK | wxICON_ERROR | wxCENTER);
414 			return;
415 		}
416 
417 		// Style name change
418 		bool did_rename = false;
419 		if (work->name != new_name) {
420 			if (!store && !is_new) {
421 				StyleRenamer renamer(c, work->name, new_name);
422 				if (renamer.NeedsReplace()) {
423 					// See if user wants to update style name through script
424 					int answer = wxMessageBox(
425 						_("Do you want to change all instances of this style in the script to this new name?"),
426 						_("Update script?"),
427 						wxYES_NO | wxCANCEL);
428 
429 					if (answer == wxCANCEL) return;
430 
431 					if (answer == wxYES) {
432 						did_rename = true;
433 						renamer.Replace();
434 					}
435 				}
436 			}
437 
438 			work->name = new_name;
439 		}
440 
441 		UpdateWorkStyle();
442 
443 		*style = *work;
444 		style->UpdateData();
445 		if (is_new) {
446 			if (store)
447 				store->push_back(std::unique_ptr<AssStyle>(style));
448 			else
449 				c->ass->Styles.push_back(*style);
450 			is_new = false;
451 		}
452 		if (!store)
453 			c->ass->Commit(_("style change"), AssFile::COMMIT_STYLES | (did_rename ? AssFile::COMMIT_DIAG_FULL : 0));
454 
455 		// Update preview
456 		if (!close) SubsPreview->SetStyle(*style);
457 	}
458 
459 	if (close) {
460 		EndModal(apply);
461 		if (PreviewText)
462 			OPT_SET("Tool/Style Editor/Preview Text")->SetString(from_wx(PreviewText->GetValue()));
463 	}
464 }
465 
UpdateWorkStyle()466 void DialogStyleEditor::UpdateWorkStyle() {
467 	updating = true;
468 	TransferDataFromWindow();
469 	updating = false;
470 
471 	work->font = from_wx(FontName->GetValue());
472 
473 	long templ = 0;
474 	Encoding->GetValue().BeforeFirst('-').ToLong(&templ);
475 	work->encoding = templ;
476 
477 	work->borderstyle = OutlineType->IsChecked() ? 3 : 1;
478 
479 	work->alignment = ControlToAlign(Alignment->GetSelection());
480 
481 	for (size_t i = 0; i < 3; ++i)
482 		work->Margin[i] = margin[i]->GetValue();
483 
484 	work->bold = BoxBold->IsChecked();
485 	work->italic = BoxItalic->IsChecked();
486 	work->underline = BoxUnderline->IsChecked();
487 	work->strikeout = BoxStrikeout->IsChecked();
488 }
489 
OnSetColor(wxThreadEvent &)490 void DialogStyleEditor::OnSetColor(wxThreadEvent&) {
491 	TransferDataFromWindow();
492 	SubsPreview->SetStyle(*work);
493 }
494 
OnChildFocus(wxChildFocusEvent & event)495 void DialogStyleEditor::OnChildFocus(wxChildFocusEvent &event) {
496 	UpdateWorkStyle();
497 	SubsPreview->SetStyle(*work);
498 	event.Skip();
499 }
500 
OnPreviewTextChange(wxCommandEvent & event)501 void DialogStyleEditor::OnPreviewTextChange (wxCommandEvent &event) {
502 	SubsPreview->SetText(from_wx(PreviewText->GetValue()));
503 	event.Skip();
504 }
505 
OnPreviewColourChange(wxThreadEvent & evt)506 void DialogStyleEditor::OnPreviewColourChange(wxThreadEvent &evt) {
507 	SubsPreview->SetColour(evt.GetPayload<agi::Color>());
508 	OPT_SET("Colour/Style Editor/Background/Preview")->SetColor(evt.GetPayload<agi::Color>());
509 }
510 
OnCommandPreviewUpdate(wxCommandEvent & event)511 void DialogStyleEditor::OnCommandPreviewUpdate(wxCommandEvent &event) {
512 	UpdateWorkStyle();
513 	SubsPreview->SetStyle(*work);
514 	event.Skip();
515 }
516 
ControlToAlign(int n)517 int DialogStyleEditor::ControlToAlign(int n) {
518 	switch (n) {
519 		case 0: return 7;
520 		case 1: return 8;
521 		case 2: return 9;
522 		case 3: return 4;
523 		case 4: return 5;
524 		case 5: return 6;
525 		case 6: return 1;
526 		case 7: return 2;
527 		case 8: return 3;
528 		default: return 2;
529 	}
530 }
531 
AlignToControl(int n)532 int DialogStyleEditor::AlignToControl(int n) {
533 	switch (n) {
534 		case 7: return 0;
535 		case 8: return 1;
536 		case 9: return 2;
537 		case 4: return 3;
538 		case 5: return 4;
539 		case 6: return 5;
540 		case 1: return 6;
541 		case 2: return 7;
542 		case 3: return 8;
543 		default: return 7;
544 	}
545 }
546