Commit c1a76f8c authored by Orgad Shaneh's avatar Orgad Shaneh Committed by Orgad Shaneh

Core: Separate theme settings widget from options page

Change-Id: I6537e592f87969af5c2e53606a704fabec62fedc
Reviewed-by: default avatarAlessandro Portale <alessandro.portale@digia.com>
parent e8712250
......@@ -17,6 +17,7 @@ SOURCES += corejsextensions.cpp \
fancytabwidget.cpp \
generalsettings.cpp \
themesettings.cpp \
themesettingswidget.cpp \
id.cpp \
icontext.cpp \
jsexpander.cpp \
......@@ -121,6 +122,7 @@ HEADERS += corejsextensions.h \
fancytabwidget.h \
generalsettings.h \
themesettings.h \
themesettingswidget.h \
id.h \
jsexpander.h \
messagemanager.h \
......
......@@ -98,9 +98,8 @@ QtcPlugin {
"styleanimator.cpp", "styleanimator.h",
"tabpositionindicator.cpp", "tabpositionindicator.h",
"textdocument.cpp", "textdocument.h",
"themesettings.cpp",
"themesettings.h",
"themesettings.ui",
"themesettings.cpp", "themesettings.h", "themesettings.ui",
"themesettingswidget.cpp", "themesettingswidget.h",
"toolsettings.cpp", "toolsettings.h",
"variablechooser.cpp", "variablechooser.h",
"vcsmanager.cpp", "vcsmanager.h",
......
......@@ -29,436 +29,44 @@
****************************************************************************/
#include "themesettings.h"
#include "themesettingswidget.h"
#include "coreconstants.h"
#include "icore.h"
#include "editormanager/editormanager_p.h"
#include "themeeditor/themesettingstablemodel.h"
#include <utils/qtcassert.h>
#include <QDebug>
#include <QDir>
#include <QInputDialog>
#include <QMessageBox>
#include <QSettings>
#include "ui_themesettings.h"
using namespace Utils;
namespace Core {
namespace Internal {
const char themeNameKey[] = "ThemeName";
static QString customThemesPath()
{
QString path = Core::ICore::userResourcePath();
path.append(QLatin1String("/themes/"));
return path;
}
static QString createThemeFileName(const QString &pattern)
{
const QString stylesPath = customThemesPath();
QString baseFileName = stylesPath;
baseFileName += pattern;
// Find an available file name
int i = 1;
QString fileName;
do {
fileName = baseFileName.arg((i == 1) ? QString() : QString::number(i));
++i;
} while (QFile::exists(fileName));
// Create the base directory when it doesn't exist
if (!QFile::exists(stylesPath) && !QDir().mkpath(stylesPath)) {
qWarning() << "Failed to create theme directory:" << stylesPath;
return QString();
}
return fileName;
}
struct ThemeEntry
{
ThemeEntry() {}
ThemeEntry(const QString &fileName, bool readOnly):
m_fileName(fileName),
m_readOnly(readOnly)
{ }
QString fileName() const { return m_fileName; }
QString name() const;
bool readOnly() const { return m_readOnly; }
private:
QString m_fileName;
bool m_readOnly;
};
QString ThemeEntry::name() const
{
QSettings settings(m_fileName, QSettings::IniFormat);
QString n = settings.value(QLatin1String(themeNameKey), QCoreApplication::tr("unnamed")).toString();
return m_readOnly ? QCoreApplication::tr("%1 (built-in)").arg(n) : n;
}
class ThemeListModel : public QAbstractListModel
{
public:
ThemeListModel(QObject *parent = 0):
QAbstractListModel(parent)
{
}
int rowCount(const QModelIndex &parent) const
{
return parent.isValid() ? 0 : m_themes.size();
}
QVariant data(const QModelIndex &index, int role) const
{
if (role == Qt::DisplayRole)
return m_themes.at(index.row()).name();
return QVariant();
}
void removeTheme(int index)
{
beginRemoveRows(QModelIndex(), index, index);
m_themes.removeAt(index);
endRemoveRows();
}
void setThemes(const QList<ThemeEntry> &themes)
{
beginResetModel();
m_themes = themes;
endResetModel();
}
const ThemeEntry &themeAt(int index) const
{
return m_themes.at(index);
}
private:
QList<ThemeEntry> m_themes;
};
class ThemeSettingsPrivate
{
public:
ThemeSettingsPrivate();
~ThemeSettingsPrivate();
public:
ThemeListModel *m_themeListModel;
bool m_refreshingThemeList;
Ui::ThemeSettings *m_ui;
QPointer<QWidget> m_widget;
ThemeEntry m_currentTheme;
};
ThemeSettingsPrivate::ThemeSettingsPrivate()
: m_themeListModel(new ThemeListModel)
, m_refreshingThemeList(false)
, m_ui(0)
{
m_currentTheme = ThemeEntry(creatorTheme()->fileName(), true);
}
ThemeSettingsPrivate::~ThemeSettingsPrivate()
{
delete m_themeListModel;
}
ThemeSettings::ThemeSettings()
ThemeSettings::ThemeSettings() :
m_widget(0)
{
setId(Core::Constants::SETTINGS_ID_ENVIRONMENT);
setDisplayName(tr("Theme"));
setCategory(Core::Constants::SETTINGS_CATEGORY_CORE);
setDisplayCategory(QCoreApplication::translate("Core", Core::Constants::SETTINGS_TR_CATEGORY_CORE));
setCategoryIcon(QLatin1String(Core::Constants::SETTINGS_CATEGORY_CORE_ICON));
d = new ThemeSettingsPrivate();
}
ThemeSettings::~ThemeSettings()
{
delete d;
}
void ThemeSettings::refreshThemeList()
{
QList<ThemeEntry> themes;
QString resourcePath = Core::ICore::resourcePath();
QDir themeDir(resourcePath + QLatin1String("/themes"));
themeDir.setNameFilters(QStringList() << QLatin1String("*.creatortheme"));
themeDir.setFilter(QDir::Files);
int selected = 0;
QStringList themeList = themeDir.entryList();
QString defaultTheme = QFileInfo(defaultThemeFileName()).fileName();
if (themeList.removeAll(defaultTheme))
themeList.prepend(defaultTheme);
foreach (const QString &file, themeList) {
const QString fileName = themeDir.absoluteFilePath(file);
if (d->m_currentTheme.fileName() == fileName)
selected = themes.size();
themes.append(ThemeEntry(fileName, true));
}
if (themes.isEmpty())
qWarning() << "Warning: no themes found in path:" << themeDir.path();
themeDir.setPath(customThemesPath());
foreach (const QString &file, themeDir.entryList()) {
const QString fileName = themeDir.absoluteFilePath(file);
if (d->m_currentTheme.fileName() == fileName)
selected = themes.size();
themes.append(ThemeEntry(fileName, false));
}
d->m_currentTheme = themes[selected];
d->m_refreshingThemeList = true;
d->m_themeListModel->setThemes(themes);
d->m_ui->themeComboBox->setCurrentIndex(selected);
d->m_refreshingThemeList = false;
}
QString ThemeSettings::defaultThemeFileName(const QString &fileName)
{
QString defaultScheme = Core::ICore::resourcePath();
defaultScheme += QLatin1String("/themes/");
if (!fileName.isEmpty() && QFile::exists(defaultScheme + fileName))
defaultScheme += fileName;
else
defaultScheme += QLatin1String("default.creatortheme");
return defaultScheme;
}
void ThemeSettings::themeSelected(int index)
{
bool readOnly = true;
if (index != -1) {
// Check whether we're switching away from a changed theme
if (!d->m_refreshingThemeList)
maybeSaveTheme();
const ThemeEntry &entry = d->m_themeListModel->themeAt(index);
readOnly = entry.readOnly();
d->m_currentTheme = entry;
QSettings settings(entry.fileName(), QSettings::IniFormat);
Theme theme;
theme.readSettings(settings);
d->m_ui->editor->initFrom(&theme);
}
d->m_ui->copyButton->setEnabled(index != -1);
d->m_ui->deleteButton->setEnabled(!readOnly);
d->m_ui->renameButton->setEnabled(!readOnly);
d->m_ui->editor->setReadOnly(readOnly);
delete m_widget;
}
QWidget *ThemeSettings::widget()
{
if (!d->m_widget) {
d->m_widget = new QWidget;
d->m_ui = new Ui::ThemeSettings();
d->m_ui->setupUi(d->m_widget);
d->m_ui->themeComboBox->setModel(d->m_themeListModel);
connect(d->m_ui->themeComboBox, static_cast<void(QComboBox::*)(int)>(&QComboBox::currentIndexChanged),
this, &ThemeSettings::themeSelected);
connect(d->m_ui->copyButton, &QAbstractButton::clicked, this, &ThemeSettings::copyTheme);
connect(d->m_ui->renameButton, &QAbstractButton::clicked, this, &ThemeSettings::renameTheme);
connect(d->m_ui->deleteButton, &QAbstractButton::clicked, this, &ThemeSettings::confirmDeleteTheme);
refreshThemeList();
}
return d->m_widget;
}
void ThemeSettings::confirmDeleteTheme()
{
const int index = d->m_ui->themeComboBox->currentIndex();
if (index == -1)
return;
const ThemeEntry &entry = d->m_themeListModel->themeAt(index);
if (entry.readOnly())
return;
QMessageBox *messageBox = new QMessageBox(QMessageBox::Warning,
tr("Delete Theme"),
tr("Are you sure you want to delete the theme '%1' permanently?").arg(entry.name()),
QMessageBox::Discard | QMessageBox::Cancel,
d->m_ui->deleteButton->window());
// Change the text and role of the discard button
QPushButton *deleteButton = static_cast<QPushButton*>(messageBox->button(QMessageBox::Discard));
deleteButton->setText(tr("Delete"));
messageBox->addButton(deleteButton, QMessageBox::AcceptRole);
messageBox->setDefaultButton(deleteButton);
connect(deleteButton, &QAbstractButton::clicked, messageBox, &QDialog::accept);
connect(messageBox, &QDialog::accepted, this, &ThemeSettings::deleteTheme);
messageBox->setAttribute(Qt::WA_DeleteOnClose);
messageBox->open();
}
void ThemeSettings::deleteTheme()
{
const int index = d->m_ui->themeComboBox->currentIndex();
QTC_ASSERT(index != -1, return);
const ThemeEntry &entry = d->m_themeListModel->themeAt(index);
QTC_ASSERT(!entry.readOnly(), return);
if (QFile::remove(entry.fileName()))
d->m_themeListModel->removeTheme(index);
}
void ThemeSettings::copyTheme()
{
QInputDialog *dialog = new QInputDialog(d->m_ui->copyButton->window());
dialog->setAttribute(Qt::WA_DeleteOnClose);
dialog->setInputMode(QInputDialog::TextInput);
dialog->setWindowTitle(tr("Copy Theme"));
dialog->setLabelText(tr("Theme name:"));
//TODO
//dialog->setTextValue(tr("%1 (copy)").arg(d_ptr->m_value.colorScheme().displayName()));
connect(dialog, &QInputDialog::textValueSelected, this, &ThemeSettings::copyThemeByName);
dialog->open();
}
void ThemeSettings::maybeSaveTheme()
{
if (!d->m_ui->editor->model()->hasChanges())
return;
QMessageBox *messageBox = new QMessageBox(QMessageBox::Warning,
tr("Theme Changed"),
tr("The theme \"%1\" was modified, do you want to save the changes?")
.arg(d->m_currentTheme.name()),
QMessageBox::Discard | QMessageBox::Save,
d->m_ui->themeComboBox->window());
// Change the text of the discard button
QPushButton *discardButton = static_cast<QPushButton*>(messageBox->button(QMessageBox::Discard));
discardButton->setText(tr("Discard"));
messageBox->addButton(discardButton, QMessageBox::DestructiveRole);
messageBox->setDefaultButton(QMessageBox::Save);
if (messageBox->exec() == QMessageBox::Save) {
Theme newTheme;
d->m_ui->editor->model()->toTheme(&newTheme);
newTheme.writeSettings(d->m_currentTheme.fileName());
}
}
void ThemeSettings::renameTheme()
{
int index = d->m_ui->themeComboBox->currentIndex();
if (index == -1)
return;
const ThemeEntry &entry = d->m_themeListModel->themeAt(index);
maybeSaveTheme();
QInputDialog *dialog = new QInputDialog(d->m_ui->renameButton->window());
dialog->setInputMode(QInputDialog::TextInput);
dialog->setWindowTitle(tr("Rename Theme"));
dialog->setLabelText(tr("Theme name:"));
dialog->setTextValue(d->m_ui->editor->model()->m_name);
int ret = dialog->exec();
QString newName = dialog->textValue();
delete dialog;
if (ret != QDialog::Accepted || newName.isEmpty())
return;
// overwrite file with new name
Theme newTheme;
d->m_ui->editor->model()->toTheme(&newTheme);
newTheme.setName(newName);
newTheme.writeSettings(entry.fileName());
refreshThemeList();
}
void ThemeSettings::copyThemeByName(const QString &name)
{
int index = d->m_ui->themeComboBox->currentIndex();
if (index == -1)
return;
const ThemeEntry &entry = d->m_themeListModel->themeAt(index);
QString baseFileName = QFileInfo(entry.fileName()).completeBaseName();
baseFileName += QLatin1String("_copy%1.creatortheme");
QString fileName = createThemeFileName(baseFileName);
if (fileName.isEmpty())
return;
// Ask about saving any existing modifactions
maybeSaveTheme();
Theme newTheme;
d->m_ui->editor->model()->toTheme(&newTheme);
newTheme.setName(name);
newTheme.writeSettings(fileName);
d->m_currentTheme = ThemeEntry(fileName, true);
refreshThemeList();
if (!m_widget)
m_widget = new ThemeSettingsWidget;
return m_widget;
}
void ThemeSettings::apply()
{
if (!d->m_ui) // wasn't shown, can't be changed
return;
{
d->m_ui->editor->model()->toTheme(creatorTheme());
if (creatorTheme()->flag(Theme::ApplyThemePaletteGlobally))
QApplication::setPalette(creatorTheme()->palette(QApplication::palette()));
foreach (QWidget *w, QApplication::topLevelWidgets())
w->update();
}
// save definition of theme
if (!d->m_currentTheme.readOnly()) {
Theme newTheme;
d->m_ui->editor->model()->toTheme(&newTheme);
newTheme.writeSettings(d->m_currentTheme.fileName());
}
// save filename of selected theme in global config
QSettings *settings = Core::ICore::settings();
settings->setValue(QLatin1String(Core::Constants::SETTINGS_THEME), d->m_currentTheme.fileName());
if (m_widget)
m_widget->apply();
}
void ThemeSettings::finish()
{
delete d->m_widget;
if (!d->m_ui) // page was never shown
return
delete d->m_ui;
d->m_ui = 0;
delete m_widget;
m_widget = 0;
}
} // namespace Internal
......
......@@ -36,7 +36,7 @@
namespace Core {
namespace Internal {
class ThemeSettingsPrivate;
class ThemeSettingsWidget;
class ThemeSettings : public IOptionsPage
{
......@@ -50,20 +50,7 @@ public:
void apply();
void finish();
static QString defaultThemeFileName(const QString &fileName = QString());
private slots:
void themeSelected(int index);
void copyTheme();
void renameTheme();
void copyThemeByName(const QString &);
void confirmDeleteTheme();
void deleteTheme();
void maybeSaveTheme();
private:
void refreshThemeList();
ThemeSettingsPrivate *d;
ThemeSettingsWidget *m_widget;
};
} // namespace Internal
......
/****************************************************************************
**
** Copyright (C) 2014 Thorben Kroeger <thorbenkroeger@gmail.com>.
** Contact: http://www.qt-project.org/legal
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Digia. For licensing terms and
** conditions see http://www.qt.io/licensing. For further information
** use the contact form at http://www.qt.io/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 or version 3 as published by the Free
** Software Foundation and appearing in the file LICENSE.LGPLv21 and
** LICENSE.LGPLv3 included in the packaging of this file. Please review the
** following information to ensure the GNU Lesser General Public License
** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Digia gives you certain additional
** rights. These rights are described in the Digia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
****************************************************************************/
#include "themesettingswidget.h"
#include "coreconstants.h"
#include "icore.h"
#include "themeeditor/themesettingstablemodel.h"
#include <utils/theme/theme.h>
#include <utils/qtcassert.h>
#include <QDir>
#include <QInputDialog>
#include <QMessageBox>
#include <QSettings>
#include "ui_themesettings.h"
using namespace Utils;
namespace Core {
namespace Internal {
const char themeNameKey[] = "ThemeName";
static QString customThemesPath()
{
QString path = Core::ICore::userResourcePath();
path.append(QLatin1String("/themes/"));
return path;
}
static QString createThemeFileName(const QString &pattern)
{
const QString stylesPath = customThemesPath();
QString baseFileName = stylesPath;
baseFileName += pattern;
// Find an available file name
int i = 1;
QString fileName;
do {
fileName = baseFileName.arg((i == 1) ? QString() : QString::number(i));
++i;
} while (QFile::exists(fileName));
// Create the base directory when it doesn't exist
if (!QFile::exists(stylesPath) && !QDir().mkpath(stylesPath)) {
qWarning() << "Failed to create theme directory:" << stylesPath;
return QString();
}
return fileName;
}
struct ThemeEntry
{
ThemeEntry() {}
ThemeEntry(const QString &fileName, bool readOnly):
m_fileName(fileName),
m_readOnly(readOnly)
{ }
QString fileName() const { return m_fileName; }
QString name() const;
bool readOnly() const { return m_readOnly; }
private:
QString m_fileName;
bool m_readOnly;
};
QString ThemeEntry::name() const
{
QSettings settings(m_fileName, QSettings::IniFormat);
QString n = settings.value(QLatin1String(themeNameKey), QCoreApplication::tr("unnamed")).toString();
return m_readOnly ? QCoreApplication::tr("%1 (built-in)").arg(n) : n;
}
class ThemeListModel : public QAbstractListModel
{
public:
ThemeListModel(QObject *parent = 0):
QAbstractListModel(parent)
{
}
int rowCount(const QModelIndex &parent) const
{
return parent.isValid() ? 0 : m_themes.size();
}
QVariant data(const QModelIndex &index, int role) const