forked from qt-creator/qt-creator
Core: Separate theme settings widget from options page
Change-Id: I6537e592f87969af5c2e53606a704fabec62fedc Reviewed-by: Alessandro Portale <alessandro.portale@digia.com>
This commit is contained in:
committed by
Orgad Shaneh
parent
e871225057
commit
c1a76f8cc1
@@ -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
|
||||
|
||||
438
src/plugins/coreplugin/themesettingswidget.cpp
Normal file
438
src/plugins/coreplugin/themesettingswidget.cpp
Normal file
@@ -0,0 +1,438 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** 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
|
||||
{
|
||||
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(QWidget *widget);
|
||||
~ThemeSettingsPrivate();
|
||||
|
||||
public:
|
||||
ThemeListModel *m_themeListModel;
|
||||
bool m_refreshingThemeList;
|
||||
Ui::ThemeSettings *m_ui;
|
||||
ThemeEntry m_currentTheme;
|
||||
};
|
||||
|
||||
ThemeSettingsPrivate::ThemeSettingsPrivate(QWidget *widget)
|
||||
: m_themeListModel(new ThemeListModel)
|
||||
, m_refreshingThemeList(false)
|
||||
, m_ui(new Ui::ThemeSettings)
|
||||
{
|
||||
m_currentTheme = ThemeEntry(creatorTheme()->fileName(), true);
|
||||
m_ui->setupUi(widget);
|
||||
m_ui->themeComboBox->setModel(m_themeListModel);
|
||||
}
|
||||
|
||||
ThemeSettingsPrivate::~ThemeSettingsPrivate()
|
||||
{
|
||||
delete m_themeListModel;
|
||||
delete m_ui;
|
||||
}
|
||||
|
||||
ThemeSettingsWidget::ThemeSettingsWidget(QWidget *parent) :
|
||||
QWidget(parent)
|
||||
{
|
||||
d = new ThemeSettingsPrivate(this);
|
||||
|
||||
connect(d->m_ui->themeComboBox, static_cast<void(QComboBox::*)(int)>(&QComboBox::currentIndexChanged),
|
||||
this, &ThemeSettingsWidget::themeSelected);
|
||||
connect(d->m_ui->copyButton, &QAbstractButton::clicked, this, &ThemeSettingsWidget::copyTheme);
|
||||
connect(d->m_ui->renameButton, &QAbstractButton::clicked, this, &ThemeSettingsWidget::renameTheme);
|
||||
connect(d->m_ui->deleteButton, &QAbstractButton::clicked, this, &ThemeSettingsWidget::confirmDeleteTheme);
|
||||
|
||||
refreshThemeList();
|
||||
}
|
||||
|
||||
ThemeSettingsWidget::~ThemeSettingsWidget()
|
||||
{
|
||||
delete d;
|
||||
}
|
||||
|
||||
void ThemeSettingsWidget::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 ThemeSettingsWidget::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 ThemeSettingsWidget::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);
|
||||
}
|
||||
|
||||
void ThemeSettingsWidget::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, &ThemeSettingsWidget::deleteTheme);
|
||||
messageBox->setAttribute(Qt::WA_DeleteOnClose);
|
||||
messageBox->open();
|
||||
}
|
||||
|
||||
void ThemeSettingsWidget::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 ThemeSettingsWidget::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, &ThemeSettingsWidget::copyThemeByName);
|
||||
dialog->open();
|
||||
}
|
||||
|
||||
void ThemeSettingsWidget::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 ThemeSettingsWidget::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 ThemeSettingsWidget::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();
|
||||
}
|
||||
|
||||
void ThemeSettingsWidget::apply()
|
||||
{
|
||||
{
|
||||
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());
|
||||
}
|
||||
|
||||
} // namespace Internal
|
||||
} // namespace Core
|
||||
70
src/plugins/coreplugin/themesettingswidget.h
Normal file
70
src/plugins/coreplugin/themesettingswidget.h
Normal file
@@ -0,0 +1,70 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** 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.
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef THEMESETTINGSWIDGET_H
|
||||
#define THEMESETTINGSWIDGET_H
|
||||
|
||||
#include <QWidget>
|
||||
|
||||
namespace Core {
|
||||
namespace Internal {
|
||||
|
||||
class ThemeSettingsPrivate;
|
||||
|
||||
class ThemeSettingsWidget : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
ThemeSettingsWidget(QWidget *parent = 0);
|
||||
~ThemeSettingsWidget();
|
||||
|
||||
static QString defaultThemeFileName(const QString &fileName = QString());
|
||||
|
||||
void apply();
|
||||
|
||||
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;
|
||||
};
|
||||
|
||||
} // namespace Internal
|
||||
} // namespace Core
|
||||
|
||||
#endif // THEMESETTINGSWIDGET_H
|
||||
Reference in New Issue
Block a user