diff --git a/.travis.yml b/.travis.yml index 06654b9c..23de3ef6 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,13 +1,15 @@ language: cpp compiler: - - gcc + - g++ - clang before_install: + - sudo add-apt-repository -y ppa:ubuntu-toolchain-r/test # for g++ 4.8 - sudo add-apt-repository -y ppa:beineri/opt-qt521 # for Qt 5.2 - sudo apt-get update -qq - "export DISPLAY=:99.0" - "sh -e /etc/init.d/xvfb start" install: + - if [ "$CXX" == "g++" ]; then sudo apt-get install -qq g++-4.8; sudo update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-4.8 90; fi - sudo apt-get install -qq qt52base qt52declarative qt52webkit qt52tools libhunspell-dev - sudo apt-get install -qq libgstreamer0.10-dev libgstreamer-plugins-base0.10-dev before_script: diff --git a/app-static/app-static.pro b/app-static/app-static.pro index 1e760ff5..d697818d 100644 --- a/app-static/app-static.pro +++ b/app-static/app-static.pro @@ -21,6 +21,10 @@ SOURCES += \ converter/revealmarkdownconverter.cpp \ template/htmltemplate.cpp \ template/presentationtemplate.cpp \ + themes/jsonthemetranslator.cpp \ + themes/stylemanager.cpp \ + themes/theme.cpp \ + themes/themecollection.cpp \ completionlistmodel.cpp \ datalocation.cpp \ slidelinemapping.cpp \ @@ -43,6 +47,11 @@ HEADERS += \ template/template.h \ template/htmltemplate.h \ template/presentationtemplate.h \ + themes/jsonthemetranslator.h \ + themes/jsonthemetranslatorfactory.h \ + themes/stylemanager.h \ + themes/theme.h \ + themes/themecollection.h \ completionlistmodel.h \ datalocation.h \ slidelinemapping.h \ diff --git a/app-static/themes/jsonthemetranslator.cpp b/app-static/themes/jsonthemetranslator.cpp new file mode 100644 index 00000000..eeb36d7e --- /dev/null +++ b/app-static/themes/jsonthemetranslator.cpp @@ -0,0 +1,51 @@ +/* + * Copyright 2015 Christian Loose + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include "jsonthemetranslator.h" + +namespace { + +static const QLatin1String NAME("name"); +static const QLatin1String MARKDOWN_HIGHLIGHTING("markdownHighlighting"); +static const QLatin1String CODE_HIGHLIGHTING("codeHighlighting"); +static const QLatin1String PREVIEW_STYLESHEET("previewStylesheet"); +static const QLatin1String BUILT_IN("builtIn"); + +} + +Theme JsonThemeTranslator::fromJsonObject(const QJsonObject &object) +{ + QString name = object.value(NAME).toString(); + QString markdownHighlighting = object.value(MARKDOWN_HIGHLIGHTING).toString(); + QString codeHighlighting = object.value(CODE_HIGHLIGHTING).toString(); + QString previewStylesheet = object.value(PREVIEW_STYLESHEET).toString(); + bool builtIn = object.value(BUILT_IN).toBool(); + + return { name, markdownHighlighting, codeHighlighting, previewStylesheet, builtIn }; +} + +QJsonObject JsonThemeTranslator::toJsonObject(const Theme &theme) +{ + QJsonObject object; + object.insert(NAME, theme.name()); + object.insert(MARKDOWN_HIGHLIGHTING, theme.markdownHighlighting()); + object.insert(CODE_HIGHLIGHTING, theme.codeHighlighting()); + object.insert(PREVIEW_STYLESHEET, theme.previewStylesheet()); + object.insert(BUILT_IN, theme.isBuiltIn()); + + return object; +} + diff --git a/app-static/themes/jsonthemetranslator.h b/app-static/themes/jsonthemetranslator.h new file mode 100644 index 00000000..de13f0d8 --- /dev/null +++ b/app-static/themes/jsonthemetranslator.h @@ -0,0 +1,34 @@ +/* + * Copyright 2015 Christian Loose + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#ifndef JSONTHEMETRANSLATOR_H +#define JSONTHEMETRANSLATOR_H + +#include +#include +#include "theme.h" + + +class JsonThemeTranslator : public JsonTranslator +{ +private: + Theme fromJsonObject(const QJsonObject &object); + QJsonObject toJsonObject(const Theme &theme); +}; + +#endif // JSONTHEMETRANSLATOR_H + + diff --git a/app-static/themes/jsonthemetranslatorfactory.h b/app-static/themes/jsonthemetranslatorfactory.h new file mode 100644 index 00000000..5d4994b7 --- /dev/null +++ b/app-static/themes/jsonthemetranslatorfactory.h @@ -0,0 +1,37 @@ +/* + * Copyright 2015 Christian Loose + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#ifndef JSONTHEMETRANSLATORFACTORY_H +#define JSONTHEMETRANSLATORFACTORY_H + +#include +#include + +#include "themes/theme.h" +#include "themes/jsonthemetranslator.h" + + +template <> class JsonTranslatorFactory +{ +public: + static JsonTranslator *create() + { + return new JsonThemeTranslator(); + } +}; + +#endif // JSONTHEMETRANSLATORFACTORY_H + diff --git a/app-static/themes/stylemanager.cpp b/app-static/themes/stylemanager.cpp new file mode 100644 index 00000000..36185d97 --- /dev/null +++ b/app-static/themes/stylemanager.cpp @@ -0,0 +1,72 @@ +/* + * Copyright 2015 Christian Loose + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include "stylemanager.h" + +#include + + +static const QMap BUILTIN_MARKDOWN_HIGHLIGHTINGS = { + { "Default", "default" }, + { "Solarized Light", "solarized-light" }, + { "Solarized Dark", "solarized-dark" }, + { "Clearness Dark", "clearness-dark" }, + { "Byword Dark", "byword-dark" } +}; + +static const QMap BUILTIN_CODE_HIGHLIGHTINGS = { + { "Default", "default" }, + { "Github", "github" }, + { "Solarized Light", "solarized_light" }, + { "Solarized Dark", "solarized_dark" } +}; + +static const QMap BUILTIN_PREVIEW_STYLESHEETS = { + { "Default", "qrc:/css/markdown.css" }, + { "Github", "qrc:/css/github.css" }, + { "Solarized Light", "qrc:/css/solarized-light.css" }, + { "Solarized Dark", "qrc:/css/solarized-dark.css" }, + { "Clearness", "qrc:/css/clearness.css" }, + { "Clearness Dark", "qrc:/css/clearness-dark.css" }, + { "Byword Dark", "qrc:/css/byword-dark.css" } +}; + +QMap StyleManager::customPreviewStylesheets; + + +void StyleManager::insertCustomPreviewStylesheet(const QString &styleName, const QString &stylePath) +{ + customPreviewStylesheets.insert(styleName, stylePath); +} + +QString StyleManager::markdownHighlightingPath(const Theme &theme) +{ + return BUILTIN_MARKDOWN_HIGHLIGHTINGS[theme.markdownHighlighting()]; +} + +QString StyleManager::codeHighlightingPath(const Theme &theme) +{ + return BUILTIN_CODE_HIGHLIGHTINGS[theme.codeHighlighting()]; +} + +QString StyleManager::previewStylesheetPath(const Theme &theme) +{ + if (customPreviewStylesheets.contains(theme.previewStylesheet())) { + return customPreviewStylesheets[theme.previewStylesheet()]; + } + + return BUILTIN_PREVIEW_STYLESHEETS[theme.previewStylesheet()]; +} diff --git a/app-static/themes/stylemanager.h b/app-static/themes/stylemanager.h new file mode 100644 index 00000000..ba4b7fd8 --- /dev/null +++ b/app-static/themes/stylemanager.h @@ -0,0 +1,39 @@ +/* + * Copyright 2015 Christian Loose + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#ifndef STYLEMANAGER_H +#define STYLEMANAGER_H + +#include +#include +#include "theme.h" + + +class StyleManager +{ +public: + void insertCustomPreviewStylesheet(const QString &styleName, const QString &stylePath); + + static QString markdownHighlightingPath(const Theme &theme); + static QString codeHighlightingPath(const Theme &theme); + static QString previewStylesheetPath(const Theme &theme); + +private: + static QMap customPreviewStylesheets; +}; + +#endif // STYLEMANAGER_H + diff --git a/app-static/themes/theme.cpp b/app-static/themes/theme.cpp new file mode 100644 index 00000000..3204f16d --- /dev/null +++ b/app-static/themes/theme.cpp @@ -0,0 +1,47 @@ +/* + * Copyright 2015 Christian Loose + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include "theme.h" + +Theme::Theme(const QString &name, + const QString &markdownHighlighting, + const QString &codeHighlighting, + const QString &previewStylesheet, + bool builtIn) : + m_name(name), + m_markdownHighlighting(markdownHighlighting), + m_codeHighlighting(codeHighlighting), + m_previewStylesheet(previewStylesheet), + m_builtIn(builtIn) +{ + checkInvariants(); +} + +void Theme::checkInvariants() const +{ + if (m_name.isEmpty()) { + throw std::runtime_error("theme name must not be empty"); + } + if (m_markdownHighlighting.isEmpty()) { + throw std::runtime_error("markdown highlighting style must not be empty"); + } + if (m_codeHighlighting.isEmpty()) { + throw std::runtime_error("code highlighting style must not be empty"); + } + if (m_previewStylesheet.isEmpty()) { + throw std::runtime_error("preview stylesheet must not be empty"); + } +} diff --git a/app-static/themes/theme.h b/app-static/themes/theme.h new file mode 100644 index 00000000..227a1a75 --- /dev/null +++ b/app-static/themes/theme.h @@ -0,0 +1,64 @@ +/* + * Copyright 2015 Christian Loose + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#ifndef THEME_H +#define THEME_H + +#include +#include + + +class Theme +{ +public: + Theme(const QString &name, + const QString &markdownHighlighting, + const QString &codeHighlighting, + const QString &previewStylesheet, + bool builtIn = false); + + QString name() const { return m_name; } + + QString markdownHighlighting() const { return m_markdownHighlighting; } + + QString codeHighlighting() const { return m_codeHighlighting; } + + QString previewStylesheet() const { return m_previewStylesheet; } + + bool isBuiltIn() const { return m_builtIn; } + + bool operator<(const Theme &rhs) const + { + return m_name < rhs.name(); + } + + bool operator ==(const Theme &rhs) const + { + return m_name == rhs.name(); + } + +private: + void checkInvariants() const; + +private: + QString m_name; + QString m_markdownHighlighting; + QString m_codeHighlighting; + QString m_previewStylesheet; + bool m_builtIn; +}; + +#endif // THEME_H diff --git a/app-static/themes/themecollection.cpp b/app-static/themes/themecollection.cpp new file mode 100644 index 00000000..4f4fe52f --- /dev/null +++ b/app-static/themes/themecollection.cpp @@ -0,0 +1,62 @@ +/* + * Copyright 2015 Christian Loose + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include "themecollection.h" + +#include +#include + +bool ThemeCollection::load(const QString &fileName) +{ + return JsonFile::load(fileName, this); +} + +int ThemeCollection::insert(const Theme &theme) +{ + themesIndex << theme.name(); + themes << theme; + return 0; +} + +int ThemeCollection::count() const +{ + return themes.count(); +} + +const Theme &ThemeCollection::at(int offset) const +{ + return themes.at(offset); +} + +bool ThemeCollection::contains(const QString &name) const +{ + return themesIndex.contains(name); +} + +const Theme ThemeCollection::theme(const QString &name) const +{ + return at(themesIndex.indexOf(name)); +} + +QStringList ThemeCollection::themeNames() const +{ + return themesIndex; +} + +const QString ThemeCollection::name() const +{ + return QStringLiteral("themes"); +} diff --git a/app-static/themes/themecollection.h b/app-static/themes/themecollection.h new file mode 100644 index 00000000..30cd0113 --- /dev/null +++ b/app-static/themes/themecollection.h @@ -0,0 +1,47 @@ +/* + * Copyright 2015 Christian Loose + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#ifndef THEMECOLLECTION_H +#define THEMECOLLECTION_H + +#include +#include +#include +#include "theme.h" + + +class ThemeCollection : public JsonCollection +{ +public: + bool load(const QString &fileName); + + int insert(const Theme &theme); + + int count() const; + const Theme &at(int offset) const; + bool contains(const QString &name) const; + const Theme theme(const QString &name) const; + QStringList themeNames() const; + + const QString name() const; + +private: + QStringList themesIndex; + QList themes; +}; + +#endif // THEMECOLLECTION_H + diff --git a/app/app.pro b/app/app.pro index 3f3197b1..a7faa183 100644 --- a/app/app.pro +++ b/app/app.pro @@ -131,7 +131,8 @@ OTHER_FILES += \ styles/clearness.css \ styles/byword-dark.css \ styles/solarized-light.css \ - markdown-snippets.json + markdown-snippets.json \ + builtin-htmlpreview-themes.json # translations lrelease.input = TRANSLATIONS diff --git a/app/builtin-htmlpreview-themes.json b/app/builtin-htmlpreview-themes.json new file mode 100644 index 00000000..49f2334f --- /dev/null +++ b/app/builtin-htmlpreview-themes.json @@ -0,0 +1,53 @@ +{ + "themes": [ + { + "name": "Default", + "markdownHighlighting": "Default", + "codeHighlighting": "Default", + "previewStylesheet": "Default", + "builtIn": true + }, + { + "name": "Github", + "markdownHighlighting": "Default", + "codeHighlighting": "Github", + "previewStylesheet": "Github", + "builtIn": true + }, + { + "name": "Solarized Light", + "markdownHighlighting": "Solarized Light", + "codeHighlighting": "Solarized Light", + "previewStylesheet": "Solarized Light", + "builtIn": true + }, + { + "name": "Solarized Dark", + "markdownHighlighting": "Solarized Dark", + "codeHighlighting": "Solarized Dark", + "previewStylesheet": "Solarized Dark", + "builtIn": true + }, + { + "name": "Clearness", + "markdownHighlighting": "Default", + "codeHighlighting": "Default", + "previewStylesheet": "Clearness", + "builtIn": true + }, + { + "name": "Clearness Dark", + "markdownHighlighting": "Clearness Dark", + "codeHighlighting": "Default", + "previewStylesheet": "Clearness Dark", + "builtIn": true + }, + { + "name": "Byword Dark", + "markdownHighlighting": "Byword Dark", + "codeHighlighting": "Default", + "previewStylesheet": "Byword Dark", + "builtIn": true + } + ] +} diff --git a/app/mainwindow.cpp b/app/mainwindow.cpp index a4d79707..f385909f 100644 --- a/app/mainwindow.cpp +++ b/app/mainwindow.cpp @@ -47,6 +47,8 @@ #include #include #include +#include +#include #include #include "controls/activelabel.h" #include "controls/findreplacewidget.h" @@ -71,6 +73,7 @@ MainWindow::MainWindow(const QString &fileName, QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow), options(new Options(this)), + stylesGroup(new QActionGroup(this)), styleLabel(0), wordCountLabel(0), viewLabel(0), @@ -78,6 +81,7 @@ MainWindow::MainWindow(const QString &fileName, QWidget *parent) : snippetCollection(new SnippetCollection(this)), viewSynchronizer(0), htmlPreviewController(0), + themeCollection(new ThemeCollection()), splitFactor(0.5), rightViewCollapsed(false) { @@ -124,8 +128,12 @@ void MainWindow::initializeApp() ui->webView->page()->setLinkDelegationPolicy(QWebPage::DelegateAllLinks); ui->tocWebView->page()->setLinkDelegationPolicy(QWebPage::DelegateAllLinks); - // set last used style - lastUsedStyle(); + themeCollection->load(":/builtin-htmlpreview-themes.json"); + loadCustomStyles(); + setupHtmlPreviewThemes(); + + // apply last used theme + lastUsedTheme(); ui->plainTextEdit->tabWidthChanged(options->tabWidth()); @@ -158,7 +166,6 @@ void MainWindow::initializeApp() QWebInspector *inspector = new QWebInspector(); inspector->setPage(ui->webView->page()); - loadCustomStyles(); ui->menuLanguages->loadDictionaries(options->dictionaryLanguage()); //: path to built-in snippets resource. @@ -464,97 +471,43 @@ void MainWindow::viewChangeSplit() } } -void MainWindow::lastUsedStyle() -{ - if (stylesGroup) { - foreach(QAction *action, stylesGroup->actions()) { - if (action->objectName() == options->lastUsedStyle()) { - action->trigger(); - } - } - } -} - -void MainWindow::styleDefault() -{ - generator->setCodeHighlightingStyle("default"); - ui->plainTextEdit->loadStyleFromStylesheet(stylePath("default")); - ui->webView->page()->settings()->setUserStyleSheetUrl(QUrl("qrc:/css/markdown.css")); - - styleLabel->setText(ui->actionDefault->text()); - options->setLastUsedStyle(ui->actionDefault->objectName()); -} - -void MainWindow::styleGithub() +void MainWindow::lastUsedTheme() { - generator->setCodeHighlightingStyle("github"); - ui->plainTextEdit->loadStyleFromStylesheet(stylePath("default")); - ui->webView->page()->settings()->setUserStyleSheetUrl(QUrl("qrc:/css/github.css")); - - styleLabel->setText(ui->actionGithub->text()); - options->setLastUsedStyle(ui->actionGithub->objectName()); -} + QString themeName = options->lastUsedTheme(); -void MainWindow::styleSolarizedLight() -{ - generator->setCodeHighlightingStyle("solarized_light"); - ui->plainTextEdit->loadStyleFromStylesheet(stylePath("solarized-light")); - ui->webView->page()->settings()->setUserStyleSheetUrl(QUrl("qrc:/css/solarized-light.css")); + currentTheme = themeCollection->theme(themeName); + applyCurrentTheme(); - styleLabel->setText(ui->actionSolarizedLight->text()); - options->setLastUsedStyle(ui->actionSolarizedLight->objectName()); + styleLabel->setText(themeName); } -void MainWindow::styleSolarizedDark() +void MainWindow::themeChanged() { - generator->setCodeHighlightingStyle("solarized_dark"); - ui->plainTextEdit->loadStyleFromStylesheet(stylePath("solarized-dark")); - ui->webView->page()->settings()->setUserStyleSheetUrl(QUrl("qrc:/css/solarized-dark.css")); - - styleLabel->setText(ui->actionSolarizedDark->text()); - options->setLastUsedStyle(ui->actionSolarizedDark->objectName()); -} - -void MainWindow::styleClearness() -{ - generator->setCodeHighlightingStyle("default"); - ui->plainTextEdit->loadStyleFromStylesheet(stylePath("default")); - ui->webView->page()->settings()->setUserStyleSheetUrl(QUrl("qrc:/css/clearness.css")); - - styleLabel->setText(ui->actionClearness->text()); - options->setLastUsedStyle(ui->actionClearness->objectName()); -} + QAction *action = qobject_cast(sender()); + QString themeName = action->text(); -void MainWindow::styleClearnessDark() -{ - generator->setCodeHighlightingStyle("default"); - ui->plainTextEdit->loadStyleFromStylesheet(stylePath("clearness-dark")); - ui->webView->page()->settings()->setUserStyleSheetUrl(QUrl("qrc:/css/clearness-dark.css")); + currentTheme = themeCollection->theme(themeName); + applyCurrentTheme(); - styleLabel->setText(ui->actionClearnessDark->text()); - options->setLastUsedStyle(ui->actionClearnessDark->objectName()); + styleLabel->setText(themeName); + options->setLastUsedTheme(themeName); } -void MainWindow::styleBywordDark() +void MainWindow::editorStyleChanged() { - generator->setCodeHighlightingStyle("default"); - ui->plainTextEdit->loadStyleFromStylesheet(stylePath("byword-dark")); - ui->webView->page()->settings()->setUserStyleSheetUrl(QUrl("qrc:/css/byword-dark.css")); - - styleLabel->setText(ui->actionBywordDark->text()); - options->setLastUsedStyle(ui->actionBywordDark->objectName()); + QString markdownHighlighting = StyleManager::markdownHighlightingPath(currentTheme); + ui->plainTextEdit->loadStyleFromStylesheet(stylePath(markdownHighlighting)); } -void MainWindow::styleCustomStyle() +void MainWindow::applyCurrentTheme() { - QAction *action = qobject_cast(sender()); - - generator->setCodeHighlightingStyle("default"); - ui->plainTextEdit->loadStyleFromStylesheet(stylePath("default")); - ui->webView->page()->settings()->setUserStyleSheetUrl(QUrl::fromLocalFile(action->data().toString())); + QString markdownHighlighting = StyleManager::markdownHighlightingPath(currentTheme); + QString codeHighlighting = StyleManager::codeHighlightingPath(currentTheme); + QString previewStylesheet = StyleManager::previewStylesheetPath(currentTheme); - styleLabel->setText(action->text()); - options->setLastUsedStyle(action->objectName()); + generator->setCodeHighlightingStyle(codeHighlighting); + ui->plainTextEdit->loadStyleFromStylesheet(stylePath(markdownHighlighting)); + ui->webView->page()->settings()->setUserStyleSheetUrl(QUrl(previewStylesheet)); } void MainWindow::viewFullScreenMode() @@ -949,8 +902,8 @@ void MainWindow::setupUi() this, SLOT(proxyConfigurationChanged())); connect(options, SIGNAL(markdownConverterChanged()), this, SLOT(markdownConverterChanged())); - connect(options, SIGNAL(editorFontChanged(QFont)), - this, SLOT(lastUsedStyle())); + connect(options, &Options::editorStyleChanged, + this, &MainWindow::editorStyleChanged); readSettings(); setupCustomShortcuts(); @@ -1036,16 +989,6 @@ void MainWindow::setupActions() connect(ui->menuLanguages, SIGNAL(languageTriggered(Dictionary)), this, SLOT(languageChanged(Dictionary))); - // put style actions in a group - stylesGroup = new QActionGroup(this); - ui->actionDefault->setActionGroup(stylesGroup); - ui->actionGithub->setActionGroup(stylesGroup); - ui->actionSolarizedLight->setActionGroup(stylesGroup); - ui->actionSolarizedDark->setActionGroup(stylesGroup); - ui->actionClearness->setActionGroup(stylesGroup); - ui->actionClearnessDark->setActionGroup(stylesGroup); - ui->actionBywordDark->setActionGroup(stylesGroup); - // help menu ui->actionMarkdownSyntax->setShortcut(QKeySequence::HelpContents); @@ -1073,7 +1016,7 @@ void MainWindow::setupStatusBar() statusBar()->setStyleSheet("QStatusBar::item { border: 0px solid black }; "); // add style label to statusbar - styleLabel = new QLabel(ui->actionDefault->text(), this); + styleLabel = new QLabel("Default", this); styleLabel->setToolTip(tr("Change Preview Style")); statusBar()->addPermanentWidget(styleLabel, 1); @@ -1231,6 +1174,39 @@ void MainWindow::updateSplitter() ui->splitter->setSizes(childSizes); } +void MainWindow::setupHtmlPreviewThemes() +{ + ui->menuStyles->clear(); + + delete stylesGroup; + stylesGroup = new QActionGroup(this); + + int key = 1; + bool separatorAdded = false; + foreach(const QString &themeName, themeCollection->themeNames()) { + QAction *action = ui->menuStyles->addAction(themeName); + action->setShortcut(QKeySequence(tr("Ctrl+%1").arg(key++))); + action->setCheckable(true); + action->setActionGroup(stylesGroup); + connect(action, &QAction::triggered, + this, &MainWindow::themeChanged); + + if (!separatorAdded && !themeCollection->theme(themeName).isBuiltIn()) { + addSeparatorAfterBuiltInThemes(); + separatorAdded = true; + } + } +} + +void MainWindow::addSeparatorAfterBuiltInThemes() +{ + ui->menuStyles->addSeparator(); + + QAction *separator = new QAction(stylesGroup); + separator->setSeparator(true); + stylesGroup->addAction(separator); +} + void MainWindow::loadCustomStyles() { QStringList paths = DataLocation::standardLocations(); @@ -1238,21 +1214,20 @@ void MainWindow::loadCustomStyles() QDir dataPath(paths.first() + QDir::separator() + "styles"); dataPath.setFilter(QDir::Files); if (dataPath.exists()) { - ui->menuStyles->addSeparator(); - // iterate over all files in the styles subdirectory QDirIterator it(dataPath); while (it.hasNext()) { it.next(); QString fileName = it.fileName(); - QAction *action = ui->menuStyles->addAction(QFileInfo(fileName).baseName()); - action->setCheckable(true); - action->setActionGroup(stylesGroup); - action->setData(it.filePath()); + QString styleName = QFileInfo(fileName).baseName(); + QString stylePath = QUrl::fromLocalFile(it.filePath()).toString(); + + Theme customTheme { styleName, "Default", "Default", styleName }; + themeCollection->insert(customTheme); - connect(action, SIGNAL(triggered()), - this, SLOT(styleCustomStyle())); + StyleManager styleManager; + styleManager.insertCustomPreviewStylesheet(styleName, stylePath); } } } diff --git a/app/mainwindow.h b/app/mainwindow.h index 9e001a71..dc3b2aba 100644 --- a/app/mainwindow.h +++ b/app/mainwindow.h @@ -20,6 +20,7 @@ #include #include #include +#include namespace Ui { class MainWindow; @@ -37,6 +38,7 @@ class RecentFilesMenu; class Options; class SlideLineMapping; class SnippetCollection; +class ThemeCollection; class ViewSynchronizer; @@ -83,15 +85,9 @@ private slots: void editInsertImage(); void viewChangeSplit(); - void lastUsedStyle(); - void styleDefault(); - void styleGithub(); - void styleSolarizedLight(); - void styleSolarizedDark(); - void styleClearness(); - void styleClearnessDark(); - void styleBywordDark(); - void styleCustomStyle(); + void lastUsedTheme(); + void themeChanged(); + void editorStyleChanged(); void viewFullScreenMode(); void viewHorizontalLayout(bool checked); @@ -143,9 +139,12 @@ private slots: bool maybeSave(); void setFileName(const QString &fileName); void updateSplitter(); + void setupHtmlPreviewThemes(); + void addSeparatorAfterBuiltInThemes(); void loadCustomStyles(); void readSettings(); void writeSettings(); + void applyCurrentTheme(); QString stylePath(const QString &styleName); private: @@ -161,6 +160,8 @@ private slots: SnippetCollection *snippetCollection; ViewSynchronizer *viewSynchronizer; HtmlPreviewController *htmlPreviewController; + ThemeCollection *themeCollection; + Theme currentTheme { "Default", "Default", "Default", "Default" }; QString fileName; float splitFactor; bool rightViewCollapsed; diff --git a/app/mainwindow.ui b/app/mainwindow.ui index 0de78cd1..0456ea92 100644 --- a/app/mainwindow.ui +++ b/app/mainwindow.ui @@ -258,13 +258,6 @@ St&yles - - - - - - - @@ -424,53 +417,6 @@ &Redo - - - true - - - true - - - Default - - - Ctrl+1 - - - - - true - - - Github - - - Ctrl+2 - - - - - true - - - Solarized Light - - - Ctrl+3 - - - - - true - - - Solarized Dark - - - Ctrl+4 - - &About CuteMarkEd... @@ -572,28 +518,6 @@ Code &Highlighting - - - true - - - Clearness - - - Ctrl+5 - - - - - true - - - Clearness Dark - - - Ctrl+6 - - Find/Replace @@ -749,14 +673,6 @@ Ctrl+T - - - Byword Dark - - - Ctrl+7 - - Insert &Image... @@ -979,70 +895,6 @@ - - actionDefault - triggered() - MainWindow - styleDefault() - - - -1 - -1 - - - 399 - 249 - - - - - actionGithub - triggered() - MainWindow - styleGithub() - - - -1 - -1 - - - 399 - 249 - - - - - actionSolarizedLight - triggered() - MainWindow - styleSolarizedLight() - - - -1 - -1 - - - 399 - 249 - - - - - actionSolarizedDark - triggered() - MainWindow - styleSolarizedDark() - - - -1 - -1 - - - 399 - 249 - - - actionAbout triggered() @@ -1363,38 +1215,6 @@ - - actionClearness - triggered() - MainWindow - styleClearness() - - - -1 - -1 - - - 399 - 249 - - - - - actionClearnessDark - triggered() - MainWindow - styleClearnessDark() - - - -1 - -1 - - - 399 - 249 - - - actionFindReplace triggered() @@ -1699,22 +1519,6 @@ - - actionBywordDark - triggered() - MainWindow - styleBywordDark() - - - -1 - -1 - - - 424 - 266 - - - actionInsertImage triggered() diff --git a/app/options.cpp b/app/options.cpp index d708acd8..602a25e1 100644 --- a/app/options.cpp +++ b/app/options.cpp @@ -16,13 +16,12 @@ */ #include "options.h" -#include #include static const char* MARKDOWN_CONVERTER = "general/converter"; -static const char* LAST_USED_STYLE = "general/lastusedstyle"; -static const char* STYLE_DEFAULT = "actionDefault"; +static const char* LAST_USED_THEME = "general/lastusedtheme"; +static const char* THEME_DEFAULT = "Default"; static const char* FONT_FAMILY_DEFAULT = "Monospace"; static const char* FONT_FAMILY = "editor/font/family"; static const char* FONT_SIZE = "editor/font/size"; @@ -56,6 +55,7 @@ static const char* DICTIONARY_LANGUAGE = "spelling/language"; static const char* YAMLHEADERSUPPORT_ENABLED = "yamlheadersupport/enabled"; static const char* DIAGRAMSUPPORT_ENABLED = "diagramsupport/enabled"; +static const char* DEPRECATED__LAST_USED_STYLE = "general/lastusedstyle"; Options::Options(QObject *parent) : QObject(parent), @@ -78,7 +78,7 @@ Options::Options(QObject *parent) : m_spellingCheckEnabled(true), m_diagramSupportEnabled(false), m_markdownConverter(DiscountMarkdownConverter), - m_lastUsedStyle(STYLE_DEFAULT) + m_lastUsedTheme(THEME_DEFAULT) { } @@ -94,7 +94,6 @@ void Options::apply() emit proxyConfigurationChanged(); emit markdownConverterChanged(); - emit editorFontChanged(editorFont()); } QFont Options::editorFont() const @@ -373,6 +372,7 @@ bool Options::isSourceAtSingleSizeEnabled() const void Options::setSourceAtSingleSizeEnabled(bool enabled) { m_sourceAtSingleSizeEnabled = enabled; + emit editorStyleChanged(); } bool Options::isSpellingCheckEnabled() const @@ -428,14 +428,14 @@ void Options::setMarkdownConverter(Options::MarkdownConverter converter) } } -QString Options::lastUsedStyle() const +QString Options::lastUsedTheme() const { - return m_lastUsedStyle; + return m_lastUsedTheme; } -void Options::setLastUsedStyle(const QString &style) +void Options::setLastUsedTheme(const QString &theme) { - m_lastUsedStyle = style; + m_lastUsedTheme = theme; } void Options::readSettings() @@ -444,7 +444,7 @@ void Options::readSettings() // general settings m_markdownConverter = (Options::MarkdownConverter)settings.value(MARKDOWN_CONVERTER, 0).toInt(); - m_lastUsedStyle = settings.value(LAST_USED_STYLE, STYLE_DEFAULT).toString(); + m_lastUsedTheme = settings.value(LAST_USED_THEME, THEME_DEFAULT).toString(); // editor settings QString fontFamily = settings.value(FONT_FAMILY, FONT_FAMILY_DEFAULT).toString(); @@ -502,6 +502,11 @@ void Options::readSettings() m_spellingCheckEnabled = settings.value(SPELLINGCHECK_ENABLED, true).toBool(); m_dictionaryLanguage = settings.value(DICTIONARY_LANGUAGE, "en_US").toString(); + // migrate deprecated lastUsedStyle option + if (settings.contains(DEPRECATED__LAST_USED_STYLE)) { + migrateLastUsedStyleOption(settings); + } + apply(); } @@ -511,7 +516,7 @@ void Options::writeSettings() // general settings settings.setValue(MARKDOWN_CONVERTER, m_markdownConverter); - settings.setValue(LAST_USED_STYLE, m_lastUsedStyle); + settings.setValue(LAST_USED_THEME, m_lastUsedTheme); // editor settings settings.setValue(FONT_FAMILY, font.family()); @@ -564,3 +569,21 @@ void Options::writeSettings() settings.setValue(SPELLINGCHECK_ENABLED, m_spellingCheckEnabled); settings.setValue(DICTIONARY_LANGUAGE, m_dictionaryLanguage); } + +void Options::migrateLastUsedStyleOption(QSettings &settings) +{ + static const QMap migrations { + { "actionDefault", "Default" }, + { "actionGithub", "Github" }, + { "actionSolarizedLight", "Solarized Light" }, + { "actionSolarizedDark", "Solarized Dark" }, + { "actionClearness", "Clearness" }, + { "actionClearnessDark", "Clearness Dark" }, + { "actionBywordDark", "Byword Dark" } + }; + + QString lastUsedStyle = settings.value(DEPRECATED__LAST_USED_STYLE).toString(); + m_lastUsedTheme = migrations[lastUsedStyle]; + + settings.remove(DEPRECATED__LAST_USED_STYLE); +} diff --git a/app/options.h b/app/options.h index 8cc2c809..c281c6a3 100644 --- a/app/options.h +++ b/app/options.h @@ -21,6 +21,7 @@ #include #include #include +#include class Options : public QObject { @@ -139,18 +140,22 @@ class Options : public QObject MarkdownConverter markdownConverter() const; void setMarkdownConverter(MarkdownConverter converter); - QString lastUsedStyle() const; - void setLastUsedStyle(const QString &style); + QString lastUsedTheme() const; + void setLastUsedTheme(const QString &theme); void readSettings(); void writeSettings(); signals: void editorFontChanged(const QFont &font); + void editorStyleChanged(); void tabWidthChanged(int tabWidth); void proxyConfigurationChanged(); void markdownConverterChanged(); +private: + void migrateLastUsedStyleOption(QSettings &settings); + private: QFont font; int m_tabWidth; @@ -177,7 +182,7 @@ class Options : public QObject bool m_diagramSupportEnabled; QString m_dictionaryLanguage; MarkdownConverter m_markdownConverter; - QString m_lastUsedStyle; + QString m_lastUsedTheme; QString m_standardFontFamily; QString m_fixedFontFamily; QString m_serifFontFamily; diff --git a/app/resources.qrc b/app/resources.qrc index c364e28d..b492a939 100644 --- a/app/resources.qrc +++ b/app/resources.qrc @@ -34,6 +34,7 @@ syntax_id.html syntax_da.html template_presentation.html + builtin-htmlpreview-themes.json images/icon-close.png diff --git a/test/integration/integration.pro b/test/integration/integration.pro index 786f4aeb..9618d74e 100644 --- a/test/integration/integration.pro +++ b/test/integration/integration.pro @@ -15,17 +15,21 @@ SOURCES += \ htmlpreviewcontrollertest.cpp \ htmltemplatetest.cpp \ jsonsnippetfiletest.cpp \ + jsonthemefiletest.cpp \ main.cpp \ pmhmarkdownparsertest.cpp \ - revealmarkdownconvertertest.cpp + revealmarkdownconvertertest.cpp \ + themecollectiontest.cpp HEADERS += \ discountmarkdownconvertertest.h \ htmlpreviewcontrollertest.h \ htmltemplatetest.h \ jsonsnippetfiletest.h \ + jsonthemefiletest.h \ pmhmarkdownparsertest.h \ - revealmarkdownconvertertest.h + revealmarkdownconvertertest.h \ + themecollectiontest.h target.CONFIG += no_default_install diff --git a/test/integration/jsonthemefiletest.cpp b/test/integration/jsonthemefiletest.cpp new file mode 100644 index 00000000..016d7c92 --- /dev/null +++ b/test/integration/jsonthemefiletest.cpp @@ -0,0 +1,156 @@ +/* + * Copyright 2015 Christian Loose + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include "jsonthemefiletest.h" + + +#include +#include +#include + +#include +#include +#include + + +void JsonThemeFileTest::loadsEmptyThemeCollectionFromFile() +{ + QTemporaryFile themeFile(this); + if (!themeFile.open()) + QFAIL("Failed to create temporary theme file"); + + QTextStream out(&themeFile); out << "{ \"themes\": [] }"; + + themeFile.close(); + + ThemeCollection collection; + bool success = JsonFile::load(themeFile.fileName(), &collection); + + QVERIFY(success); + QCOMPARE(collection.count(), 0); +} + +void JsonThemeFileTest::loadsThemesCollectionFromFile() +{ + QTemporaryFile themeFile(this); + if (!themeFile.open()) + QFAIL("Failed to create temporary theme file"); + + QTextStream out(&themeFile); + out << "{ \"themes\": [" + << " { \"name\": \"default\"," + << " \"markdownHighlighting\": \"default\"," + << " \"codeHighlighting\": \"default\"," + << " \"previewStylesheet\": \"default\"," + << " \"builtIn\": true }," + << " { \"name\": \"dark\"," + << " \"markdownHighlighting\": \"dark\"," + << " \"codeHighlighting\": \"black\"," + << " \"previewStylesheet\": \"dark\" } ] }"; + + themeFile.close(); + + ThemeCollection collection; + bool success = JsonFile::load(themeFile.fileName(), &collection); + + QVERIFY(success); + QCOMPARE(collection.count(), 2); + QCOMPARE(collection.at(0).name(), QLatin1String("default")); + QCOMPARE(collection.at(1).name(), QLatin1String("dark")); +} + +void JsonThemeFileTest::savesEmptyThemesCollectionToFile() +{ + QTemporaryFile themeFile(this); + if (!themeFile.open()) + QFAIL("Failed to create temporary theme file"); + + ThemeCollection collection; + bool success = JsonFile::save(themeFile.fileName(), &collection); + + QVERIFY(success); + + QTextStream in(&themeFile); + QString fileContent = in.readAll().trimmed(); + + QVERIFY(fileContent.startsWith("{")); + QVERIFY(fileContent.contains("\"themes\": [")); + QVERIFY(fileContent.endsWith("}")); +} + +void JsonThemeFileTest::savesThemesCollectionToFile() +{ + QTemporaryFile themeFile(this); + if (!themeFile.open()) + QFAIL("Failed to create temporary theme file"); + + Theme theme1("default", "default", "default", "default"); + + Theme theme2("dark", "dark", "black", "dark"); + + ThemeCollection collection; + collection.insert(theme1); + collection.insert(theme2); + + bool success = JsonFile::save(themeFile.fileName(), &collection); + + QVERIFY(success); + + QTextStream in(&themeFile); + QString fileContent = in.readAll().trimmed(); + + QVERIFY(fileContent.startsWith("{")); + QVERIFY(fileContent.contains("\"themes\": [")); + QVERIFY(fileContent.contains("\"name\": \"default\"")); + QVERIFY(fileContent.contains("\"name\": \"dark\"")); + QVERIFY(fileContent.endsWith("}")); +} + +void JsonThemeFileTest::roundtripTest() +{ + QTemporaryFile themeFile(this); + if (!themeFile.open()) + QFAIL("Failed to create temporary theme file"); + + Theme theme1("default", "default", "default", "default", true); + + Theme theme2("dark", "dark", "black", "dark"); + + ThemeCollection collection1; + collection1.insert(theme1); + collection1.insert(theme2); + + bool saveSuccess = JsonFile::save(themeFile.fileName(), &collection1); + QVERIFY(saveSuccess); + + ThemeCollection collection2; + bool loadSuccess = JsonFile::load(themeFile.fileName(), &collection2); + QVERIFY(loadSuccess); + + QCOMPARE(collection2.count(), 2); + + QCOMPARE(collection2.at(0).name(), theme1.name()); + QCOMPARE(collection2.at(0).markdownHighlighting(), theme1.markdownHighlighting()); + QCOMPARE(collection2.at(0).codeHighlighting(), theme1.codeHighlighting()); + QCOMPARE(collection2.at(0).previewStylesheet(), theme1.previewStylesheet()); + QCOMPARE(collection2.at(0).isBuiltIn(), theme1.isBuiltIn()); + + QCOMPARE(collection2.at(1).name(), theme2.name()); + QCOMPARE(collection2.at(1).markdownHighlighting(), theme2.markdownHighlighting()); + QCOMPARE(collection2.at(1).codeHighlighting(), theme2.codeHighlighting()); + QCOMPARE(collection2.at(1).previewStylesheet(), theme2.previewStylesheet()); + QCOMPARE(collection2.at(0).isBuiltIn(), theme1.isBuiltIn()); +} diff --git a/test/integration/jsonthemefiletest.h b/test/integration/jsonthemefiletest.h new file mode 100644 index 00000000..f2898303 --- /dev/null +++ b/test/integration/jsonthemefiletest.h @@ -0,0 +1,36 @@ +/* + * Copyright 2015 Christian Loose + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#ifndef JSONTHEMEFILETEST_H +#define JSONTHEMEFILETEST_H + +#include + +class JsonThemeFileTest : public QObject +{ + Q_OBJECT + +private slots: + void loadsEmptyThemeCollectionFromFile(); + void loadsThemesCollectionFromFile(); + + void savesEmptyThemesCollectionToFile(); + void savesThemesCollectionToFile(); + + void roundtripTest(); +}; + +#endif // JSONTHEMEFILETEST_H diff --git a/test/integration/main.cpp b/test/integration/main.cpp index 48f5d183..a670e1c6 100644 --- a/test/integration/main.cpp +++ b/test/integration/main.cpp @@ -22,8 +22,10 @@ #include "htmlpreviewcontrollertest.h" #include "htmltemplatetest.h" #include "jsonsnippetfiletest.h" +#include "jsonthemefiletest.h" #include "pmhmarkdownparsertest.h" #include "revealmarkdownconvertertest.h" +#include "themecollectiontest.h" #ifdef ENABLE_HOEDOWN #include "hoedownmarkdownconvertertest.h" @@ -55,8 +57,14 @@ int main(int argc, char *argv[]) HtmlPreviewControllerTest test6; ret += QTest::qExec(&test6, argc, argv); - HtmlTemplateTest test7; - ret += QTest::qExec(&test7, argc, argv); + HtmlTemplateTest test7; + ret += QTest::qExec(&test7, argc, argv); + + JsonThemeFileTest test8; + ret += QTest::qExec(&test8, argc, argv); + + ThemeCollectionTest test9; + ret += QTest::qExec(&test9, argc, argv); return ret; } diff --git a/test/integration/themecollectiontest.cpp b/test/integration/themecollectiontest.cpp new file mode 100644 index 00000000..f97e78ff --- /dev/null +++ b/test/integration/themecollectiontest.cpp @@ -0,0 +1,56 @@ +/* + * Copyright 2015 Christian Loose + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include "themecollectiontest.h" + +#include + +#include + +void ThemeCollectionTest::initTestCase() +{ + themeFile = new QTemporaryFile(this); + if (!themeFile->open()) + QFAIL("Failed to create temporary theme file"); + + QTextStream out(themeFile); + out << "{ \"themes\": [" + << " { \"name\": \"default\"," + << " \"markdownHighlighting\": \"default\"," + << " \"codeHighlighting\": \"default\"," + << " \"previewStylesheet\": \"default\" }," + << " { \"name\": \"dark\"," + << " \"markdownHighlighting\": \"dark\"," + << " \"codeHighlighting\": \"black\"," + << " \"previewStylesheet\": \"dark\" } ] }"; +} + +void ThemeCollectionTest::cleanupTestCase() +{ + themeFile->close(); +} + +void ThemeCollectionTest::loadsThemesFromFileIntoCollection() +{ + ThemeCollection themeCollection; + + themeCollection.load(themeFile->fileName()); + + QStringList themeNames = themeCollection.themeNames(); + QCOMPARE(themeNames.count(), 2); + QCOMPARE(themeNames.at(0), QLatin1String("default")); + QCOMPARE(themeNames.at(1), QLatin1String("dark")); +} diff --git a/test/integration/themecollectiontest.h b/test/integration/themecollectiontest.h new file mode 100644 index 00000000..80f2c3d3 --- /dev/null +++ b/test/integration/themecollectiontest.h @@ -0,0 +1,41 @@ +/* + * Copyright 2015 Christian Loose + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#ifndef THEMEMANAGERTEST_H +#define THEMEMANAGERTEST_H + +#include +class QTemporaryFile; +class ThemeCollection; + + +class ThemeCollectionTest : public QObject +{ + Q_OBJECT + +private slots: + void initTestCase(); + void cleanupTestCase(); + + void loadsThemesFromFileIntoCollection(); + +private: + QTemporaryFile *themeFile; +}; + +#endif // THEMEMANAGERTEST_H + + diff --git a/test/unit/jsonthemetranslatortest.cpp b/test/unit/jsonthemetranslatortest.cpp new file mode 100644 index 00000000..a100bc9a --- /dev/null +++ b/test/unit/jsonthemetranslatortest.cpp @@ -0,0 +1,120 @@ +/* + * Copyright 2015 Christian Loose + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include "jsonthemetranslatortest.h" + +#include + +#include +#include +#include + +static const QLatin1String A_THEME_NAME("mytheme"); +static const QLatin1String A_MARKDOWN_HIGHLIGHTING("default"); +static const QLatin1String A_CODE_HIGHLIGHTING("monokai"); +static const QLatin1String A_PREVIEW_STYLESHEET("github"); + + +QJsonDocument NewJsonDocumentWithObject(const QJsonObject &jsonObject) +{ + QJsonArray themeArray; + themeArray.append(jsonObject); + + QJsonObject object; + object.insert("themes", themeArray); + + return QJsonDocument(object); +} + +QJsonObject NewJsonThemeObject() +{ + QJsonObject jsonObject; + jsonObject.insert("name", A_THEME_NAME); + jsonObject.insert("markdownHighlighting", A_MARKDOWN_HIGHLIGHTING); + jsonObject.insert("codeHighlighting", A_CODE_HIGHLIGHTING); + jsonObject.insert("previewStylesheet", A_PREVIEW_STYLESHEET); + jsonObject.insert("builtIn", true); + + return jsonObject; +} + +void JsonThemeTranslatorTest::initTestCase() +{ + translator = new JsonThemeTranslator(); +} + +void JsonThemeTranslatorTest::cleanupTestCase() +{ + delete translator; +} + +void JsonThemeTranslatorTest::doesNotProcessInvalidJsonDocument() +{ + QJsonDocument doc; + + ThemeCollection collection; + bool success = translator->processDocument(doc, &collection); + + QVERIFY(!success); + QCOMPARE(collection.count(), 0); +} + +void JsonThemeTranslatorTest::translatesEmptyJsonDocumentToEmptyThemes() +{ + QJsonObject themesObject; + themesObject.insert("themes", QJsonArray()); + + QJsonDocument doc(themesObject); + + ThemeCollection collection; + bool success = translator->processDocument(doc, &collection); + + QVERIFY(success); + QCOMPARE(collection.count(), 0); +} + +void JsonThemeTranslatorTest::translatesJsonDocumentToThemes() +{ + QJsonDocument doc = NewJsonDocumentWithObject(NewJsonThemeObject()); + + ThemeCollection collection; + bool success = translator->processDocument(doc, &collection); + + QVERIFY(success); + QCOMPARE(collection.count(), 1); + QCOMPARE(collection.at(0).name(), A_THEME_NAME); + QCOMPARE(collection.at(0).markdownHighlighting(), A_MARKDOWN_HIGHLIGHTING); + QCOMPARE(collection.at(0).codeHighlighting(), A_CODE_HIGHLIGHTING); + QCOMPARE(collection.at(0).previewStylesheet(), A_PREVIEW_STYLESHEET); + QCOMPARE(collection.at(0).isBuiltIn(), true); +} + +void JsonThemeTranslatorTest::translatesThemesToJsonDocument() +{ + Theme theme(A_THEME_NAME, A_MARKDOWN_HIGHLIGHTING, A_CODE_HIGHLIGHTING, A_PREVIEW_STYLESHEET, true); + ThemeCollection collection; + collection.insert(theme); + + QJsonDocument doc = translator->createDocument(&collection); + + QJsonObject actual = doc.object().value("themes").toArray().first().toObject(); + QCOMPARE(actual["name"].toString(), A_THEME_NAME); + QCOMPARE(actual["markdownHighlighting"].toString(), A_MARKDOWN_HIGHLIGHTING); + QCOMPARE(actual["codeHighlighting"].toString(), A_CODE_HIGHLIGHTING); + QCOMPARE(actual["previewStylesheet"].toString(), A_PREVIEW_STYLESHEET); + QCOMPARE(actual["builtIn"].toBool(), true); +} + diff --git a/test/unit/jsonthemetranslatortest.h b/test/unit/jsonthemetranslatortest.h new file mode 100644 index 00000000..ab44d4de --- /dev/null +++ b/test/unit/jsonthemetranslatortest.h @@ -0,0 +1,44 @@ +/* + * Copyright 2015 Christian Loose + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#ifndef JSONTHEMETRANSLATORTEST_H +#define JSONTHEMETRANSLATORTEST_H + +#include +class JsonThemeTranslator; + + +class JsonThemeTranslatorTest : public QObject +{ + Q_OBJECT + +private slots: + void initTestCase(); + void cleanupTestCase(); + + void doesNotProcessInvalidJsonDocument(); + void translatesEmptyJsonDocumentToEmptyThemes(); + void translatesJsonDocumentToThemes(); + void translatesThemesToJsonDocument(); + +private: + JsonThemeTranslator *translator; +}; + +#endif // JSONTHEMETRANSLATORTEST_H + + + diff --git a/test/unit/main.cpp b/test/unit/main.cpp index 155b2995..768e25cb 100644 --- a/test/unit/main.cpp +++ b/test/unit/main.cpp @@ -1,5 +1,5 @@ /* - * Copyright 2013-2014 Christian Loose + * Copyright 2013-2015 Christian Loose * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -18,11 +18,15 @@ #include "dictionarytest.h" #include "jsonsnippettranslatortest.h" +#include "jsonthemetranslatortest.h" #include "jsontranslatorfactorytest.h" #include "slidelinemappingtest.h" #include "snippetcollectiontest.h" #include "completionlistmodeltest.h" #include "snippettest.h" +#include "stylemanagertest.h" +#include "themecollectiontest.h" +#include "themetest.h" #include "yamlheadercheckertest.h" int main(int argc, char *argv[]) @@ -53,5 +57,17 @@ int main(int argc, char *argv[]) YamlHeaderCheckerTest test8; ret += QTest::qExec(&test8, argc, argv); + ThemeTest test9; + ret += QTest::qExec(&test9, argc, argv); + + ThemeCollectionTest test10; + ret += QTest::qExec(&test10, argc, argv); + + StyleManagerTest test11; + ret += QTest::qExec(&test11, argc, argv); + + JsonThemeTranslatorTest test12; + ret += QTest::qExec(&test12, argc, argv); + return ret; } diff --git a/test/unit/stylemanagertest.cpp b/test/unit/stylemanagertest.cpp new file mode 100644 index 00000000..87161278 --- /dev/null +++ b/test/unit/stylemanagertest.cpp @@ -0,0 +1,85 @@ +/* + * Copyright 2015 Christian Loose + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include "stylemanagertest.h" + +#include + +#include + +static const Theme defaultTheme("Default", "Default", "Default", "Default"); +static const Theme githubTheme("Github", "Github", "Github", "Github"); +static const Theme solarizedLightTheme("Solarized Light", "Solarized Light", "Solarized Light", "Solarized Light"); +static const Theme solarizedDarkTheme("Solarized Dark", "Solarized Dark", "Solarized Dark", "Solarized Dark"); +static const Theme clearnessTheme("Clearness", "Clearness", "Clearness", "Clearness"); +static const Theme clearnessDarkTheme("Clearness Dark", "Clearness Dark", "Clearness Dark", "Clearness Dark"); +static const Theme bywordDarkTheme("Byword Dark", "Byword Dark", "Byword Dark", "Byword Dark"); + +void StyleManagerTest::returnsPathForMarkdownHighlighting() +{ + StyleManager styleManager; + + QCOMPARE(styleManager.markdownHighlightingPath(defaultTheme), QLatin1String("default")); + QCOMPARE(styleManager.markdownHighlightingPath(solarizedLightTheme), QLatin1String("solarized-light")); + QCOMPARE(styleManager.markdownHighlightingPath(solarizedDarkTheme), QLatin1String("solarized-dark")); + QCOMPARE(styleManager.markdownHighlightingPath(clearnessDarkTheme), QLatin1String("clearness-dark")); + QCOMPARE(styleManager.markdownHighlightingPath(bywordDarkTheme), QLatin1String("byword-dark")); +} + +void StyleManagerTest::returnsPathForCodeHighlighting() +{ + StyleManager styleManager; + + QCOMPARE(styleManager.codeHighlightingPath(defaultTheme), QLatin1String("default")); + QCOMPARE(styleManager.codeHighlightingPath(githubTheme), QLatin1String("github")); + QCOMPARE(styleManager.codeHighlightingPath(solarizedLightTheme), QLatin1String("solarized_light")); + QCOMPARE(styleManager.codeHighlightingPath(solarizedDarkTheme), QLatin1String("solarized_dark")); +} + +void StyleManagerTest::returnsPathForPreviewStylesheet() +{ + StyleManager styleManager; + + QCOMPARE(styleManager.previewStylesheetPath(defaultTheme), QLatin1String("qrc:/css/markdown.css")); + QCOMPARE(styleManager.previewStylesheetPath(githubTheme), QLatin1String("qrc:/css/github.css")); + QCOMPARE(styleManager.previewStylesheetPath(solarizedLightTheme), QLatin1String("qrc:/css/solarized-light.css")); + QCOMPARE(styleManager.previewStylesheetPath(solarizedDarkTheme), QLatin1String("qrc:/css/solarized-dark.css")); + QCOMPARE(styleManager.previewStylesheetPath(clearnessTheme), QLatin1String("qrc:/css/clearness.css")); + QCOMPARE(styleManager.previewStylesheetPath(clearnessDarkTheme), QLatin1String("qrc:/css/clearness-dark.css")); + QCOMPARE(styleManager.previewStylesheetPath(bywordDarkTheme), QLatin1String("qrc:/css/byword-dark.css")); +} + +void StyleManagerTest::returnsPathForCustomPreviewStylesheet() +{ + QString expectedPath = "file:///C:/User/Test/custom.css"; + Theme customTheme("Custom", "Default", "Default", "Custom"); + StyleManager styleManager; + + styleManager.insertCustomPreviewStylesheet("Custom", expectedPath); + + QCOMPARE(styleManager.previewStylesheetPath(customTheme), expectedPath); +} + +void StyleManagerTest::customPreviewStylesheetOverwritesBuiltin() +{ + QString expectedPath = "file:///C:/User/Test/custom.css"; + Theme customTheme("Custom", "Default", "Default", "Github"); + StyleManager styleManager; + + styleManager.insertCustomPreviewStylesheet("Github", expectedPath); + + QCOMPARE(styleManager.previewStylesheetPath(customTheme), expectedPath); +} diff --git a/test/unit/stylemanagertest.h b/test/unit/stylemanagertest.h new file mode 100644 index 00000000..773c9460 --- /dev/null +++ b/test/unit/stylemanagertest.h @@ -0,0 +1,36 @@ +/* + * Copyright 2015 Christian Loose + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#ifndef STYLEMANAGERTEST_H +#define STYLEMANAGERTEST_H + +#include + +class StyleManagerTest : public QObject +{ + Q_OBJECT + +private slots: + void returnsPathForMarkdownHighlighting(); + void returnsPathForCodeHighlighting(); + void returnsPathForPreviewStylesheet(); + void returnsPathForCustomPreviewStylesheet(); + void customPreviewStylesheetOverwritesBuiltin(); +}; + +#endif // STYLEMANAGERTEST_H + + diff --git a/test/unit/themecollectiontest.cpp b/test/unit/themecollectiontest.cpp new file mode 100644 index 00000000..3fbf9511 --- /dev/null +++ b/test/unit/themecollectiontest.cpp @@ -0,0 +1,85 @@ +/* + * Copyright 2015 Christian Loose + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include "themecollectiontest.h" + +#include + +#include + + +void ThemeCollectionTest::returnsConstantNameOfJsonArray() +{ + ThemeCollection collection; + QCOMPARE(collection.name(), QStringLiteral("themes")); +} + +void ThemeCollectionTest::returnsNumberOfThemesInCollection() +{ + ThemeCollection collection; + Theme theme("name", "markdown", "code", "preview"); + collection.insert(theme); + + QCOMPARE(collection.count(), 1); +} + +void ThemeCollectionTest::returnsThemeAtIndexPosition() +{ + ThemeCollection collection; + Theme theme("name", "markdown", "code", "preview"); + collection.insert(theme); + + Theme actual = collection.at(0); + + QCOMPARE(actual, theme); +} + +void ThemeCollectionTest::returnsIfCollectionContainsTheme() +{ + ThemeCollection collection; + Theme theme("name", "markdown", "code", "preview"); + collection.insert(theme); + + QCOMPARE(collection.contains("name"), true); + QCOMPARE(collection.contains("missing"), false); +} + +void ThemeCollectionTest::returnsThemeByName() +{ + ThemeCollection collection; + Theme theme("name", "markdown", "code", "preview"); + collection.insert(theme); + + Theme actual = collection.theme("name"); + + QCOMPARE(actual, theme); +} + +void ThemeCollectionTest::returnsNameOfAllThemes() +{ + Theme theme1("name 1", "markdown", "code", "preview"); + Theme theme2("name 2", "markdown", "code", "preview"); + ThemeCollection collection; + collection.insert(theme1); + collection.insert(theme2); + + QStringList themeNames = collection.themeNames(); + + QCOMPARE(themeNames.count(), 2); + QCOMPARE(themeNames.at(0), theme1.name()); + QCOMPARE(themeNames.at(1), theme2.name()); +} + diff --git a/test/unit/themecollectiontest.h b/test/unit/themecollectiontest.h new file mode 100644 index 00000000..3fc8d15b --- /dev/null +++ b/test/unit/themecollectiontest.h @@ -0,0 +1,37 @@ +/* + * Copyright 2015 Christian Loose + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#ifndef THEMECOLLECTIONTEST_H +#define THEMECOLLECTIONTEST_H + +#include + +class ThemeCollectionTest : public QObject +{ + Q_OBJECT + +private slots: + void returnsConstantNameOfJsonArray(); + void returnsNumberOfThemesInCollection(); + void returnsThemeAtIndexPosition(); + void returnsIfCollectionContainsTheme(); + void returnsThemeByName(); + void returnsNameOfAllThemes(); +}; + +#endif // THEMECOLLECTIONTEST_H + + diff --git a/test/unit/themetest.cpp b/test/unit/themetest.cpp new file mode 100644 index 00000000..5d97e36b --- /dev/null +++ b/test/unit/themetest.cpp @@ -0,0 +1,84 @@ +/* + * Copyright 2015 Christian Loose + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include "themetest.h" + +#include + +#include + +static const QLatin1String A_THEME_NAME("name"); +static const QLatin1String A_MARKDOWN_HIGHLIGHTING("markdown"); +static const QLatin1String A_CODE_HIGHLIGHTING("code"); +static const QLatin1String A_PREVIEW_STYLESHEET("preview"); + +void ThemeTest::isLessThanComparable() +{ + Theme theme1("abc", A_MARKDOWN_HIGHLIGHTING, A_CODE_HIGHLIGHTING, A_PREVIEW_STYLESHEET); + + Theme theme2("xyz", A_MARKDOWN_HIGHLIGHTING, A_CODE_HIGHLIGHTING, A_PREVIEW_STYLESHEET); + + QCOMPARE(theme1 < theme2, true); + QCOMPARE(theme2 < theme1, false); + QCOMPARE(theme1 < theme1, false); +} + +void ThemeTest::isEqualComparable() +{ + Theme theme1("abc", A_MARKDOWN_HIGHLIGHTING, A_CODE_HIGHLIGHTING, A_PREVIEW_STYLESHEET); + + Theme theme2("abc", A_MARKDOWN_HIGHLIGHTING, A_CODE_HIGHLIGHTING, A_PREVIEW_STYLESHEET); + + Theme theme3("xyz", A_MARKDOWN_HIGHLIGHTING, A_CODE_HIGHLIGHTING, A_PREVIEW_STYLESHEET); + + QCOMPARE(theme1 == theme1, true); + QCOMPARE(theme1 == theme2, true); + QCOMPARE(theme1 == theme3, false); +} + +void ThemeTest::throwsIfNameIsEmpty() +{ + try { + Theme theme("", A_MARKDOWN_HIGHLIGHTING, A_CODE_HIGHLIGHTING, A_PREVIEW_STYLESHEET); + QFAIL("Expected exception of type runtime_error not thrown"); + } catch(const std::runtime_error &) {} +} + +void ThemeTest::throwsIfMarkdownHighlightingIsEmpty() +{ + try { + Theme theme(A_THEME_NAME, "", A_CODE_HIGHLIGHTING, A_PREVIEW_STYLESHEET); + QFAIL("Expected exception of type runtime_error not thrown"); + } catch(const std::runtime_error &) {} +} + +void ThemeTest::throwsIfCodeHighlightingIsEmpty() +{ + try { + Theme theme(A_THEME_NAME, A_MARKDOWN_HIGHLIGHTING, "", A_PREVIEW_STYLESHEET); + QFAIL("Expected exception of type runtime_error not thrown"); + } catch(const std::runtime_error &) {} +} + +void ThemeTest::throwsIfPreviewStylesheetIsEmpty() +{ + try { + Theme theme(A_THEME_NAME, A_MARKDOWN_HIGHLIGHTING, A_CODE_HIGHLIGHTING, ""); + QFAIL("Expected exception of type runtime_error not thrown"); + } catch(const std::runtime_error &) {} +} + + diff --git a/test/unit/themetest.h b/test/unit/themetest.h new file mode 100644 index 00000000..9e567742 --- /dev/null +++ b/test/unit/themetest.h @@ -0,0 +1,36 @@ +/* + * Copyright 2015 Christian Loose + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#ifndef THEMETEST_H +#define THEMETEST_H + +#include + +class ThemeTest : public QObject +{ + Q_OBJECT + +private slots: + void isLessThanComparable(); + void isEqualComparable(); + void throwsIfNameIsEmpty(); + void throwsIfMarkdownHighlightingIsEmpty(); + void throwsIfCodeHighlightingIsEmpty(); + void throwsIfPreviewStylesheetIsEmpty(); +}; + +#endif // THEMETEST_H + diff --git a/test/unit/unit.pro b/test/unit/unit.pro index ac004fe8..fe6d6d20 100644 --- a/test/unit/unit.pro +++ b/test/unit/unit.pro @@ -14,21 +14,29 @@ SOURCES += \ completionlistmodeltest.cpp \ snippettest.cpp \ jsonsnippettranslatortest.cpp \ + jsonthemetranslatortest.cpp \ jsontranslatorfactorytest.cpp \ slidelinemappingtest.cpp \ snippetcollectiontest.cpp \ dictionarytest.cpp \ - yamlheadercheckertest.cpp + yamlheadercheckertest.cpp \ + themetest.cpp \ + themecollectiontest.cpp \ + stylemanagertest.cpp HEADERS += \ completionlistmodeltest.h \ snippettest.h \ jsonsnippettranslatortest.h \ + jsonthemetranslatortest.h \ jsontranslatorfactorytest.h \ slidelinemappingtest.h \ snippetcollectiontest.h \ dictionarytest.h \ - yamlheadercheckertest.h + yamlheadercheckertest.h \ + themetest.h \ + themecollectiontest.h \ + stylemanagertest.h target.CONFIG += no_default_install