@ -322,8 +322,6 @@ SOURCES += \
|
||||
widgets/RegisterRefsWidget.cpp \
|
||||
dialogs/SetToDataDialog.cpp \
|
||||
dialogs/EditVariablesDialog.cpp \
|
||||
widgets/ColorSchemePrefWidget.cpp \
|
||||
common/ColorSchemeFileSaver.cpp \
|
||||
dialogs/EditFunctionDialog.cpp \
|
||||
widgets/CutterTreeView.cpp \
|
||||
widgets/ComboQuickFilterView.cpp \
|
||||
@ -340,6 +338,11 @@ SOURCES += \
|
||||
plugins/PluginManager.cpp \
|
||||
common/BasicBlockHighlighter.cpp \
|
||||
dialogs/LinkTypeDialog.cpp \
|
||||
widgets/ColorPicker.cpp \
|
||||
common/ColorThemeWorker.cpp \
|
||||
widgets/ColorThemeComboBox.cpp \
|
||||
widgets/ColorThemeListView.cpp \
|
||||
dialogs/preferences/ColorThemeEditDialog.cpp \
|
||||
common/UpdateWorker.cpp \
|
||||
widgets/MemoryDockWidget.cpp \
|
||||
common/CrashHandler.cpp \
|
||||
@ -442,8 +445,6 @@ HEADERS += \
|
||||
dialogs/SetToDataDialog.h \
|
||||
common/InitialOptions.h \
|
||||
dialogs/EditVariablesDialog.h \
|
||||
common/ColorSchemeFileSaver.h \
|
||||
widgets/ColorSchemePrefWidget.h \
|
||||
dialogs/EditFunctionDialog.h \
|
||||
widgets/CutterTreeView.h \
|
||||
widgets/ComboQuickFilterView.h \
|
||||
@ -462,8 +463,13 @@ HEADERS += \
|
||||
plugins/PluginManager.h \
|
||||
common/BasicBlockHighlighter.h \
|
||||
common/UpdateWorker.h \
|
||||
dialogs/LinkTypeDialog.h \
|
||||
widgets/ColorPicker.h \
|
||||
common/ColorThemeWorker.h \
|
||||
widgets/ColorThemeComboBox.h \
|
||||
widgets/MemoryDockWidget.h \
|
||||
widgets/ColorThemeListView.h \
|
||||
dialogs/preferences/ColorThemeEditDialog.h \
|
||||
dialogs/LinkTypeDialog.h \
|
||||
common/BugReporting.h \
|
||||
common/HighDpiPixmap.h \
|
||||
widgets/GraphLayout.h \
|
||||
@ -520,7 +526,6 @@ FORMS += \
|
||||
widgets/RegisterRefsWidget.ui \
|
||||
dialogs/SetToDataDialog.ui \
|
||||
dialogs/EditVariablesDialog.ui \
|
||||
widgets/ColorSchemePrefWidget.ui \
|
||||
widgets/CutterTreeView.ui \
|
||||
widgets/ComboQuickFilterView.ui \
|
||||
dialogs/HexdumpRangeDialog.ui \
|
||||
@ -528,7 +533,9 @@ FORMS += \
|
||||
dialogs/EditMethodDialog.ui \
|
||||
dialogs/LoadNewTypesDialog.ui \
|
||||
widgets/SdbWidget.ui \
|
||||
dialogs/LinkTypeDialog.ui
|
||||
dialogs/LinkTypeDialog.ui \
|
||||
widgets/ColorPicker.ui \
|
||||
dialogs/preferences/ColorThemeEditDialog.ui
|
||||
|
||||
RESOURCES += \
|
||||
resources.qrc \
|
||||
|
24
src/Main.cpp
@ -2,9 +2,12 @@
|
||||
#include "CutterApplication.h"
|
||||
#include "core/MainWindow.h"
|
||||
#include "common/UpdateWorker.h"
|
||||
#include "common/ColorThemeWorker.h"
|
||||
#include "CutterConfig.h"
|
||||
#include "common/CrashHandler.h"
|
||||
|
||||
#include <QJsonObject>
|
||||
|
||||
/**
|
||||
* @brief Migrate Settings used before Cutter 1.8
|
||||
*/
|
||||
@ -48,6 +51,27 @@ int main(int argc, char *argv[])
|
||||
|
||||
CutterApplication a(argc, argv);
|
||||
|
||||
// Removes obsolete color options (highlight and highlightWord) from custom theme files
|
||||
if (!settings.value("updated_custom_themes", false).toBool()) {
|
||||
const QStringList options = Core()->cmdj("ecj").object().keys()
|
||||
<< ColorThemeWorker::cutterSpecificOptions;
|
||||
for (auto theme : Core()->cmdList("eco*")) {
|
||||
theme = theme.trimmed();
|
||||
if (!ThemeWorker().isCustomTheme(theme)) {
|
||||
continue;
|
||||
}
|
||||
QJsonObject updatedTheme;
|
||||
auto sch = ThemeWorker().getTheme(theme);
|
||||
for (auto key : sch.object().keys()) {
|
||||
if (options.contains(key)) {
|
||||
updatedTheme.insert(key, sch[key]);
|
||||
}
|
||||
}
|
||||
ThemeWorker().save(QJsonDocument(updatedTheme), theme);
|
||||
}
|
||||
settings.setValue("updated_custom_themes", true);
|
||||
}
|
||||
|
||||
if (Config()->getAutoUpdateEnabled()) {
|
||||
UpdateWorker *updateWorker = new UpdateWorker;
|
||||
QObject::connect(updateWorker, &UpdateWorker::checkComplete,
|
||||
|
@ -1,186 +0,0 @@
|
||||
#include "common/ColorSchemeFileSaver.h"
|
||||
|
||||
#include <QDir>
|
||||
#include <QDebug>
|
||||
#include <QColor>
|
||||
#include <QJsonArray>
|
||||
#include <QJsonObject>
|
||||
#include <QStandardPaths>
|
||||
#include "common/Configuration.h"
|
||||
|
||||
static const QStringList cutterSpecificOptions = {
|
||||
"gui.main",
|
||||
"highlight",
|
||||
"gui.imports",
|
||||
"highlightPC",
|
||||
"highlightWord",
|
||||
"gui.navbar.err",
|
||||
"gui.navbar.seek",
|
||||
"gui.navbar.pc",
|
||||
"gui.navbar.sym",
|
||||
"gui.dataoffset",
|
||||
"gui.navbar.code",
|
||||
"gui.navbar.empty",
|
||||
"gui.navbar.str",
|
||||
"gui.disass_selected",
|
||||
"gui.breakpoint_background",
|
||||
"gui.overview.node",
|
||||
"gui.tooltip.background",
|
||||
"gui.tooltip.foreground"
|
||||
"gui.overview.node"
|
||||
};
|
||||
|
||||
ColorSchemeFileSaver::ColorSchemeFileSaver(QObject *parent) : QObject (parent)
|
||||
{
|
||||
char* szThemes = r_str_home(R2_HOME_THEMES);
|
||||
customR2ThemesLocationPath = szThemes;
|
||||
r_mem_free(szThemes);
|
||||
if (!QDir(customR2ThemesLocationPath).exists()) {
|
||||
QDir().mkpath(customR2ThemesLocationPath);
|
||||
}
|
||||
|
||||
QDir currDir { QStringLiteral("%1%2%3")
|
||||
.arg(r_sys_prefix(nullptr))
|
||||
.arg(R_SYS_DIR)
|
||||
.arg(R2_THEMES)
|
||||
};
|
||||
if (currDir.exists()) {
|
||||
standardR2ThemesLocationPath = currDir.absolutePath();
|
||||
} else {
|
||||
QMessageBox::critical(nullptr,
|
||||
tr("Standard themes not found!"),
|
||||
tr("The radare2 standard themes could not be found! This probably means radare2 is not properly installed. If you think it is open an issue please.")
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
QFile::FileError ColorSchemeFileSaver::copy(const QString &srcThemeName,
|
||||
const QString ©ThemeName) const
|
||||
{
|
||||
QFile fIn(standardR2ThemesLocationPath + QDir::separator() + srcThemeName);
|
||||
QFile fOut(customR2ThemesLocationPath + QDir::separator() + copyThemeName);
|
||||
|
||||
if (srcThemeName != QStringLiteral("default") && !fIn.open(QFile::ReadOnly)) {
|
||||
fIn.setFileName(customR2ThemesLocationPath + QDir::separator() + srcThemeName);
|
||||
if (!fIn.open(QFile::ReadOnly)) {
|
||||
return fIn.error();
|
||||
}
|
||||
}
|
||||
|
||||
const QString &srcTheme = fIn.readAll();
|
||||
fIn.close();
|
||||
|
||||
if (!fOut.open(QFile::WriteOnly | QFile::Truncate)) {
|
||||
return fOut.error();
|
||||
}
|
||||
|
||||
QStringList options = Core()->cmdj("ecj").object().keys();
|
||||
options << cutterSpecificOptions;
|
||||
QStringList src;
|
||||
|
||||
if (srcThemeName == "default") {
|
||||
const QString &theme = Config()->getColorTheme();
|
||||
Core()->cmd("ecd");
|
||||
QJsonObject obj = Core()->cmdj("ecj").object();
|
||||
Core()->cmd(QStringLiteral("eco %1").arg(theme));
|
||||
QColor back = Config()->getColor(standardBackgroundOptionName);
|
||||
obj[standardBackgroundOptionName] = QJsonArray({back.red(), back.green(), back.blue()});
|
||||
for (const QString &it : obj.keys()) {
|
||||
QJsonArray rgb = obj[it].toArray();
|
||||
if (rgb.size() != 3) {
|
||||
continue;
|
||||
}
|
||||
src.push_back(QStringLiteral("ec %1 rgb:%2")
|
||||
.arg(it)
|
||||
.arg(QColor(rgb[0].toInt(), rgb[1].toInt(), rgb[2].toInt()).name().remove('#')));
|
||||
}
|
||||
} else {
|
||||
src = srcTheme.split('\n');
|
||||
}
|
||||
|
||||
QStringList tmp;
|
||||
for (const QString &it : src) {
|
||||
if (it.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
fOut.write(it.toUtf8() + '\n');
|
||||
|
||||
tmp = it.split(' ');
|
||||
if (it.length() > 2 && it.left(2) == "#~") {
|
||||
options.removeOne(tmp[0].remove("#~").toUtf8());
|
||||
} else if (tmp.size() > 1) {
|
||||
options.removeOne(tmp.at(1));
|
||||
}
|
||||
}
|
||||
|
||||
for (const QString &it : options) {
|
||||
if (cutterSpecificOptions.contains(it)) {
|
||||
fOut.write("#~");
|
||||
} else {
|
||||
fOut.write("ec ");
|
||||
}
|
||||
fOut.write(QStringLiteral("%1 rgb:%2\n")
|
||||
.arg(it)
|
||||
.arg(Config()->getColor(it).name().remove('#')).toUtf8());
|
||||
}
|
||||
|
||||
fOut.close();
|
||||
|
||||
return QFile::FileError::NoError;
|
||||
}
|
||||
|
||||
QFile::FileError ColorSchemeFileSaver::save(const QString &scheme, const QString &schemeName) const
|
||||
{
|
||||
QFile fOut(customR2ThemesLocationPath + QDir::separator() + schemeName);
|
||||
if (!fOut.open(QFile::WriteOnly | QFile::Truncate))
|
||||
return fOut.error();
|
||||
|
||||
fOut.write(scheme.toUtf8());
|
||||
fOut.close();
|
||||
return QFile::FileError::NoError;
|
||||
}
|
||||
|
||||
bool ColorSchemeFileSaver::isCustomScheme(const QString &schemeName) const
|
||||
{
|
||||
return QFile::exists(QDir(customR2ThemesLocationPath).filePath(schemeName));
|
||||
}
|
||||
|
||||
bool ColorSchemeFileSaver::isNameEngaged(const QString &name) const
|
||||
{
|
||||
return (!standardR2ThemesLocationPath.isEmpty() && QFile::exists(standardR2ThemesLocationPath + QDir::separator() + name)) ||
|
||||
QFile::exists(customR2ThemesLocationPath + QDir::separator() + name);
|
||||
}
|
||||
|
||||
QMap<QString, QColor> ColorSchemeFileSaver::getCutterSpecific() const
|
||||
{
|
||||
QFile f(customR2ThemesLocationPath + QDir::separator() + Config()->getColorTheme());
|
||||
if (!f.open(QFile::ReadOnly))
|
||||
return QMap<QString, QColor>();
|
||||
|
||||
const QStringList &data = QString(f.readAll()).split('\n');
|
||||
f.close();
|
||||
|
||||
QMap<QString, QColor> ret;
|
||||
for (const QString &it : data) {
|
||||
if (it.length() > 2 && it.left(2) == "#~") {
|
||||
QStringList currLine = it.split(' ');
|
||||
if (currLine.size() > 1) {
|
||||
ret.insert(currLine[0].remove("#~"), currLine[1].replace("rgb:", "#"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
QStringList ColorSchemeFileSaver::getCustomSchemes() const
|
||||
{
|
||||
return QDir(customR2ThemesLocationPath).entryList(QDir::Files | QDir::NoDotAndDotDot, QDir::Name);
|
||||
}
|
||||
|
||||
void ColorSchemeFileSaver::deleteScheme(const QString &schemeName) const
|
||||
{
|
||||
if (!isCustomScheme(schemeName))
|
||||
return;
|
||||
QFile::remove(customR2ThemesLocationPath + QDir::separator() + schemeName);
|
||||
}
|
@ -1,47 +0,0 @@
|
||||
#ifndef COLORSCHEMEFILESAVER_H
|
||||
#define COLORSCHEMEFILESAVER_H
|
||||
|
||||
#include <QFile>
|
||||
#include <QColor>
|
||||
#include <QObject>
|
||||
|
||||
constexpr const char *standardBackgroundOptionName = "gui.background";
|
||||
constexpr const char *standardTextOptionName = "btext";
|
||||
#define ColorSchemeFileWorker() (ColorSchemeFileSaver::instance())
|
||||
|
||||
class ColorSchemeFileSaver : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
static ColorSchemeFileSaver &instance()
|
||||
{
|
||||
static ColorSchemeFileSaver ex;
|
||||
return ex;
|
||||
}
|
||||
|
||||
virtual ~ColorSchemeFileSaver() {}
|
||||
|
||||
QFile::FileError copy(const QString &srcSchemeName, const QString ©SchemeName) const;
|
||||
|
||||
QFile::FileError save(const QString &scheme, const QString &schemeName) const;
|
||||
|
||||
bool isCustomScheme(const QString &schemeName) const;
|
||||
|
||||
bool isNameEngaged(const QString &name) const;
|
||||
|
||||
QMap<QString, QColor> getCutterSpecific() const;
|
||||
|
||||
QStringList getCustomSchemes() const;
|
||||
|
||||
void deleteScheme(const QString &schemeName) const;
|
||||
|
||||
private:
|
||||
QString standardR2ThemesLocationPath;
|
||||
QString customR2ThemesLocationPath;
|
||||
|
||||
ColorSchemeFileSaver(QObject *parent = nullptr);
|
||||
ColorSchemeFileSaver(const ColorSchemeFileSaver &root) = delete;
|
||||
ColorSchemeFileSaver &operator=(const ColorSchemeFileSaver &) = delete;
|
||||
};
|
||||
|
||||
#endif // COLORSCHEMEFILESAVER_H
|
306
src/common/ColorThemeWorker.cpp
Normal file
@ -0,0 +1,306 @@
|
||||
#include "ColorThemeWorker.h"
|
||||
|
||||
#include <QDir>
|
||||
#include <QFile>
|
||||
#include <QColor>
|
||||
#include <QJsonArray>
|
||||
#include <QStandardPaths>
|
||||
#include "common/Configuration.h"
|
||||
|
||||
const QStringList ColorThemeWorker::cutterSpecificOptions = {
|
||||
"linehl",
|
||||
"wordhl",
|
||||
"gui.main",
|
||||
"gui.imports",
|
||||
"highlightPC",
|
||||
"gui.navbar.err",
|
||||
"gui.navbar.seek",
|
||||
"gui.navbar.pc",
|
||||
"gui.navbar.sym",
|
||||
"gui.dataoffset",
|
||||
"gui.navbar.code",
|
||||
"gui.navbar.empty",
|
||||
"angui.navbar.str",
|
||||
"gui.disass_selected",
|
||||
"gui.breakpoint_background",
|
||||
"gui.overview.node",
|
||||
"gui.border",
|
||||
"gui.background",
|
||||
"gui.alt_background",
|
||||
"gui.disass_selected"
|
||||
};
|
||||
|
||||
const QStringList ColorThemeWorker::radare2UnusedOptions = {
|
||||
"graph.box",
|
||||
"graph.box2",
|
||||
"graph.box3",
|
||||
"graph.box4",
|
||||
"graph.current",
|
||||
"graph.box2",
|
||||
"widget_sel",
|
||||
"widget_bg",
|
||||
"label",
|
||||
"ai.write",
|
||||
"invalid",
|
||||
"ai.seq",
|
||||
"args",
|
||||
"ai.read",
|
||||
"ai.exec",
|
||||
"ai.ascii",
|
||||
"prompt",
|
||||
"graph.traced"
|
||||
};
|
||||
|
||||
ColorThemeWorker::ColorThemeWorker(QObject *parent) : QObject (parent)
|
||||
{
|
||||
char* szThemes = r_str_home(R2_HOME_THEMES);
|
||||
customR2ThemesLocationPath = szThemes;
|
||||
r_mem_free(szThemes);
|
||||
if (!QDir(customR2ThemesLocationPath).exists()) {
|
||||
QDir().mkpath(customR2ThemesLocationPath);
|
||||
}
|
||||
|
||||
QDir currDir { QStringLiteral("%1%2%3")
|
||||
.arg(r_sys_prefix(nullptr))
|
||||
.arg(R_SYS_DIR)
|
||||
.arg(R2_THEMES)
|
||||
};
|
||||
if (currDir.exists()) {
|
||||
standardR2ThemesLocationPath = currDir.absolutePath();
|
||||
} else {
|
||||
QMessageBox::critical(nullptr,
|
||||
tr("Standard themes not found"),
|
||||
tr("The radare2 standard themes could not be found. "
|
||||
"Most likely, radare2 is not properly installed.")
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
QColor ColorThemeWorker::mergeColors(const QColor& upper, const QColor& lower) const
|
||||
{
|
||||
qreal r1, g1, b1, a1;
|
||||
qreal r2, g2, b2, a2;
|
||||
qreal r, g, b, a;
|
||||
|
||||
upper.getRgbF(&r1, &g1, &b1, &a1);
|
||||
lower.getRgbF(&r2, &g2, &b2, &a2);
|
||||
|
||||
a = (1.0 - a1) * a2 + a1;
|
||||
r = ((1.0 - a1) * a2 * r2 + a1 * r1) / a;
|
||||
g = ((1.0 - a1) * a2 * g2 + a1 * g1) / a;
|
||||
b = ((1.0 - a1) * a2 * b2 + a1 * b1) / a;
|
||||
|
||||
QColor res;
|
||||
res.setRgbF(r, g, b, a);
|
||||
return res;
|
||||
}
|
||||
|
||||
QString ColorThemeWorker::copy(const QString &srcThemeName,
|
||||
const QString ©ThemeName) const
|
||||
{
|
||||
if (!isThemeExist(srcThemeName)) {
|
||||
return tr("Theme <b>%1</b> does not exist.")
|
||||
.arg(srcThemeName);
|
||||
}
|
||||
|
||||
return save(getTheme(srcThemeName), copyThemeName);
|
||||
}
|
||||
|
||||
QString ColorThemeWorker::save(const QJsonDocument &theme, const QString &themeName) const
|
||||
{
|
||||
QFile fOut(QDir(customR2ThemesLocationPath).filePath(themeName));
|
||||
if (!fOut.open(QFile::WriteOnly | QFile::Truncate)) {
|
||||
return tr("The file <b>%1</b> cannot be opened.")
|
||||
.arg(QFileInfo(fOut).filePath());
|
||||
}
|
||||
|
||||
QJsonObject obj = theme.object();
|
||||
for (auto it = obj.constBegin(); it != obj.constEnd(); it++) {
|
||||
QString line;
|
||||
if (cutterSpecificOptions.contains(it.key())) {
|
||||
line = "#~%1 %2\n";
|
||||
} else {
|
||||
line = "ec %1 %2\n";
|
||||
}
|
||||
QJsonArray arr = it.value().toArray();
|
||||
if (arr.isEmpty()) {
|
||||
fOut.write(line.arg(it.key())
|
||||
.arg(it.value().toVariant().value<QColor>().name()).toUtf8());
|
||||
} else if (arr.size() == 3) {
|
||||
fOut.write(line.arg(it.key())
|
||||
.arg(QColor(arr[0].toInt(), arr[1].toInt(), arr[2].toInt()).name()).toUtf8());
|
||||
}
|
||||
}
|
||||
|
||||
fOut.close();
|
||||
return "";
|
||||
}
|
||||
|
||||
bool ColorThemeWorker::isCustomTheme(const QString &themeName) const
|
||||
{
|
||||
return QFile::exists(QDir(customR2ThemesLocationPath).filePath(themeName));
|
||||
}
|
||||
|
||||
bool ColorThemeWorker::isThemeExist(const QString &name) const
|
||||
{
|
||||
QStringList themes = Core()->getColorThemes();
|
||||
return themes.contains(name);
|
||||
}
|
||||
|
||||
QJsonDocument ColorThemeWorker::getTheme(const QString& themeName) const
|
||||
{
|
||||
int r, g, b;
|
||||
QJsonObject theme;
|
||||
QString curr = Config()->getColorTheme();
|
||||
|
||||
if (themeName != curr) {
|
||||
Core()->cmd(QString("eco %1").arg(themeName));
|
||||
theme = Core()->cmdj("ecj").object();
|
||||
Core()->cmd(QString("eco %1").arg(curr));
|
||||
} else {
|
||||
theme = Core()->cmdj("ecj").object();
|
||||
}
|
||||
|
||||
if (isCustomTheme(themeName)) {
|
||||
QFile src(QDir(customR2ThemesLocationPath).filePath(themeName));
|
||||
if (!src.open(QFile::ReadOnly)) {
|
||||
return QJsonDocument();
|
||||
}
|
||||
QStringList sl;
|
||||
for (auto &line : QString(src.readAll()).split('\n', QString::SkipEmptyParts)) {
|
||||
sl = line.replace("#~", "ec ").replace("rgb:", "#").split(' ', QString::SkipEmptyParts);
|
||||
if (sl.size() != 3 || sl[0][0] == '#') {
|
||||
continue;
|
||||
}
|
||||
QColor(sl[2]).getRgb(&r, &g, &b);
|
||||
theme.insert(sl[1], QJsonArray({r, g, b}));
|
||||
}
|
||||
} else {
|
||||
for (auto &it : cutterSpecificOptions) {
|
||||
mergeColors(Config()->getColor(it),
|
||||
Config()->getColor("gui.background")).getRgb(&r, &g, &b);
|
||||
theme.insert(it, QJsonArray({r, g, b}));
|
||||
}
|
||||
}
|
||||
|
||||
for (auto &key : radare2UnusedOptions) {
|
||||
theme.remove(key);
|
||||
}
|
||||
return QJsonDocument(theme);
|
||||
}
|
||||
|
||||
QString ColorThemeWorker::deleteTheme(const QString &themeName) const
|
||||
{
|
||||
if (!isCustomTheme(themeName)) {
|
||||
return tr("You can not delete standard radare2 color themes.");
|
||||
}
|
||||
if (!isThemeExist(themeName)) {
|
||||
return tr("Theme <b>%1</b> does not exist.").arg(themeName);
|
||||
}
|
||||
|
||||
QFile file(QDir(customR2ThemesLocationPath).filePath(themeName));
|
||||
if (file.isWritable()) {
|
||||
return tr("You have no permission to write to <b>%1</b>")
|
||||
.arg(QFileInfo(file).filePath());
|
||||
}
|
||||
if (!file.open(QFile::ReadOnly)) {
|
||||
return tr("File <b>%1</b> can not be opened.")
|
||||
.arg(QFileInfo(file).filePath());
|
||||
}
|
||||
if (!file.remove()) {
|
||||
return tr("File <b>%1</b> can not be removed.")
|
||||
.arg(QFileInfo(file).filePath());
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
QString ColorThemeWorker::importTheme(const QString& file) const
|
||||
{
|
||||
QFileInfo src(file);
|
||||
if (!src.exists()) {
|
||||
return tr("File <b>%1</b> does not exist.").arg(file);
|
||||
}
|
||||
|
||||
bool ok;
|
||||
bool isTheme = isFileTheme(file, &ok);
|
||||
if (!ok) {
|
||||
return tr("File <b>%1</b> could not be opened. "
|
||||
"Please make sure you have access to it and try again.")
|
||||
.arg(file);
|
||||
} else if (!isTheme) {
|
||||
return tr("File <b>%1</b> is not a Cutter color theme").arg(file);
|
||||
}
|
||||
|
||||
QString name = src.fileName();
|
||||
if (isThemeExist(name)) {
|
||||
return tr("A color theme named <b>%1</b> already exists.").arg(name);
|
||||
}
|
||||
|
||||
if (QFile::copy(file, QDir(customR2ThemesLocationPath).filePath(name))) {
|
||||
return "";
|
||||
} else {
|
||||
return tr("Error occurred during importing. "
|
||||
"Please make sure you have an access to "
|
||||
"the directory <b>%1</b> and try again.")
|
||||
.arg(src.dir().path());
|
||||
}
|
||||
}
|
||||
|
||||
QString ColorThemeWorker::renameTheme(const QString& themeName, const QString& newName) const
|
||||
{
|
||||
if (isThemeExist(newName)) {
|
||||
return tr("A color theme named <b>\"%1\"</b> already exists.").arg(newName);
|
||||
}
|
||||
|
||||
if (!isCustomTheme(themeName)) {
|
||||
return tr("You can not rename standard radare2 themes.");
|
||||
}
|
||||
|
||||
QDir dir = customR2ThemesLocationPath;
|
||||
bool ok = QFile::rename(dir.filePath(themeName), dir.filePath(newName));
|
||||
if (!ok) {
|
||||
return tr("Something went wrong during renaming. "
|
||||
"Please make sure you have access to the directory <b>\"%1\"</b>.").arg(dir.path());
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
bool ColorThemeWorker::isFileTheme(const QString& filePath, bool* ok) const
|
||||
{
|
||||
QFile f(filePath);
|
||||
if (!f.open(QFile::ReadOnly)) {
|
||||
*ok = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
const QString colors = "black|red|white|green|magenta|yellow|cyan|blue|gray|none";
|
||||
QString options = (Core()->cmdj("ecj").object().keys() << cutterSpecificOptions)
|
||||
.join('|')
|
||||
.replace(".", "\\.");
|
||||
QRegExp regexp = QRegExp(QString("((ec\\s+(%1)\\s+(((rgb:|#)([0-9a-fA-F]{3}){1,2})|(%2))))\\s*")
|
||||
.arg(options)
|
||||
.arg(colors));
|
||||
|
||||
for (auto &line : QString(f.readAll()).split('\n', QString::SkipEmptyParts)) {
|
||||
line.replace("#~", "ec ");
|
||||
if (!line.isEmpty() && !regexp.exactMatch(line)) {
|
||||
*ok = true;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
*ok = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
QStringList ColorThemeWorker::customThemes() const
|
||||
{
|
||||
QStringList themes = Core()->getColorThemes();
|
||||
QStringList ret;
|
||||
for (int i = 0; i < themes.size(); i++) {
|
||||
if (isCustomTheme(themes[i])) {
|
||||
ret.push_back(themes[i]);
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
130
src/common/ColorThemeWorker.h
Normal file
@ -0,0 +1,130 @@
|
||||
#ifndef COLORTHEMEWORKER_H
|
||||
#define COLORTHEMEWORKER_H
|
||||
|
||||
#include <QFile>
|
||||
#include <QColor>
|
||||
#include <QObject>
|
||||
#include "Cutter.h"
|
||||
#include <QJsonObject>
|
||||
|
||||
#define ThemeWorker() (ColorThemeWorker::instance())
|
||||
|
||||
/**
|
||||
* @brief The ColorThemeWorker class is a singletone that provides API for working with
|
||||
* color themes.
|
||||
*/
|
||||
class ColorThemeWorker : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
/**
|
||||
* @brief radare2SpecificOptions is list of all available radare2-only color options.
|
||||
*/
|
||||
const QStringList radare2SpecificOptions = Core()->cmdj("ecj").object().keys();
|
||||
|
||||
/**
|
||||
* @brief cutterSpecificOptions is list of all available Cutter-only color options.
|
||||
*/
|
||||
static const QStringList cutterSpecificOptions;
|
||||
|
||||
/**
|
||||
* @brief radare2UnusedOptions is a list of all radare2 options that Cutter does not use.
|
||||
*/
|
||||
static const QStringList radare2UnusedOptions;
|
||||
|
||||
static ColorThemeWorker &instance()
|
||||
{
|
||||
static ColorThemeWorker ex;
|
||||
return ex;
|
||||
}
|
||||
|
||||
virtual ~ColorThemeWorker() {}
|
||||
|
||||
|
||||
/**
|
||||
* @brief Copies @a srcThemeName with name @a copyThemeName.
|
||||
* @param srcThemeName
|
||||
* Name of theme to be copied.
|
||||
* @param copyThemeName
|
||||
* Name of copy.
|
||||
* @return "" on success or error message.
|
||||
*/
|
||||
QString copy(const QString &srcThemeName, const QString ©ThemeName) const;
|
||||
|
||||
/**
|
||||
* @brief Saves @a theme as @a themeName theme.
|
||||
* @param theme
|
||||
* Theme to be saved.
|
||||
* @param themeName
|
||||
* Name of theme to save.
|
||||
* @return "" on success or error message.
|
||||
*/
|
||||
QString save(const QJsonDocument& theme, const QString &themeName) const;
|
||||
|
||||
/**
|
||||
* @brief Returns whether or not @a themeName theme is custom (created by user or imported) or not.
|
||||
* @param themeName
|
||||
* Name of theme to check.
|
||||
*/
|
||||
bool isCustomTheme(const QString &themeName) const;
|
||||
|
||||
/**
|
||||
* @brief Returns whether or not @a name theme already exists.
|
||||
* @return true if theme exists, false - if not.
|
||||
*/
|
||||
bool isThemeExist(const QString &name) const;
|
||||
|
||||
/**
|
||||
* @brief Returns theme as Json where key is option name and value is array of 3 Ints (Red, Green, Blue).
|
||||
* @param themeName
|
||||
* Theme to get.
|
||||
*/
|
||||
QJsonDocument getTheme(const QString &themeName) const;
|
||||
|
||||
/**
|
||||
* @brief Deletes theme named @a themeName.
|
||||
* @param themeName
|
||||
* Name of theme to be removed.
|
||||
* @return "" on success or error message.
|
||||
*/
|
||||
QString deleteTheme(const QString &themeName) const;
|
||||
|
||||
/**
|
||||
* @brief Imports theme from @a file.
|
||||
* @return "" on success or error message.
|
||||
*/
|
||||
QString importTheme(const QString& file) const;
|
||||
|
||||
/**
|
||||
* @brief Renames theme from @a themeName to @a newName.
|
||||
* @return "" on success or error message.
|
||||
*/
|
||||
QString renameTheme(const QString& themeName, const QString& newName) const;
|
||||
|
||||
/**
|
||||
* @brief Returns whether or not file at @a filePath is a color theme.
|
||||
* @param filePath
|
||||
* Path to file to check.
|
||||
* @param ok
|
||||
* Output parameter. Indicates wheter or not check was successfull.
|
||||
* @return true if given file is color theme and ok == true, otherwise returns false.
|
||||
*/
|
||||
bool isFileTheme(const QString &filePath, bool *ok) const;
|
||||
|
||||
/**
|
||||
* @brief Returns list of all custom themes.
|
||||
*/
|
||||
QStringList customThemes() const;
|
||||
|
||||
private:
|
||||
QString standardR2ThemesLocationPath;
|
||||
QString customR2ThemesLocationPath;
|
||||
|
||||
ColorThemeWorker(QObject *parent = nullptr);
|
||||
ColorThemeWorker(const ColorThemeWorker &root) = delete;
|
||||
ColorThemeWorker &operator=(const ColorThemeWorker &) = delete;
|
||||
|
||||
QColor mergeColors(const QColor &upper, const QColor &lower) const;
|
||||
};
|
||||
|
||||
#endif // COLORTHEMEWORKER_H
|
@ -7,9 +7,9 @@
|
||||
#include <QApplication>
|
||||
#include <QLibraryInfo>
|
||||
|
||||
#include "common/ColorSchemeFileSaver.h"
|
||||
#include "common/ColorThemeWorker.h"
|
||||
|
||||
const QList<CutterQtTheme> kCutterQtThemesList = {
|
||||
const QList<CutterInterfaceTheme> kCutterInterfaceThemesList = {
|
||||
{ "Native", static_cast<ColorFlags>(LightFlag | DarkFlag) },
|
||||
{ "Dark", DarkFlag },
|
||||
{ "Light", LightFlag }
|
||||
@ -54,7 +54,7 @@ static const QHash<QString, QVariant> asmOptions = {
|
||||
};
|
||||
|
||||
|
||||
Configuration::Configuration() : QObject()
|
||||
Configuration::Configuration() : QObject(), nativePalette(qApp->palette())
|
||||
{
|
||||
mPtr = this;
|
||||
if (!s.isWritable()) {
|
||||
@ -75,7 +75,7 @@ Configuration *Configuration::instance()
|
||||
|
||||
void Configuration::loadInitial()
|
||||
{
|
||||
setTheme(getTheme());
|
||||
setInterfaceTheme(getInterfaceTheme());
|
||||
setColorTheme(getColorTheme());
|
||||
applySavedAsmOptions();
|
||||
}
|
||||
@ -210,6 +210,15 @@ void Configuration::loadBaseThemeNative()
|
||||
qApp->setStyleSheet(stylesheet);
|
||||
}
|
||||
|
||||
qApp->setPalette(nativePalette);
|
||||
/* Some widgets does not change its palette when QApplication changes one
|
||||
* so this loop force all widgets do this, but all widgets take palette from
|
||||
* QApplication::palette() when they are created so line above is necessary too.
|
||||
*/
|
||||
for (auto widget : qApp->allWidgets()) {
|
||||
widget->setPalette(nativePalette);
|
||||
}
|
||||
|
||||
/* Colors */
|
||||
// GUI
|
||||
setColor("gui.cflow", QColor(0, 0, 0));
|
||||
@ -240,18 +249,18 @@ void Configuration::loadNativeTheme()
|
||||
setColor("gui.background", QColor(30, 30, 30));
|
||||
setColor("gui.alt_background", QColor(42, 42, 42));
|
||||
setColor("gui.disass_selected", QColor(35, 35, 35));
|
||||
setColor("highlight", QColor(255, 255, 255, 15));
|
||||
setColor("highlightWord", QColor(20, 20, 20, 255));
|
||||
setColor("linehl", QColor(255, 255, 255, 15));
|
||||
setColor("wordhl", QColor(20, 20, 20, 255));
|
||||
setColor("highlightPC", QColor(87, 26, 7));
|
||||
setColor("gui.tooltip.background", QColor(42, 44, 46));
|
||||
setColor("gui.tooltip.foreground", QColor(250, 252, 254));
|
||||
} else {
|
||||
setColor("gui.border", QColor(0, 0, 0));
|
||||
setColor("gui.border", QColor(0, 0, 0));
|
||||
setColor("gui.background", QColor(255, 255, 255));
|
||||
setColor("gui.alt_background", QColor(245, 250, 255));
|
||||
setColor("gui.disass_selected", QColor(255, 255, 255));
|
||||
setColor("highlight", QColor(210, 210, 255, 150));
|
||||
setColor("highlightWord", QColor(179, 119, 214, 60));
|
||||
setColor("linehl", QColor(210, 210, 255, 150));
|
||||
setColor("wordhl", QColor(179, 119, 214, 60));
|
||||
setColor("highlightPC", QColor(214, 255, 210));
|
||||
}
|
||||
}
|
||||
@ -269,6 +278,11 @@ void Configuration::loadLightTheme()
|
||||
f.open(QFile::ReadOnly | QFile::Text);
|
||||
QTextStream ts(&f);
|
||||
QString stylesheet = ts.readAll();
|
||||
|
||||
QPalette p = qApp->palette();
|
||||
p.setColor(QPalette::Text, Qt::black);
|
||||
qApp->setPalette(p);
|
||||
|
||||
qApp->setStyleSheet(stylesheet);
|
||||
}
|
||||
|
||||
@ -276,8 +290,8 @@ void Configuration::loadLightTheme()
|
||||
setColor("gui.background", QColor(255, 255, 255));
|
||||
setColor("gui.alt_background", QColor(245, 250, 255));
|
||||
setColor("gui.disass_selected", QColor(255, 255, 255));
|
||||
setColor("highlight", QColor(210, 210, 255, 150));
|
||||
setColor("highlightWord", QColor(179, 119, 214, 60));
|
||||
setColor("linehl", QColor(210, 210, 255, 150));
|
||||
setColor("wordhl", QColor(179, 119, 214, 60));
|
||||
setColor("highlightPC", QColor(214, 255, 210));
|
||||
setColor("gui.navbar.empty", QColor(220, 236, 245));
|
||||
setColor("gui.navbar.err", QColor(3, 170, 245));
|
||||
@ -304,6 +318,9 @@ void Configuration::loadBaseThemeDark()
|
||||
" height: 12px;"
|
||||
"}";
|
||||
#endif
|
||||
QPalette p = qApp->palette();
|
||||
p.setColor(QPalette::Text, Qt::white);
|
||||
qApp->setPalette(p);
|
||||
qApp->setStyleSheet(stylesheet);
|
||||
}
|
||||
|
||||
@ -343,10 +360,10 @@ void Configuration::loadDarkTheme()
|
||||
// Disassembly nodes background when selected
|
||||
setColor("gui.disass_selected", QColor(31, 34, 40));
|
||||
// Disassembly line selected
|
||||
setColor("highlight", QColor(21, 29, 29, 150));
|
||||
setColor("highlightWord", QColor(52, 58, 71, 255));
|
||||
setColor("gui.tooltip.background", QColor(42, 44, 46));
|
||||
setColor("gui.tooltip.foreground", QColor(250, 252, 254));
|
||||
setColor("linehl", QColor(21, 29, 29, 150));
|
||||
setColor("wordhl", QColor(52, 58, 71, 255));
|
||||
}
|
||||
|
||||
const QFont Configuration::getFont() const
|
||||
@ -361,20 +378,20 @@ void Configuration::setFont(const QFont &font)
|
||||
emit fontsUpdated();
|
||||
}
|
||||
|
||||
QString Configuration::getLastThemeOf(const CutterQtTheme &currQtTheme) const
|
||||
QString Configuration::getLastThemeOf(const CutterInterfaceTheme &currInterfaceTheme) const
|
||||
{
|
||||
return s.value("lastThemeOf." + currQtTheme.name,
|
||||
return s.value("lastThemeOf." + currInterfaceTheme.name,
|
||||
Config()->getColorTheme()).toString();
|
||||
}
|
||||
|
||||
void Configuration::setTheme(int theme)
|
||||
void Configuration::setInterfaceTheme(int theme)
|
||||
{
|
||||
if (theme >= kCutterQtThemesList.size() ||
|
||||
if (theme >= kCutterInterfaceThemesList.size() ||
|
||||
theme < 0) {
|
||||
theme = 0;
|
||||
}
|
||||
s.setValue("ColorPalette", theme);
|
||||
QString themeName = kCutterQtThemesList[theme].name;
|
||||
QString themeName = kCutterInterfaceThemesList[theme].name;
|
||||
|
||||
if (themeName == "Native") {
|
||||
loadNativeTheme();
|
||||
@ -386,18 +403,18 @@ void Configuration::setTheme(int theme)
|
||||
loadNativeTheme();
|
||||
}
|
||||
|
||||
emit themeChanged();
|
||||
emit interfaceThemeChanged();
|
||||
emit colorsUpdated();
|
||||
}
|
||||
|
||||
const CutterQtTheme *Configuration::getCurrentTheme()
|
||||
const CutterInterfaceTheme *Configuration::getCurrentTheme()
|
||||
{
|
||||
int i = getTheme();
|
||||
if (i < 0 || i >= kCutterQtThemesList.size()) {
|
||||
int i = getInterfaceTheme();
|
||||
if (i < 0 || i >= kCutterInterfaceThemesList.size()) {
|
||||
i = 0;
|
||||
setTheme(i);
|
||||
setInterfaceTheme(i);
|
||||
}
|
||||
return &kCutterQtThemesList[i];
|
||||
return &kCutterInterfaceThemesList[i];
|
||||
}
|
||||
|
||||
QString Configuration::getLogoFile()
|
||||
@ -417,9 +434,9 @@ void Configuration::setColor(const QString &name, const QColor &color)
|
||||
s.setValue("colors." + name, color);
|
||||
}
|
||||
|
||||
void Configuration::setLastThemeOf(const CutterQtTheme &currQtTheme, const QString &theme)
|
||||
void Configuration::setLastThemeOf(const CutterInterfaceTheme &currInterfaceTheme, const QString &theme)
|
||||
{
|
||||
s.setValue("lastThemeOf." + currQtTheme.name, theme);
|
||||
s.setValue("lastThemeOf." + currInterfaceTheme.name, theme);
|
||||
}
|
||||
|
||||
const QColor Configuration::getColor(const QString &name) const
|
||||
@ -440,25 +457,19 @@ void Configuration::setColorTheme(const QString &theme)
|
||||
Core()->cmd(QStringLiteral("eco %1").arg(theme));
|
||||
s.setValue("theme", theme);
|
||||
}
|
||||
// Duplicate interesting colors into our Cutter Settings
|
||||
// Dirty fix for arrow colors, TODO refactor getColor, setColor, etc.
|
||||
QJsonDocument colors = Core()->cmdj("ecj");
|
||||
QJsonObject colorsObject = colors.object();
|
||||
|
||||
for (auto it = colorsObject.constBegin(); it != colorsObject.constEnd(); it++) {
|
||||
QJsonObject colorTheme = ThemeWorker().getTheme(theme).object();
|
||||
for (auto it = colorTheme.constBegin(); it != colorTheme.constEnd(); it++) {
|
||||
QJsonArray rgb = it.value().toArray();
|
||||
if (rgb.size() != 3) {
|
||||
continue;
|
||||
}
|
||||
s.setValue("colors." + it.key(), QColor(rgb[0].toInt(), rgb[1].toInt(), rgb[2].toInt()));
|
||||
setColor(it.key(), QColor(rgb[0].toInt(), rgb[1].toInt(), rgb[2].toInt()));
|
||||
}
|
||||
|
||||
QMap<QString, QColor> cutterSpecific = ColorSchemeFileWorker().getCutterSpecific();
|
||||
for (auto &it : cutterSpecific.keys())
|
||||
setColor(it, cutterSpecific[it]);
|
||||
|
||||
if (!ColorSchemeFileWorker().isCustomScheme(theme)) {
|
||||
setTheme(getTheme());
|
||||
// Trick Cutter to load colors that are not specified in standard theme
|
||||
if (!ThemeWorker().isCustomTheme(theme)) {
|
||||
setInterfaceTheme(getInterfaceTheme());
|
||||
}
|
||||
|
||||
emit colorsUpdated();
|
||||
|
@ -13,17 +13,18 @@ enum ColorFlags {
|
||||
DarkFlag = 2
|
||||
};
|
||||
|
||||
struct CutterQtTheme {
|
||||
struct CutterInterfaceTheme {
|
||||
QString name;
|
||||
ColorFlags flag;
|
||||
};
|
||||
|
||||
extern const QList<CutterQtTheme> kCutterQtThemesList;
|
||||
extern const QList<CutterInterfaceTheme> kCutterInterfaceThemesList;
|
||||
|
||||
class Configuration : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
private:
|
||||
QPalette nativePalette;
|
||||
QSettings s;
|
||||
static Configuration *mPtr;
|
||||
|
||||
@ -33,7 +34,6 @@ private:
|
||||
void loadNativeTheme();
|
||||
void loadLightTheme();
|
||||
void loadDarkTheme();
|
||||
void setColor(const QString &name, const QColor &color);
|
||||
|
||||
// Asm Options
|
||||
void applySavedAsmOptions();
|
||||
@ -63,16 +63,15 @@ public:
|
||||
|
||||
// Colors
|
||||
bool windowColorIsDark();
|
||||
void setLastThemeOf(const CutterQtTheme &currQtTheme, const QString &theme);
|
||||
QString getLastThemeOf(const CutterQtTheme &currQtTheme) const;
|
||||
const QColor getColor(const QString &name) const;
|
||||
void setTheme(int theme);
|
||||
int getTheme()
|
||||
void setLastThemeOf(const CutterInterfaceTheme &currInterfaceTheme, const QString &theme);
|
||||
QString getLastThemeOf(const CutterInterfaceTheme &currInterfaceTheme) const;
|
||||
void setInterfaceTheme(int theme);
|
||||
int getInterfaceTheme()
|
||||
{
|
||||
return s.value("ColorPalette", 0).toInt();
|
||||
}
|
||||
|
||||
const CutterQtTheme *getCurrentTheme();
|
||||
const CutterInterfaceTheme *getCurrentTheme();
|
||||
|
||||
QString getDirProjects();
|
||||
void setDirProjects(const QString &dir);
|
||||
@ -102,6 +101,9 @@ public:
|
||||
QString getColorTheme() const { return s.value("theme", "cutter").toString(); }
|
||||
void setColorTheme(const QString &theme);
|
||||
|
||||
void setColor(const QString &name, const QColor &color);
|
||||
const QColor getColor(const QString &name) const;
|
||||
|
||||
/**
|
||||
* @brief Get the value of a config var either from r2 or settings, depending on the key.
|
||||
*/
|
||||
@ -125,7 +127,7 @@ public:
|
||||
signals:
|
||||
void fontsUpdated();
|
||||
void colorsUpdated();
|
||||
void themeChanged();
|
||||
void interfaceThemeChanged();
|
||||
};
|
||||
|
||||
#endif // CONFIGURATION_H
|
||||
|
@ -240,7 +240,7 @@ void MainWindow::initToolBar()
|
||||
QObject::connect(configuration, &Configuration::colorsUpdated, [this]() {
|
||||
this->visualNavbar->updateGraphicsScene();
|
||||
});
|
||||
QObject::connect(configuration, &Configuration::themeChanged, this, &MainWindow::chooseThemeIcons);
|
||||
QObject::connect(configuration, &Configuration::interfaceThemeChanged, this, &MainWindow::chooseThemeIcons);
|
||||
}
|
||||
|
||||
void MainWindow::initDocks()
|
||||
|
@ -19,7 +19,7 @@ WelcomeDialog::WelcomeDialog(QWidget *parent) :
|
||||
setWindowFlag(Qt::WindowContextHelpButtonHint, false);
|
||||
ui->logoSvgWidget->load(Config()->getLogoFile());
|
||||
ui->versionLabel->setText("<font color='#a4a9b2'>" + tr("Version ") + CUTTER_VERSION_FULL + "</font>");
|
||||
ui->themeComboBox->setCurrentIndex(Config()->getTheme());
|
||||
ui->themeComboBox->setCurrentIndex(Config()->getInterfaceTheme());
|
||||
|
||||
QSignalBlocker s(ui->updatesCheckBox);
|
||||
ui->updatesCheckBox->setChecked(Config()->getAutoUpdateEnabled());
|
||||
@ -53,7 +53,7 @@ WelcomeDialog::~WelcomeDialog()
|
||||
*/
|
||||
void WelcomeDialog::on_themeComboBox_currentIndexChanged(int index)
|
||||
{
|
||||
Config()->setTheme(index);
|
||||
Config()->setInterfaceTheme(index);
|
||||
|
||||
// use "ayu" as the default color theme for dark interface
|
||||
if (Config()->windowColorIsDark()) {
|
||||
|
@ -119,7 +119,7 @@ void XrefsDialog::highlightCurrentLine()
|
||||
if (ui->previewTextEdit->isReadOnly()) {
|
||||
QTextEdit::ExtraSelection selection = QTextEdit::ExtraSelection();
|
||||
|
||||
selection.format.setBackground(ConfigColor("highlight"));
|
||||
selection.format.setBackground(ConfigColor("linehl"));
|
||||
selection.format.setProperty(QTextFormat::FullWidthSelection, true);
|
||||
selection.cursor = ui->previewTextEdit->textCursor();
|
||||
selection.cursor.clearSelection();
|
||||
|
@ -1,9 +1,14 @@
|
||||
#include <QDir>
|
||||
#include <QFile>
|
||||
#include <QLabel>
|
||||
#include <QPainter>
|
||||
#include <QFontDialog>
|
||||
#include <QFileDialog>
|
||||
#include <QTranslator>
|
||||
#include <QInputDialog>
|
||||
#include <QSignalBlocker>
|
||||
#include <QStandardPaths>
|
||||
#include <QtSvg/QSvgRenderer>
|
||||
|
||||
#include <QComboBox>
|
||||
#include "PreferencesDialog.h"
|
||||
@ -13,22 +18,9 @@
|
||||
#include "common/Helpers.h"
|
||||
#include "common/Configuration.h"
|
||||
|
||||
#include "common/ColorSchemeFileSaver.h"
|
||||
#include "widgets/ColorSchemePrefWidget.h"
|
||||
|
||||
static const QHash<QString, ColorFlags> kRelevantSchemes = {
|
||||
{ "ayu", DarkFlag },
|
||||
{ "consonance", DarkFlag },
|
||||
{ "darkda", DarkFlag },
|
||||
{ "onedark", DarkFlag },
|
||||
{ "solarized", DarkFlag },
|
||||
{ "zenburn", DarkFlag },
|
||||
{ "cutter", LightFlag },
|
||||
{ "dark", LightFlag },
|
||||
{ "matrix", LightFlag },
|
||||
{ "tango", LightFlag },
|
||||
{ "white", LightFlag }
|
||||
};
|
||||
#include "common/ColorThemeWorker.h"
|
||||
#include "dialogs/preferences/ColorThemeEditDialog.h"
|
||||
#include "widgets/ColorPicker.h"
|
||||
|
||||
AppearanceOptionsWidget::AppearanceOptionsWidget(PreferencesDialog *dialog)
|
||||
: QDialog(dialog),
|
||||
@ -47,6 +39,19 @@ AppearanceOptionsWidget::AppearanceOptionsWidget(PreferencesDialog *dialog)
|
||||
curr = "English";
|
||||
}
|
||||
ui->languageComboBox->setCurrentText(curr);
|
||||
|
||||
auto setIcons = [this]() {
|
||||
QColor textColor = palette().text().color();
|
||||
ui->editButton->setIcon(getIconFromSvg(":/img/icons/pencil_thin.svg", textColor));
|
||||
ui->deleteButton->setIcon(getIconFromSvg(":/img/icons/trash_bin.svg", textColor));
|
||||
ui->copyButton->setIcon(getIconFromSvg(":/img/icons/copy.svg", textColor));
|
||||
ui->importButton->setIcon(getIconFromSvg(":/img/icons/download_black.svg", textColor));
|
||||
ui->exportButton->setIcon(getIconFromSvg(":/img/icons/upload_black.svg", textColor));
|
||||
ui->renameButton->setIcon(getIconFromSvg(":/img/icons/rename.svg", textColor));
|
||||
};
|
||||
setIcons();
|
||||
connect(Config(), &Configuration::interfaceThemeChanged, this, setIcons);
|
||||
|
||||
connect(ui->languageComboBox,
|
||||
static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged),
|
||||
this,
|
||||
@ -54,6 +59,11 @@ AppearanceOptionsWidget::AppearanceOptionsWidget(PreferencesDialog *dialog)
|
||||
|
||||
connect(Config(), &Configuration::fontsUpdated, this,
|
||||
&AppearanceOptionsWidget::updateFontFromConfig);
|
||||
|
||||
connect(ui->colorComboBox, &QComboBox::currentTextChanged,
|
||||
this, [this](const QString &str) {
|
||||
ui->editButton->setEnabled(ThemeWorker().isCustomTheme(str));
|
||||
});
|
||||
}
|
||||
|
||||
AppearanceOptionsWidget::~AppearanceOptionsWidget() {}
|
||||
@ -64,54 +74,23 @@ void AppearanceOptionsWidget::updateFontFromConfig()
|
||||
ui->fontSelectionLabel->setText(currentFont.toString());
|
||||
}
|
||||
|
||||
void AppearanceOptionsWidget::updateThemeFromConfig(bool qtThemeChanged)
|
||||
void AppearanceOptionsWidget::updateThemeFromConfig(bool interfaceThemeChanged)
|
||||
{
|
||||
// Disconnect currentIndexChanged because clearing the comboxBox and refiling it causes its index to change.
|
||||
QSignalBlocker signalBlockerColorBox(ui->colorComboBox);
|
||||
QSignalBlocker signalBlockerThemeBox(ui->themeComboBox);
|
||||
Q_UNUSED(signalBlockerColorBox);
|
||||
Q_UNUSED(signalBlockerThemeBox);
|
||||
|
||||
ui->themeComboBox->clear();
|
||||
for (auto &it : kCutterQtThemesList) {
|
||||
for (auto &it : kCutterInterfaceThemesList) {
|
||||
ui->themeComboBox->addItem(it.name);
|
||||
}
|
||||
int curQtThemeIndex = Config()->getTheme();
|
||||
if (curQtThemeIndex >= kCutterQtThemesList.size()) {
|
||||
curQtThemeIndex = 0;
|
||||
Config()->setTheme(curQtThemeIndex);
|
||||
int currInterfaceThemeIndex = Config()->getInterfaceTheme();
|
||||
if (currInterfaceThemeIndex >= kCutterInterfaceThemesList.size()) {
|
||||
currInterfaceThemeIndex = 0;
|
||||
Config()->setInterfaceTheme(currInterfaceThemeIndex);
|
||||
}
|
||||
ui->themeComboBox->setCurrentIndex(curQtThemeIndex);
|
||||
|
||||
QList<QString> themes = Core()->getColorThemes();
|
||||
ui->colorComboBox->clear();
|
||||
for (const QString &theme : themes) {
|
||||
if (ColorSchemeFileWorker().isCustomScheme(theme) ||
|
||||
(kCutterQtThemesList[curQtThemeIndex].flag & kRelevantSchemes[theme])) {
|
||||
ui->colorComboBox->addItem(theme);
|
||||
}
|
||||
}
|
||||
|
||||
QString curTheme = qtThemeChanged
|
||||
? Config()->getLastThemeOf(kCutterQtThemesList[curQtThemeIndex])
|
||||
: Config()->getColorTheme();
|
||||
const int index = ui->colorComboBox->findText(curTheme);
|
||||
|
||||
ui->colorComboBox->setCurrentIndex(index == -1 ? 0 : index);
|
||||
if (qtThemeChanged || index == -1) {
|
||||
curTheme = ui->colorComboBox->currentText();
|
||||
Config()->setColorTheme(curTheme);
|
||||
}
|
||||
ui->colorSchemePrefWidget->updateSchemeFromConfig();
|
||||
int maxThemeLen = 0;
|
||||
for (const QString &str : themes) {
|
||||
int strLen = str.length();
|
||||
if (strLen > maxThemeLen) {
|
||||
maxThemeLen = strLen;
|
||||
}
|
||||
}
|
||||
ui->colorComboBox->setMinimumContentsLength(maxThemeLen);
|
||||
ui->colorComboBox->setSizeAdjustPolicy(QComboBox::AdjustToMinimumContentsLength);
|
||||
ui->themeComboBox->setCurrentIndex(currInterfaceThemeIndex);
|
||||
ui->colorComboBox->updateFromConfig(interfaceThemeChanged);
|
||||
ui->editButton->setEnabled(ThemeWorker().isCustomTheme(ui->colorComboBox->currentText()));
|
||||
}
|
||||
|
||||
void AppearanceOptionsWidget::on_fontSelectionButton_clicked()
|
||||
@ -127,68 +106,154 @@ void AppearanceOptionsWidget::on_fontSelectionButton_clicked()
|
||||
|
||||
void AppearanceOptionsWidget::on_themeComboBox_currentIndexChanged(int index)
|
||||
{
|
||||
Config()->setTheme(index);
|
||||
Config()->setInterfaceTheme(index);
|
||||
updateThemeFromConfig();
|
||||
}
|
||||
|
||||
void AppearanceOptionsWidget::on_colorComboBox_currentIndexChanged(int index)
|
||||
void AppearanceOptionsWidget::on_editButton_clicked()
|
||||
{
|
||||
QString theme = ui->colorComboBox->itemText(index);
|
||||
|
||||
int curQtThemeIndex = Config()->getTheme();
|
||||
if (curQtThemeIndex >= kCutterQtThemesList.size()) {
|
||||
curQtThemeIndex = 0;
|
||||
Config()->setTheme(curQtThemeIndex);
|
||||
}
|
||||
|
||||
Config()->setLastThemeOf(kCutterQtThemesList[curQtThemeIndex], theme);
|
||||
Config()->setColorTheme(theme);
|
||||
ui->colorSchemePrefWidget->updateSchemeFromConfig();
|
||||
ColorThemeEditDialog dial;
|
||||
dial.setWindowTitle(tr("Theme Editor - <%1>").arg(ui->colorComboBox->currentText()));
|
||||
dial.exec();
|
||||
ui->colorComboBox->updateFromConfig(false);
|
||||
}
|
||||
|
||||
void AppearanceOptionsWidget::on_copyButton_clicked()
|
||||
{
|
||||
QString newSchemeName;
|
||||
do {
|
||||
newSchemeName = QInputDialog::getText(this, tr("Enter scheme name"),
|
||||
tr("Name:"), QLineEdit::Normal,
|
||||
QDir::home().dirName());
|
||||
} while ((!newSchemeName.isEmpty() && ColorSchemeFileWorker().isNameEngaged(newSchemeName))
|
||||
|| newSchemeName.contains(QRegExp("[^\\w.()\\[\\]_-]"))
|
||||
|| newSchemeName.startsWith('.'));
|
||||
QString currColorTheme = ui->colorComboBox->currentText();
|
||||
|
||||
if (newSchemeName.isEmpty())
|
||||
QString newThemeName;
|
||||
do {
|
||||
newThemeName = QInputDialog::getText(this, tr("Enter theme name"),
|
||||
tr("Name:"), QLineEdit::Normal,
|
||||
currColorTheme + tr(" - copy"));
|
||||
} while (!newThemeName.isEmpty() && ThemeWorker().isThemeExist(newThemeName));
|
||||
|
||||
if (newThemeName.isEmpty()) {
|
||||
return;
|
||||
ColorSchemeFileWorker().copy(Config()->getColorTheme(), newSchemeName);
|
||||
Config()->setColorTheme(newSchemeName);
|
||||
ui->colorSchemePrefWidget->updateSchemeFromConfig();
|
||||
}
|
||||
ThemeWorker().copy(currColorTheme, newThemeName);
|
||||
Config()->setColorTheme(newThemeName);
|
||||
updateThemeFromConfig(false);
|
||||
}
|
||||
|
||||
void AppearanceOptionsWidget::on_deleteButton_clicked()
|
||||
{
|
||||
if (ColorSchemeFileWorker().isCustomScheme(Config()->getColorTheme())) {
|
||||
QMessageBox mb;
|
||||
mb.setWindowTitle(tr("Delete"));
|
||||
mb.setText(tr("Are you sure you want to delete theme ") + Config()->getColorTheme());
|
||||
mb.setIcon(QMessageBox::Question);
|
||||
mb.setStandardButtons(QMessageBox::Yes | QMessageBox::No);
|
||||
if (mb.exec() == QMessageBox::Yes) {
|
||||
ColorSchemeFileWorker().deleteScheme(Config()->getColorTheme());
|
||||
updateThemeFromConfig(false);
|
||||
QString currTheme = ui->colorComboBox->currentText();
|
||||
if (!ThemeWorker().isCustomTheme(currTheme)) {
|
||||
QMessageBox::critical(nullptr, tr("Error"), ThemeWorker().deleteTheme(currTheme));
|
||||
return;
|
||||
}
|
||||
int ret = QMessageBox::question(nullptr,
|
||||
tr("Delete"),
|
||||
tr("Are you sure you want to delete <b>%1</b>?")
|
||||
.arg(currTheme));
|
||||
if (ret == QMessageBox::Yes) {
|
||||
QString err = ThemeWorker().deleteTheme(currTheme);
|
||||
updateThemeFromConfig(false);
|
||||
if (!err.isEmpty()) {
|
||||
QMessageBox::critical(nullptr, tr("Error"), err);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AppearanceOptionsWidget::on_importButton_clicked()
|
||||
{
|
||||
QString fileName = QFileDialog::getOpenFileName(this,
|
||||
"",
|
||||
QStandardPaths::writableLocation(QStandardPaths::HomeLocation));
|
||||
if (fileName.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
QString err = ThemeWorker().importTheme(fileName);
|
||||
QString themeName = QFileInfo(fileName).fileName();
|
||||
if (err.isEmpty()) {
|
||||
QMessageBox::information(this,
|
||||
tr("Success"),
|
||||
tr("Color theme <b>%1</b> was successfully imported.").arg(themeName));
|
||||
Config()->setColorTheme(themeName);
|
||||
updateThemeFromConfig(false);
|
||||
} else {
|
||||
QMessageBox::critical(this, tr("Error"), err);
|
||||
}
|
||||
}
|
||||
|
||||
void AppearanceOptionsWidget::on_exportButton_clicked()
|
||||
{
|
||||
QString theme = ui->colorComboBox->currentText();
|
||||
QString file = QFileDialog::getSaveFileName(this,
|
||||
"",
|
||||
QStandardPaths::writableLocation(QStandardPaths::HomeLocation)
|
||||
+ QDir::separator() + theme);
|
||||
if (file.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// User already gave his consent for this in QFileDialog::getSaveFileName()
|
||||
if (QFileInfo(file).exists()) {
|
||||
QFile(file).remove();
|
||||
}
|
||||
QString err = ThemeWorker().save(ThemeWorker().getTheme(theme), file);
|
||||
if (err.isEmpty()) {
|
||||
QMessageBox::information(this,
|
||||
tr("Success"),
|
||||
tr("Color theme <b>%1</b> was successfully exported.").arg(theme));
|
||||
} else {
|
||||
QMessageBox::critical(this, tr("Error"), err);
|
||||
}
|
||||
}
|
||||
|
||||
void AppearanceOptionsWidget::on_renameButton_clicked()
|
||||
{
|
||||
QString currColorTheme = Config()->getColorTheme();
|
||||
QString newName = QInputDialog::getText(this,
|
||||
tr("Enter new theme name"),
|
||||
tr("Name:"),
|
||||
QLineEdit::Normal,
|
||||
currColorTheme);
|
||||
if (newName.isEmpty() || newName == currColorTheme) {
|
||||
return;
|
||||
}
|
||||
|
||||
QString err = ThemeWorker().renameTheme(currColorTheme, newName);
|
||||
if (!err.isEmpty()) {
|
||||
QMessageBox::critical(this, tr("Error"), err);
|
||||
} else {
|
||||
Config()->setColorTheme(newName);
|
||||
updateThemeFromConfig(false);
|
||||
}
|
||||
}
|
||||
|
||||
void AppearanceOptionsWidget::onLanguageComboBoxCurrentIndexChanged(int index)
|
||||
{
|
||||
QString language = ui->languageComboBox->itemText(index).toLower();
|
||||
if (Config()->setLocaleByName(language)) {
|
||||
QMessageBox::information(this,
|
||||
tr("Language settings"),
|
||||
tr("Language will be changed after next application start."));
|
||||
tr("Language settings"),
|
||||
tr("Language will be changed after next application start."));
|
||||
return;
|
||||
}
|
||||
|
||||
qWarning() << tr("Cannot set language, not found in available ones");
|
||||
}
|
||||
}
|
||||
|
||||
QIcon AppearanceOptionsWidget::getIconFromSvg(const QString& fileName, const QColor& after, const QColor& before)
|
||||
{
|
||||
QFile file(fileName);
|
||||
if (!file.open(QIODevice::ReadOnly)) {
|
||||
return QIcon();
|
||||
}
|
||||
QString data = file.readAll();
|
||||
data.replace(QRegExp(QString("#%1").arg(before.isValid() ? before.name().remove(0, 1) : "[0-9a-fA-F]{6}")),
|
||||
QString("%1").arg(after.name()));
|
||||
|
||||
QSvgRenderer svgRenderer(data.toUtf8());
|
||||
QPixmap pix(svgRenderer.defaultSize());
|
||||
pix.fill(Qt::transparent);
|
||||
|
||||
QPainter pixPainter(&pix);
|
||||
svgRenderer.render(&pixPainter);
|
||||
|
||||
return pix;
|
||||
}
|
||||
|
@ -27,14 +27,45 @@ private:
|
||||
|
||||
private slots:
|
||||
void updateFontFromConfig();
|
||||
void updateThemeFromConfig(bool qtThemeChanged = true);
|
||||
void updateThemeFromConfig(bool interfaceThemeChanged = true);
|
||||
|
||||
void on_fontSelectionButton_clicked();
|
||||
void on_themeComboBox_currentIndexChanged(int index);
|
||||
void on_colorComboBox_currentIndexChanged(int index);
|
||||
void on_copyButton_clicked();
|
||||
void on_deleteButton_clicked();
|
||||
|
||||
/**
|
||||
* @brief Imports theme file specified by user to custom themes
|
||||
* directory.
|
||||
*/
|
||||
void on_importButton_clicked();
|
||||
|
||||
/**
|
||||
* @brief Exports current color theme to file
|
||||
* specified by user.
|
||||
*/
|
||||
void on_exportButton_clicked();
|
||||
|
||||
/**
|
||||
* @brief Shows dialog to rename current color theme.
|
||||
*/
|
||||
void on_renameButton_clicked();
|
||||
void on_editButton_clicked();
|
||||
void onLanguageComboBoxCurrentIndexChanged(int index);
|
||||
|
||||
|
||||
/**
|
||||
* @brief Changes all @a before colors in given @a fileName svg file to @a after
|
||||
* and returns result icon. If @a before is not specified, changes all colors.
|
||||
* @param fileName
|
||||
* Path to svg file.
|
||||
* @param after
|
||||
* What color should be inserted instead of old one.
|
||||
* @param before
|
||||
* Color that should be repalced.
|
||||
*/
|
||||
QIcon getIconFromSvg(const QString &fileName, const QColor &after, const QColor &before = QColor());
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
@ -6,19 +6,22 @@
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>442</width>
|
||||
<height>268</height>
|
||||
<width>619</width>
|
||||
<height>225</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Appearance</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<property name="sizeConstraint">
|
||||
<enum>QLayout::SetMinAndMaxSize</enum>
|
||||
</property>
|
||||
<item>
|
||||
<layout class="QFormLayout" name="formLayout">
|
||||
<property name="labelAlignment">
|
||||
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
|
||||
</property>
|
||||
<property name="formAlignment">
|
||||
<set>Qt::AlignCenter</set>
|
||||
</property>
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="fontLabel">
|
||||
<property name="text">
|
||||
@ -56,10 +59,27 @@
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="languageLabel">
|
||||
<property name="text">
|
||||
<string>Language:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QComboBox" name="languageComboBox">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="themeLabel">
|
||||
<property name="text">
|
||||
<string>Qt Theme:</string>
|
||||
<string>Interface Theme:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
@ -86,14 +106,14 @@
|
||||
<item row="3" column="0">
|
||||
<widget class="QLabel" name="colorLabel">
|
||||
<property name="text">
|
||||
<string>Color Theme</string>
|
||||
<string>Color Theme:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="1">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_3">
|
||||
<item>
|
||||
<widget class="QComboBox" name="colorComboBox">
|
||||
<widget class="ColorThemeComboBox" name="colorComboBox">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
@ -102,6 +122,34 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="editButton">
|
||||
<property name="toolTip">
|
||||
<string>Edit Theme</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="../../resources.qrc">
|
||||
<normaloff>:/img/icons/pencil_thin.svg</normaloff>:/img/icons/pencil_thin.svg</iconset>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="renameButton">
|
||||
<property name="toolTip">
|
||||
<string>Rename</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="../../resources.qrc">
|
||||
<normaloff>:/img/icons/rename.svg</normaloff>:/img/icons/rename.svg</iconset>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="copyButton">
|
||||
<property name="sizePolicy">
|
||||
@ -110,18 +158,63 @@
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<property name="toolTip">
|
||||
<string>Copy</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="../../resources.qrc">
|
||||
<normaloff>:/img/icons/copy.svg</normaloff>:/img/icons/copy.svg</iconset>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="deleteButton">
|
||||
<property name="text">
|
||||
<property name="toolTip">
|
||||
<string>Delete</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="../../resources.qrc">
|
||||
<normaloff>:/img/icons/trash_bin.svg</normaloff>:/img/icons/trash_bin.svg</iconset>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="exportButton">
|
||||
<property name="toolTip">
|
||||
<string>Export</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="../../resources.qrc">
|
||||
<normaloff>:/img/icons/upload_black.svg</normaloff>:/img/icons/upload_black.svg</iconset>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="importButton">
|
||||
<property name="toolTip">
|
||||
<string>Import</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="../../resources.qrc">
|
||||
<normaloff>:/img/icons/download_black.svg</normaloff>:/img/icons/download_black.svg</iconset>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_3"/>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer">
|
||||
<property name="orientation">
|
||||
@ -137,27 +230,20 @@
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="languageLabel">
|
||||
<property name="text">
|
||||
<string>Language</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QComboBox" name="languageComboBox">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="ColorSchemePrefWidget" name="colorSchemePrefWidget" native="true"/>
|
||||
<spacer name="verticalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
<action name="actionSaveAsDefault">
|
||||
@ -168,12 +254,13 @@
|
||||
</widget>
|
||||
<customwidgets>
|
||||
<customwidget>
|
||||
<class>ColorSchemePrefWidget</class>
|
||||
<extends>QWidget</extends>
|
||||
<header>widgets/ColorSchemePrefWidget.h</header>
|
||||
<container>1</container>
|
||||
<class>ColorThemeComboBox</class>
|
||||
<extends>QComboBox</extends>
|
||||
<header>widgets/ColorThemeComboBox.h</header>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<resources/>
|
||||
<resources>
|
||||
<include location="../../resources.qrc"/>
|
||||
</resources>
|
||||
<connections/>
|
||||
</ui>
|
||||
|
146
src/dialogs/preferences/ColorThemeEditDialog.cpp
Normal file
@ -0,0 +1,146 @@
|
||||
#include "ColorThemeEditDialog.h"
|
||||
#include "ui_ColorThemeEditDialog.h"
|
||||
|
||||
#include "common/ColorThemeWorker.h"
|
||||
#include "common/Configuration.h"
|
||||
|
||||
#include "widgets/ColorThemeListView.h"
|
||||
#include "widgets/DisassemblyWidget.h"
|
||||
|
||||
#include <QScreen>
|
||||
#include <QKeyEvent>
|
||||
|
||||
ColorThemeEditDialog::ColorThemeEditDialog(QWidget *parent) :
|
||||
QDialog(parent),
|
||||
ui(new Ui::ColorThemeEditDialog),
|
||||
configSignalBlocker(Config()), // Blocks signals from Config to avoid updating of widgets during editing
|
||||
colorTheme(Config()->getColorTheme())
|
||||
{
|
||||
ui->setupUi(this);
|
||||
ui->colorComboBox->setShowOnlyCustom(true);
|
||||
|
||||
previewDisasmWidget = new DisassemblyWidget(nullptr);
|
||||
previewDisasmWidget->setObjectName("Preview Disasm");
|
||||
previewDisasmWidget->setPreviewMode(true);
|
||||
previewDisasmWidget->setMinimumSize(qApp->screenAt(previewDisasmWidget->pos())->size() * 0.5);
|
||||
previewDisasmWidget->setWindowTitle(tr("Disassembly Preview"));
|
||||
previewDisasmWidget->setFeatures(QDockWidget::NoDockWidgetFeatures);
|
||||
ui->colorPickerAndPreviewLayout->addWidget(previewDisasmWidget);
|
||||
|
||||
|
||||
connect(ui->colorThemeListView, &ColorThemeListView::blink,
|
||||
previewDisasmWidget, &DisassemblyWidget::colorsUpdatedSlot);
|
||||
|
||||
connect(ui->colorThemeListView, &ColorThemeListView::itemChanged,
|
||||
ui->colorPicker, &ColorPicker::updateColor);
|
||||
ui->colorThemeListView->setCurrentIndex(ui->colorThemeListView->model()->index(0, 0));
|
||||
|
||||
connect(ui->colorPicker, &ColorPicker::colorChanged, this, &ColorThemeEditDialog::colorOptionChanged);
|
||||
|
||||
connect(ui->colorComboBox, &ColorThemeComboBox::currentTextChanged,
|
||||
this, &ColorThemeEditDialog::editThemeChanged);
|
||||
}
|
||||
|
||||
ColorThemeEditDialog::~ColorThemeEditDialog()
|
||||
{
|
||||
delete ui;
|
||||
previewDisasmWidget->deleteLater();
|
||||
}
|
||||
|
||||
void ColorThemeEditDialog::accept()
|
||||
{
|
||||
colorTheme = Config()->getColorTheme();
|
||||
QJsonDocument sch = qobject_cast<ColorSettingsModel*>(ui->colorThemeListView->model())->getTheme();
|
||||
if (ThemeWorker().isCustomTheme(colorTheme)) {
|
||||
QString err = ThemeWorker().save(sch, colorTheme);
|
||||
if (!err.isEmpty()) {
|
||||
QMessageBox::critical(this, tr("Error"), err);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
configSignalBlocker.unblock();
|
||||
Config()->setColorTheme(colorTheme);
|
||||
|
||||
QDialog::accept();
|
||||
}
|
||||
|
||||
void ColorThemeEditDialog::reject()
|
||||
{
|
||||
if (themeWasEdited(ui->colorComboBox->currentText()) &&
|
||||
QMessageBox::question(this,
|
||||
tr("Unsaved changes"),
|
||||
tr("Are you sure you want to exit without saving? "
|
||||
"All changes will be lost.")) == QMessageBox::No) {
|
||||
return;
|
||||
}
|
||||
configSignalBlocker.unblock();
|
||||
Config()->setColorTheme(colorTheme);
|
||||
|
||||
QDialog::reject();
|
||||
}
|
||||
|
||||
void ColorThemeEditDialog::keyPressEvent(QKeyEvent *event)
|
||||
{
|
||||
switch (event->key()) {
|
||||
case Qt::Key_Escape:
|
||||
if (ui->colorPicker->isPickingFromScreen()) {
|
||||
ui->colorPicker->stopPickingFromScreen();
|
||||
}
|
||||
// fallthrough
|
||||
case Qt::Key_Return:
|
||||
event->accept();
|
||||
return;
|
||||
default:
|
||||
QDialog::keyPressEvent(event);
|
||||
}
|
||||
}
|
||||
|
||||
void ColorThemeEditDialog::colorOptionChanged(const QColor& newColor)
|
||||
{
|
||||
auto model = qobject_cast<ColorSettingsModel*>(ui->colorThemeListView->model());
|
||||
QModelIndex currIndex = ui->colorThemeListView->currentIndex();
|
||||
|
||||
if (!currIndex.isValid()) {
|
||||
return;
|
||||
}
|
||||
|
||||
ColorOption currOption = currIndex.data(Qt::UserRole).value<ColorOption>();
|
||||
currOption.color = newColor;
|
||||
currOption.changed = true;
|
||||
model->setData(currIndex, QVariant::fromValue(currOption));
|
||||
|
||||
Config()->setColor(currOption.optionName, currOption.color);
|
||||
if (!ColorThemeWorker::cutterSpecificOptions.contains(currOption.optionName)) {
|
||||
Core()->cmd(QString("ec %1 %2").arg(currOption.optionName).arg(currOption.color.name()));
|
||||
}
|
||||
previewDisasmWidget->colorsUpdatedSlot();
|
||||
}
|
||||
|
||||
void ColorThemeEditDialog::editThemeChanged(const QString& newTheme)
|
||||
{
|
||||
if (themeWasEdited(colorTheme)) {
|
||||
int ret = QMessageBox::question(this,
|
||||
tr("Unsaved changes"),
|
||||
tr("Are you sure you want to exit without saving? "
|
||||
"All changes will be lost."));
|
||||
if (ret == QMessageBox::No) {
|
||||
QSignalBlocker s(ui->colorComboBox); // avoid second call of this func
|
||||
int index = ui->colorComboBox->findText(colorTheme);
|
||||
index = index == -1 ? 0 : index;
|
||||
ui->colorComboBox->setCurrentIndex(index);
|
||||
Config()->setColorTheme(colorTheme);
|
||||
return;
|
||||
}
|
||||
}
|
||||
colorTheme = newTheme;
|
||||
qobject_cast<ColorSettingsModel*>(ui->colorThemeListView->model())->updateTheme();
|
||||
previewDisasmWidget->colorsUpdatedSlot();
|
||||
setWindowTitle(tr("Theme Editor - <%1>").arg(colorTheme));
|
||||
}
|
||||
|
||||
bool ColorThemeEditDialog::themeWasEdited(const QString& theme) const
|
||||
{
|
||||
auto model = qobject_cast<ColorSettingsModel*>(ui->colorThemeListView->model());
|
||||
return ThemeWorker().getTheme(theme) != model->getTheme();
|
||||
}
|
52
src/dialogs/preferences/ColorThemeEditDialog.h
Normal file
@ -0,0 +1,52 @@
|
||||
#ifndef COLORTHEMEEDITDIALOG_H
|
||||
#define COLORTHEMEEEDITDIALOG_H
|
||||
|
||||
#include <QDialog>
|
||||
|
||||
class DisassemblyWidget;
|
||||
|
||||
namespace Ui {
|
||||
class ColorThemeEditDialog;
|
||||
}
|
||||
|
||||
class ColorThemeEditDialog : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit ColorThemeEditDialog(QWidget *parent = nullptr);
|
||||
~ColorThemeEditDialog() override;
|
||||
|
||||
public slots:
|
||||
void accept() override;
|
||||
void reject() override;
|
||||
|
||||
protected slots:
|
||||
void keyPressEvent(QKeyEvent *event) override;
|
||||
|
||||
private slots:
|
||||
/**
|
||||
* @brief Sets @a newColor color for current option.
|
||||
* @param newColor
|
||||
* New color for current color option.
|
||||
*/
|
||||
void colorOptionChanged(const QColor &newColor);
|
||||
|
||||
/**
|
||||
* @brief Changes current theme to edit.
|
||||
* @param newTheme
|
||||
* Name of new theme.
|
||||
*/
|
||||
void editThemeChanged(const QString &newTheme);
|
||||
|
||||
private:
|
||||
bool themeWasEdited(const QString &theme) const;
|
||||
|
||||
private:
|
||||
Ui::ColorThemeEditDialog *ui;
|
||||
QSignalBlocker configSignalBlocker;
|
||||
DisassemblyWidget *previewDisasmWidget;
|
||||
QString colorTheme;
|
||||
};
|
||||
|
||||
#endif // COLORTHEMEEDITDIALOG_H
|
158
src/dialogs/preferences/ColorThemeEditDialog.ui
Normal file
@ -0,0 +1,158 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>ColorThemeEditDialog</class>
|
||||
<widget class="QDialog" name="ColorThemeEditDialog">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>1437</width>
|
||||
<height>617</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Dialog</string>
|
||||
</property>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_3">
|
||||
<item>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_3">
|
||||
<item>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
||||
<item>
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="text">
|
||||
<string>Color Theme:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="ColorThemeComboBox" name="colorComboBox"/>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
<widget class="ColorThemeListView" name="colorThemeListView" native="true">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Minimum" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>400</width>
|
||||
<height>400</height>
|
||||
</size>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QVBoxLayout" name="colorPickerAndPreviewLayout">
|
||||
<item>
|
||||
<widget class="ColorPicker" name="colorPicker" native="true">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Minimum" vsizetype="Minimum">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QDialogButtonBox" name="buttonBox">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="standardButtons">
|
||||
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<customwidgets>
|
||||
<customwidget>
|
||||
<class>ColorPicker</class>
|
||||
<extends>QWidget</extends>
|
||||
<header>widgets/ColorPicker.h</header>
|
||||
<container>1</container>
|
||||
</customwidget>
|
||||
<customwidget>
|
||||
<class>ColorThemeListView</class>
|
||||
<extends>QWidget</extends>
|
||||
<header>widgets/ColorThemeListView.h</header>
|
||||
<container>1</container>
|
||||
</customwidget>
|
||||
<customwidget>
|
||||
<class>ColorThemeComboBox</class>
|
||||
<extends>QComboBox</extends>
|
||||
<header>widgets/ColorThemeComboBox.h</header>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<resources/>
|
||||
<connections>
|
||||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
<signal>accepted()</signal>
|
||||
<receiver>ColorThemeEditDialog</receiver>
|
||||
<slot>accept()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>248</x>
|
||||
<y>254</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>157</x>
|
||||
<y>274</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
<signal>rejected()</signal>
|
||||
<receiver>ColorThemeEditDialog</receiver>
|
||||
<slot>reject()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>316</x>
|
||||
<y>260</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>286</x>
|
||||
<y>274</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
</connections>
|
||||
</ui>
|
@ -69,7 +69,7 @@ PreferencesDialog::PreferencesDialog(QWidget *parent)
|
||||
QTreeWidgetItem *defitem = ui->configCategories->topLevelItem(0);
|
||||
ui->configCategories->setCurrentItem(defitem, 0);
|
||||
|
||||
connect(Config(), &Configuration::themeChanged, this, &PreferencesDialog::chooseThemeIcons);
|
||||
connect(Config(), &Configuration::interfaceThemeChanged, this, &PreferencesDialog::chooseThemeIcons);
|
||||
chooseThemeIcons();
|
||||
}
|
||||
|
||||
|
101
src/img/icons/download_black.svg
Normal file
@ -0,0 +1,101 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Generator: Adobe Illustrator 19.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
version="1.1"
|
||||
id="Capa_1"
|
||||
x="0px"
|
||||
y="0px"
|
||||
viewBox="0 0 67.671 67.671"
|
||||
style="enable-background:new 0 0 67.671 67.671;"
|
||||
xml:space="preserve"
|
||||
sodipodi:docname="download_black.svg"
|
||||
inkscape:version="0.92.4 5da689c313, 2019-01-14"><metadata
|
||||
id="metadata43"><rdf:RDF><cc:Work
|
||||
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title /></cc:Work></rdf:RDF></metadata><defs
|
||||
id="defs41">
|
||||
|
||||
|
||||
</defs><sodipodi:namedview
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1"
|
||||
objecttolerance="10"
|
||||
gridtolerance="10"
|
||||
guidetolerance="10"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1044"
|
||||
id="namedview39"
|
||||
showgrid="false"
|
||||
inkscape:zoom="6.974923"
|
||||
inkscape:cx="-28.423796"
|
||||
inkscape:cy="32.283942"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="36"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="Capa_1" />
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
id="path2"
|
||||
d="M 52.946,23.348 H 42.834 v 6 h 10.112 c 3.007,0 5.34,1.536 5.34,2.858 v 26.606 c 0,1.322 -2.333,2.858 -5.34,2.858 H 14.724 c -3.007,0 -5.34,-1.536 -5.34,-2.858 V 32.207 c 0,-1.322 2.333,-2.858 5.34,-2.858 h 10.11 v -6 h -10.11 c -6.359,0 -11.34,3.891 -11.34,8.858 v 26.606 c 0,4.968 4.981,8.858 11.34,8.858 h 38.223 c 6.358,0 11.34,-3.891 11.34,-8.858 V 32.207 C 64.286,27.239 59.305,23.348 52.946,23.348 Z"
|
||||
style="fill:#1a1a1a" /><path
|
||||
inkscape:connector-curvature="0"
|
||||
id="path4"
|
||||
d="m 24.957,34.75389 c 0.768,0 1.535,0.343782 2.121,1.031347 l 3.756,4.406986 V 24.906224 17.86631 4.3801818 c 0,-1.9441899 1.343,-3.51995726 3,-3.51995726 1.657,0 3,1.57576736 3,3.51995726 V 17.86631 24.906224 40.430407 l 3.959,-4.64517 c 0.586,-0.687565 1.354,-1.031347 2.121,-1.031347 0.767,0 1.535,0.343782 2.121,1.031347 1.172,1.373957 1.172,3.603263 0,4.977219 l -8.957,10.509418 c -0.586,0.687565 -1.353,1.029001 -2.12,1.029001 -0.008,0 -0.015,0 -0.023,0 -0.008,0 -0.015,0 -0.023,0 -0.767,0 -1.534,-0.341436 -2.12,-1.029001 L 22.835,40.762456 c -1.172,-1.373956 -1.172,-3.603262 0,-4.977219 0.587,-0.687565 1.354,-1.031347 2.122,-1.031347 z"
|
||||
style="stroke-width:1.08319855;fill:#1a1a1a" />
|
||||
<g
|
||||
id="g8">
|
||||
</g>
|
||||
<g
|
||||
id="g10">
|
||||
</g>
|
||||
<g
|
||||
id="g12">
|
||||
</g>
|
||||
<g
|
||||
id="g14">
|
||||
</g>
|
||||
<g
|
||||
id="g16">
|
||||
</g>
|
||||
<g
|
||||
id="g18">
|
||||
</g>
|
||||
<g
|
||||
id="g20">
|
||||
</g>
|
||||
<g
|
||||
id="g22">
|
||||
</g>
|
||||
<g
|
||||
id="g24">
|
||||
</g>
|
||||
<g
|
||||
id="g26">
|
||||
</g>
|
||||
<g
|
||||
id="g28">
|
||||
</g>
|
||||
<g
|
||||
id="g30">
|
||||
</g>
|
||||
<g
|
||||
id="g32">
|
||||
</g>
|
||||
<g
|
||||
id="g34">
|
||||
</g>
|
||||
<g
|
||||
id="g36">
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 3.1 KiB |
99
src/img/icons/pencil_thin.svg
Normal file
@ -0,0 +1,99 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
version="1.1"
|
||||
id="Capa_1"
|
||||
x="0px"
|
||||
y="0px"
|
||||
width="528.899px"
|
||||
height="528.899px"
|
||||
viewBox="0 0 528.899 528.899"
|
||||
style="enable-background:new 0 0 528.899 528.899;"
|
||||
xml:space="preserve"
|
||||
sodipodi:docname="pencil_thin.svg"
|
||||
inkscape:version="0.92.4 5da689c313, 2019-01-14"><metadata
|
||||
id="metadata41"><rdf:RDF><cc:Work
|
||||
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /></cc:Work></rdf:RDF></metadata><defs
|
||||
id="defs39" /><sodipodi:namedview
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1"
|
||||
objecttolerance="10"
|
||||
gridtolerance="10"
|
||||
guidetolerance="10"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1044"
|
||||
id="namedview37"
|
||||
showgrid="false"
|
||||
inkscape:zoom="1.2620724"
|
||||
inkscape:cx="101.91659"
|
||||
inkscape:cy="301.06573"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="36"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="Capa_1" />
|
||||
<g
|
||||
id="g4"
|
||||
style="fill:#800000">
|
||||
<path
|
||||
d="M328.883,89.125l107.59,107.589l-272.34,272.34L56.604,361.465L328.883,89.125z M518.113,63.177l-47.981-47.981 c-18.543-18.543-48.653-18.543-67.259,0l-45.961,45.961l107.59,107.59l53.611-53.611 C532.495,100.753,532.495,77.559,518.113,63.177z M0.3,512.69c-1.958,8.812,5.998,16.708,14.811,14.565l119.891-29.069 L27.473,390.597L0.3,512.69z"
|
||||
id="path2"
|
||||
style="fill:#800000" />
|
||||
</g>
|
||||
<g
|
||||
id="g6">
|
||||
</g>
|
||||
<g
|
||||
id="g8">
|
||||
</g>
|
||||
<g
|
||||
id="g10">
|
||||
</g>
|
||||
<g
|
||||
id="g12">
|
||||
</g>
|
||||
<g
|
||||
id="g14">
|
||||
</g>
|
||||
<g
|
||||
id="g16">
|
||||
</g>
|
||||
<g
|
||||
id="g18">
|
||||
</g>
|
||||
<g
|
||||
id="g20">
|
||||
</g>
|
||||
<g
|
||||
id="g22">
|
||||
</g>
|
||||
<g
|
||||
id="g24">
|
||||
</g>
|
||||
<g
|
||||
id="g26">
|
||||
</g>
|
||||
<g
|
||||
id="g28">
|
||||
</g>
|
||||
<g
|
||||
id="g30">
|
||||
</g>
|
||||
<g
|
||||
id="g32">
|
||||
</g>
|
||||
<g
|
||||
id="g34">
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 2.3 KiB |
126
src/img/icons/rename.svg
Normal file
@ -0,0 +1,126 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
height="60.0000000"
|
||||
id="svg1"
|
||||
inkscape:version="0.92.4 5da689c313, 2019-01-14"
|
||||
sodipodi:docname="rename.svg"
|
||||
sodipodi:version="0.32"
|
||||
version="1.0"
|
||||
width="60.0000000"
|
||||
x="0"
|
||||
y="0">
|
||||
<metadata
|
||||
id="metadata2">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:title>Part of the Flat Icon Collection (Wed Aug 25 23:29:46 2004)</dc:title>
|
||||
<dc:description />
|
||||
<dc:subject>
|
||||
<rdf:Bag>
|
||||
<rdf:li>hash</rdf:li>
|
||||
<rdf:li />
|
||||
<rdf:li>action</rdf:li>
|
||||
<rdf:li>computer</rdf:li>
|
||||
<rdf:li>icons</rdf:li>
|
||||
<rdf:li>theme</rdf:li>
|
||||
</rdf:Bag>
|
||||
</dc:subject>
|
||||
<dc:publisher>
|
||||
<cc:Agent
|
||||
rdf:about="http://www.openclipart.org">
|
||||
<dc:title>Danny Allen</dc:title>
|
||||
</cc:Agent>
|
||||
</dc:publisher>
|
||||
<dc:creator>
|
||||
<cc:Agent>
|
||||
<dc:title>Danny Allen</dc:title>
|
||||
</cc:Agent>
|
||||
</dc:creator>
|
||||
<dc:rights>
|
||||
<cc:Agent>
|
||||
<dc:title>Danny Allen</dc:title>
|
||||
</cc:Agent>
|
||||
</dc:rights>
|
||||
<dc:date />
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<cc:license
|
||||
rdf:resource="http://web.resource.org/cc/PublicDomain" />
|
||||
<dc:language>en</dc:language>
|
||||
</cc:Work>
|
||||
<cc:License
|
||||
rdf:about="http://web.resource.org/cc/PublicDomain">
|
||||
<cc:permits
|
||||
rdf:resource="http://web.resource.org/cc/Reproduction" />
|
||||
<cc:permits
|
||||
rdf:resource="http://web.resource.org/cc/Distribution" />
|
||||
<cc:permits
|
||||
rdf:resource="http://web.resource.org/cc/DerivativeWorks" />
|
||||
</cc:License>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<sodipodi:namedview
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
id="base"
|
||||
inkscape:cx="85.381225"
|
||||
inkscape:cy="-5.5666582"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:window-height="1044"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="36"
|
||||
inkscape:zoom="6.9465338"
|
||||
pagecolor="#ffffff"
|
||||
showgrid="false"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="svg1" />
|
||||
<defs
|
||||
id="defs3">
|
||||
<linearGradient
|
||||
id="linearGradient772">
|
||||
<stop
|
||||
id="stop773"
|
||||
offset="0"
|
||||
style="stop-color:#000;stop-opacity:1;" />
|
||||
<stop
|
||||
id="stop774"
|
||||
offset="1"
|
||||
style="stop-color:#fff;stop-opacity:1;" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="linearGradient775"
|
||||
xlink:href="#linearGradient772" />
|
||||
</defs>
|
||||
<g
|
||||
id="g15"
|
||||
transform="matrix(1.1498728,0,0,1.3623405,-5.1593925,-10.810511)">
|
||||
<path
|
||||
transform="matrix(1.150661,0,0,1.150661,-1.880879,-3.851635)"
|
||||
style="fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:#333333;stroke-width:4.34532833;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none"
|
||||
sodipodi:stroke-cmyk="(0.0000000 0.0000000 0.0000000 0.80000001)"
|
||||
sodipodi:nodetypes="cccccc"
|
||||
id="path862"
|
||||
d="m 38.710354,12.794888 h 9.735871 m -4.636129,0 v 33.008494 m -5.099742,0.89212 h 9.967677"
|
||||
inkscape:connector-curvature="0" />
|
||||
<path
|
||||
transform="scale(4.132782)"
|
||||
style="font-weight:900;font-size:12px;font-family:'VAG Rounded Black SSi';fill:#333333;fill-opacity:1;stroke-width:1.00000003pt"
|
||||
id="path837"
|
||||
d="m 1.3728005,9.8524771 1.875,-5.0976563 c 0.1679687,-0.4492187 0.3066406,-0.78125 0.4160156,-0.9960937 0.109375,-0.21875 0.2597656,-0.4023438 0.4511719,-0.5507813 0.1914062,-0.1523437 0.4414062,-0.2285156 0.75,-0.2285156 0.3164062,0 0.5722656,0.076172 0.7675781,0.2285156 0.1992187,0.1523438 0.3554687,0.34375 0.46875,0.5742188 0.1171875,0.2265625 0.2539062,0.5507812 0.4101562,0.9726562 L 8.380613,9.8524771 c 0.1171875,0.3085939 0.1933593,0.5468749 0.2285156,0.7148439 0.039063,0.164062 0.058594,0.330078 0.058594,0.498047 0,0.242187 -0.085937,0.4375 -0.2578125,0.585937 -0.1679687,0.144531 -0.3925781,0.216797 -0.6738281,0.216797 -0.1953125,0 -0.359375,-0.03906 -0.4921875,-0.117187 C 7.1110817,11.67279 7.0036598,11.563415 6.9216286,11.42279 6.8435036,11.282165 6.7731911,11.11029 6.7106911,10.907165 L 6.4763161,10.139586 H 3.2829567 l -0.2402344,0.767579 c -0.1015625,0.316406 -0.2246093,0.55664 -0.3691406,0.720703 -0.140625,0.160156 -0.3574219,0.240234 -0.6503906,0.240234 -0.2773438,0 -0.5039063,-0.07227 -0.6796875,-0.216797 -0.171875,-0.148437 -0.2578125,-0.34375 -0.2578125,-0.585937 0,-0.144532 0.015625,-0.294922 0.046875,-0.451172 0.035156,-0.160156 0.1152344,-0.414063 0.2402344,-0.7617189 z M 5.896238,8.3173208 4.9235817,5.1591177 H 4.9001442 L 3.8571755,8.3173208 Z"
|
||||
inkscape:connector-curvature="0" />
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 5.3 KiB |
53
src/img/icons/reset.svg
Normal file
@ -0,0 +1,53 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Svg Vector Icons : http://www.onlinewebfonts.com/icon -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
version="1.1"
|
||||
x="0px"
|
||||
y="0px"
|
||||
viewBox="0 0 1000 1000"
|
||||
enable-background="new 0 0 1000 1000"
|
||||
xml:space="preserve"
|
||||
id="svg8"
|
||||
sodipodi:docname="reset.svg"
|
||||
inkscape:version="0.92.4 5da689c313, 2019-01-14"><defs
|
||||
id="defs12" /><sodipodi:namedview
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1"
|
||||
objecttolerance="10"
|
||||
gridtolerance="10"
|
||||
guidetolerance="10"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1044"
|
||||
id="namedview10"
|
||||
showgrid="false"
|
||||
inkscape:zoom="0.472"
|
||||
inkscape:cx="934.50546"
|
||||
inkscape:cy="442.18807"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="36"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="svg8" />
|
||||
<metadata
|
||||
id="metadata2"> Svg Vector Icons : http://www.onlinewebfonts.com/icon <rdf:RDF><cc:Work
|
||||
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /></cc:Work></rdf:RDF></metadata>
|
||||
<g
|
||||
id="g6"
|
||||
style="fill:#333333"
|
||||
transform="matrix(0.72717911,0,0,0.72717911,122.47529,119.25363)"><path
|
||||
d="m 825.5,581.9 c 0,165.1 -140.2,299.5 -312.4,299.5 -172.3,0 -312.5,-134.3 -312.5,-299.5 0,-165.1 99.9,-299.6 272.2,-299.6 V 445.6 L 799.5,227.8 472.8,10 V 173.3 C 247.2,173.3 91.7,360.4 91.7,584.4 91.6,808.4 274.4,990 500,990 725.6,990 908.4,808.4 908.4,584.5 Z"
|
||||
id="path4"
|
||||
style="fill:#333333"
|
||||
inkscape:connector-curvature="0" /></g>
|
||||
</svg>
|
After Width: | Height: | Size: 1.9 KiB |
54
src/img/icons/save_black.svg
Normal file
@ -0,0 +1,54 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
viewBox="0 0 24 24"
|
||||
enable-background="new 0 0 24 24"
|
||||
version="1.1"
|
||||
id="svg4"
|
||||
sodipodi:docname="save_black.svg"
|
||||
inkscape:version="0.92.4 5da689c313, 2019-01-14">
|
||||
<metadata
|
||||
id="metadata10">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<defs
|
||||
id="defs8" />
|
||||
<sodipodi:namedview
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1"
|
||||
objecttolerance="10"
|
||||
gridtolerance="10"
|
||||
guidetolerance="10"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1044"
|
||||
id="namedview6"
|
||||
showgrid="false"
|
||||
inkscape:zoom="6.9532167"
|
||||
inkscape:cx="18.045111"
|
||||
inkscape:cy="6.6268597"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="36"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="svg4" />
|
||||
<path
|
||||
d="M 2.6666667,0 C 1.2,0 0,1.2 0,2.6666666 V 21.333333 C 0,22.8 1.2,24 2.6666667,24 H 21.333333 C 22.8,24 24,22.8 24,21.333333 V 4 L 20.000001,0 Z m 4,1.3333333 H 17.333334 V 8 H 6.6666667 Z m 6.6666663,1.3333333 v 4 H 16 v -4 z M 5.3333334,12 H 18.666667 c 0.8,0 1.333334,0.533333 1.333334,1.333334 V 20 c 0,0.8 -0.533334,1.333333 -1.333334,1.333333 H 5.3333334 C 4.5333334,21.333333 4,20.8 4,20 V 13.333334 C 4,12.533333 4.5333334,12 5.3333334,12 Z"
|
||||
id="path2"
|
||||
style="fill:#1a1a1a;stroke-width:1.33333337"
|
||||
inkscape:connector-curvature="0" />
|
||||
</svg>
|
After Width: | Height: | Size: 2.0 KiB |
103
src/img/icons/trash_bin.svg
Normal file
@ -0,0 +1,103 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
version="1.1"
|
||||
id="Capa_1"
|
||||
x="0px"
|
||||
y="0px"
|
||||
width="900.5px"
|
||||
height="900.5px"
|
||||
viewBox="0 0 900.5 900.5"
|
||||
style="enable-background:new 0 0 900.5 900.5;"
|
||||
xml:space="preserve"
|
||||
sodipodi:docname="trash_bin.svg"
|
||||
inkscape:version="0.92.4 5da689c313, 2019-01-14"><metadata
|
||||
id="metadata43"><rdf:RDF><cc:Work
|
||||
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /></cc:Work></rdf:RDF></metadata><defs
|
||||
id="defs41" /><sodipodi:namedview
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1"
|
||||
objecttolerance="10"
|
||||
gridtolerance="10"
|
||||
guidetolerance="10"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:window-width="911"
|
||||
inkscape:window-height="480"
|
||||
id="namedview39"
|
||||
showgrid="false"
|
||||
inkscape:zoom="0.26207662"
|
||||
inkscape:cx="450.25"
|
||||
inkscape:cy="450.25"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="36"
|
||||
inkscape:window-maximized="0"
|
||||
inkscape:current-layer="Capa_1" />
|
||||
<g
|
||||
id="g6"
|
||||
style="fill:#1a1a1a">
|
||||
<path
|
||||
d="M176.415,880.5c0,11.046,8.954,20,20,20h507.67c11.046,0,20-8.954,20-20V232.487h-547.67V880.5L176.415,880.5z M562.75,342.766h75v436.029h-75V342.766z M412.75,342.766h75v436.029h-75V342.766z M262.75,342.766h75v436.029h-75V342.766z"
|
||||
id="path2"
|
||||
style="fill:#1a1a1a" />
|
||||
<path
|
||||
d="M618.825,91.911V20c0-11.046-8.954-20-20-20h-297.15c-11.046,0-20,8.954-20,20v71.911v12.5v12.5H141.874 c-11.046,0-20,8.954-20,20v50.576c0,11.045,8.954,20,20,20h34.541h547.67h34.541c11.046,0,20-8.955,20-20v-50.576 c0-11.046-8.954-20-20-20H618.825v-12.5V91.911z M543.825,112.799h-187.15v-8.389v-12.5V75h187.15v16.911v12.5V112.799z"
|
||||
id="path4"
|
||||
style="fill:#1a1a1a" />
|
||||
</g>
|
||||
<g
|
||||
id="g8">
|
||||
</g>
|
||||
<g
|
||||
id="g10">
|
||||
</g>
|
||||
<g
|
||||
id="g12">
|
||||
</g>
|
||||
<g
|
||||
id="g14">
|
||||
</g>
|
||||
<g
|
||||
id="g16">
|
||||
</g>
|
||||
<g
|
||||
id="g18">
|
||||
</g>
|
||||
<g
|
||||
id="g20">
|
||||
</g>
|
||||
<g
|
||||
id="g22">
|
||||
</g>
|
||||
<g
|
||||
id="g24">
|
||||
</g>
|
||||
<g
|
||||
id="g26">
|
||||
</g>
|
||||
<g
|
||||
id="g28">
|
||||
</g>
|
||||
<g
|
||||
id="g30">
|
||||
</g>
|
||||
<g
|
||||
id="g32">
|
||||
</g>
|
||||
<g
|
||||
id="g34">
|
||||
</g>
|
||||
<g
|
||||
id="g36">
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 2.5 KiB |
101
src/img/icons/upload_black.svg
Normal file
@ -0,0 +1,101 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Generator: Adobe Illustrator 19.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
version="1.1"
|
||||
id="Capa_1"
|
||||
x="0px"
|
||||
y="0px"
|
||||
viewBox="0 0 67.671 67.671"
|
||||
style="enable-background:new 0 0 67.671 67.671;"
|
||||
xml:space="preserve"
|
||||
sodipodi:docname="upload_black.svg"
|
||||
inkscape:version="0.92.4 5da689c313, 2019-01-14"><metadata
|
||||
id="metadata43"><rdf:RDF><cc:Work
|
||||
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title /></cc:Work></rdf:RDF></metadata><defs
|
||||
id="defs41">
|
||||
|
||||
|
||||
</defs><sodipodi:namedview
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1"
|
||||
objecttolerance="10"
|
||||
gridtolerance="10"
|
||||
guidetolerance="10"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1044"
|
||||
id="namedview39"
|
||||
showgrid="false"
|
||||
inkscape:zoom="6.974923"
|
||||
inkscape:cx="-28.423796"
|
||||
inkscape:cy="32.283942"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="36"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="Capa_1" />
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
id="path2"
|
||||
d="M 52.946,23.348 H 42.834 v 6 h 10.112 c 3.007,0 5.34,1.536 5.34,2.858 v 26.606 c 0,1.322 -2.333,2.858 -5.34,2.858 H 14.724 c -3.007,0 -5.34,-1.536 -5.34,-2.858 V 32.207 c 0,-1.322 2.333,-2.858 5.34,-2.858 h 10.11 v -6 h -10.11 c -6.359,0 -11.34,3.891 -11.34,8.858 v 26.606 c 0,4.968 4.981,8.858 11.34,8.858 h 38.223 c 6.358,0 11.34,-3.891 11.34,-8.858 V 32.207 C 64.286,27.239 59.305,23.348 52.946,23.348 Z"
|
||||
style="fill:#1a1a1a" /><path
|
||||
inkscape:connector-curvature="0"
|
||||
id="path4"
|
||||
d="m 24.957,18.40721 c 0.768,0 1.535,-0.343782 2.121,-1.031347 l 3.756,-4.406986 v 15.285999 7.039914 13.486128 c 0,1.94419 1.343,3.519957 3,3.519957 1.657,0 3,-1.575767 3,-3.519957 V 35.29479 28.254876 12.730693 l 3.959,4.64517 c 0.586,0.687565 1.354,1.031347 2.121,1.031347 0.767,0 1.535,-0.343782 2.121,-1.031347 1.172,-1.373957 1.172,-3.603263 0,-4.977219 L 36.078,1.8892255 c -0.586,-0.687565 -1.353,-1.02900096 -2.12,-1.02900096 -0.008,0 -0.015,0 -0.023,0 -0.008,0 -0.015,0 -0.023,0 -0.767,0 -1.534,0.34143596 -2.12,1.02900096 L 22.835,12.398644 c -1.172,1.373956 -1.172,3.603262 0,4.977219 0.587,0.687565 1.354,1.031347 2.122,1.031347 z"
|
||||
style="stroke-width:1.08319855;fill:#1a1a1a" />
|
||||
<g
|
||||
id="g8">
|
||||
</g>
|
||||
<g
|
||||
id="g10">
|
||||
</g>
|
||||
<g
|
||||
id="g12">
|
||||
</g>
|
||||
<g
|
||||
id="g14">
|
||||
</g>
|
||||
<g
|
||||
id="g16">
|
||||
</g>
|
||||
<g
|
||||
id="g18">
|
||||
</g>
|
||||
<g
|
||||
id="g20">
|
||||
</g>
|
||||
<g
|
||||
id="g22">
|
||||
</g>
|
||||
<g
|
||||
id="g24">
|
||||
</g>
|
||||
<g
|
||||
id="g26">
|
||||
</g>
|
||||
<g
|
||||
id="g28">
|
||||
</g>
|
||||
<g
|
||||
id="g30">
|
||||
</g>
|
||||
<g
|
||||
id="g32">
|
||||
</g>
|
||||
<g
|
||||
id="g34">
|
||||
</g>
|
||||
<g
|
||||
id="g36">
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 3.0 KiB |
@ -879,10 +879,7 @@ void DisassemblyContextMenu::initAction(QAction *action, QString name,
|
||||
return;
|
||||
}
|
||||
action->setShortcut(keySequence);
|
||||
auto pWidget = parentWidget();
|
||||
auto shortcut = new QShortcut(keySequence, pWidget);
|
||||
shortcut->setContext(Qt::WidgetWithChildrenShortcut);
|
||||
connect(shortcut, SIGNAL(activated()), this, slot);
|
||||
action->setShortcutContext(Qt::WidgetWithChildrenShortcut);
|
||||
}
|
||||
|
||||
void DisassemblyContextMenu::initAction(QAction *action, QString name,
|
||||
@ -893,10 +890,5 @@ void DisassemblyContextMenu::initAction(QAction *action, QString name,
|
||||
return;
|
||||
}
|
||||
action->setShortcuts(keySequenceList);
|
||||
auto pWidget = parentWidget();
|
||||
for (auto keySequence : keySequenceList) {
|
||||
auto shortcut = new QShortcut(keySequence, pWidget);
|
||||
shortcut->setContext(Qt::WidgetWithChildrenShortcut);
|
||||
connect(shortcut, SIGNAL(activated()), this, slot);
|
||||
}
|
||||
action->setShortcutContext(Qt::WidgetWithChildrenShortcut);
|
||||
}
|
||||
|
@ -90,5 +90,12 @@
|
||||
<file>img/icons/light/plugins.svg</file>
|
||||
<file>python/cutter.py</file>
|
||||
<file>python/reg_qtres_importer.py</file>
|
||||
<file>img/icons/download_black.svg</file>
|
||||
<file>img/icons/pencil_thin.svg</file>
|
||||
<file>img/icons/save_black.svg</file>
|
||||
<file>img/icons/trash_bin.svg</file>
|
||||
<file>img/icons/upload_black.svg</file>
|
||||
<file>img/icons/rename.svg</file>
|
||||
<file>img/icons/reset.svg</file>
|
||||
</qresource>
|
||||
</RCC>
|
||||
|
333
src/widgets/ColorPicker.cpp
Normal file
@ -0,0 +1,333 @@
|
||||
#include "ColorPicker.h"
|
||||
#include "ui_ColorPicker.h"
|
||||
|
||||
#include <QPaintEvent>
|
||||
#include <QPainter>
|
||||
#include <QMouseEvent>
|
||||
#include <QDesktopWidget>
|
||||
#include <QPixmap>
|
||||
#include <QCursor>
|
||||
#include <QScreen>
|
||||
|
||||
using namespace ColorPickerHelpers;
|
||||
|
||||
ColorPickArea::ColorPickArea(QWidget *parent) : ColorPickerWidget(parent)
|
||||
{
|
||||
setMouseTracking(false);
|
||||
}
|
||||
|
||||
void ColorPickArea::paintEvent(QPaintEvent* event)
|
||||
{
|
||||
QPainter p(this);
|
||||
|
||||
for (int x = event->rect().x(); x <= event->rect().right(); x++) {
|
||||
for (int y = event->rect().y(); y <= event->rect().bottom(); y++) {
|
||||
qreal h, s, v;
|
||||
QColor c = pointToColor(x, y);
|
||||
c.getHsvF(&h, &s, &v);
|
||||
c.setHsvF(h, s, 1);
|
||||
p.setPen(c);
|
||||
p.drawPoint(x, y);
|
||||
}
|
||||
}
|
||||
|
||||
p.setPen(QPen(Qt::black, 3));
|
||||
QPoint curr = colorToPoint(currColor);
|
||||
p.drawLine(curr - QPoint(0, 10), curr + QPoint(0, 10));
|
||||
p.drawLine(curr - QPoint(10, 0), curr + QPoint(10, 0));
|
||||
|
||||
p.end();
|
||||
QWidget::paintEvent(event);
|
||||
}
|
||||
|
||||
ColorPickerWidget::ColorPickerWidget(QWidget* parent) : ColorPickWidgetAbstract(parent)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void ColorPickerWidget::mouseReleaseEvent(QMouseEvent* event)
|
||||
{
|
||||
mouseEvent(event);
|
||||
}
|
||||
|
||||
void ColorPickerWidget::mousePressEvent(QMouseEvent* event)
|
||||
{
|
||||
mouseEvent(event);
|
||||
}
|
||||
|
||||
void ColorPickerWidget::mouseMoveEvent(QMouseEvent* event)
|
||||
{
|
||||
mouseEvent(event);
|
||||
}
|
||||
|
||||
QColor ColorPickArea::pointToColor(int x, int y) const
|
||||
{
|
||||
QColor color;
|
||||
qreal h, s, v;
|
||||
currColor.getHsvF(&h, &s, &v);
|
||||
color.setHsvF((qreal)x / width(),
|
||||
1.0 - (qreal)y / height(),
|
||||
v);
|
||||
return color;
|
||||
}
|
||||
|
||||
QPoint ColorPickArea::colorToPoint(const QColor& color) const
|
||||
{
|
||||
qreal h, s, v;
|
||||
color.getHsvF(&h, &s, &v);
|
||||
return QPoint(h * width(), (1.0 - s) * height());
|
||||
}
|
||||
|
||||
void ColorPickerWidget::mouseEvent(QMouseEvent* event)
|
||||
{
|
||||
QPoint pos = event->pos();
|
||||
if (!rect().contains(pos.x(), rect().y())) {
|
||||
pos.setX(rect().x() < pos.x()
|
||||
? rect().right() + 1
|
||||
: rect().x());
|
||||
}
|
||||
if (!rect().contains(rect().x(), pos.y())) {
|
||||
pos.setY(rect().y() < pos.y()
|
||||
? rect().bottom() + 1
|
||||
: rect().y());
|
||||
}
|
||||
currColor = pointToColor(pos.x(), pos.y());
|
||||
emit colorChanged(currColor);
|
||||
repaint(QRect(event->pos() - QPoint(10, 10), event->pos() + QPoint(10, 10)));
|
||||
}
|
||||
|
||||
void ColorPickWidgetAbstract::setColor(const QColor& color)
|
||||
{
|
||||
currColor = color;
|
||||
repaint();
|
||||
}
|
||||
|
||||
void ColorValueBar::paintEvent(QPaintEvent* event)
|
||||
{
|
||||
QPainter p(this);
|
||||
QColor color = currColor;
|
||||
qreal h, s, v;
|
||||
currColor.getHsvF(&h, &s, &v);
|
||||
v = 1.0 - v;
|
||||
|
||||
const int trianleSize = 10;
|
||||
QRect barRect = rect();
|
||||
barRect.setWidth(barRect.width() - trianleSize);
|
||||
|
||||
|
||||
for (int y = barRect.y(); y <= barRect.bottom(); y++) {
|
||||
color.setHsvF(h, s, 1.0 - (qreal)y / height());
|
||||
p.setPen(color);
|
||||
p.drawLine(barRect.x(), y, barRect.right(), y);
|
||||
}
|
||||
|
||||
p.setPen(palette().alternateBase().color());
|
||||
p.drawRect(rect());
|
||||
|
||||
QRectF triangleRect = QRectF(barRect.right(), v * height() - trianleSize / 2,
|
||||
trianleSize, trianleSize);
|
||||
|
||||
QPainterPath path;
|
||||
path.moveTo(triangleRect.left(), triangleRect.top() + triangleRect.height() / 2);
|
||||
path.lineTo(triangleRect.topRight());
|
||||
path.lineTo(triangleRect.bottomRight());
|
||||
path.lineTo(triangleRect.left(), triangleRect.top() + triangleRect.height() / 2);
|
||||
|
||||
p.fillPath(path, palette().text().color());
|
||||
|
||||
p.end();
|
||||
QWidget::paintEvent(event);
|
||||
}
|
||||
|
||||
QColor ColorValueBar::pointToColor(int x, int y) const
|
||||
{
|
||||
Q_UNUSED(x)
|
||||
QColor color = currColor;
|
||||
qreal h, s, v;
|
||||
color.getHsvF(&h, &s, &v);
|
||||
color.setHsvF(h, s, 1.0 - (qreal)y / height());
|
||||
return color;
|
||||
}
|
||||
|
||||
QPoint ColorValueBar::colorToPoint(const QColor& color) const
|
||||
{
|
||||
qreal h, s, v;
|
||||
color.getHsvF(&h, &s, &v);
|
||||
return QPoint(rect().x(), (1.0 - v) * height());
|
||||
}
|
||||
|
||||
ColorPicker::ColorPicker(QWidget* parent) :
|
||||
ColorPickWidgetAbstract(parent),
|
||||
ui(new Ui::ColorPicker),
|
||||
pickingFromScreen(false)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
connect(ui->colorPickArea, &ColorPickArea::colorChanged,
|
||||
this, &ColorPicker::setColor);
|
||||
connect(ui->valuePickBar, &ColorValueBar::colorChanged,
|
||||
this, &ColorPicker::setColor);
|
||||
connect(this, &ColorPicker::colorChanged,
|
||||
ui->colorPickArea, &ColorPickArea::setColor);
|
||||
connect(this, &ColorPicker::colorChanged,
|
||||
ui->valuePickBar, &ColorValueBar::setColor);
|
||||
connect(this, &ColorPicker::colorChanged,
|
||||
ui->colorShow, &ColorShowWidget::setColor);
|
||||
|
||||
connect(ui->hueSpinBox, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged),
|
||||
this, &ColorPicker::colorChannelChanged);
|
||||
connect(ui->satSpinBox, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged),
|
||||
this, &ColorPicker::colorChannelChanged);
|
||||
connect(ui->valSpinBox, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged),
|
||||
this, &ColorPicker::colorChannelChanged);
|
||||
connect(ui->redSpinBox, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged),
|
||||
this, &ColorPicker::colorChannelChanged);
|
||||
connect(ui->blueSpinBox, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged),
|
||||
this, &ColorPicker::colorChannelChanged);
|
||||
connect(ui->greenSpinBox, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged),
|
||||
this, &ColorPicker::colorChannelChanged);
|
||||
|
||||
connect(ui->hexLineEdit, &QLineEdit::textChanged, this, &ColorPicker::colorChannelChanged);
|
||||
|
||||
connect(ui->pickColorFromScreenButton, &QPushButton::clicked, this, &ColorPicker::startPickingFromScreen);
|
||||
}
|
||||
|
||||
ColorPicker::~ColorPicker()
|
||||
{
|
||||
if (pickingFromScreen) {
|
||||
setColor(QApplication::screenAt(QCursor::pos())->grabWindow(QApplication::desktop()->winId())
|
||||
.toImage().pixelColor(QCursor::pos()));
|
||||
stopPickingFromScreen();
|
||||
}
|
||||
}
|
||||
|
||||
void ColorPicker::setColor(const QColor& color)
|
||||
{
|
||||
updateColor(color);
|
||||
emit colorChanged(currColor);
|
||||
}
|
||||
|
||||
void ColorPicker::colorChannelChanged()
|
||||
{
|
||||
QString txt = ui->hexLineEdit->text();
|
||||
if (!QRegExp("#[0-9a-fA-F]{6}").exactMatch(txt)) {
|
||||
return;
|
||||
}
|
||||
QColor hexColor = txt;
|
||||
|
||||
int h, s, v;
|
||||
h = ui->hueSpinBox->value();
|
||||
s = ui->satSpinBox->value();
|
||||
v = ui->valSpinBox->value();
|
||||
QColor hsvColor;
|
||||
hsvColor.setHsv(h, s, v);
|
||||
|
||||
int r, g, b;
|
||||
r = ui->redSpinBox->value();
|
||||
g = ui->greenSpinBox->value();
|
||||
b = ui->blueSpinBox->value();
|
||||
QColor rgbColor;
|
||||
rgbColor.setRgb(r, g, b);
|
||||
|
||||
if (hexColor.isValid() && hexColor != currColor) {
|
||||
setColor(hexColor);
|
||||
} else if (rgbColor.isValid() && rgbColor != currColor) {
|
||||
setColor(rgbColor);
|
||||
} else if (hsvColor.isValid() && hsvColor != currColor) {
|
||||
setColor(hsvColor);
|
||||
}
|
||||
}
|
||||
|
||||
void ColorPicker::updateColor(const QColor& color)
|
||||
{
|
||||
QSignalBlocker s0(ui->redSpinBox);
|
||||
QSignalBlocker s1(ui->blueSpinBox);
|
||||
QSignalBlocker s2(ui->greenSpinBox);
|
||||
|
||||
QSignalBlocker s3(ui->valSpinBox);
|
||||
QSignalBlocker s4(ui->satSpinBox);
|
||||
QSignalBlocker s5(ui->hueSpinBox);
|
||||
|
||||
QSignalBlocker s6(ui->hexLineEdit);
|
||||
currColor = color;
|
||||
|
||||
ui->hexLineEdit->setText(currColor.name());
|
||||
|
||||
int h, s, v;
|
||||
currColor.getHsv(&h, &s, &v);
|
||||
ui->hueSpinBox->setValue(h);
|
||||
ui->satSpinBox->setValue(s);
|
||||
ui->valSpinBox->setValue(v);
|
||||
|
||||
int r, g, b;
|
||||
currColor.getRgb(&r, &g, &b);
|
||||
ui->redSpinBox->setValue(r);
|
||||
ui->greenSpinBox->setValue(g);
|
||||
ui->blueSpinBox->setValue(b);
|
||||
|
||||
ui->valuePickBar->setColor(color);
|
||||
ui->colorPickArea->setColor(color);
|
||||
ui->colorShow->setColor(color);
|
||||
}
|
||||
|
||||
void ColorPicker::startPickingFromScreen()
|
||||
{
|
||||
if (!pickingFromScreen) {
|
||||
setMouseTracking(true);
|
||||
grabMouse(Qt::CursorShape::CrossCursor);
|
||||
pickingFromScreen = true;
|
||||
bufferColor = currColor;
|
||||
}
|
||||
}
|
||||
|
||||
void ColorPicker::mouseReleaseEvent(QMouseEvent* event)
|
||||
{
|
||||
if (pickingFromScreen) {
|
||||
const QDesktopWidget *desktop = QApplication::desktop();
|
||||
const QPixmap pixmap = QGuiApplication::screens().at(desktop->screenNumber())
|
||||
->grabWindow(desktop->winId(),
|
||||
QCursor::pos().x(), QCursor::pos().y(), 1, 1);
|
||||
setColor(pixmap.toImage().pixel(0, 0));
|
||||
pickingFromScreen = false;
|
||||
setMouseTracking(false);
|
||||
releaseMouse();
|
||||
}
|
||||
QWidget::mouseReleaseEvent(event);
|
||||
}
|
||||
|
||||
void ColorPicker::mouseMoveEvent(QMouseEvent* event)
|
||||
{
|
||||
if (pickingFromScreen) {
|
||||
const QDesktopWidget *desktop = QApplication::desktop();
|
||||
const QPixmap pixmap = QGuiApplication::screens().at(desktop->screenNumber())
|
||||
->grabWindow(desktop->winId(),
|
||||
QCursor::pos().x(), QCursor::pos().y(), 1, 1);
|
||||
updateColor(pixmap.toImage().pixel(0, 0));
|
||||
}
|
||||
QWidget::mouseMoveEvent(event);
|
||||
}
|
||||
|
||||
bool ColorPicker::isPickingFromScreen() const
|
||||
{
|
||||
return pickingFromScreen;
|
||||
}
|
||||
|
||||
void ColorPicker::stopPickingFromScreen()
|
||||
{
|
||||
if (pickingFromScreen) {
|
||||
pickingFromScreen = false;
|
||||
updateColor(bufferColor);
|
||||
releaseMouse();
|
||||
setMouseTracking(false);
|
||||
}
|
||||
}
|
||||
|
||||
ColorShowWidget::ColorShowWidget(QWidget* parent) : ColorPickWidgetAbstract(parent) { }
|
||||
|
||||
void ColorShowWidget::paintEvent(QPaintEvent* event)
|
||||
{
|
||||
QPainter p(this);
|
||||
p.setPen(currColor);
|
||||
p.setBrush(QBrush(currColor));
|
||||
p.drawRect(event->rect());
|
||||
p.end();
|
||||
}
|
165
src/widgets/ColorPicker.h
Normal file
@ -0,0 +1,165 @@
|
||||
#ifndef COLORPICKER_H
|
||||
#define COLORPICKER_H
|
||||
|
||||
#include <QWidget>
|
||||
|
||||
/**
|
||||
* @namespace ColorPickerHelpers is a namespace that hides all classes needed for ColorPicker class,
|
||||
* because classes inherite QObject can not be declared in *.cpp files or inside of another class.
|
||||
*/
|
||||
namespace ColorPickerHelpers {
|
||||
class ColorPickWidgetAbstract : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
ColorPickWidgetAbstract(QWidget *parent = nullptr): QWidget(parent) {}
|
||||
virtual ~ColorPickWidgetAbstract() {}
|
||||
|
||||
signals:
|
||||
void colorChanged(const QColor& color);
|
||||
|
||||
public slots:
|
||||
virtual void setColor(const QColor& color);
|
||||
|
||||
protected:
|
||||
QColor currColor;
|
||||
};
|
||||
}
|
||||
|
||||
namespace Ui {
|
||||
class ColorPicker;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief The ColorPicker class provides widget that allows user to pick color
|
||||
* from screen or from palette or type in HSV or RGB or HEX representation of color.
|
||||
*/
|
||||
class ColorPicker : public ColorPickerHelpers::ColorPickWidgetAbstract
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit ColorPicker(QWidget *parent = nullptr);
|
||||
~ColorPicker();
|
||||
|
||||
/**
|
||||
* @brief isPickingFromScreen returns true if color picker is picking from screen.
|
||||
*/
|
||||
bool isPickingFromScreen() const;
|
||||
|
||||
|
||||
public slots:
|
||||
/**
|
||||
* @brief setColor sets displayed color to @a color and emits colorChanged signal.
|
||||
*/
|
||||
virtual void setColor(const QColor &color) override;
|
||||
|
||||
void colorChannelChanged();
|
||||
|
||||
/**
|
||||
* @brief updateColor sets displayed color to @a color.
|
||||
*/
|
||||
void updateColor(const QColor& color);
|
||||
|
||||
/**
|
||||
* @brief startPickingFromScreen starts process of picking from screen.
|
||||
* Function is called automatically when "Pick from screen" button is clicked.
|
||||
*/
|
||||
void startPickingFromScreen();
|
||||
|
||||
/**
|
||||
* @brief stopPickingFromScreen terminates process of picking from screen.
|
||||
*/
|
||||
void stopPickingFromScreen();
|
||||
|
||||
protected:
|
||||
void mouseReleaseEvent(QMouseEvent* event) override;
|
||||
void mouseMoveEvent(QMouseEvent* event) override;
|
||||
|
||||
private:
|
||||
Ui::ColorPicker *ui;
|
||||
bool pickingFromScreen;
|
||||
|
||||
/**
|
||||
* @brief bufferColor is used to buffer current color while picking from screen.
|
||||
*/
|
||||
QColor bufferColor;
|
||||
};
|
||||
|
||||
namespace ColorPickerHelpers {
|
||||
/**
|
||||
* @brief The ColorPickerWidget class is parent class for ColorPickArea and ColorValueBar classes.
|
||||
*/
|
||||
class ColorPickerWidget : public ColorPickWidgetAbstract {
|
||||
Q_OBJECT
|
||||
public:
|
||||
ColorPickerWidget(QWidget *parent = nullptr);
|
||||
|
||||
protected:
|
||||
void mouseReleaseEvent(QMouseEvent* event) override;
|
||||
void mousePressEvent(QMouseEvent* event) override;
|
||||
void mouseMoveEvent(QMouseEvent* event) override;
|
||||
|
||||
private:
|
||||
void mouseEvent(QMouseEvent* event);
|
||||
|
||||
/**
|
||||
* @brief pointToColor converts coordinates on widget to color these coordinates represents.
|
||||
*/
|
||||
virtual QColor pointToColor(int x, int y) const = 0;
|
||||
|
||||
/**
|
||||
* @brief colorToPoint converts color to coordinates that represent this color.
|
||||
*/
|
||||
virtual QPoint colorToPoint(const QColor& color) const = 0;
|
||||
};
|
||||
|
||||
class ColorShowWidget : public ColorPickWidgetAbstract
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit ColorShowWidget(QWidget *parent = nullptr);
|
||||
|
||||
protected:
|
||||
void paintEvent(QPaintEvent *event) override;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief The ColorPickArea class provides widget that helps to pick
|
||||
* Saturation and Hue of color in HSV colorspace.
|
||||
*/
|
||||
class ColorPickArea : public ColorPickerWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit ColorPickArea(QWidget *parent = nullptr);
|
||||
|
||||
protected:
|
||||
void paintEvent(QPaintEvent *event) override;
|
||||
|
||||
private:
|
||||
QColor pointToColor(int x, int y) const override;
|
||||
|
||||
QPoint colorToPoint(const QColor& color) const override;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief The ColorValueBar class provides widget that helps to set Valuse of color
|
||||
* in HSV colorspace.
|
||||
*/
|
||||
class ColorValueBar : public ColorPickerWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
ColorValueBar(QWidget *parent = nullptr) : ColorPickerWidget(parent) {}
|
||||
|
||||
protected:
|
||||
void paintEvent(QPaintEvent *event) override;
|
||||
|
||||
private:
|
||||
QColor pointToColor(int x, int y) const override;
|
||||
|
||||
QPoint colorToPoint(const QColor& color) const override;
|
||||
};
|
||||
}
|
||||
|
||||
#endif // COLORPICKER_H
|
349
src/widgets/ColorPicker.ui
Normal file
@ -0,0 +1,349 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>ColorPicker</class>
|
||||
<widget class="QWidget" name="ColorPicker">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>1027</width>
|
||||
<height>232</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Form</string>
|
||||
</property>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_4">
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_3">
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
<widget class="ColorPickerHelpers::ColorPickArea" name="colorPickArea" native="true">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>200</width>
|
||||
<height>200</height>
|
||||
</size>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="ColorPickerHelpers::ColorValueBar" name="valuePickBar" native="true">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>30</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="ColorPickerHelpers::ColorShowWidget" name="colorShow" native="true">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>30</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QVBoxLayout" name="colorEditLayout">
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
||||
<item>
|
||||
<layout class="QVBoxLayout" name="hsvLayout">
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="valLayout">
|
||||
<item>
|
||||
<spacer name="horizontalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="valLabel">
|
||||
<property name="text">
|
||||
<string>Val:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QSpinBox" name="valSpinBox">
|
||||
<property name="maximum">
|
||||
<number>255</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="satLayout">
|
||||
<item>
|
||||
<spacer name="horizontalSpacer_2">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="satLabel">
|
||||
<property name="text">
|
||||
<string>Sat:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QSpinBox" name="satSpinBox">
|
||||
<property name="maximum">
|
||||
<number>255</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="hueLayout">
|
||||
<item>
|
||||
<spacer name="horizontalSpacer_3">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="hueLabel">
|
||||
<property name="text">
|
||||
<string>Hue:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QSpinBox" name="hueSpinBox">
|
||||
<property name="maximum">
|
||||
<number>359</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QVBoxLayout" name="rgbLayout">
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="redLayout">
|
||||
<item>
|
||||
<spacer name="horizontalSpacer_4">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="redLabel">
|
||||
<property name="text">
|
||||
<string>Red:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QSpinBox" name="redSpinBox">
|
||||
<property name="maximum">
|
||||
<number>255</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="greenLayout">
|
||||
<item>
|
||||
<spacer name="horizontalSpacer_5">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="greenLabel">
|
||||
<property name="text">
|
||||
<string>Green:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QSpinBox" name="greenSpinBox">
|
||||
<property name="maximum">
|
||||
<number>255</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="blueLayout">
|
||||
<item>
|
||||
<spacer name="horizontalSpacer_6">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="blueLabel">
|
||||
<property name="text">
|
||||
<string>Blue:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QSpinBox" name="blueSpinBox">
|
||||
<property name="maximum">
|
||||
<number>255</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="hexLayout">
|
||||
<item>
|
||||
<spacer name="horizontalSpacer_7">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="hexLabel">
|
||||
<property name="text">
|
||||
<string>Hex:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLineEdit" name="hexLineEdit">
|
||||
<property name="inputMask">
|
||||
<string>\#HHHHHH</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_5">
|
||||
<item>
|
||||
<spacer name="horizontalSpacer_9">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="pickColorFromScreenButton">
|
||||
<property name="text">
|
||||
<string>Pick color from screen</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer_8">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<customwidgets>
|
||||
<customwidget>
|
||||
<class>ColorPickerHelpers::ColorPickArea</class>
|
||||
<extends>QWidget</extends>
|
||||
<header>widgets/ColorPicker.h</header>
|
||||
<container>1</container>
|
||||
</customwidget>
|
||||
<customwidget>
|
||||
<class>ColorPickerHelpers::ColorValueBar</class>
|
||||
<extends>QWidget</extends>
|
||||
<header>widgets/ColorPicker.h</header>
|
||||
<container>1</container>
|
||||
</customwidget>
|
||||
<customwidget>
|
||||
<class>ColorPickerHelpers::ColorShowWidget</class>
|
||||
<extends>QWidget</extends>
|
||||
<header>widgets/ColorPicker.h</header>
|
||||
<container>1</container>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
@ -1,562 +0,0 @@
|
||||
#include "common/Configuration.h"
|
||||
#include "common/Configuration.h"
|
||||
#include "ColorSchemePrefWidget.h"
|
||||
#include "ui_ColorSchemePrefWidget.h"
|
||||
#include "common/ColorSchemeFileSaver.h"
|
||||
|
||||
#include <QMap>
|
||||
#include <QFile>
|
||||
#include <QDebug>
|
||||
#include <QPainter>
|
||||
#include <QJsonArray>
|
||||
#include <QMouseEvent>
|
||||
#include <QApplication>
|
||||
#include <QColorDialog>
|
||||
#include <QJsonDocument>
|
||||
|
||||
struct OptionIfo {
|
||||
QString info;
|
||||
QString displayingtext;
|
||||
bool isUsedStandardTextColor;
|
||||
};
|
||||
|
||||
static const QMap<QString, OptionIfo> optionInfoMap = {
|
||||
{
|
||||
"comment", {
|
||||
QObject::tr("Color of comment generated by radare2"),
|
||||
QObject::tr("Comment"), false
|
||||
}
|
||||
},
|
||||
{
|
||||
"usrcmt", {
|
||||
QObject::tr("Comment created by user"),
|
||||
QObject::tr("Color of user Comment"), false
|
||||
}
|
||||
},
|
||||
{
|
||||
"args", {
|
||||
QObject::tr("Color of function arguments"),
|
||||
QObject::tr("Arguments"), false
|
||||
}
|
||||
},
|
||||
{
|
||||
"fname", {
|
||||
QObject::tr("Color of names of functions"),
|
||||
QObject::tr("Function name"), false
|
||||
}
|
||||
},
|
||||
{
|
||||
"floc", {
|
||||
QObject::tr("Color of function location"),
|
||||
QObject::tr("Function location"), false
|
||||
}
|
||||
},
|
||||
{
|
||||
"fline", {
|
||||
"",
|
||||
"fline", false
|
||||
}
|
||||
},
|
||||
{
|
||||
"flag", {
|
||||
QObject::tr("Color of flags (similar to bookmarks for offset)"),
|
||||
QObject::tr("Flag"), false
|
||||
}
|
||||
},
|
||||
{ "label", { "", QObject::tr("Label"), false } },
|
||||
{ "help", { "", QObject::tr("Help"), false } },
|
||||
{ "flow", { "", QObject::tr("flow"), false } },
|
||||
{ "flow2", { "", QObject::tr("flow2"), false } },
|
||||
{ "prompt", { QObject::tr("Info"), QObject::tr("prompt"), false } },
|
||||
{
|
||||
"offset", {
|
||||
QObject::tr("Color of offsets"),
|
||||
QObject::tr("Offset"), false
|
||||
}
|
||||
},
|
||||
{ "input", { QObject::tr("Info"), QObject::tr("input"), false } },
|
||||
{
|
||||
"invalid", {
|
||||
QObject::tr("Invalid opcode color"),
|
||||
QObject::tr("invalid"), false
|
||||
}
|
||||
},
|
||||
{ "other", { "", QObject::tr("other"), false } },
|
||||
{
|
||||
"b0x00", {
|
||||
QObject::tr("0x00 opcode color"),
|
||||
"b0x00", false
|
||||
}
|
||||
},
|
||||
{
|
||||
"b0x7f", {
|
||||
QObject::tr("0x7f opcode color"),
|
||||
"b0x7f", false
|
||||
}
|
||||
},
|
||||
{
|
||||
"b0xff", {
|
||||
QObject::tr("0xff opcode color"),
|
||||
"b0xff", false
|
||||
}
|
||||
},
|
||||
{
|
||||
"math", {
|
||||
QObject::tr("arithmetic color (+, -, *, / etc.)"),
|
||||
"math", false
|
||||
}
|
||||
},
|
||||
{ "bin", { "", QObject::tr("bin"), false } },
|
||||
{ "btext", { "", QObject::tr("btext"), false } },
|
||||
{ "push", { QObject::tr("push opcode color"), "push", false } },
|
||||
{ "pop", { QObject::tr("pop opcode color"), "pop", false } },
|
||||
{ "crypto", { QObject::tr("Cryptographic color"), "crypto", false } },
|
||||
{
|
||||
"jmp", {
|
||||
QObject::tr("jmp instructions color"),
|
||||
"jmp", false
|
||||
}
|
||||
},
|
||||
{
|
||||
"cjmp", {
|
||||
"",
|
||||
"cjmp", false
|
||||
}
|
||||
},
|
||||
{
|
||||
"call", {
|
||||
QObject::tr("call instructions color (ccall, rcall, call etc)"),
|
||||
"call", false
|
||||
}
|
||||
},
|
||||
{ "nop", { QObject::tr("nop opcode color"), "nop", false } },
|
||||
{
|
||||
"ret", {
|
||||
QObject::tr("ret opcode color"),
|
||||
"ret", false
|
||||
}
|
||||
},
|
||||
{
|
||||
"trap", {
|
||||
QObject::tr("Color of interrupts"),
|
||||
QObject::tr("Interrupts"), false
|
||||
}
|
||||
},
|
||||
{ "swi", { QObject::tr("swi opcode color"), "swi", false } },
|
||||
{ "cmp", { QObject::tr("cmp opcode color"), "cmp", false } },
|
||||
{ "reg", { QObject::tr("Registers color"), QObject::tr("Register"), false } },
|
||||
{ "creg", { QObject::tr("Info"), "creg", false } },
|
||||
{ "num", { QObject::tr("Numeric constants color"), QObject::tr("Numbers"), false } },
|
||||
{
|
||||
"mov", {
|
||||
QObject::tr("mov instructions color (mov, movd, movw etc"),
|
||||
QObject::tr("mov"), false
|
||||
}
|
||||
},
|
||||
{
|
||||
"func_var", {
|
||||
QObject::tr("Function variable color"),
|
||||
QObject::tr("Function variable"), false
|
||||
}
|
||||
},
|
||||
{
|
||||
"func_var_type", {
|
||||
QObject::tr("Function variable (local or argument) type color"),
|
||||
QObject::tr("Variable type"), false
|
||||
}
|
||||
},
|
||||
{
|
||||
"func_var_addr", {
|
||||
QObject::tr("Function variable address color"),
|
||||
QObject::tr("Variable address"), false
|
||||
}
|
||||
},
|
||||
{ "widget_bg", { "", "widget_bg", false } },
|
||||
{ "widget_sel", { "", "widget_sel", false } },
|
||||
{ "ai.read", { "", "ai.read", false } },
|
||||
{ "ai.write", { "", "ai.write", false } },
|
||||
{ "ai.exec", { "", "ai.exec", false } },
|
||||
{ "ai.seq", { "", "ai.seq", false } },
|
||||
{ "ai.ascii", { "", "ai.ascii", false } },
|
||||
{ "graph.box", { "", "graph.box", false } },
|
||||
{ "graph.box2", { "", "graph.box2", false } },
|
||||
{ "graph.box3", { "", "graph.box3", false } },
|
||||
{ "graph.box4", { "", "graph.box4", false } },
|
||||
{
|
||||
"graph.true", {
|
||||
QObject::tr("In graph view jump arrow true"),
|
||||
QObject::tr("Arrow true"), false
|
||||
}
|
||||
},
|
||||
{
|
||||
"graph.false", {
|
||||
QObject::tr("In graph view jump arrow false"),
|
||||
QObject::tr("Arrow false"), false
|
||||
}
|
||||
},
|
||||
{
|
||||
"graph.trufae", {
|
||||
QObject::tr("In graph view jump arrow (no condition)"),
|
||||
QObject::tr("Arrow"), false
|
||||
}
|
||||
},
|
||||
{ "graph.current", { "", "graph.current", false } },
|
||||
{ "graph.traced", { "", "graph.traced", false } },
|
||||
{
|
||||
"gui.overview.node", {
|
||||
QObject::tr("Background color of Graph Overview's node"),
|
||||
QObject::tr("Graph Overview node"), true
|
||||
}
|
||||
},
|
||||
{ "gui.cflow", { "", "gui.cflow", true } },
|
||||
{ "gui.dataoffset", { "", "gui.dataoffset", true } },
|
||||
{
|
||||
"gui.background", {
|
||||
QObject::tr("General background color"),
|
||||
QObject::tr("Background"), true
|
||||
}
|
||||
},
|
||||
{
|
||||
"gui.alt_background", {
|
||||
"",
|
||||
QObject::tr("Alt. background"), true
|
||||
}
|
||||
},
|
||||
{
|
||||
"gui.disass_selected", {
|
||||
QObject::tr("Background of current graph node"),
|
||||
QObject::tr("Current graph node"), true
|
||||
}
|
||||
},
|
||||
{ "gui.border", { "", "gui.border", true } },
|
||||
|
||||
// TODO: find out different
|
||||
{
|
||||
"linehl", {
|
||||
QObject::tr("Selected line background color"),
|
||||
QObject::tr("Line highlight"), true
|
||||
}
|
||||
},
|
||||
{
|
||||
"highlight", {
|
||||
QObject::tr("Selected line background color"),
|
||||
QObject::tr("Line highlight"), true
|
||||
}
|
||||
},
|
||||
|
||||
// TODO: find out different
|
||||
{
|
||||
"highlightWord", {
|
||||
QObject::tr("Highlighted word text color"),
|
||||
QObject::tr("Word higlight"), true
|
||||
}
|
||||
},
|
||||
{
|
||||
"wordhl", {
|
||||
QObject::tr("Highlighted word text color"),
|
||||
QObject::tr("Word higlight"), true
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
"gui.main", {
|
||||
QObject::tr("Main function color"),
|
||||
QObject::tr("Main"), true
|
||||
}
|
||||
},
|
||||
{ "gui.imports", { "", "gui.imports", true } },
|
||||
{ "highlightPC", { "", "highlightPC", true } },
|
||||
{ "gui.navbar.err", { "", "gui.navbar.err", true } },
|
||||
{ "gui.navbar.seek", { "", "gui.navbar.seek", true } },
|
||||
{ "gui.navbar.pc", { "", "gui.navbar.pc", true } },
|
||||
{ "gui.navbar.sym", { "", "gui.navbar.sym", true } },
|
||||
{
|
||||
"gui.navbar.code", {
|
||||
QObject::tr("Code section color in navigation bar"),
|
||||
QObject::tr("Navbar code"), true
|
||||
}
|
||||
},
|
||||
{
|
||||
"gui.navbar.empty", {
|
||||
QObject::tr("Empty section color in navigation bar"),
|
||||
QObject::tr("Navbar empty"), true
|
||||
}
|
||||
},
|
||||
{ "gui.breakpoint_background", { "", QObject::tr("Breakpoint background"), true } },
|
||||
{
|
||||
"gui.tooltip.background", {
|
||||
QObject::tr("Background color for tooltips"),
|
||||
QObject::tr("Tooltip background"), true
|
||||
}
|
||||
},
|
||||
{
|
||||
"gui.tooltip.foreground", {
|
||||
QObject::tr("Foregorund color for tooltips"),
|
||||
QObject::tr("Tooltip foreground"), true
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
void ColorOptionDelegate::paint(QPainter *painter,
|
||||
const QStyleOptionViewItem &option,
|
||||
const QModelIndex &index) const
|
||||
{
|
||||
painter->setPen(QPen(Qt::OpaqueMode));
|
||||
painter->setBrush(QBrush(backgroundColor));
|
||||
painter->drawRect(option.rect);
|
||||
|
||||
QPalette pal;
|
||||
|
||||
QStyleOptionViewItem op = option;
|
||||
|
||||
op.palette = pal;
|
||||
op.displayAlignment = Qt::AlignCenter;
|
||||
|
||||
if (option.state & QStyle::State_Selected) {
|
||||
QLinearGradient lgrd = QLinearGradient(option.rect.topLeft(), option.rect.bottomRight());
|
||||
QColor highlighted = QApplication::palette().highlight().color(),
|
||||
highlightedOpaque = highlighted;
|
||||
highlightedOpaque.setAlpha(0);
|
||||
|
||||
lgrd.setColorAt(0.00, highlighted);
|
||||
lgrd.setColorAt(0.25, highlightedOpaque);
|
||||
lgrd.setColorAt(0.75, highlightedOpaque);
|
||||
lgrd.setColorAt(1.00, highlighted);
|
||||
painter->setBrush(lgrd);
|
||||
painter->drawRect(option.rect);
|
||||
op.state &= ~ QStyle::State_Selected;
|
||||
}
|
||||
|
||||
op.state &= ~ QStyle::State_Editing;
|
||||
pal = option.palette;
|
||||
|
||||
QColor txtColor;
|
||||
|
||||
ColorOption co = index.data(Qt::UserRole).value<ColorOption>();
|
||||
txtColor = co.color;
|
||||
|
||||
if (optionInfoMap[co.optionName].isUsedStandardTextColor) {
|
||||
txtColor = textColor;
|
||||
}
|
||||
pal.setColor(QPalette::Text, txtColor);
|
||||
|
||||
op.palette = pal;
|
||||
QStyledItemDelegate::paint(painter, op, index);
|
||||
}
|
||||
|
||||
void ColorOptionDelegate::setBackgroundColor(const QColor &c)
|
||||
{
|
||||
backgroundColor = c;
|
||||
}
|
||||
|
||||
void ColorOptionDelegate::setTextColor(const QColor &c)
|
||||
{
|
||||
textColor = c;
|
||||
}
|
||||
|
||||
ColorViewButton::ColorViewButton(QWidget *parent) : QFrame (parent)
|
||||
{
|
||||
setLineWidth(3);
|
||||
setFrameShape(QFrame::Panel);
|
||||
setFrameShadow(QFrame::Raised);
|
||||
setColor(palette().background().color());
|
||||
setMaximumWidth(100);
|
||||
}
|
||||
|
||||
void ColorViewButton::setColor(const QColor &c)
|
||||
{
|
||||
setStyleSheet(QString("background-color:%1;").arg(c.name().toLower()));
|
||||
repaint();
|
||||
}
|
||||
|
||||
void ColorViewButton::mouseReleaseEvent(QMouseEvent *event)
|
||||
{
|
||||
if (event->button() != Qt::LeftButton) {
|
||||
return;
|
||||
}
|
||||
|
||||
emit clicked();
|
||||
}
|
||||
|
||||
PreferencesListView::PreferencesListView(QWidget *parent) :
|
||||
QListView (parent)
|
||||
{
|
||||
setModel(new ColorSettingsModel(static_cast<QObject *>(this)));
|
||||
|
||||
static_cast<ColorSettingsModel *>(this->model())->updateScheme();
|
||||
setStandardColors();
|
||||
}
|
||||
|
||||
void PreferencesListView::setStandardColors()
|
||||
{
|
||||
delegate = new ColorOptionDelegate(this);
|
||||
ColorSettingsModel *model = static_cast<ColorSettingsModel *>(this->model());
|
||||
|
||||
delegate->setBackgroundColor(model->getBackroundColor());
|
||||
delegate->setTextColor(model->getTextColor());
|
||||
// I can't free last delegate, but PreferencesListView will delete it,
|
||||
// because every delegate is its child.
|
||||
setItemDelegate(static_cast<QAbstractItemDelegate *>(delegate));
|
||||
}
|
||||
|
||||
void PreferencesListView::currentChanged(const QModelIndex ¤t,
|
||||
const QModelIndex &previous)
|
||||
{
|
||||
emit indexChanged(current);
|
||||
QListView::currentChanged(current, previous);
|
||||
}
|
||||
|
||||
ColorSchemePrefWidget::ColorSchemePrefWidget(QWidget *parent) : QWidget (parent),
|
||||
ui (new Ui::ColorSchemePrefWidget), isEditable (false)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
connect(ui->colorViewFore, &ColorViewButton::clicked, this, &ColorSchemePrefWidget::newColor);
|
||||
connect(ui->preferencesListView, &PreferencesListView::indexChanged, this,
|
||||
&ColorSchemePrefWidget::indexChanged);
|
||||
connect(ui->preferencesListView, &PreferencesListView::indexChanged, [this](const QModelIndex & i) {
|
||||
ui->infoBoard->setText(optionInfoMap[i.data(Qt::UserRole).value<ColorOption>().optionName].info);
|
||||
});
|
||||
}
|
||||
|
||||
ColorSchemePrefWidget::~ColorSchemePrefWidget()
|
||||
{
|
||||
apply();
|
||||
delete ui;
|
||||
}
|
||||
|
||||
void ColorSchemePrefWidget::apply()
|
||||
{
|
||||
if (!isEditable) {
|
||||
return;
|
||||
}
|
||||
ColorSettingsModel *model = static_cast<ColorSettingsModel *>(ui->preferencesListView->model());
|
||||
|
||||
QString scheme = "";
|
||||
ColorOption curr;
|
||||
QMap<QString, QColor> cutterSpecific = ColorSchemeFileWorker().getCutterSpecific();
|
||||
|
||||
for (int i = 0; i < model->rowCount(); i++) {
|
||||
curr = model->data(model->index(i), Qt::UserRole).value<ColorOption>();
|
||||
if (cutterSpecific.contains(curr.optionName)) {
|
||||
scheme += "#~";
|
||||
} else {
|
||||
scheme += "ec ";
|
||||
}
|
||||
scheme += curr.optionName + " rgb:" + curr.color.name().remove(QLatin1Char('#')).toLower() + "\n";
|
||||
}
|
||||
ColorSchemeFileWorker().save(scheme, Config()->getColorTheme());
|
||||
Config()->setColorTheme(Config()->getColorTheme());
|
||||
}
|
||||
|
||||
void ColorSchemePrefWidget::newColor()
|
||||
{
|
||||
if (ui->preferencesListView->currentIndex().row() == -1 || !isEditable) {
|
||||
return;
|
||||
}
|
||||
|
||||
ColorOption currCO = ui->preferencesListView->model()->data(ui->preferencesListView->currentIndex(),
|
||||
Qt::UserRole).value<ColorOption>();
|
||||
QColorDialog d(currCO.color, this);
|
||||
if (QDialog::Accepted != d.exec())
|
||||
return;
|
||||
|
||||
static_cast<ColorSettingsModel *>(ui->preferencesListView->model())->setColor(currCO.optionName,
|
||||
d.selectedColor());
|
||||
ui->preferencesListView->setStandardColors();
|
||||
if (ui->preferencesListView->model()->rowCount())
|
||||
indexChanged(ui->preferencesListView->selectionModel()->selectedIndexes().first());
|
||||
}
|
||||
|
||||
void ColorSchemePrefWidget::indexChanged(const QModelIndex &ni)
|
||||
{
|
||||
ui->colorViewFore->setColor(ni.data(Qt::UserRole).value<ColorOption>().color);
|
||||
}
|
||||
|
||||
void ColorSchemePrefWidget::updateSchemeFromConfig()
|
||||
{
|
||||
isEditable = ColorSchemeFileWorker().isCustomScheme(Config()->getColorTheme());
|
||||
static_cast<ColorSettingsModel *>(ui->preferencesListView->model())->updateScheme();
|
||||
}
|
||||
|
||||
ColorSettingsModel::ColorSettingsModel(QObject *parent) : QAbstractListModel (parent) { }
|
||||
|
||||
QVariant ColorSettingsModel::data(const QModelIndex &index, int role) const
|
||||
{
|
||||
if (!index.isValid()) {
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
if (index.row() < 0 || index.row() >= m_data.size()) {
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
if (role == Qt::DisplayRole) {
|
||||
return QVariant::fromValue(m_data.at(index.row()).displayingText);
|
||||
}
|
||||
|
||||
if (role == Qt::UserRole) {
|
||||
return QVariant::fromValue(m_data.at(index.row()));
|
||||
}
|
||||
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
void ColorSettingsModel::setColor(const QString &option, const QColor &color)
|
||||
{
|
||||
int row = 0;
|
||||
for (auto &it : m_data) {
|
||||
if (it.optionName == option) {
|
||||
it.color = color;
|
||||
emit dataChanged(index(row), index(row));
|
||||
return;
|
||||
}
|
||||
row++;
|
||||
}
|
||||
}
|
||||
|
||||
QColor ColorSettingsModel::getBackroundColor() const
|
||||
{
|
||||
if (!ColorSchemeFileWorker().isCustomScheme(Config()->getColorTheme())) {
|
||||
return Config()->getColor(standardBackgroundOptionName);
|
||||
}
|
||||
|
||||
for (auto &it : m_data) {
|
||||
if (it.optionName == standardBackgroundOptionName) {
|
||||
return it.color;
|
||||
}
|
||||
}
|
||||
return QColor();
|
||||
}
|
||||
|
||||
QColor ColorSettingsModel::getTextColor() const
|
||||
{
|
||||
if (!ColorSchemeFileWorker().isCustomScheme(Config()->getColorTheme())) {
|
||||
return Config()->getColor(standardTextOptionName);
|
||||
}
|
||||
|
||||
for (auto &it : m_data) {
|
||||
if (it.optionName == standardTextOptionName) {
|
||||
return it.color;
|
||||
}
|
||||
}
|
||||
return QColor();
|
||||
}
|
||||
|
||||
void ColorSettingsModel::updateScheme()
|
||||
{
|
||||
m_data.clear();
|
||||
QJsonObject obj = Core()->cmdj("ecj").object();
|
||||
|
||||
m_data.reserve(obj.size());
|
||||
for (auto &it : obj.keys()) {
|
||||
QJsonArray rgb = obj[it].toArray();
|
||||
m_data.push_back({it, optionInfoMap[it].displayingtext, QColor(rgb[0].toInt(), rgb[1].toInt(), rgb[2].toInt())});
|
||||
}
|
||||
|
||||
QMap<QString, QColor> cutterSpecific = ColorSchemeFileWorker().getCutterSpecific();
|
||||
for (auto &it : cutterSpecific.keys()) {
|
||||
m_data.push_back({it, optionInfoMap[it].displayingtext, cutterSpecific[it]});
|
||||
}
|
||||
|
||||
qobject_cast<PreferencesListView *>(parent())->setStandardColors();
|
||||
}
|
@ -1,130 +0,0 @@
|
||||
#ifndef COLORSCHEMEPREFWIDGET_H
|
||||
#define COLORSCHEMEPREFWIDGET_H
|
||||
|
||||
#include <QFrame>
|
||||
#include <QListView>
|
||||
#include <QJsonObject>
|
||||
#include <QAbstractListModel>
|
||||
#include <QStyledItemDelegate>
|
||||
|
||||
namespace Ui {
|
||||
class ColorSchemePrefWidget;
|
||||
}
|
||||
|
||||
class ColorSchemePrefWidget : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
ColorSchemePrefWidget(QWidget *parent = nullptr);
|
||||
virtual ~ColorSchemePrefWidget();
|
||||
|
||||
public slots:
|
||||
void apply();
|
||||
|
||||
void updateSchemeFromConfig();
|
||||
|
||||
private slots:
|
||||
void newColor();
|
||||
|
||||
void indexChanged(const QModelIndex &ni);
|
||||
|
||||
private:
|
||||
Ui::ColorSchemePrefWidget *ui;
|
||||
bool isEditable;
|
||||
};
|
||||
|
||||
//===============SERVICE STUFF BELOW===============
|
||||
|
||||
class ColorViewButton : public QFrame
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
ColorViewButton(QWidget *parent = nullptr);
|
||||
virtual ~ColorViewButton() override {}
|
||||
|
||||
public slots:
|
||||
void setColor(const QColor &c);
|
||||
|
||||
protected slots:
|
||||
void mouseReleaseEvent(QMouseEvent *event) override;
|
||||
|
||||
signals:
|
||||
void clicked();
|
||||
};
|
||||
|
||||
struct ColorOption {
|
||||
QString optionName;
|
||||
QString displayingText;
|
||||
QColor color;
|
||||
};
|
||||
Q_DECLARE_METATYPE(ColorOption);
|
||||
|
||||
|
||||
class ColorSettingsModel : public QAbstractListModel
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
ColorSettingsModel(QObject *parent = nullptr);
|
||||
virtual ~ColorSettingsModel() override {}
|
||||
|
||||
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
|
||||
|
||||
void setColor(const QString &option, const QColor &color);
|
||||
|
||||
QColor getBackroundColor() const;
|
||||
|
||||
QColor getTextColor() const;
|
||||
|
||||
int rowCount(const QModelIndex &parent = QModelIndex()) const override
|
||||
{
|
||||
Q_UNUSED(parent)
|
||||
return m_data.size();
|
||||
}
|
||||
|
||||
void updateScheme();
|
||||
|
||||
private:
|
||||
QList<ColorOption> m_data;
|
||||
};
|
||||
|
||||
class ColorOptionDelegate : public QStyledItemDelegate
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
ColorOptionDelegate(QObject *parent = nullptr) : QStyledItemDelegate (parent) {}
|
||||
~ColorOptionDelegate() override {}
|
||||
|
||||
void paint(QPainter *painter,
|
||||
const QStyleOptionViewItem &option,
|
||||
const QModelIndex &index) const override;
|
||||
|
||||
void setBackgroundColor(const QColor &c);
|
||||
|
||||
void setTextColor(const QColor &c);
|
||||
|
||||
private:
|
||||
QColor backgroundColor;
|
||||
QColor textColor;
|
||||
};
|
||||
|
||||
class PreferencesListView : public QListView
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
PreferencesListView(QWidget *parent = nullptr);
|
||||
virtual ~PreferencesListView() override {}
|
||||
|
||||
void setStandardColors();
|
||||
|
||||
protected slots:
|
||||
void currentChanged(const QModelIndex ¤t,
|
||||
const QModelIndex &previous) override;
|
||||
|
||||
signals:
|
||||
void indexChanged(const QModelIndex &ni);
|
||||
|
||||
private:
|
||||
ColorOptionDelegate *delegate;
|
||||
};
|
||||
|
||||
#endif // COLORSCHEMEPREFWIDGET_H
|
@ -1,101 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>ColorSchemePrefWidget</class>
|
||||
<widget class="QWidget" name="ColorSchemePrefWidget">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>670</width>
|
||||
<height>306</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Form</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_3">
|
||||
<item>
|
||||
<widget class="PreferencesListView" name="preferencesListView"/>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
||||
<item>
|
||||
<widget class="ColorViewButton" name="colorViewFore">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="frameShape">
|
||||
<enum>QFrame::StyledPanel</enum>
|
||||
</property>
|
||||
<property name="frameShadow">
|
||||
<enum>QFrame::Raised</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="setDefFore">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Set Default</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="infoBoard">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<customwidgets>
|
||||
<customwidget>
|
||||
<class>PreferencesListView</class>
|
||||
<extends>QListView</extends>
|
||||
<header>widgets/ColorSchemePrefWidget.h</header>
|
||||
</customwidget>
|
||||
<customwidget>
|
||||
<class>ColorViewButton</class>
|
||||
<extends>QFrame</extends>
|
||||
<header>widgets/ColorSchemePrefWidget.h</header>
|
||||
<container>1</container>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
89
src/widgets/ColorThemeComboBox.cpp
Normal file
@ -0,0 +1,89 @@
|
||||
#include "ColorThemeComboBox.h"
|
||||
|
||||
#include "core/Cutter.h"
|
||||
|
||||
#include "common/ColorThemeWorker.h"
|
||||
#include "common/Configuration.h"
|
||||
|
||||
/* Map with names of themes associated with its color palette
|
||||
* (Dark or Light), so for dark interface themes will be shown only Dark color themes
|
||||
* and for light - only light ones.
|
||||
*/
|
||||
static const QHash<QString, ColorFlags> kRelevantThemes = {
|
||||
{ "ayu", DarkFlag },
|
||||
{ "consonance", DarkFlag },
|
||||
{ "darkda", DarkFlag },
|
||||
{ "onedark", DarkFlag },
|
||||
{ "solarized", DarkFlag },
|
||||
{ "zenburn", DarkFlag },
|
||||
{ "cutter", LightFlag },
|
||||
{ "dark", LightFlag },
|
||||
{ "matrix", LightFlag },
|
||||
{ "tango", LightFlag },
|
||||
{ "white", LightFlag }
|
||||
};
|
||||
|
||||
ColorThemeComboBox::ColorThemeComboBox(QWidget *parent) : QComboBox(parent), showOnlyCustom(false)
|
||||
{
|
||||
connect(this, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged),
|
||||
this, &ColorThemeComboBox::onCurrentIndexChanged);
|
||||
updateFromConfig();
|
||||
}
|
||||
|
||||
void ColorThemeComboBox::updateFromConfig(bool interfaceThemeChanged)
|
||||
{
|
||||
QSignalBlocker signalBlockerColorBox(this);
|
||||
|
||||
const int curInterfaceThemeIndex = Config()->getInterfaceTheme();
|
||||
const QList<QString> themes(showOnlyCustom
|
||||
? ThemeWorker().customThemes()
|
||||
: Core()->getColorThemes());
|
||||
|
||||
clear();
|
||||
for (const QString &theme : themes) {
|
||||
if (ThemeWorker().isCustomTheme(theme) ||
|
||||
(kCutterInterfaceThemesList[curInterfaceThemeIndex].flag & kRelevantThemes[theme])) {
|
||||
addItem(theme);
|
||||
}
|
||||
}
|
||||
|
||||
QString curTheme = interfaceThemeChanged
|
||||
? Config()->getLastThemeOf(kCutterInterfaceThemesList[curInterfaceThemeIndex])
|
||||
: Config()->getColorTheme();
|
||||
const int index = findText(curTheme);
|
||||
|
||||
setCurrentIndex(index == -1 ? 0 : index);
|
||||
if (interfaceThemeChanged || index == -1) {
|
||||
curTheme = currentText();
|
||||
Config()->setColorTheme(curTheme);
|
||||
}
|
||||
int maxThemeLen = 0;
|
||||
for (const QString &str : themes) {
|
||||
int strLen = str.length();
|
||||
if (strLen > maxThemeLen) {
|
||||
maxThemeLen = strLen;
|
||||
}
|
||||
}
|
||||
setMinimumContentsLength(maxThemeLen);
|
||||
setSizeAdjustPolicy(QComboBox::AdjustToMinimumContentsLength);
|
||||
}
|
||||
|
||||
void ColorThemeComboBox::onCurrentIndexChanged(int index)
|
||||
{
|
||||
QString theme = itemText(index);
|
||||
|
||||
int curQtThemeIndex = Config()->getInterfaceTheme();
|
||||
if (curQtThemeIndex >= kCutterInterfaceThemesList.size()) {
|
||||
curQtThemeIndex = 0;
|
||||
Config()->setInterfaceTheme(curQtThemeIndex);
|
||||
}
|
||||
|
||||
Config()->setLastThemeOf(kCutterInterfaceThemesList[curQtThemeIndex], theme);
|
||||
Config()->setColorTheme(theme);
|
||||
}
|
||||
|
||||
void ColorThemeComboBox::setShowOnlyCustom(bool value)
|
||||
{
|
||||
showOnlyCustom = value;
|
||||
updateFromConfig();
|
||||
}
|
37
src/widgets/ColorThemeComboBox.h
Normal file
@ -0,0 +1,37 @@
|
||||
#ifndef COLORTHEMECOMBOBOX_H
|
||||
#define COLORTHEMECOMBOBOX_H
|
||||
|
||||
#include <QComboBox>
|
||||
|
||||
/**
|
||||
* @brief The ColorThemeComboBox class provides combobox with Cutter color themes.
|
||||
*/
|
||||
class ColorThemeComboBox : public QComboBox
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit ColorThemeComboBox(QWidget *parent = nullptr);
|
||||
|
||||
/**
|
||||
* @brief setShowOnlyCustom sets whether or not combobox should contain only
|
||||
* custom themes (created by user or imported) or custom and srandard radare2 themes.
|
||||
*/
|
||||
void setShowOnlyCustom(bool value);
|
||||
|
||||
public slots:
|
||||
/**
|
||||
* @brief updateFromConfig updates list of themes to be shown.
|
||||
* @param interfaceThemeChanged should be set to true if the interface theme of Cutter was changed
|
||||
* since the last call to the function. This will preserve the selected item in the combo box.
|
||||
*
|
||||
*/
|
||||
void updateFromConfig(bool interfaceThemeChanged = false);
|
||||
|
||||
private slots:
|
||||
void onCurrentIndexChanged(int index);
|
||||
|
||||
private:
|
||||
bool showOnlyCustom;
|
||||
};
|
||||
|
||||
#endif // COLORTHEMECOMBOBOX_H
|
823
src/widgets/ColorThemeListView.cpp
Normal file
@ -0,0 +1,823 @@
|
||||
#include <QDebug>
|
||||
#include <QJsonObject>
|
||||
#include <QJsonArray>
|
||||
#include <QMap>
|
||||
#include <QPainter>
|
||||
#include <QFontMetrics>
|
||||
#include <QScreen>
|
||||
#include <QJsonArray>
|
||||
#include <QScrollBar>
|
||||
#include <QApplication>
|
||||
#include <QSvgRenderer>
|
||||
#include <QMouseEvent>
|
||||
|
||||
#include "common/Configuration.h"
|
||||
#include "common/ColorThemeWorker.h"
|
||||
|
||||
#include "widgets/ColorThemeListView.h"
|
||||
|
||||
|
||||
struct OptionInfo {
|
||||
QString info;
|
||||
QString displayingtext;
|
||||
};
|
||||
|
||||
extern const QMap<QString, OptionInfo> optionInfoMap__;
|
||||
|
||||
ColorOptionDelegate::ColorOptionDelegate(QObject* parent) : QStyledItemDelegate (parent)
|
||||
{
|
||||
resetButtonPixmap = getPixmapFromSvg(":/img/icons/reset.svg", qApp->palette().text().color());
|
||||
connect(qApp, &QGuiApplication::paletteChanged, this,
|
||||
[this](const QPalette &p){
|
||||
resetButtonPixmap = getPixmapFromSvg(":/img/icons/reset.svg", qApp->palette().text().color());
|
||||
});
|
||||
}
|
||||
|
||||
void ColorOptionDelegate::paint(QPainter *painter,
|
||||
const QStyleOptionViewItem &option,
|
||||
const QModelIndex &index) const
|
||||
{
|
||||
int margin = this->margin * qApp->screenAt(option.rect.topLeft())->devicePixelRatio();
|
||||
painter->save();
|
||||
painter->setFont(option.font);
|
||||
painter->setRenderHint(QPainter::Antialiasing);
|
||||
|
||||
ColorOption currCO = index.data(Qt::UserRole).value<ColorOption>();
|
||||
|
||||
int penWidth = painter->pen().width();
|
||||
int fontHeight = painter->fontMetrics().height();
|
||||
QPoint tl = option.rect.topLeft();
|
||||
|
||||
QRect optionNameRect;
|
||||
optionNameRect.setTopLeft(tl + QPoint(margin, penWidth));
|
||||
optionNameRect.setWidth(option.rect.width() - margin * 2);
|
||||
optionNameRect.setHeight(fontHeight);
|
||||
|
||||
QRect optionRect;
|
||||
optionRect.setTopLeft(optionNameRect.bottomLeft() + QPoint(margin / 2, margin / 2));
|
||||
optionRect.setWidth(option.rect.width() - (optionRect.topLeft() - tl).x() * 2);
|
||||
optionRect.setHeight(option.rect.height() - (optionRect.topLeft() - tl).y() - margin);
|
||||
|
||||
QRect colorRect;
|
||||
colorRect.setTopLeft(optionRect.topLeft() + QPoint(margin / 4, margin / 4));
|
||||
colorRect.setBottom(optionRect.bottom() - margin / 4);
|
||||
colorRect.setWidth(colorRect.height());
|
||||
|
||||
QRect descTextRect;
|
||||
descTextRect.setTopLeft(colorRect.topRight() + QPoint(margin, colorRect.height() / 2 - fontHeight / 2));
|
||||
descTextRect.setWidth(optionRect.width() - (descTextRect.left() - optionRect.left()) - margin);
|
||||
descTextRect.setHeight(fontHeight);
|
||||
|
||||
bool paintResetButton = false;
|
||||
QRect resetButtonRect;
|
||||
|
||||
if (option.state & (QStyle::State_Selected | QStyle::State_MouseOver)) {
|
||||
QBrush br;
|
||||
QPen pen;
|
||||
if (option.state.testFlag(QStyle::State_Selected)) {
|
||||
QColor c = qApp->palette().highlight().color();
|
||||
c.setAlphaF(0.4);
|
||||
br = c;
|
||||
pen = QPen(qApp->palette().highlight().color(), margin / 2);
|
||||
if (currCO.changed) {
|
||||
paintResetButton = true;
|
||||
descTextRect.setWidth(descTextRect.width() - descTextRect.height() - margin / 2);
|
||||
resetButtonRect.setTopLeft(descTextRect.topRight() + QPoint(margin, 0));
|
||||
resetButtonRect.setWidth(descTextRect.height());
|
||||
resetButtonRect.setHeight(descTextRect.height());
|
||||
resetButtonRect.setSize(resetButtonRect.size() * 1.0);
|
||||
}
|
||||
} else {
|
||||
QColor c = qApp->palette().placeholderText().color();
|
||||
c.setAlphaF(0.2);
|
||||
br = c;
|
||||
pen = QPen(qApp->palette().placeholderText().color().darker(), margin / 2);
|
||||
}
|
||||
|
||||
painter->fillRect(option.rect, br);
|
||||
|
||||
painter->setPen(pen);
|
||||
int pw = painter->pen().width() / 2;
|
||||
QPoint top = option.rect.topLeft() + QPoint(pw, pw);
|
||||
QPoint bottom = option.rect.bottomLeft() - QPoint(-pw, pw - 1);
|
||||
painter->drawLine(top, bottom);
|
||||
}
|
||||
|
||||
if (paintResetButton) {
|
||||
painter->drawPixmap(resetButtonRect, resetButtonPixmap);
|
||||
auto self = const_cast<ColorOptionDelegate*>(this);
|
||||
self->resetButtonRect = resetButtonRect;
|
||||
}
|
||||
if (option.rect.contains(this->resetButtonRect) && this->resetButtonRect != resetButtonRect) {
|
||||
auto self = const_cast<ColorOptionDelegate*>(this);
|
||||
self->resetButtonRect = QRect(0,0,0,0);
|
||||
}
|
||||
|
||||
painter->setPen(qApp->palette().text().color());
|
||||
|
||||
QString name = painter->fontMetrics().elidedText(
|
||||
optionInfoMap__[currCO.optionName].displayingtext,
|
||||
Qt::ElideRight, optionNameRect.width());
|
||||
painter->drawText(optionNameRect, name);
|
||||
|
||||
QPainterPath roundedOptionRect;
|
||||
roundedOptionRect.addRoundedRect(optionRect, fontHeight / 4, fontHeight / 4);
|
||||
painter->setPen(qApp->palette().text().color());
|
||||
painter->drawPath(roundedOptionRect);
|
||||
|
||||
QPainterPath roundedColorRect;
|
||||
roundedColorRect.addRoundedRect(colorRect, fontHeight / 4, fontHeight / 4);
|
||||
painter->setPen(Qt::NoPen);
|
||||
painter->fillPath(roundedColorRect, currCO.color);
|
||||
|
||||
QString desc = painter->fontMetrics().elidedText(
|
||||
currCO.optionName + ": " +
|
||||
optionInfoMap__[currCO.optionName].info, Qt::ElideRight,
|
||||
descTextRect.width());
|
||||
painter->setPen(qApp->palette().text().color());
|
||||
painter->setBrush(qApp->palette().text());
|
||||
painter->drawText(descTextRect, desc);
|
||||
|
||||
painter->restore();
|
||||
}
|
||||
|
||||
QSize ColorOptionDelegate::sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const
|
||||
{
|
||||
int margin = this->margin * qApp->screenAt(option.rect.topLeft())->devicePixelRatio();
|
||||
int fontHeight = option.fontMetrics.height();
|
||||
int h = QPen().width();
|
||||
h += fontHeight; // option name
|
||||
h += margin / 2; // margin between option rect and option name
|
||||
h += margin / 4; // margin betveen option rect and color rect
|
||||
h += fontHeight; // color rect
|
||||
h += margin / 4; // margin betveen option rect and color rect
|
||||
h += margin; // last margin
|
||||
|
||||
Q_UNUSED(index)
|
||||
return QSize(-1, h);
|
||||
}
|
||||
|
||||
QRect ColorOptionDelegate::getResetButtonRect() const
|
||||
{
|
||||
return resetButtonRect;
|
||||
}
|
||||
|
||||
QPixmap ColorOptionDelegate::getPixmapFromSvg(const QString& fileName, const QColor& after) const
|
||||
{
|
||||
QFile file(fileName);
|
||||
if (!file.open(QIODevice::ReadOnly)) {
|
||||
return QPixmap();
|
||||
}
|
||||
QString data = file.readAll();
|
||||
data.replace(QRegExp("#[0-9a-fA-F]{6}"), QString("%1").arg(after.name()));
|
||||
|
||||
QSvgRenderer svgRenderer(data.toUtf8());
|
||||
QPixmap pix(QSize(qApp->fontMetrics().height(), qApp->fontMetrics().height()));
|
||||
pix.fill(Qt::transparent);
|
||||
|
||||
QPainter pixPainter(&pix);
|
||||
svgRenderer.render(&pixPainter);
|
||||
|
||||
return pix;
|
||||
}
|
||||
|
||||
ColorThemeListView::ColorThemeListView(QWidget *parent) :
|
||||
QListView (parent)
|
||||
{
|
||||
setModel(new ColorSettingsModel(static_cast<QObject *>(this)));
|
||||
static_cast<ColorSettingsModel *>(this->model())->updateTheme();
|
||||
setItemDelegate(new ColorOptionDelegate(this));
|
||||
|
||||
QJsonArray rgb = qobject_cast<ColorSettingsModel*>(model())->getTheme()
|
||||
.object().find("gui.background").value().toArray();
|
||||
if (rgb.size() == 3) {
|
||||
backgroundColor = QColor(rgb[0].toInt(), rgb[1].toInt(), rgb[2].toInt());
|
||||
} else {
|
||||
backgroundColor = palette().base().color();
|
||||
}
|
||||
|
||||
connect(&blinkTimer, &QTimer::timeout, this, &ColorThemeListView::blinkTimeout);
|
||||
|
||||
blinkTimer.setInterval(400);
|
||||
blinkTimer.start();
|
||||
|
||||
setMouseTracking(true);
|
||||
}
|
||||
|
||||
void ColorThemeListView::currentChanged(const QModelIndex ¤t,
|
||||
const QModelIndex &previous)
|
||||
{
|
||||
ColorOption prev = previous.data(Qt::UserRole).value<ColorOption>();
|
||||
Config()->setColor(prev.optionName, prev.color);
|
||||
if (ThemeWorker().radare2SpecificOptions.contains(prev.optionName)) {
|
||||
Core()->cmd(QString("ec %1 %2").arg(prev.optionName).arg(prev.color.name()));
|
||||
}
|
||||
|
||||
QListView::currentChanged(current, previous);
|
||||
emit itemChanged(current.data(Qt::UserRole).value<ColorOption>().color);
|
||||
}
|
||||
|
||||
void ColorThemeListView::dataChanged(const QModelIndex& topLeft, const QModelIndex& bottomRight,
|
||||
const QVector<int>& roles)
|
||||
{
|
||||
ColorOption curr = topLeft.data(Qt::UserRole).value<ColorOption>();
|
||||
if (curr.optionName == "gui.background") {
|
||||
backgroundColor = curr.color;
|
||||
}
|
||||
QListView::dataChanged(topLeft, bottomRight, roles);
|
||||
emit itemChanged(curr.color);
|
||||
emit dataChanged(curr);
|
||||
}
|
||||
|
||||
void ColorThemeListView::mouseReleaseEvent(QMouseEvent* e)
|
||||
{
|
||||
if (qobject_cast<ColorOptionDelegate*>(itemDelegate())->getResetButtonRect().contains(e->pos())) {
|
||||
auto model = qobject_cast<ColorSettingsModel*>(this->model());
|
||||
ColorOption co = currentIndex().data(Qt::UserRole).value<ColorOption>();
|
||||
co.changed = false;
|
||||
QJsonArray rgb = ThemeWorker().getTheme(Config()->getColorTheme())[co.optionName].toArray();
|
||||
co.color = QColor(rgb[0].toInt(), rgb[1].toInt(), rgb[2].toInt());
|
||||
model->setData(currentIndex(), QVariant::fromValue(co));
|
||||
QCursor c;
|
||||
c.setShape(Qt::CursorShape::ArrowCursor);
|
||||
setCursor(c);
|
||||
}
|
||||
}
|
||||
|
||||
void ColorThemeListView::mouseMoveEvent(QMouseEvent* e)
|
||||
{
|
||||
if (qobject_cast<ColorOptionDelegate*>(itemDelegate())->getResetButtonRect().contains(e->pos())) {
|
||||
QCursor c;
|
||||
c.setShape(Qt::CursorShape::PointingHandCursor);
|
||||
setCursor(c);
|
||||
} else if (cursor().shape() == Qt::CursorShape::PointingHandCursor) {
|
||||
QCursor c;
|
||||
c.setShape(Qt::CursorShape::ArrowCursor);
|
||||
setCursor(c);
|
||||
}
|
||||
}
|
||||
|
||||
void ColorThemeListView::blinkTimeout()
|
||||
{
|
||||
static enum { Normal, Invisible } state = Normal;
|
||||
state = state == Normal ? Invisible : Normal;
|
||||
backgroundColor.setAlphaF(1);
|
||||
|
||||
auto updateColor = [](const QString &name, const QColor &color) {
|
||||
Config()->setColor(name, color);
|
||||
if (ThemeWorker().radare2SpecificOptions.contains(name)) {
|
||||
Core()->cmd(QString("ec %1 %2").arg(name).arg(color.name()));
|
||||
}
|
||||
};
|
||||
|
||||
ColorOption curr = currentIndex().data(Qt::UserRole).value<ColorOption>();
|
||||
switch (state) {
|
||||
case Normal:
|
||||
updateColor(curr.optionName, curr.color);
|
||||
break;
|
||||
case Invisible:
|
||||
updateColor(curr.optionName, backgroundColor);
|
||||
break;
|
||||
}
|
||||
emit blink();
|
||||
}
|
||||
|
||||
ColorSettingsModel::ColorSettingsModel(QObject *parent) : QAbstractListModel (parent) { }
|
||||
|
||||
QVariant ColorSettingsModel::data(const QModelIndex &index, int role) const
|
||||
{
|
||||
if (!index.isValid()) {
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
if (index.row() < 0 || index.row() >= theme.size()) {
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
if (role == Qt::DisplayRole) {
|
||||
return QVariant::fromValue(optionInfoMap__[theme.at(index.row()).optionName].displayingtext);
|
||||
}
|
||||
|
||||
if (role == Qt::UserRole) {
|
||||
return QVariant::fromValue(theme.at(index.row()));
|
||||
}
|
||||
|
||||
if (role == Qt::ToolTipRole) {
|
||||
return QVariant::fromValue(optionInfoMap__[theme.at(index.row()).optionName].info);
|
||||
}
|
||||
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
bool ColorSettingsModel::setData(const QModelIndex& index, const QVariant& value, int role)
|
||||
{
|
||||
if (!index.isValid() || role != Qt::EditRole) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ColorOption currOpt = value.value<ColorOption>();
|
||||
theme[index.row()] = currOpt;
|
||||
emit dataChanged(index, index);
|
||||
return true;
|
||||
}
|
||||
|
||||
void ColorSettingsModel::updateTheme()
|
||||
{
|
||||
theme.clear();
|
||||
QJsonObject obj = ThemeWorker().getTheme(Config()->getColorTheme()).object();
|
||||
|
||||
for (auto it = obj.constBegin(); it != obj.constEnd(); it++) {
|
||||
QJsonArray rgb = it.value().toArray();
|
||||
if (rgb.size() != 3) {
|
||||
continue;
|
||||
}
|
||||
theme.push_back({it.key(), QColor(rgb[0].toInt(), rgb[1].toInt(), rgb[2].toInt()), false});
|
||||
}
|
||||
|
||||
if (!theme.isEmpty()) {
|
||||
dataChanged(index(0), index(theme.size() - 1));
|
||||
}
|
||||
}
|
||||
|
||||
QJsonDocument ColorSettingsModel::getTheme() const
|
||||
{
|
||||
QJsonObject obj;
|
||||
int r, g, b;
|
||||
for (auto &it : theme) {
|
||||
it.color.getRgb(&r, &g, &b);
|
||||
obj.insert(it.optionName, QJsonArray({r, g, b}));
|
||||
}
|
||||
return QJsonDocument(obj);
|
||||
}
|
||||
|
||||
const QMap<QString, OptionInfo> optionInfoMap__ = {
|
||||
{
|
||||
"comment", {
|
||||
QObject::tr("Color of comment generated by radare2"),
|
||||
QObject::tr("Comment")
|
||||
}
|
||||
},
|
||||
{
|
||||
"usrcmt", {
|
||||
QObject::tr("Comment created by user"),
|
||||
QObject::tr("Color of user Comment")
|
||||
}
|
||||
},
|
||||
{
|
||||
"args", {
|
||||
"",
|
||||
"args"
|
||||
}
|
||||
},
|
||||
{
|
||||
"fname", {
|
||||
QObject::tr("Color of names of functions"),
|
||||
QObject::tr("Function name")
|
||||
}
|
||||
},
|
||||
{
|
||||
"floc", {
|
||||
QObject::tr("Color of function location"),
|
||||
QObject::tr("Function location")
|
||||
}
|
||||
},
|
||||
{
|
||||
"fline", {
|
||||
QObject::tr("Color of ascii line in left side that shows what opcodes are belong to function"),
|
||||
QObject::tr("Function line")
|
||||
}
|
||||
},
|
||||
{
|
||||
"flag", {
|
||||
QObject::tr("Color of flags (similar to bookmarks for offset)"),
|
||||
QObject::tr("Flag")
|
||||
}
|
||||
},
|
||||
{
|
||||
"label", {
|
||||
"",
|
||||
QObject::tr("Label")
|
||||
}
|
||||
},
|
||||
{
|
||||
"help", {
|
||||
"",
|
||||
QObject::tr("Help")
|
||||
}
|
||||
},
|
||||
{
|
||||
"flow", {
|
||||
QObject::tr("Color of lines showing jump destination"),
|
||||
QObject::tr("Flow")
|
||||
}
|
||||
},
|
||||
{
|
||||
"flow2", {
|
||||
"",
|
||||
QObject::tr("flow2")
|
||||
}
|
||||
},
|
||||
{
|
||||
"prompt", {
|
||||
QObject::tr("Info"),
|
||||
QObject::tr("prompt")
|
||||
}
|
||||
},
|
||||
{
|
||||
"offset", {
|
||||
QObject::tr("Color of offsets"),
|
||||
QObject::tr("Offset")
|
||||
}
|
||||
},
|
||||
{
|
||||
"input", {
|
||||
QObject::tr("Info"),
|
||||
QObject::tr("input")
|
||||
}
|
||||
},
|
||||
{
|
||||
"invalid", {
|
||||
QObject::tr("Invalid opcode color"),
|
||||
QObject::tr("invalid")
|
||||
}
|
||||
},
|
||||
{
|
||||
"other", {
|
||||
"",
|
||||
QObject::tr("other")
|
||||
}
|
||||
},
|
||||
{
|
||||
"b0x00", {
|
||||
QObject::tr("0x00 opcode color"),
|
||||
"b0x00"
|
||||
}
|
||||
},
|
||||
{
|
||||
"b0x7f", {
|
||||
QObject::tr("0x7f opcode color"),
|
||||
"b0x7f"
|
||||
}
|
||||
},
|
||||
{
|
||||
"b0xff", {
|
||||
QObject::tr("0xff opcode color"),
|
||||
"b0xff"
|
||||
}
|
||||
},
|
||||
{
|
||||
"math", {
|
||||
QObject::tr("Color of arithmetic opcodes (add, div, mul etc)"),
|
||||
QObject::tr("Arithmetic")
|
||||
}
|
||||
},
|
||||
{
|
||||
"bin", {
|
||||
QObject::tr("Color of binary operations (and, or, xor etc)."),
|
||||
QObject::tr("Binary")
|
||||
}
|
||||
},
|
||||
{
|
||||
"btext", {
|
||||
QObject::tr("Color of object names, commas between operators, squared brackets and operators "
|
||||
"inside them."),
|
||||
QObject::tr("Text")
|
||||
}
|
||||
},
|
||||
{
|
||||
"push", {
|
||||
QObject::tr("push opcode color"),
|
||||
"push"
|
||||
}
|
||||
},
|
||||
{
|
||||
"pop", {
|
||||
QObject::tr("pop opcode color"),
|
||||
"pop"
|
||||
}
|
||||
},
|
||||
{
|
||||
"crypto", {
|
||||
QObject::tr("Cryptographic color"),
|
||||
"crypto"
|
||||
}
|
||||
},
|
||||
{
|
||||
"jmp", {
|
||||
QObject::tr("jmp instructions color"),
|
||||
"jmp"
|
||||
}
|
||||
},
|
||||
{
|
||||
"cjmp", {
|
||||
QObject::tr("Color of conditional jump opcodes such as je, jg, jne etc"),
|
||||
QObject::tr("Conditional jump")
|
||||
}
|
||||
},
|
||||
{
|
||||
"call", {
|
||||
QObject::tr("call instructions color (ccall, rcall, call etc)"),
|
||||
"call"
|
||||
}
|
||||
},
|
||||
{
|
||||
"nop", {
|
||||
QObject::tr("nop opcode color"),
|
||||
"nop"
|
||||
}
|
||||
},
|
||||
{
|
||||
"ret", {
|
||||
QObject::tr("ret opcode color"),
|
||||
"ret"
|
||||
}
|
||||
},
|
||||
{
|
||||
"trap", {
|
||||
QObject::tr("Color of interrupts"),
|
||||
QObject::tr("Interrupts")
|
||||
}
|
||||
},
|
||||
{
|
||||
"swi", {
|
||||
QObject::tr("swi opcode color"),
|
||||
"swi"
|
||||
}
|
||||
},
|
||||
{
|
||||
"cmp", {
|
||||
QObject::tr("Color of compare instructions such as test and cmp"),
|
||||
QObject::tr("Compare instructions")
|
||||
}
|
||||
},
|
||||
{
|
||||
"reg", {
|
||||
QObject::tr("Registers color"),
|
||||
QObject::tr("Register")
|
||||
}
|
||||
},
|
||||
{
|
||||
"creg", {
|
||||
"",
|
||||
"creg"
|
||||
}
|
||||
},
|
||||
{
|
||||
"num", {
|
||||
QObject::tr("Color of numeric constants and object pointers"),
|
||||
QObject::tr("Constants")
|
||||
}
|
||||
},
|
||||
{
|
||||
"mov", {
|
||||
QObject::tr("Color of move instructions such as mov, movd, lea etc"),
|
||||
QObject::tr("Move instructions")
|
||||
}
|
||||
},
|
||||
{
|
||||
"func_var", {
|
||||
QObject::tr("Function variable color"),
|
||||
QObject::tr("Function variable")
|
||||
}
|
||||
},
|
||||
{
|
||||
"func_var_type", {
|
||||
QObject::tr("Function variable (local or argument) type color"),
|
||||
QObject::tr("Variable type")
|
||||
}
|
||||
},
|
||||
{
|
||||
"func_var_addr", {
|
||||
QObject::tr("Function variable address color"),
|
||||
QObject::tr("Variable address")
|
||||
}
|
||||
},
|
||||
{
|
||||
"widget_bg", {
|
||||
"",
|
||||
"widget_bg"
|
||||
}
|
||||
},
|
||||
{
|
||||
"widget_sel", {
|
||||
"",
|
||||
"widget_sel"
|
||||
}
|
||||
},
|
||||
{
|
||||
"ai.read", {
|
||||
"",
|
||||
"ai.read"
|
||||
}
|
||||
},
|
||||
{
|
||||
"ai.write", {
|
||||
"",
|
||||
"ai.write"
|
||||
}
|
||||
},
|
||||
{
|
||||
"ai.exec", {
|
||||
"",
|
||||
"ai.exec"
|
||||
}
|
||||
},
|
||||
{
|
||||
"ai.seq", {
|
||||
"",
|
||||
"ai.seq"
|
||||
}
|
||||
},
|
||||
{
|
||||
"ai.ascii", {
|
||||
"",
|
||||
"ai.ascii"
|
||||
}
|
||||
},
|
||||
{
|
||||
"graph.box", {
|
||||
"",
|
||||
"graph.box"
|
||||
}
|
||||
},
|
||||
{
|
||||
"graph.box2", {
|
||||
"",
|
||||
"graph.box2"
|
||||
}
|
||||
},
|
||||
{
|
||||
"graph.box3", {
|
||||
"",
|
||||
"graph.box3"
|
||||
}
|
||||
},
|
||||
{
|
||||
"graph.box4", {
|
||||
"",
|
||||
"graph.box4"
|
||||
}
|
||||
},
|
||||
{
|
||||
"graph.true", {
|
||||
QObject::tr("In graph view jump arrow true"),
|
||||
QObject::tr("Arrow true")
|
||||
}
|
||||
},
|
||||
{
|
||||
"graph.false", {
|
||||
QObject::tr("In graph view jump arrow false"),
|
||||
QObject::tr("Arrow false")
|
||||
}
|
||||
},
|
||||
{
|
||||
"graph.trufae", {
|
||||
QObject::tr("In graph view jump arrow (no condition)"),
|
||||
QObject::tr("Arrow")
|
||||
}
|
||||
},
|
||||
{
|
||||
"graph.current", {
|
||||
"",
|
||||
"graph.current"
|
||||
}
|
||||
},
|
||||
{
|
||||
"graph.traced", {
|
||||
"",
|
||||
"graph.traced"
|
||||
}
|
||||
},
|
||||
{
|
||||
"gui.overview.node", {
|
||||
QObject::tr("Background color of Graph Overview's node"),
|
||||
QObject::tr("Graph Overview node")
|
||||
}
|
||||
},
|
||||
{
|
||||
"gui.cflow", {
|
||||
"",
|
||||
"gui.cflow"
|
||||
}
|
||||
},
|
||||
{
|
||||
"gui.dataoffset", {
|
||||
"",
|
||||
"gui.dataoffset"
|
||||
}
|
||||
},
|
||||
{
|
||||
"gui.background", {
|
||||
QObject::tr("General background color"),
|
||||
QObject::tr("Background")
|
||||
}
|
||||
},
|
||||
{
|
||||
"gui.alt_background", {
|
||||
QObject::tr("Background color of non-focused graph node"),
|
||||
QObject::tr("Node background")
|
||||
}
|
||||
},
|
||||
{
|
||||
"gui.disass_selected", {
|
||||
QObject::tr("Background of current graph node"),
|
||||
QObject::tr("Current graph node")
|
||||
}
|
||||
},
|
||||
{
|
||||
"gui.border", {
|
||||
QObject::tr("Color of node border in graph view"),
|
||||
QObject::tr("Node border")
|
||||
}
|
||||
},
|
||||
{
|
||||
"linehl", {
|
||||
QObject::tr("Selected line background color"),
|
||||
QObject::tr("Line highlight")
|
||||
}
|
||||
},
|
||||
{
|
||||
"wordhl", {
|
||||
QObject::tr("Background color of selected word"),
|
||||
QObject::tr("Word higlight")
|
||||
}
|
||||
},
|
||||
{
|
||||
"gui.main", {
|
||||
QObject::tr("Main function color"),
|
||||
QObject::tr("Main")
|
||||
}
|
||||
},
|
||||
{
|
||||
"gui.imports", {
|
||||
"",
|
||||
"gui.imports"
|
||||
}
|
||||
},
|
||||
{
|
||||
"highlightPC", {
|
||||
"",
|
||||
"highlightPC"
|
||||
}
|
||||
},
|
||||
{
|
||||
"gui.navbar.err", {
|
||||
"",
|
||||
"gui.navbar.err"
|
||||
}
|
||||
},
|
||||
{
|
||||
"gui.navbar.seek", {
|
||||
"",
|
||||
"gui.navbar.seek"
|
||||
}
|
||||
},
|
||||
{
|
||||
"angui.navbar.str", {
|
||||
"",
|
||||
"angui.navbar.str"
|
||||
}
|
||||
},
|
||||
{
|
||||
"gui.navbar.pc", {
|
||||
"",
|
||||
"gui.navbar.pc"
|
||||
}
|
||||
},
|
||||
{
|
||||
"gui.navbar.sym", {
|
||||
"",
|
||||
"gui.navbar.sym"
|
||||
}
|
||||
},
|
||||
{
|
||||
"gui.navbar.code", {
|
||||
QObject::tr("Code section color in navigation bar"),
|
||||
QObject::tr("Navbar code")
|
||||
}
|
||||
},
|
||||
{
|
||||
"gui.navbar.empty", {
|
||||
QObject::tr("Empty section color in navigation bar"),
|
||||
QObject::tr("Navbar empty")
|
||||
}
|
||||
},
|
||||
{
|
||||
"ucall", {
|
||||
"",
|
||||
QObject::tr("ucall")
|
||||
}
|
||||
},
|
||||
{
|
||||
"ujmp", {
|
||||
"",
|
||||
QObject::tr("ujmp")
|
||||
}
|
||||
},
|
||||
{
|
||||
"gui.breakpoint_background", {
|
||||
"",
|
||||
QObject::tr("Breakpoint background")
|
||||
}
|
||||
}
|
||||
};
|
101
src/widgets/ColorThemeListView.h
Normal file
@ -0,0 +1,101 @@
|
||||
#ifndef COLORTHEMELISTVIEW_H
|
||||
#define COLORTHEMELISTVIEW_H
|
||||
|
||||
#include <QTimer>
|
||||
#include <QListView>
|
||||
#include <QJsonObject>
|
||||
#include <QAbstractListModel>
|
||||
#include <QStyledItemDelegate>
|
||||
|
||||
struct ColorOption {
|
||||
QString optionName;
|
||||
QColor color;
|
||||
bool changed;
|
||||
};
|
||||
Q_DECLARE_METATYPE(ColorOption);
|
||||
|
||||
|
||||
class ColorThemeListView : public QListView
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
ColorThemeListView(QWidget *parent = nullptr);
|
||||
virtual ~ColorThemeListView() override {}
|
||||
|
||||
protected slots:
|
||||
void currentChanged(const QModelIndex ¤t,
|
||||
const QModelIndex &previous) override;
|
||||
|
||||
void dataChanged(const QModelIndex & topLeft, const QModelIndex & bottomRight,
|
||||
const QVector<int> &roles = QVector<int>()) override;
|
||||
|
||||
void mouseReleaseEvent(QMouseEvent *e) override;
|
||||
|
||||
void mouseMoveEvent(QMouseEvent *e) override;
|
||||
|
||||
private slots:
|
||||
void blinkTimeout();
|
||||
|
||||
signals:
|
||||
void itemChanged(const QColor &option);
|
||||
|
||||
void dataChanged(const ColorOption &data);
|
||||
|
||||
void blink();
|
||||
|
||||
private:
|
||||
QTimer blinkTimer;
|
||||
QColor backgroundColor;
|
||||
};
|
||||
|
||||
//==============================================
|
||||
|
||||
class ColorSettingsModel : public QAbstractListModel
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
ColorSettingsModel(QObject *parent = nullptr);
|
||||
virtual ~ColorSettingsModel() override {}
|
||||
|
||||
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
|
||||
|
||||
bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override;
|
||||
|
||||
int rowCount(const QModelIndex &parent = QModelIndex()) const override
|
||||
{
|
||||
Q_UNUSED(parent)
|
||||
return theme.size();
|
||||
}
|
||||
|
||||
void updateTheme();
|
||||
|
||||
QJsonDocument getTheme() const;
|
||||
|
||||
private:
|
||||
QList<ColorOption> theme;
|
||||
};
|
||||
|
||||
class ColorOptionDelegate : public QStyledItemDelegate
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
ColorOptionDelegate(QObject *parent = nullptr);
|
||||
~ColorOptionDelegate() override {}
|
||||
|
||||
void paint(QPainter *painter,
|
||||
const QStyleOptionViewItem &option,
|
||||
const QModelIndex &index) const override;
|
||||
|
||||
QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const override;
|
||||
|
||||
QRect getResetButtonRect() const;
|
||||
|
||||
private:
|
||||
const int margin = 12;
|
||||
QPixmap resetButtonPixmap;
|
||||
QRect resetButtonRect;
|
||||
|
||||
QPixmap getPixmapFromSvg(const QString& fileName, const QColor& after) const;
|
||||
};
|
||||
|
||||
#endif // COLORTHEMELISTVIEW_H
|
@ -8,10 +8,12 @@ CutterDockWidget::CutterDockWidget(MainWindow *parent, QAction *action) :
|
||||
QDockWidget(parent),
|
||||
action(action)
|
||||
{
|
||||
parent->addToDockWidgetList(this);
|
||||
if (action) {
|
||||
parent->addDockWidgetAction(this, action);
|
||||
connect(action, &QAction::triggered, this, &CutterDockWidget::toggleDockWidget);
|
||||
if (parent) {
|
||||
parent->addToDockWidgetList(this);
|
||||
if (action) {
|
||||
parent->addDockWidgetAction(this, action);
|
||||
connect(action, &QAction::triggered, this, &CutterDockWidget::toggleDockWidget);
|
||||
}
|
||||
}
|
||||
|
||||
// Install event filter to catch redraw widgets when needed
|
||||
|
@ -487,7 +487,7 @@ void DisassemblerGraphView::drawBlock(QPainter &p, GraphView::GraphBlock &block)
|
||||
highlightWidth = static_cast<int>(block.width - widthBefore - (10 + 4 * charWidth));
|
||||
}
|
||||
|
||||
QColor selectionColor = ConfigColor("highlightWord");
|
||||
QColor selectionColor = ConfigColor("wordhl");
|
||||
|
||||
p.fillRect(QRect(static_cast<int>(blockX + charWidth * 3 + widthBefore), y, highlightWidth,
|
||||
charHeight), selectionColor);
|
||||
@ -653,7 +653,7 @@ void DisassemblerGraphView::colorsUpdatedSlot()
|
||||
mDisabledBreakpointColor = disassemblyBackgroundColor;
|
||||
graphNodeColor = ConfigColor("gui.border");
|
||||
backgroundColor = ConfigColor("gui.background");
|
||||
disassemblySelectionColor = ConfigColor("highlight");
|
||||
disassemblySelectionColor = ConfigColor("linehl");
|
||||
PCSelectionColor = ConfigColor("highlightPC");
|
||||
|
||||
jmpColor = ConfigColor("graph.trufae");
|
||||
|
@ -99,15 +99,6 @@ DisassemblyWidget::DisassemblyWidget(MainWindow *main, QAction *action)
|
||||
this, SLOT(showDisasContextMenu(const QPoint &)));
|
||||
|
||||
|
||||
// Space to switch to graph
|
||||
QShortcut *graphShortcut = new QShortcut(QKeySequence(Qt::Key_Space), this);
|
||||
graphShortcut->setContext(Qt::WidgetWithChildrenShortcut);
|
||||
connect(graphShortcut, &QShortcut::activated, this, [] {
|
||||
Core()->setMemoryWidgetPriority(CutterCore::MemoryWidgetType::Graph);
|
||||
Core()->triggerRaisePrioritizedMemoryWidget();
|
||||
});
|
||||
|
||||
|
||||
connect(mDisasScrollArea, SIGNAL(scrollLines(int)), this, SLOT(scrollInstructions(int)));
|
||||
connect(mDisasScrollArea, SIGNAL(disassemblyResized()), this, SLOT(updateMaxLines()));
|
||||
|
||||
@ -146,46 +137,54 @@ DisassemblyWidget::DisassemblyWidget(MainWindow *main, QAction *action)
|
||||
connect(Core(), &CutterCore::refreshAll, this, [this]() {
|
||||
refreshDisasm(seekable->getOffset());
|
||||
});
|
||||
refreshDisasm(seekable->getOffset());
|
||||
|
||||
connect(mCtxMenu, SIGNAL(copy()), mDisasTextEdit, SLOT(copy()));
|
||||
|
||||
// Dirty
|
||||
QShortcut *shortcut_escape = new QShortcut(QKeySequence(Qt::Key_Escape), this);
|
||||
shortcut_escape->setContext(Qt::WidgetShortcut);
|
||||
connect(shortcut_escape, SIGNAL(activated()), this, SLOT(seekPrev()));
|
||||
|
||||
mCtxMenu->addSeparator();
|
||||
syncIt.setText(tr("Sync/unsync offset"));
|
||||
mCtxMenu->addAction(&syncIt);
|
||||
connect(&syncIt, SIGNAL(triggered(bool)), this, SLOT(toggleSync()));
|
||||
connect(seekable, &CutterSeekable::seekableSeekChanged, this, &DisassemblyWidget::on_seekChanged);
|
||||
|
||||
#define ADD_SHORTCUT(ksq, slot) { \
|
||||
QShortcut *s = new QShortcut((ksq), this); \
|
||||
s->setContext(Qt::WidgetShortcut); \
|
||||
connect(s, &QShortcut::activated, this, (slot)); \
|
||||
}
|
||||
ADD_SHORTCUT(QKeySequence(Qt::Key_J), [this]() {
|
||||
addActions(mCtxMenu->actions());
|
||||
|
||||
#define ADD_ACTION(ksq, ctx, slot) {\
|
||||
QAction *a = new QAction(this); \
|
||||
a->setShortcut(ksq); \
|
||||
a->setShortcutContext(ctx); \
|
||||
addAction(a); \
|
||||
connect(a, &QAction::triggered, this, (slot)); }
|
||||
|
||||
// Space to switch to graph
|
||||
ADD_ACTION(Qt::Key_Space, Qt::WidgetWithChildrenShortcut, [] {
|
||||
Core()->setMemoryWidgetPriority(CutterCore::MemoryWidgetType::Graph);
|
||||
Core()->triggerRaisePrioritizedMemoryWidget();
|
||||
})
|
||||
|
||||
ADD_ACTION(Qt::Key_Escape, Qt::WidgetWithChildrenShortcut, &DisassemblyWidget::seekPrev)
|
||||
|
||||
ADD_ACTION(Qt::Key_J, Qt::WidgetWithChildrenShortcut, [this]() {
|
||||
moveCursorRelative(false, false);
|
||||
})
|
||||
ADD_SHORTCUT(QKeySequence::MoveToNextLine, [this]() {
|
||||
ADD_ACTION(QKeySequence::MoveToNextLine, Qt::WidgetWithChildrenShortcut, [this]() {
|
||||
moveCursorRelative(false, false);
|
||||
})
|
||||
ADD_SHORTCUT(QKeySequence(Qt::Key_K), [this]() {
|
||||
ADD_ACTION(Qt::Key_K, Qt::WidgetWithChildrenShortcut, [this]() {
|
||||
moveCursorRelative(true, false);
|
||||
})
|
||||
ADD_SHORTCUT(QKeySequence::MoveToPreviousLine, [this]() {
|
||||
ADD_ACTION(QKeySequence::MoveToPreviousLine, Qt::WidgetWithChildrenShortcut, [this]() {
|
||||
moveCursorRelative(true, false);
|
||||
})
|
||||
ADD_SHORTCUT(QKeySequence::MoveToNextPage, [this]() {
|
||||
ADD_ACTION(QKeySequence::MoveToNextPage, Qt::WidgetWithChildrenShortcut, [this]() {
|
||||
moveCursorRelative(false, true);
|
||||
})
|
||||
ADD_SHORTCUT(QKeySequence::MoveToPreviousPage, [this]() {
|
||||
ADD_ACTION(QKeySequence::MoveToPreviousPage, Qt::WidgetWithChildrenShortcut, [this]() {
|
||||
moveCursorRelative(true, true);
|
||||
})
|
||||
ADD_SHORTCUT(QKeySequence(Qt::CTRL + Qt::Key_Plus), &DisassemblyWidget::zoomIn)
|
||||
ADD_SHORTCUT(QKeySequence(Qt::CTRL + Qt::Key_Minus), &DisassemblyWidget::zoomOut)
|
||||
#undef ADD_SHORTCUT
|
||||
ADD_ACTION(QKeySequence(Qt::CTRL + Qt::Key_Equal), Qt::WidgetWithChildrenShortcut, &DisassemblyWidget::zoomIn)
|
||||
ADD_ACTION(QKeySequence(Qt::CTRL + Qt::Key_Minus), Qt::WidgetWithChildrenShortcut, &DisassemblyWidget::zoomOut)
|
||||
#undef ADD_ACTION
|
||||
}
|
||||
|
||||
void DisassemblyWidget::toggleSync()
|
||||
@ -199,6 +198,26 @@ void DisassemblyWidget::toggleSync()
|
||||
}
|
||||
}
|
||||
|
||||
void DisassemblyWidget::setPreviewMode(bool previewMode)
|
||||
{
|
||||
mDisasTextEdit->setContextMenuPolicy(previewMode
|
||||
? Qt::NoContextMenu
|
||||
: Qt::CustomContextMenu);
|
||||
mCtxMenu->setEnabled(!previewMode);
|
||||
for (auto action : mCtxMenu->actions()) {
|
||||
action->setEnabled(!previewMode);
|
||||
}
|
||||
for (auto action : actions()) {
|
||||
if (action->shortcut() == Qt::Key_Space ||
|
||||
action->shortcut() == Qt::Key_Escape) {
|
||||
action->setEnabled(!previewMode);
|
||||
}
|
||||
}
|
||||
if (seekable->isSynchronized() && previewMode) {
|
||||
toggleSync();
|
||||
}
|
||||
}
|
||||
|
||||
QWidget *DisassemblyWidget::getTextWidget()
|
||||
{
|
||||
return mDisasTextEdit;
|
||||
@ -335,7 +354,7 @@ void DisassemblyWidget::highlightCurrentLine()
|
||||
{
|
||||
QList<QTextEdit::ExtraSelection> extraSelections;
|
||||
|
||||
QColor highlightColor = ConfigColor("highlight");
|
||||
QColor highlightColor = ConfigColor("linehl");
|
||||
QColor highlightPCColor = ConfigColor("highlightPC");
|
||||
|
||||
// Highlight the current word
|
||||
@ -581,13 +600,13 @@ QList<QTextEdit::ExtraSelection> DisassemblyWidget::getSameWordsSelections()
|
||||
QList<QTextEdit::ExtraSelection> selections;
|
||||
QTextEdit::ExtraSelection highlightSelection;
|
||||
QTextDocument *document = mDisasTextEdit->document();
|
||||
QColor highlightWordColor = ConfigColor("highlightWord");
|
||||
QColor highlightWordColor = ConfigColor("wordhl");
|
||||
|
||||
if (curHighlightedWord.isNull()) {
|
||||
return QList<QTextEdit::ExtraSelection>();
|
||||
}
|
||||
|
||||
highlightSelection.cursor = mDisasTextEdit->textCursor();;
|
||||
highlightSelection.cursor = mDisasTextEdit->textCursor();
|
||||
highlightSelection.cursor.movePosition(QTextCursor::Start, QTextCursor::MoveAnchor);
|
||||
|
||||
while (!highlightSelection.cursor.isNull() && !highlightSelection.cursor.atEnd()) {
|
||||
|
@ -30,8 +30,9 @@ public slots:
|
||||
void colorsUpdatedSlot();
|
||||
void seekPrev();
|
||||
void toggleSync();
|
||||
void setPreviewMode(bool previewMode);
|
||||
|
||||
private slots:
|
||||
protected slots:
|
||||
void on_seekChanged(RVA offset);
|
||||
void refreshDisasm(RVA offset = RVA_INVALID);
|
||||
|
||||
@ -43,11 +44,12 @@ private slots:
|
||||
void zoomIn();
|
||||
void zoomOut();
|
||||
|
||||
private:
|
||||
protected:
|
||||
DisassemblyContextMenu *mCtxMenu;
|
||||
DisassemblyScrollArea *mDisasScrollArea;
|
||||
DisassemblyTextEdit *mDisasTextEdit;
|
||||
|
||||
private:
|
||||
RVA topOffset;
|
||||
RVA bottomOffset;
|
||||
int maxLines;
|
||||
|