1 /***********************************************************************
2  *
3  * Copyright (C) 2009, 2010, 2011, 2012, 2013, 2014, 2016, 2019 Graeme Gott <graeme@gottcode.org>
4  *
5  * This program is free software: you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation, either version 3 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
17  *
18  ***********************************************************************/
19 
20 #include "theme_dialog.h"
21 
22 #include "color_button.h"
23 #include "font_combobox.h"
24 #include "image_button.h"
25 #include "theme.h"
26 #include "theme_renderer.h"
27 
28 #include <QApplication>
29 #include <QCheckBox>
30 #include <QComboBox>
31 #include <QDesktopWidget>
32 #include <QDialogButtonBox>
33 #include <QDoubleValidator>
34 #include <QFile>
35 #include <QFormLayout>
36 #include <QGroupBox>
37 #include <QImageReader>
38 #include <QLabel>
39 #include <QLineEdit>
40 #include <QScrollArea>
41 #include <QSettings>
42 #include <QSpinBox>
43 #include <QTabWidget>
44 #include <QVBoxLayout>
45 
46 #include <cmath>
47 
48 //-----------------------------------------------------------------------------
49 
50 // Exported by QtGui
51 void qt_blurImage(QPainter* p, QImage& blurImage, qreal radius, bool quality, bool alphaOnly, int transposed = 0);
52 
53 //-----------------------------------------------------------------------------
54 
ThemeDialog(Theme & theme,QWidget * parent)55 ThemeDialog::ThemeDialog(Theme& theme, QWidget* parent)
56 	: QDialog(parent, Qt::WindowTitleHint | Qt::WindowSystemMenuHint | Qt::WindowCloseButtonHint),
57 	m_theme(theme)
58 {
59 	setWindowTitle(tr("Edit Theme"));
60 	setWindowModality(Qt::WindowModal);
61 
62 	// Create name edit
63 	m_name = new QLineEdit(this);
64 	m_name->setText(m_theme.name());
65 	connect(m_name, &QLineEdit::textChanged, this, &ThemeDialog::checkNameAvailable);
66 
67 	QHBoxLayout* name_layout = new QHBoxLayout;
68 	name_layout->setContentsMargins(0, 0, 0, 0);
69 	name_layout->addWidget(new QLabel(tr("Name:"), this));
70 	name_layout->addWidget(m_name);
71 
72 
73 	// Create scrollarea
74 	QWidget* contents = new QWidget(this);
75 
76 	QScrollArea* scroll = new QScrollArea(this);
77 	scroll->setWidget(contents);
78 	scroll->setWidgetResizable(true);
79 
80 
81 	// Create text group
82 	QGroupBox* text_group = new QGroupBox(tr("Text"), contents);
83 
84 	m_text_color = new ColorButton(text_group);
85 	m_text_color->setColor(m_theme.textColor());
86 	connect(m_text_color, &ColorButton::changed, this, &ThemeDialog::renderPreview);
87 
88 	m_font_names = new FontComboBox(text_group);
89 	m_font_names->setEditable(false);
90 	m_font_names->setCurrentFont(m_theme.textFont());
91 	connect(m_font_names, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &ThemeDialog::fontChanged);
92 	connect(m_font_names, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &ThemeDialog::renderPreview);
93 
94 	m_font_sizes = new QComboBox(text_group);
95 	m_font_sizes->setEditable(true);
96 	m_font_sizes->setMinimumContentsLength(3);
97 	connect(m_font_sizes, &QComboBox::editTextChanged, this, &ThemeDialog::renderPreview);
98 	fontChanged();
99 
100 	m_misspelled_color = new ColorButton(text_group);
101 	m_misspelled_color->setColor(m_theme.misspelledColor());
102 	connect(m_misspelled_color, &ColorButton::changed, this, &ThemeDialog::renderPreview);
103 
104 	QHBoxLayout* font_layout = new QHBoxLayout;
105 	font_layout->addWidget(m_font_names);
106 	font_layout->addWidget(m_font_sizes);
107 
108 	QFormLayout* text_layout = new QFormLayout(text_group);
109 	text_layout->setFieldGrowthPolicy(QFormLayout::FieldsStayAtSizeHint);
110 	text_layout->addRow(tr("Color:"), m_text_color);
111 	text_layout->addRow(tr("Font:"), font_layout);
112 	text_layout->addRow(tr("Misspelled:"), m_misspelled_color);
113 
114 
115 	// Create background group
116 	QGroupBox* background_group = new QGroupBox(tr("Window Background"), contents);
117 
118 	m_background_color = new ColorButton(background_group);
119 	m_background_color->setColor(m_theme.backgroundColor());
120 	connect(m_background_color, &ColorButton::changed, this, &ThemeDialog::renderPreview);
121 
122 	m_background_image = new ImageButton(background_group);
123 	m_background_image->setImage(m_theme.backgroundImage(), m_theme.backgroundPath());
124 	connect(m_background_image, &ImageButton::changed, this, &ThemeDialog::imageChanged);
125 
126 	m_clear_image = new QPushButton(tr("Remove"), background_group);
127 	connect(m_clear_image, &QPushButton::clicked, m_background_image, &ImageButton::unsetImage);
128 
129 	m_background_type = new QComboBox(background_group);
130 	m_background_type->addItems(QStringList() << tr("No Image") << tr("Tiled") << tr("Centered") << tr("Stretched") << tr("Scaled") << tr("Zoomed"));
131 	m_background_type->setCurrentIndex(m_theme.backgroundType());
132 	connect(m_background_type, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &ThemeDialog::renderPreview);
133 
134 	QVBoxLayout* image_layout = new QVBoxLayout;
135 	image_layout->setSpacing(0);
136 	image_layout->setContentsMargins(0, 0, 0, 0);
137 	image_layout->addWidget(m_background_image);
138 	image_layout->addWidget(m_clear_image);
139 
140 	QFormLayout* background_layout = new QFormLayout(background_group);
141 	background_layout->setFieldGrowthPolicy(QFormLayout::FieldsStayAtSizeHint);
142 	background_layout->addRow(tr("Color:"), m_background_color);
143 	background_layout->addRow(tr("Image:"), image_layout);
144 	background_layout->addRow(tr("Type:"), m_background_type);
145 
146 
147 	// Create foreground group
148 	QGroupBox* foreground_group = new QGroupBox(tr("Text Background"), contents);
149 
150 	m_foreground_color = new ColorButton(foreground_group);
151 	m_foreground_color->setColor(m_theme.foregroundColor());
152 	connect(m_foreground_color, &ColorButton::changed, this, &ThemeDialog::renderPreview);
153 
154 	m_foreground_opacity = new QSpinBox(foreground_group);
155 	m_foreground_opacity->setCorrectionMode(QSpinBox::CorrectToNearestValue);
156 	m_foreground_opacity->setSuffix(QLocale().percent());
157 	m_foreground_opacity->setRange(theme.foregroundOpacity().minimumValue(), theme.foregroundOpacity().maximumValue());
158 	m_foreground_opacity->setValue(m_theme.foregroundOpacity());
159 	connect(m_foreground_opacity, QOverload<int>::of(&QSpinBox::valueChanged), this, &ThemeDialog::renderPreview);
160 
161 	m_foreground_position = new QComboBox(foreground_group);
162 	m_foreground_position->addItems(QStringList() << tr("Left") << tr("Centered") << tr("Right") << tr("Stretched"));
163 	m_foreground_position->setCurrentIndex(m_theme.foregroundPosition());
164 	connect(m_foreground_position, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &ThemeDialog::positionChanged);
165 
166 	m_foreground_width = new QSpinBox(foreground_group);
167 	m_foreground_width->setCorrectionMode(QSpinBox::CorrectToNearestValue);
168 	m_foreground_width->setSuffix(tr(" pixels"));
169 	m_foreground_width->setRange(theme.foregroundWidth().minimumValue(), theme.foregroundWidth().maximumValue());
170 	m_foreground_width->setValue(m_theme.foregroundWidth());
171 	m_foreground_width->setEnabled(m_theme.foregroundPosition() != 3);
172 	connect(m_foreground_width, QOverload<int>::of(&QSpinBox::valueChanged), this, &ThemeDialog::renderPreview);
173 
174 	QFormLayout* foreground_layout = new QFormLayout(foreground_group);
175 	foreground_layout->setFieldGrowthPolicy(QFormLayout::FieldsStayAtSizeHint);
176 	foreground_layout->addRow(tr("Color:"), m_foreground_color);
177 	foreground_layout->addRow(tr("Opacity:"), m_foreground_opacity);
178 	foreground_layout->addRow(tr("Position:"), m_foreground_position);
179 	foreground_layout->addRow(tr("Width:"), m_foreground_width);
180 
181 
182 	// Create rounding group
183 	m_round_corners = new QGroupBox(tr("Round Text Background Corners"), contents);
184 	m_round_corners->setCheckable(true);
185 	m_round_corners->setChecked(m_theme.roundCornersEnabled());
186 	connect(m_round_corners, &QGroupBox::clicked, this, &ThemeDialog::renderPreview);
187 
188 	m_corner_radius = new QSpinBox(m_round_corners);
189 	m_corner_radius->setCorrectionMode(QSpinBox::CorrectToNearestValue);
190 	m_corner_radius->setSuffix(tr(" pixels"));
191 	m_corner_radius->setRange(theme.cornerRadius().minimumValue(), theme.cornerRadius().maximumValue());
192 	m_corner_radius->setValue(m_theme.cornerRadius());
193 	connect(m_corner_radius, QOverload<int>::of(&QSpinBox::valueChanged), this, &ThemeDialog::renderPreview);
194 
195 	QFormLayout* corner_layout = new QFormLayout(m_round_corners);
196 	corner_layout->setFieldGrowthPolicy(QFormLayout::FieldsStayAtSizeHint);
197 	corner_layout->addRow(tr("Radius:"), m_corner_radius);
198 
199 
200 	// Create blur group
201 	m_blur = new QGroupBox(tr("Blur Text Background"), contents);
202 	m_blur->setCheckable(true);
203 	m_blur->setChecked(m_theme.blurEnabled());
204 	connect(m_blur, &QGroupBox::clicked, this, &ThemeDialog::renderPreview);
205 
206 	m_blur_radius = new QSpinBox(m_blur);
207 	m_blur_radius->setCorrectionMode(QSpinBox::CorrectToNearestValue);
208 	m_blur_radius->setSuffix(tr(" pixels"));
209 	m_blur_radius->setRange(theme.blurRadius().minimumValue(), theme.blurRadius().maximumValue());
210 	m_blur_radius->setValue(m_theme.blurRadius());
211 	connect(m_blur_radius, QOverload<int>::of(&QSpinBox::valueChanged), this, &ThemeDialog::renderPreview);
212 
213 	QFormLayout* blur_layout = new QFormLayout(m_blur);
214 	blur_layout->setFieldGrowthPolicy(QFormLayout::FieldsStayAtSizeHint);
215 	blur_layout->addRow(tr("Radius:"), m_blur_radius);
216 
217 
218 	// Create shadow group
219 	m_shadow = new QGroupBox(tr("Text Background Drop Shadow"), contents);
220 	m_shadow->setCheckable(true);
221 	m_shadow->setChecked(m_theme.shadowEnabled());
222 	connect(m_shadow, &QGroupBox::clicked, this, &ThemeDialog::renderPreview);
223 
224 	m_shadow_color = new ColorButton(m_shadow);
225 	m_shadow_color->setColor(m_theme.shadowColor());
226 	connect(m_shadow_color, &ColorButton::changed, this, &ThemeDialog::renderPreview);
227 
228 	m_shadow_radius = new QSpinBox(m_shadow);
229 	m_shadow_radius->setCorrectionMode(QSpinBox::CorrectToNearestValue);
230 	m_shadow_radius->setSuffix(tr(" pixels"));
231 	m_shadow_radius->setRange(theme.shadowRadius().minimumValue(), theme.shadowRadius().maximumValue());
232 	m_shadow_radius->setValue(m_theme.shadowRadius());
233 	connect(m_shadow_radius, QOverload<int>::of(&QSpinBox::valueChanged), this, &ThemeDialog::renderPreview);
234 
235 	m_shadow_offset = new QSpinBox(m_shadow);
236 	m_shadow_offset->setCorrectionMode(QSpinBox::CorrectToNearestValue);
237 	m_shadow_offset->setSuffix(tr(" pixels"));
238 	m_shadow_offset->setRange(theme.shadowOffset().minimumValue(), theme.shadowOffset().maximumValue());
239 	m_shadow_offset->setValue(m_theme.shadowOffset());
240 	connect(m_shadow_offset, QOverload<int>::of(&QSpinBox::valueChanged), this, &ThemeDialog::renderPreview);
241 
242 	QFormLayout* shadow_layout = new QFormLayout(m_shadow);
243 	shadow_layout->setFieldGrowthPolicy(QFormLayout::FieldsStayAtSizeHint);
244 	shadow_layout->addRow(tr("Color:"), m_shadow_color);
245 	shadow_layout->addRow(tr("Radius:"), m_shadow_radius);
246 	shadow_layout->addRow(tr("Vertical Offset:"), m_shadow_offset);
247 
248 
249 	// Create margins group
250 	QGroupBox* margins_group = new QGroupBox(tr("Margins"), contents);
251 
252 	m_foreground_margin = new QSpinBox(margins_group);
253 	m_foreground_margin->setCorrectionMode(QSpinBox::CorrectToNearestValue);
254 	m_foreground_margin->setSuffix(tr(" pixels"));
255 	m_foreground_margin->setRange(theme.foregroundMargin().minimumValue(), theme.foregroundMargin().maximumValue());
256 	m_foreground_margin->setValue(m_theme.foregroundMargin());
257 	connect(m_foreground_margin, QOverload<int>::of(&QSpinBox::valueChanged), this, &ThemeDialog::renderPreview);
258 
259 	m_foreground_padding = new QSpinBox(margins_group);
260 	m_foreground_padding->setCorrectionMode(QSpinBox::CorrectToNearestValue);
261 	m_foreground_padding->setSuffix(tr(" pixels"));
262 	m_foreground_padding->setRange(theme.foregroundPadding().minimumValue(), theme.foregroundPadding().maximumValue());
263 	m_foreground_padding->setValue(m_theme.foregroundPadding());
264 	connect(m_foreground_padding, QOverload<int>::of(&QSpinBox::valueChanged), this, &ThemeDialog::renderPreview);
265 
266 	QFormLayout* margins_layout = new QFormLayout(margins_group);
267 	margins_layout->setFieldGrowthPolicy(QFormLayout::FieldsStayAtSizeHint);
268 	margins_layout->addRow(tr("Window:"), m_foreground_margin);
269 	margins_layout->addRow(tr("Page:"), m_foreground_padding);
270 
271 
272 	// Create line spacing group
273 	QGroupBox* line_spacing = new QGroupBox(tr("Line Spacing"), contents);
274 
275 	m_line_spacing_type = new QComboBox(line_spacing);
276 	m_line_spacing_type->setEditable(false);
277 	m_line_spacing_type->addItems(QStringList() << tr("Single") << tr("1.5 Lines") << tr("Double") << tr("Proportional"));
278 	m_line_spacing_type->setCurrentIndex(3);
279 
280 	m_line_spacing = new QSpinBox(line_spacing);
281 	m_line_spacing->setSuffix(QLocale().percent());
282 	m_line_spacing->setRange(theme.lineSpacing().minimumValue(), theme.lineSpacing().maximumValue());
283 	m_line_spacing->setValue(m_theme.lineSpacing());
284 	m_line_spacing->setEnabled(false);
285 
286 	switch (m_theme.lineSpacing()) {
287 	case 100: m_line_spacing_type->setCurrentIndex(0); break;
288 	case 150: m_line_spacing_type->setCurrentIndex(1); break;
289 	case 200: m_line_spacing_type->setCurrentIndex(2); break;
290 	default: m_line_spacing->setEnabled(true); break;
291 	}
292 	connect(m_line_spacing_type, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &ThemeDialog::lineSpacingChanged);
293 	connect(m_line_spacing_type, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &ThemeDialog::renderPreview);
294 	connect(m_line_spacing, QOverload<int>::of(&QSpinBox::valueChanged), this, &ThemeDialog::renderPreview);
295 
296 	QFormLayout* line_spacing_layout = new QFormLayout(line_spacing);
297 	line_spacing_layout->setFieldGrowthPolicy(QFormLayout::FieldsStayAtSizeHint);
298 	line_spacing_layout->addRow(tr("Type:"), m_line_spacing_type);
299 	line_spacing_layout->addRow(tr("Height:"), m_line_spacing);
300 
301 
302 	// Create paragraph spacing group
303 	QGroupBox* paragraph_spacing = new QGroupBox(tr("Paragraph Spacing"), contents);
304 
305 	m_tab_width = new QSpinBox(paragraph_spacing);
306 	m_tab_width->setSuffix(tr(" pixels"));
307 	m_tab_width->setRange(theme.tabWidth().minimumValue(), theme.tabWidth().maximumValue());
308 	m_tab_width->setValue(m_theme.tabWidth());
309 	connect(m_tab_width, QOverload<int>::of(&QSpinBox::valueChanged), this, &ThemeDialog::renderPreview);
310 
311 	m_spacing_above_paragraph = new QSpinBox(paragraph_spacing);
312 	m_spacing_above_paragraph->setSuffix(tr(" pixels"));
313 	m_spacing_above_paragraph->setRange(theme.spacingAboveParagraph().minimumValue(), theme.spacingAboveParagraph().maximumValue());
314 	m_spacing_above_paragraph->setValue(m_theme.spacingAboveParagraph());
315 	connect(m_spacing_above_paragraph, QOverload<int>::of(&QSpinBox::valueChanged), this, &ThemeDialog::renderPreview);
316 
317 	m_spacing_below_paragraph = new QSpinBox(paragraph_spacing);
318 	m_spacing_below_paragraph->setSuffix(tr(" pixels"));
319 	m_spacing_below_paragraph->setRange(theme.spacingBelowParagraph().minimumValue(), theme.spacingBelowParagraph().maximumValue());
320 	m_spacing_below_paragraph->setValue(m_theme.spacingBelowParagraph());
321 	connect(m_spacing_below_paragraph, QOverload<int>::of(&QSpinBox::valueChanged), this, &ThemeDialog::renderPreview);
322 
323 	m_indent_first_line = new QCheckBox(tr("Indent first line"), paragraph_spacing);
324 	m_indent_first_line->setChecked(m_theme.indentFirstLine());
325 	connect(m_indent_first_line, &QCheckBox::toggled, this, &ThemeDialog::renderPreview);
326 
327 	QFormLayout* paragraph_spacing_layout = new QFormLayout(paragraph_spacing);
328 	paragraph_spacing_layout->setFieldGrowthPolicy(QFormLayout::FieldsStayAtSizeHint);
329 	paragraph_spacing_layout->addRow(tr("Tab Width:"), m_tab_width);
330 	paragraph_spacing_layout->addRow(tr("Above:"), m_spacing_above_paragraph);
331 	paragraph_spacing_layout->addRow(tr("Below:"), m_spacing_below_paragraph);
332 	paragraph_spacing_layout->addRow("", m_indent_first_line);
333 
334 
335 	// Create preview
336 	m_preview = new QLabel(this);
337 	m_preview->setAlignment(Qt::AlignCenter);
338 	m_preview->setFrameStyle(QFrame::Sunken | QFrame::StyledPanel);
339 
340 	QPixmap pixmap(480, 270);
341 	pixmap.fill(palette().window().color());
342 	m_preview->setPixmap(pixmap);
343 
344 	m_theme_renderer = new ThemeRenderer(this);
345 	connect(m_theme_renderer, &ThemeRenderer::rendered, this, &ThemeDialog::renderText);
346 	renderPreview();
347 
348 
349 	// Lay out dialog
350 	QDialogButtonBox* buttons = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, Qt::Horizontal, this);
351 	m_ok = buttons->button(QDialogButtonBox::Ok);
352 	connect(buttons, &QDialogButtonBox::accepted, this, &ThemeDialog::accept);
353 	connect(buttons, &QDialogButtonBox::rejected, this, &ThemeDialog::reject);
354 
355 	QVBoxLayout* groups_layout = new QVBoxLayout(contents);
356 	groups_layout->addWidget(text_group);
357 	groups_layout->addWidget(background_group);
358 	groups_layout->addWidget(foreground_group);
359 	groups_layout->addWidget(m_round_corners);
360 	groups_layout->addWidget(m_blur);
361 	groups_layout->addWidget(m_shadow);
362 	groups_layout->addWidget(margins_group);
363 	groups_layout->addWidget(line_spacing);
364 	groups_layout->addWidget(paragraph_spacing);
365 
366 	QGridLayout* layout = new QGridLayout(this);
367 	layout->setColumnStretch(0, 1);
368 	layout->setRowStretch(1, 1);
369 	layout->setRowMinimumHeight(2, layout->contentsMargins().top());
370 	layout->addLayout(name_layout, 0, 0, 1, 2);
371 	layout->addWidget(scroll, 1, 0, 1, 1);
372 	layout->addWidget(m_preview, 1, 1, 1, 1, Qt::AlignCenter);
373 	layout->addWidget(buttons, 3, 0, 1, 2);
374 
375 	resize(QSettings().value("ThemeDialog/Size", sizeHint()).toSize());
376 }
377 
378 //-----------------------------------------------------------------------------
379 
~ThemeDialog()380 ThemeDialog::~ThemeDialog()
381 {
382 	m_theme_renderer->wait();
383 }
384 
385 //-----------------------------------------------------------------------------
386 
accept()387 void ThemeDialog::accept()
388 {
389 	m_theme.setName(m_name->text().simplified());
390 	setValues(m_theme);
391 	if (!m_theme.isDefault()) {
392 		m_theme.setLoadColor(m_load_color);
393 	}
394 	m_theme.saveChanges();
395 
396 	savePreview();
397 
398 	QDialog::accept();
399 }
400 
401 //-----------------------------------------------------------------------------
402 
hideEvent(QHideEvent * event)403 void ThemeDialog::hideEvent(QHideEvent* event)
404 {
405 	QSettings().setValue("ThemeDialog/Size", size());
406 	QDialog::hideEvent(event);
407 }
408 
409 //-----------------------------------------------------------------------------
410 
checkNameAvailable()411 void ThemeDialog::checkNameAvailable()
412 {
413 	QString name = m_name->text().simplified();
414 	bool empty = name.isEmpty();
415 	bool changed = (name != m_theme.name());
416 	bool exists = Theme::exists(name);
417 	m_ok->setEnabled(!changed || (!empty && !exists));
418 }
419 
420 //-----------------------------------------------------------------------------
421 
fontChanged()422 void ThemeDialog::fontChanged()
423 {
424 	QFontDatabase db;
425 
426 	QFont font = m_font_names->currentFont();
427 	QList<int> font_sizes = db.smoothSizes(font.family(), QString());
428 	if (font_sizes.isEmpty()) {
429 		font_sizes = db.standardSizes();
430 	}
431 	qreal font_size = m_font_sizes->currentText().toDouble();
432 	if (font_size < 0.1) {
433 		font_size = std::lround(m_theme.textFont().pointSizeF() * 10.0) * 0.1;
434 	}
435 
436 	m_font_sizes->blockSignals(true);
437 	m_font_sizes->clear();
438 	int index = 0;
439 	for (int i = 0; i < font_sizes.count(); ++i) {
440 		int size = font_sizes.at(i);
441 		if (size <= font_size) {
442 			index = i;
443 		}
444 		m_font_sizes->addItem(QString::number(size));
445 	}
446 	m_font_sizes->setCurrentIndex(index);
447 	m_font_sizes->setEditText(QString::number(font_size));
448 	m_font_sizes->setValidator(new QDoubleValidator(font_sizes.first(), font_sizes.last(), 1, m_font_sizes));
449 	m_font_sizes->blockSignals(false);
450 }
451 
452 //-----------------------------------------------------------------------------
453 
imageChanged()454 void ThemeDialog::imageChanged()
455 {
456 	if (!m_background_image->image().isEmpty()) {
457 		QSize image = QImageReader(m_background_image->image()).size();
458 		QSize desktop = QApplication::desktop()->size();
459 		if ((image.width() * image.height() * 4) <= (desktop.width() * desktop.height())) {
460 			m_background_type->setCurrentIndex(1);
461 		} else if (m_background_type->currentIndex() < 2) {
462 			m_background_type->setCurrentIndex(5);
463 		}
464 	} else {
465 		m_background_type->setCurrentIndex(0);
466 	}
467 	renderPreview();
468 }
469 
470 //-----------------------------------------------------------------------------
471 
lineSpacingChanged(int index)472 void ThemeDialog::lineSpacingChanged(int index)
473 {
474 	switch (index) {
475 	case 0:
476 		m_line_spacing->setValue(100);
477 		m_line_spacing->setEnabled(false);
478 		break;
479 
480 	case 1:
481 		m_line_spacing->setValue(150);
482 		m_line_spacing->setEnabled(false);
483 		break;
484 
485 	case 2:
486 		m_line_spacing->setValue(200);
487 		m_line_spacing->setEnabled(false);
488 		break;
489 
490 	default:
491 		m_line_spacing->setEnabled(true);
492 		break;
493 	}
494 }
495 
496 //-----------------------------------------------------------------------------
497 
positionChanged(int index)498 void ThemeDialog::positionChanged(int index)
499 {
500 	m_foreground_width->setEnabled(index != 3);
501 	renderPreview();
502 }
503 
504 //-----------------------------------------------------------------------------
505 
renderPreview()506 void ThemeDialog::renderPreview()
507 {
508 	m_clear_image->setEnabled(m_background_image->isEnabled() && !m_background_image->image().isEmpty());
509 
510 	// Load theme
511 	Theme theme;
512 	setValues(theme);
513 	theme.setBackgroundImage(m_background_image->image());
514 
515 	// Fetch load color
516 	m_load_color = theme.calculateLoadColor();
517 
518 	// Render theme
519 	m_theme_renderer->create(theme, QSize(1920, 1080), 0, devicePixelRatioF());
520 }
521 
522 //-----------------------------------------------------------------------------
523 
renderText(const QImage & background,const QRect & foreground,const Theme & theme)524 void ThemeDialog::renderText(const QImage& background, const QRect& foreground, const Theme& theme)
525 {
526 	QImage preview;
527 	theme.renderText(background, foreground, devicePixelRatioF(), &preview, &m_preview_icon);
528 	m_preview->setPixmap(QPixmap::fromImage(preview));
529 }
530 
531 //-----------------------------------------------------------------------------
532 
savePreview()533 void ThemeDialog::savePreview()
534 {
535 	Theme::removeIcon(m_theme.id(), m_theme.isDefault());
536 	m_preview_icon.save(Theme::iconPath(m_theme.id(), m_theme.isDefault(), devicePixelRatioF()), "", 0);
537 }
538 
539 //-----------------------------------------------------------------------------
540 
setValues(Theme & theme)541 void ThemeDialog::setValues(Theme& theme)
542 {
543 	theme.setBackgroundType(m_background_type->currentIndex());
544 	theme.setBackgroundColor(m_background_color->color());
545 	theme.setBackgroundImage(m_background_image->toString());
546 
547 	theme.setForegroundColor(m_foreground_color->color());
548 	theme.setForegroundOpacity(m_foreground_opacity->value());
549 	theme.setForegroundWidth(m_foreground_width->value());
550 	theme.setForegroundMargin(m_foreground_margin->value());
551 	theme.setForegroundPadding(m_foreground_padding->value());
552 	theme.setForegroundPosition(m_foreground_position->currentIndex());
553 
554 	theme.setRoundCornersEnabled(m_round_corners->isChecked());
555 	theme.setCornerRadius(m_corner_radius->value());
556 
557 	theme.setBlurEnabled(m_blur->isChecked());
558 	theme.setBlurRadius(m_blur_radius->value());
559 
560 	theme.setShadowEnabled(m_shadow->isChecked());
561 	theme.setShadowColor(m_shadow_color->color());
562 	theme.setShadowRadius(m_shadow_radius->value());
563 	theme.setShadowOffset(m_shadow_offset->value());
564 
565 	theme.setTextColor(m_text_color->color());
566 	QFont font = m_font_names->currentFont();
567 	font.setPointSizeF(m_font_sizes->currentText().toDouble());
568 	theme.setTextFont(font);
569 	theme.setMisspelledColor(m_misspelled_color->color());
570 
571 	theme.setIndentFirstLine(m_indent_first_line->isChecked());
572 	theme.setLineSpacing(m_line_spacing->value());
573 	theme.setSpacingAboveParagraph(m_spacing_above_paragraph->value());
574 	theme.setSpacingBelowParagraph(m_spacing_below_paragraph->value());
575 	theme.setTabWidth(m_tab_width->value());
576 }
577 
578 //-----------------------------------------------------------------------------
579