refactor theme edit feature (#1461)

Refactor Theme Editor
This commit is contained in:
optizone 2019-05-01 19:15:33 +03:00 committed by Itay Cohen
parent c534b1bce8
commit 089be2b87c
41 changed files with 3812 additions and 1263 deletions

View File

@ -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 \

View File

@ -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,

View File

@ -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 &copyThemeName) 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);
}

View File

@ -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 &copySchemeName) 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

View 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 &copyThemeName) 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;
}

View 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 &copyThemeName) 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

View File

@ -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();

View File

@ -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

View File

@ -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()

View File

@ -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()) {

View File

@ -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();

View File

@ -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;
}

View File

@ -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());
};

View File

@ -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>

View 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();
}

View 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

View 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>

View File

@ -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();
}

View 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

View 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
View 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
View 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

View 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
View 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

View 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

View File

@ -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);
}

View File

@ -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
View 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
View 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
View 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>

View File

@ -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 &current,
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();
}

View File

@ -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 &current,
const QModelIndex &previous) override;
signals:
void indexChanged(const QModelIndex &ni);
private:
ColorOptionDelegate *delegate;
};
#endif // COLORSCHEMEPREFWIDGET_H

View File

@ -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>

View 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();
}

View 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

View 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 &current,
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")
}
}
};

View 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 &current,
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

View File

@ -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

View File

@ -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");

View File

@ -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()) {

View File

@ -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;