diff --git a/src/Cutter.pro b/src/Cutter.pro index d7bc0f05..9f35771d 100644 --- a/src/Cutter.pro +++ b/src/Cutter.pro @@ -195,7 +195,9 @@ SOURCES += \ dialogs/AttachProcDialog.cpp \ widgets/RegisterRefsWidget.cpp \ dialogs/SetToDataDialog.cpp \ - dialogs/SetFunctionVarTypes.cpp + dialogs/SetFunctionVarTypes.cpp \ + widgets/ColorSchemePrefWidget.cpp \ + utils/ColorSchemeFileSaver.cpp HEADERS += \ Cutter.h \ @@ -291,7 +293,9 @@ HEADERS += \ widgets/RegisterRefsWidget.h \ dialogs/SetToDataDialog.h \ utils/InitialOptions.h \ - dialogs/SetFunctionVarTypes.h + dialogs/SetFunctionVarTypes.h \ + utils/ColorSchemeFileSaver.h \ + widgets/ColorSchemePrefWidget.h FORMS += \ dialogs/AboutDialog.ui \ @@ -346,7 +350,8 @@ FORMS += \ dialogs/AttachProcDialog.ui \ widgets/RegisterRefsWidget.ui \ dialogs/SetToDataDialog.ui \ - dialogs/SetFunctionVarTypes.ui + dialogs/SetFunctionVarTypes.ui \ + widgets/ColorSchemePrefWidget.ui RESOURCES += \ resources.qrc \ diff --git a/src/MainWindow.cpp b/src/MainWindow.cpp index 8918e9c1..f0fa2865 100644 --- a/src/MainWindow.cpp +++ b/src/MainWindow.cpp @@ -182,6 +182,9 @@ void MainWindow::initUI() this->visualNavbar->setMovable(false); addToolBarBreak(Qt::TopToolBarArea); addToolBar(visualNavbar); + QObject::connect(configuration, &Configuration::colorsUpdated, [this]() { + this->visualNavbar->updateGraphicsScene(); + }); /* * Dock Widgets diff --git a/src/dialogs/preferences/GeneralOptionsWidget.cpp b/src/dialogs/preferences/GeneralOptionsWidget.cpp index ae331d11..4c10bdbf 100644 --- a/src/dialogs/preferences/GeneralOptionsWidget.cpp +++ b/src/dialogs/preferences/GeneralOptionsWidget.cpp @@ -1,14 +1,19 @@ +#include #include #include +#include -#include "GeneralOptionsWidget.h" -#include "ui_GeneralOptionsWidget.h" #include #include "PreferencesDialog.h" +#include "GeneralOptionsWidget.h" +#include "ui_GeneralOptionsWidget.h" #include "utils/Helpers.h" #include "utils/Configuration.h" +#include "utils/ColorSchemeFileSaver.h" +#include "widgets/ColorSchemePrefWidget.h" + GeneralOptionsWidget::GeneralOptionsWidget(PreferencesDialog *dialog, QWidget *parent) : QDialog(parent), ui(new Ui::GeneralOptionsWidget) @@ -20,7 +25,11 @@ GeneralOptionsWidget::GeneralOptionsWidget(PreferencesDialog *dialog, QWidget *p updateThemeFromConfig(); connect(Config(), &Configuration::fontsUpdated, this, &GeneralOptionsWidget::updateFontFromConfig); - //connect(Config(), SIGNAL(colorsUpdated()), this, SLOT(updateThemeFromConfig())); + connect(ui.get()->colorComboBox, &QComboBox::currentTextChanged, [&](const QString & name) { + static_cast(ui.get()->colorSchemePrefWidget)->setNewScheme(name); + }); + static_cast + (ui.get()->colorSchemePrefWidget)->setNewScheme(Config()->getCurrentTheme()); } GeneralOptionsWidget::~GeneralOptionsWidget() {} @@ -63,7 +72,8 @@ void GeneralOptionsWidget::on_fontSelectionButton_clicked() { QFont currentFont = Config()->getFont(); bool ok; - QFont newFont = QFontDialog::getFont(&ok, currentFont, this, QString(), QFontDialog::DontUseNativeDialog); + QFont newFont = QFontDialog::getFont(&ok, currentFont, this, QString(), + QFontDialog::DontUseNativeDialog); if (ok) { Config()->setFont(newFont); } @@ -81,3 +91,26 @@ void GeneralOptionsWidget::on_colorComboBox_currentIndexChanged(int index) QString theme = ui->colorComboBox->itemText(index); Config()->setColorTheme(theme); } + +void GeneralOptionsWidget::on_copyButton_clicked() +{ + QString newSchemeName; + do { + newSchemeName = QInputDialog::getText(this, tr("Enter scheme name"), + tr("Name:"), QLineEdit::Normal, + QDir::home().dirName()); + } while (ColorSchemeFileWorker().isNameEngaged(newSchemeName) && !newSchemeName.isEmpty()); + + if (newSchemeName.isEmpty()) + return; + ColorSchemeFileWorker().copy(Config()->getCurrentTheme(), newSchemeName); + Config()->setColorTheme(newSchemeName); + ui.get()->colorSchemePrefWidget->setNewScheme(newSchemeName); + updateThemeFromConfig(); + ui.get()->colorComboBox->setCurrentIndex(ui.get()->colorComboBox->findText(newSchemeName)); +} + +void GeneralOptionsWidget::on_deleteButton_clicked() +{ + ColorSchemeFileWorker().deleteScheme(Config()->getCurrentTheme()); +} diff --git a/src/dialogs/preferences/GeneralOptionsWidget.h b/src/dialogs/preferences/GeneralOptionsWidget.h index 10ce6891..093d913e 100644 --- a/src/dialogs/preferences/GeneralOptionsWidget.h +++ b/src/dialogs/preferences/GeneralOptionsWidget.h @@ -32,6 +32,8 @@ private slots: void on_fontSelectionButton_clicked(); void on_themeComboBox_currentIndexChanged(int index); void on_colorComboBox_currentIndexChanged(int index); + void on_copyButton_clicked(); + void on_deleteButton_clicked(); }; diff --git a/src/dialogs/preferences/GeneralOptionsWidget.ui b/src/dialogs/preferences/GeneralOptionsWidget.ui index 368eb1f4..fd2fbf2f 100644 --- a/src/dialogs/preferences/GeneralOptionsWidget.ui +++ b/src/dialogs/preferences/GeneralOptionsWidget.ui @@ -7,7 +7,7 @@ 0 0 442 - 225 + 268 @@ -91,17 +91,57 @@ - - - - 0 - 0 - - - + + + + + + 0 + 0 + + + + + + + + + 0 + 0 + + + + Copy + + + + + + + Delete + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + @@ -109,6 +149,14 @@ + + + ColorSchemePrefWidget + QWidget +
widgets/ColorSchemePrefWidget.h
+ 1 +
+
diff --git a/src/utils/ColorSchemeFileSaver.cpp b/src/utils/ColorSchemeFileSaver.cpp new file mode 100644 index 00000000..ffe84953 --- /dev/null +++ b/src/utils/ColorSchemeFileSaver.cpp @@ -0,0 +1,174 @@ +#include "utils/ColorSchemeFileSaver.h" + +#include +#include +#include +#include +#include +#include +#include "utils/Configuration.h" + +static const QStringList cutterSpecificOptions = { + "gui.main", + "highlight", + "gui.imports", + "highlightPC", + "highlightWord", + "gui.navbar.err", + "gui.navbar.sym", + "gui.dataoffset", + "gui.navbar.code", + "gui.navbar.empty", + "angui.navbar.str", + "gui.disass_selected", + "gui.breakpoint_background" +}; + +ColorSchemeFileSaver::ColorSchemeFileSaver(QObject *parent) : QObject (parent) +{ + QDir currDir; + QStringList dirs = QStandardPaths::standardLocations(QStandardPaths::GenericDataLocation); + dirs.removeOne(QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation)); + + currDir = QDir(dirs.at(0)).filePath("radare2"); + // currDir.entryList(QDir::Dirs, QDir::Name) returns { current dir, upper dir, dirs ... } + // so it takes first (and only) dir using .at(2) + currDir = currDir.filePath(currDir.entryList(QDir::Dirs, + QDir::Name).at(2) + QDir::separator() + "cons"); + standardR2ThemesLocationPath = currDir.absolutePath(); + + customR2ThemesLocationPath = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + + QDir::separator() + + "radare2" + QDir::separator() + + "cons"; + if (!QDir(customR2ThemesLocationPath).exists()) { + QDir().mkpath(customR2ThemesLocationPath); + } +} + +QFile::FileError ColorSchemeFileSaver::copy(const QString &srcThemeName, + const QString ©ThemeName) const +{ + QFile fIn(standardR2ThemesLocationPath + QDir::separator() + srcThemeName); + QFile fOut(customR2ThemesLocationPath + QDir::separator() + copyThemeName); + + if (srcThemeName != "default" && !fIn.open(QFile::ReadOnly)) { + fIn.setFileName(customR2ThemesLocationPath + QDir::separator() + srcThemeName); + if (!fIn.open(QFile::ReadOnly)) { + return fIn.error(); + } + } + + if (!fOut.open(QFile::WriteOnly | QFile::Truncate)) { + fIn.close(); + return fOut.error(); + } + + QStringList options = Core()->cmdj("ecj").object().keys(); + options << cutterSpecificOptions; + QStringList src; + + if (srcThemeName == "default") { + QString theme = Config()->getCurrentTheme(); + Core()->cmd("ecd"); + QJsonObject _obj = Core()->cmdj("ecj").object(); + Core()->cmd(QString("eco %1").arg(theme)); + QColor back = Config()->getColor(standardBackgroundOptionName); + _obj[standardBackgroundOptionName] = QJsonArray({back.red(), back.green(), back.blue()}); + for (auto &it : _obj.keys()) { + QJsonArray rgb = _obj[it].toArray(); + src.push_back("ec " + it + " " + + QColor(rgb[0].toInt(), rgb[1].toInt(), rgb[2].toInt()).name().replace("#", "rgb:")); + } + } else { + src = QString(fIn.readAll()).split('\n'); + } + + for (auto &it : src) { + if (it.isEmpty()) { + continue; + } + fOut.write(it.toUtf8() + '\n'); + + if (it.length() > 2 && it.left(2) == "#~") { + options.removeOne(it.split(' ')[0].remove("#~").toUtf8()); + } else { + options.removeOne(it.split(' ').at(1)); + } + } + + for (auto &it : options) { + if (cutterSpecificOptions.contains(it)) { + fOut.write("#~"); + } else { + fOut.write("ec "); + } + fOut.write(QString(it + " rgb:%1\n"). + arg(Config()->getColor(it).name().remove("#")).toUtf8()); + } + + fOut.close(); + fIn.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 +{ + for (auto &it : QDir(customR2ThemesLocationPath).entryInfoList()) + if (it.fileName() == schemeName) + return true; + return false; +} + +bool ColorSchemeFileSaver::isNameEngaged(const QString &name) const +{ + return QFile::exists(standardR2ThemesLocationPath + QDir::separator() + name) || + QFile::exists(customR2ThemesLocationPath + QDir::separator() + name); +} + +QMap ColorSchemeFileSaver::getCutterSpecific() const +{ + QFile f(customR2ThemesLocationPath + QDir::separator() + Config()->getCurrentTheme()); + if (!f.open(QFile::ReadOnly)) + return QMap(); + + QMap ret; + QStringList data = QString(f.readAll()).split('\n'); + for (auto &it : data) { + if (it.length() > 2 && it.left(2) == "#~") { + QStringList currLine = it.split(' '); + ret.insert(currLine[0].remove("#~"), currLine[1].replace("rgb:", "#")); + } + } + + f.close(); + return ret; +} + +QStringList ColorSchemeFileSaver::getCustomSchemes() const +{ + QStringList sl; + sl = QDir(customR2ThemesLocationPath).entryList(QDir::Files, QDir::Name); + sl.removeOne("."); + sl.removeOne(".."); + return sl; +} + +void ColorSchemeFileSaver::deleteScheme(const QString &schemeName) const +{ + if (!isCustomScheme(schemeName)) + return; + QFile::remove(customR2ThemesLocationPath + QDir::separator() + schemeName); +} diff --git a/src/utils/ColorSchemeFileSaver.h b/src/utils/ColorSchemeFileSaver.h new file mode 100644 index 00000000..cd19048a --- /dev/null +++ b/src/utils/ColorSchemeFileSaver.h @@ -0,0 +1,47 @@ +#ifndef COLORSCHEMEFILESAVER_H +#define COLORSCHEMEFILESAVER_H + +#include +#include +#include + +constexpr const char *standardBackgroundOptionName = "gui.background"; +constexpr const char *standardTextOptionName = "btext"; +#define ColorSchemeFileWorker() (ColorSchemeFileSaver::instance()) + +class ColorSchemeFileSaver : public QObject +{ + Q_OBJECT +public: + static ColorSchemeFileSaver &instance() + { + static ColorSchemeFileSaver ex; + return ex; + } + + virtual ~ColorSchemeFileSaver() {} + + QFile::FileError copy(const QString &srcSchemeName, const QString ©SchemeName) const; + + QFile::FileError save(const QString &scheme, const QString &schemeName) const; + + bool isCustomScheme(const QString &schemeName) const; + + bool isNameEngaged(const QString &name) const; + + QMap 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 diff --git a/src/utils/Configuration.cpp b/src/utils/Configuration.cpp index ec026dec..d6de76dd 100644 --- a/src/utils/Configuration.cpp +++ b/src/utils/Configuration.cpp @@ -5,6 +5,8 @@ #include #include +#include "utils/ColorSchemeFileSaver.h" + Configuration *Configuration::mPtr = nullptr; /*! @@ -216,7 +218,7 @@ void Configuration::setFont(const QFont &font) void Configuration::setTheme(int theme) { s.setValue("ColorPalette", theme); - switch(theme){ + switch (theme) { case 0: loadDefaultTheme(); break; @@ -268,11 +270,18 @@ void Configuration::setColorTheme(QString theme) QJsonObject colorsObject = colors.object(); QJsonObject::iterator it; for (it = colorsObject.begin(); it != colorsObject.end(); it++) { - if (it.key().contains("gui")) - continue; QJsonArray rgb = it.value().toArray(); s.setValue("colors." + it.key(), QColor(rgb[0].toInt(), rgb[1].toInt(), rgb[2].toInt())); } + + QMap cutterSpecific = ColorSchemeFileWorker().getCutterSpecific(); + for (auto &it : cutterSpecific.keys()) + setColor(it, cutterSpecific[it]); + + if (!ColorSchemeFileWorker().isCustomScheme(theme)) { + setTheme(getTheme()); + } + emit colorsUpdated(); } @@ -294,7 +303,7 @@ QVariant Configuration::getConfigVar(const QString &key) { QHash::const_iterator it = asmOptions.find(key); if (it != asmOptions.end()) { - switch(it.value().type()) { + switch (it.value().type()) { case QVariant::Type::Bool: return Core()->getConfigb(key); case QVariant::Type::Int: diff --git a/src/utils/Configuration.h b/src/utils/Configuration.h index de07e0c1..6cbd0d5b 100644 --- a/src/utils/Configuration.h +++ b/src/utils/Configuration.h @@ -49,7 +49,7 @@ public: } QString getDirProjects(); - void setDirProjects(const QString& dir); + void setDirProjects(const QString &dir); void setNewFileLastClicked(int lastClicked); int getNewFileLastClicked(); diff --git a/src/widgets/ColorSchemePrefWidget.cpp b/src/widgets/ColorSchemePrefWidget.cpp new file mode 100644 index 00000000..3bd3f4a9 --- /dev/null +++ b/src/widgets/ColorSchemePrefWidget.cpp @@ -0,0 +1,529 @@ +#include "utils/Configuration.h" +#include "utils/Configuration.h" +#include "ColorSchemePrefWidget.h" +#include "ui_ColorSchemePrefWidget.h" +#include "utils/ColorSchemeFileSaver.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct OptionIfo { + QString info; + QString displayingtext; + bool isUsedStandardTextColor; +}; + +static const QMap 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", { + QObject::tr(""), + QObject::tr("fline"), false + } + }, + { + "flag", { + QObject::tr("Color of flags (similar to bookmarks for offset)"), + QObject::tr("Flag"), false + } + }, + { "label", { QObject::tr(""), QObject::tr("Label"), false } }, + { "help", { QObject::tr(""), QObject::tr("Help"), false } }, + { "flow", { QObject::tr(""), QObject::tr("flow"), false } }, + { "flow2", { QObject::tr(""), 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(""), QObject::tr("other"), false } }, + { + "b0x00", { + QObject::tr("0x00 opcode color"), + QObject::tr("b0x00"), false + } + }, + { + "b0x7f", { + QObject::tr("0x7f opcode color"), + QObject::tr("b0x7f"), false + } + }, + { + "b0xff", { + QObject::tr("0xff opcode color"), + QObject::tr("b0xff"), false + } + }, + { + "math", { + QObject::tr("arithmetic color (+, -, *, / etc.)"), + QObject::tr("math"), false + } + }, + { "bin", { QObject::tr(""), QObject::tr("bin"), false } }, + { "btext", { QObject::tr(""), QObject::tr("btext"), false } }, + { "push", { QObject::tr("push opcode color"), QObject::tr("push"), false } }, + { "pop", { QObject::tr("pop opcode color"), QObject::tr("pop"), false } }, + { "crypto", { QObject::tr("Cryptographic color"), QObject::tr("crypto"), false } }, + { + "jmp", { + QObject::tr("jmp instructions color"), + QObject::tr("jmp"), false + } + }, + { + "cjmp", { + QObject::tr(""), + QObject::tr("cjmp"), false + } + }, + { + "call", { + QObject::tr("call instructions color (ccall, rcall, call etc)"), + QObject::tr("call"), false + } + }, + { "nop", { QObject::tr("nop opcode color"), QObject::tr("nop"), false } }, + { + "ret", { + QObject::tr("ret opcode color"), + QObject::tr("ret"), false + } + }, + { + "trap", { + QObject::tr("Color of interrputs"), + QObject::tr("Interrputs"), false + } + }, + { "swi", { QObject::tr("swi opcode color"), QObject::tr("swi"), false } }, + { "cmp", { QObject::tr("cmp opcode color"), QObject::tr("cmp"), false } }, + { "reg", { QObject::tr("Registers color"), QObject::tr("Register"), false } }, + { "creg", { QObject::tr("Info"), QObject::tr("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", { QObject::tr(""), QObject::tr("widget_bg"), false } }, + { "widget_sel", { QObject::tr(""), QObject::tr("widget_sel"), false } }, + { "ai.read", { QObject::tr(""), QObject::tr("ai.read"), false } }, + { "ai.write", { QObject::tr(""), QObject::tr("ai.write"), false } }, + { "ai.exec", { QObject::tr(""), QObject::tr("ai.exec"), false } }, + { "ai.seq", { QObject::tr(""), QObject::tr("ai.seq"), false } }, + { "ai.ascii", { QObject::tr(""), QObject::tr("ai.ascii"), false } }, + { "graph.box", { QObject::tr(""), QObject::tr("graph.box"), false } }, + { "graph.box2", { QObject::tr(""), QObject::tr("graph.box2"), false } }, + { "graph.box3", { QObject::tr(""), QObject::tr("graph.box3"), false } }, + { "graph.box4", { QObject::tr(""), QObject::tr("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", { QObject::tr(""), QObject::tr("graph.current"), false } }, + { "graph.traced", { QObject::tr(""), QObject::tr("graph.traced"), false } }, + { "gui.cflow", { QObject::tr(""), QObject::tr("gui.cflow"), true } }, + { "gui.dataoffset", { QObject::tr(""), QObject::tr("gui.dataoffset"), true } }, + { + "gui.background", { + QObject::tr("General background color"), + QObject::tr("Background"), true + } + }, + { + "gui.alt_background", { + QObject::tr(""), + QObject::tr("Alt. background"), true + } + }, + { + "gui.disass_selected", { + QObject::tr("Background of current graph node"), + QObject::tr("Current graph node"), true + } + }, + { "gui.border", { QObject::tr(""), QObject::tr("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("Color of main function color"), + QObject::tr("Main"), true + } + }, + { "gui.imports", { QObject::tr(""), QObject::tr("gui.imports"), true } }, + { "highlightPC", { QObject::tr(""), QObject::tr("highlightPC"), true } }, + { "gui.navbar.err", { QObject::tr(""), QObject::tr("gui.navbar.err"), true } }, + { "gui.navbar.sym", { QObject::tr(""), QObject::tr("gui.navbar.sym"), true } }, + { "gui.dataoffset", { QObject::tr(""), QObject::tr("gui.dataoffset"), 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 + } + }, + { "angui.navbar.str", { QObject::tr(""), QObject::tr("angui.navbar.str"), true } }, + { "gui.breakpoint_background", { QObject::tr(""), QObject::tr("Breakpoint background"), 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(); + 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(this))); + + static_cast(this->model())->updateScheme(); + setStandardColors(); +} + +void PreferencesListView::setStandardColors() +{ + delegate = new ColorOptionDelegate(this); + ColorSettingsModel *model = static_cast(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(delegate)); +} + +void PreferencesListView::currentChanged(const QModelIndex ¤t, + const QModelIndex &previous) +{ + emit indexChanged(current); + QListView::currentChanged(current, previous); +} + +ColorSchemePrefWidget::ColorSchemePrefWidget(QWidget *parent) : QWidget (parent), + ui (new Ui::ColorSchemePrefWidget), isEditable (false) +{ + ui->setupUi(this); + connect(ui->colorViewFore, &ColorViewButton::clicked, this, &ColorSchemePrefWidget::newColor); + connect(ui->preferencesListView, &PreferencesListView::indexChanged, this, + &ColorSchemePrefWidget::indexChanged); + connect(ui->preferencesListView, &PreferencesListView::indexChanged, [this](const QModelIndex & i) { + ui->infoBoard->setText(optionInfoMap[i.data(Qt::UserRole).value().optionName].info); + }); +} + +ColorSchemePrefWidget::~ColorSchemePrefWidget() +{ + apply(); + delete ui; +} + +void ColorSchemePrefWidget::apply() +{ + if (!isEditable) + return; + ColorSettingsModel *model = static_cast(ui->preferencesListView->model()); + + QString scheme = ""; + ColorOption curr; + QMap cutterSpecific = ColorSchemeFileWorker().getCutterSpecific(); + + for (int i = 0; i < model->rowCount(); i++) { + curr = model->data(model->index(i), Qt::UserRole).value(); + if (cutterSpecific.contains(curr.optionName)) { + scheme += "#~"; + } else { + scheme += "ec "; + } + scheme += curr.optionName + " rgb:" + curr.color.name().remove("#").toLower() + "\n"; + } + ColorSchemeFileWorker().save(scheme, Config()->getCurrentTheme()); + Config()->setColorTheme(Config()->getCurrentTheme()); +} + +void ColorSchemePrefWidget::newColor() +{ + if (ui->preferencesListView->currentIndex().row() == -1 || !isEditable) + return; + + ColorOption currCO = ui->preferencesListView->model()->data(ui->preferencesListView->currentIndex(), + Qt::UserRole).value(); + QColorDialog d; + d.setCurrentColor(currCO.color); + d.exec(); + + static_cast(ui->preferencesListView->model())->setColor(currCO.optionName, + d.selectedColor()); + + static_cast(QObject::sender())->setColor(d.selectedColor()); + if (currCO.optionName == standardBackgroundOptionName) { + static_cast(ui->preferencesListView)->setStandardColors(); + } else { + static_cast(ui->preferencesListView)->setStandardColors(); + } +} + +void ColorSchemePrefWidget::indexChanged(const QModelIndex &ni) +{ + ui->colorViewFore->setColor(ni.data(Qt::UserRole).value().color); +} + +void ColorSchemePrefWidget::setNewScheme(const QString &schemeName) +{ + isEditable = ColorSchemeFileWorker().isCustomScheme(schemeName); + static_cast(ui->preferencesListView->model())->updateScheme(); +} + +ColorSettingsModel::ColorSettingsModel(QObject *parent) : QAbstractListModel (parent) { } + +QVariant ColorSettingsModel::data(const QModelIndex &index, int role) const +{ + 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()->getCurrentTheme())) { + 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()->getCurrentTheme())) { + 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 cutterSpecific = ColorSchemeFileWorker().getCutterSpecific(); + for (auto &it : cutterSpecific.keys()) + m_data.push_back({it, optionInfoMap[it].displayingtext, cutterSpecific[it]}); + + qobject_cast(parent())->setStandardColors(); +} diff --git a/src/widgets/ColorSchemePrefWidget.h b/src/widgets/ColorSchemePrefWidget.h new file mode 100644 index 00000000..ea02125f --- /dev/null +++ b/src/widgets/ColorSchemePrefWidget.h @@ -0,0 +1,127 @@ +#ifndef COLORSCHEMEPREFWIDGET_H +#define COLORSCHEMEPREFWIDGET_H + +#include +#include +#include +#include +#include + +namespace Ui { +class ColorSchemePrefWidget; +} + +class ColorSchemePrefWidget : public QWidget +{ + Q_OBJECT +public: + ColorSchemePrefWidget(QWidget *parent = nullptr); + virtual ~ColorSchemePrefWidget(); + +public slots: + void apply(); + + void setNewScheme(const QString &schemeName); + +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 { return m_data.size(); } + + void updateScheme(); + +private: + QList m_data; +}; + +class ColorOptionDelegate : public QStyledItemDelegate +{ + Q_OBJECT +public: + ColorOptionDelegate(QObject *parent = nullptr) : QStyledItemDelegate (parent) {} + ~ColorOptionDelegate() override {} + + void paint(QPainter *painter, + const QStyleOptionViewItem &option, + const QModelIndex &index) const override; + + void setBackgroundColor(const QColor &c); + + void setTextColor(const QColor &c); + +private: + QColor backgroundColor; + QColor textColor; +}; + +class PreferencesListView : public QListView +{ + Q_OBJECT +public: + PreferencesListView(QWidget *parent = nullptr); + virtual ~PreferencesListView() override {} + + void setStandardColors(); + +protected slots: + void currentChanged(const QModelIndex ¤t, + const QModelIndex &previous) override; + +signals: + void indexChanged(const QModelIndex &ni); + +private: + ColorOptionDelegate *delegate; +}; + +#endif // COLORSCHEMEPREFWIDGET_H diff --git a/src/widgets/ColorSchemePrefWidget.ui b/src/widgets/ColorSchemePrefWidget.ui new file mode 100644 index 00000000..97f07303 --- /dev/null +++ b/src/widgets/ColorSchemePrefWidget.ui @@ -0,0 +1,98 @@ + + + ColorSchemePrefWidget + + + + 0 + 0 + 670 + 306 + + + + Form + + + + + + + + + + + + + + + + 0 + 0 + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + + + + + 0 + 0 + + + + Set Default + + + + + + + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + + + PreferencesListView + QListView +
widgets/ColorSchemePrefWidget.h
+
+ + ColorViewButton + QFrame +
widgets/ColorSchemePrefWidget.h
+ 1 +
+
+ + +
diff --git a/src/widgets/VisualNavbar.h b/src/widgets/VisualNavbar.h index 41472a25..fe7ed332 100644 --- a/src/widgets/VisualNavbar.h +++ b/src/widgets/VisualNavbar.h @@ -25,11 +25,11 @@ public: public slots: void paintEvent(QPaintEvent *event) override; + void updateGraphicsScene(); private slots: void fetchAndPaintData(); void fetchStats(); - void updateGraphicsScene(); void drawCursor(); void on_seekChanged(RVA addr);