"Fossies" - the Fresh Open Source Software Archive

Member "cutter-1.10.3/src/common/ColorThemeWorker.cpp" (8 May 2020, 10635 Bytes) of package /linux/privat/cutter-1.10.3.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "ColorThemeWorker.cpp" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 1.10.2_vs_1.10.3.

    1 #include "ColorThemeWorker.h"
    2 
    3 #include <QDir>
    4 #include <QFile>
    5 #include <QColor>
    6 #include <QJsonArray>
    7 #include <QStandardPaths>
    8 #include <QRegularExpression>
    9 
   10 #include "common/Configuration.h"
   11 
   12 const QStringList ColorThemeWorker::cutterSpecificOptions = {
   13     "wordHighlight",
   14     "lineHighlight",
   15     "gui.main",
   16     "gui.imports",
   17     "highlightPC",
   18     "gui.navbar.err",
   19     "gui.navbar.seek",
   20     "gui.navbar.pc",
   21     "gui.navbar.sym",
   22     "gui.dataoffset",
   23     "gui.navbar.code",
   24     "gui.navbar.empty",
   25     "angui.navbar.str",
   26     "gui.disass_selected",
   27     "gui.breakpoint_background",
   28     "gui.overview.node",
   29     "gui.overview.fill",
   30     "gui.overview.border",
   31     "gui.border",
   32     "gui.background",
   33     "gui.alt_background",
   34     "gui.disass_selected"
   35 };
   36 
   37 const QStringList ColorThemeWorker::radare2UnusedOptions = {
   38     "linehl",
   39     "wordhl",
   40     "graph.box",
   41     "graph.box2",
   42     "graph.box3",
   43     "graph.box4",
   44     "graph.current",
   45     "graph.box2",
   46     "widget_sel",
   47     "widget_bg",
   48     "label",
   49     "ai.write",
   50     "invalid",
   51     "ai.seq",
   52     "args",
   53     "ai.read",
   54     "ai.exec",
   55     "ai.ascii",
   56     "prompt",
   57     "graph.traced"
   58 };
   59 
   60 ColorThemeWorker::ColorThemeWorker(QObject *parent) : QObject (parent)
   61 {
   62     char* szThemes = r_str_home(R2_HOME_THEMES);
   63     customR2ThemesLocationPath = szThemes;
   64     r_mem_free(szThemes);
   65     if (!QDir(customR2ThemesLocationPath).exists()) {
   66         QDir().mkpath(customR2ThemesLocationPath);
   67     }
   68 
   69     QDir currDir { QStringLiteral("%1%2%3")
   70         .arg(r_sys_prefix(nullptr))
   71         .arg(R_SYS_DIR)
   72         .arg(R2_THEMES)
   73     };
   74     if (currDir.exists()) {
   75         standardR2ThemesLocationPath = currDir.absolutePath();
   76     } else {
   77         QMessageBox::critical(nullptr,
   78             tr("Standard themes not found"),
   79             tr("The radare2 standard themes could not be found in '%1'. "
   80                "Most likely, radare2 is not properly installed.")
   81                 .arg(currDir.path())
   82         );
   83     }
   84 }
   85 
   86 QColor ColorThemeWorker::mergeColors(const QColor& upper, const QColor& lower) const
   87 {
   88     qreal r1, g1, b1, a1;
   89     qreal r2, g2, b2, a2;
   90     qreal r, g, b, a;
   91 
   92     upper.getRgbF(&r1, &g1, &b1, &a1);
   93     lower.getRgbF(&r2, &g2, &b2, &a2);
   94 
   95     a = (1.0 - a1) * a2 + a1;
   96     r = ((1.0 - a1) * a2 * r2 + a1 * r1) / a;
   97     g = ((1.0 - a1) * a2 * g2 + a1 * g1) / a;
   98     b = ((1.0 - a1) * a2 * b2 + a1 * b1) / a;
   99 
  100     QColor res;
  101     res.setRgbF(r, g, b, a);
  102     return res;
  103 }
  104 
  105 QString ColorThemeWorker::copy(const QString &srcThemeName,
  106                                    const QString &copyThemeName) const
  107 {
  108     if (!isThemeExist(srcThemeName)) {
  109         return tr("Theme <b>%1</b> does not exist.")
  110                 .arg(srcThemeName);
  111     }
  112 
  113     return save(getTheme(srcThemeName), copyThemeName);
  114 }
  115 
  116 QString ColorThemeWorker::save(const QJsonDocument &theme, const QString &themeName) const
  117 {
  118     QFile fOut(QDir(customR2ThemesLocationPath).filePath(themeName));
  119     if (!fOut.open(QFile::WriteOnly | QFile::Truncate)) {
  120         return tr("The file <b>%1</b> cannot be opened.")
  121                 .arg(QFileInfo(fOut).filePath());
  122     }
  123 
  124     QJsonObject obj = theme.object();
  125     QString line;
  126     QColor::NameFormat format;
  127     for (auto it = obj.constBegin(); it != obj.constEnd(); it++) {
  128         if (cutterSpecificOptions.contains(it.key())) {
  129             line = "#~%1 %2\n";
  130             format = QColor::HexArgb;
  131         } else {
  132             line = "ec %1 %2\n";
  133             format = QColor::HexRgb;
  134         }
  135         QJsonArray arr = it.value().toArray();
  136         if (arr.isEmpty()) {
  137             fOut.write(line.arg(it.key())
  138                        .arg(it.value().toVariant().value<QColor>().name(format)).toUtf8());
  139         } else if (arr.size() == 4) {
  140             fOut.write(line.arg(it.key())
  141                        .arg(QColor(arr[0].toInt(), arr[1].toInt(), arr[2].toInt(), arr[3].toInt()).name(format)).toUtf8());
  142         } else if (arr.size() == 3) {
  143             fOut.write(line.arg(it.key())
  144                        .arg(QColor(arr[0].toInt(), arr[1].toInt(), arr[2].toInt()).name(format)).toUtf8());
  145         }
  146     }
  147 
  148     fOut.close();
  149     return "";
  150 }
  151 
  152 bool ColorThemeWorker::isCustomTheme(const QString &themeName) const
  153 {
  154     return QFile::exists(QDir(customR2ThemesLocationPath).filePath(themeName));
  155 }
  156 
  157 bool ColorThemeWorker::isThemeExist(const QString &name) const
  158 {
  159     QStringList themes = Core()->getColorThemes();
  160     return themes.contains(name);
  161 }
  162 
  163 QJsonDocument ColorThemeWorker::getTheme(const QString& themeName) const
  164 {
  165     int r, g, b, a;
  166     QVariantMap theme;
  167     QString curr = Config()->getColorTheme();
  168 
  169     if (themeName != curr) {
  170         Core()->cmdRaw(QString("eco %1").arg(themeName));
  171         theme = Core()->cmdj("ecj").object().toVariantMap();
  172         Core()->cmdRaw(QString("eco %1").arg(curr));
  173     } else {
  174         theme = Core()->cmdj("ecj").object().toVariantMap();
  175     }
  176 
  177     for (auto it = theme.begin(); it != theme.end(); it++) {
  178         auto arr = it.value().toList();
  179         QColor(arr[0].toInt(), arr[1].toInt(), arr[2].toInt()).getRgb(&r, &g, &b, &a);
  180         theme[it.key()] = QJsonArray({r, g, b, a});
  181     }
  182 
  183     ColorFlags colorFlags = ColorFlags::DarkFlag;
  184     if (Configuration::relevantThemes.contains(themeName)) {
  185         colorFlags = Configuration::relevantThemes[themeName];
  186     }
  187 
  188     for (auto& it : cutterSpecificOptions) {
  189         Configuration::cutterOptionColors[it][colorFlags].getRgb(&r, &g, &b, &a);
  190         theme.insert(it, QJsonArray{r, g, b, a});
  191     }
  192 
  193     if (isCustomTheme(themeName)) {
  194         QFile src(QDir(customR2ThemesLocationPath).filePath(themeName));
  195         if (!src.open(QFile::ReadOnly)) {
  196             return QJsonDocument();
  197         }
  198         QStringList sl;
  199         for (auto &line : QString(src.readAll()).split('\n', QString::SkipEmptyParts)) {
  200             sl = line.replace("#~", "ec ").replace("rgb:", "#").split(' ', QString::SkipEmptyParts);
  201             if (sl.size() != 3 || sl[0][0] == '#') {
  202                 continue;
  203             }
  204             QColor(sl[2]).getRgb(&r, &g, &b, &a);
  205             theme.insert(sl[1], QJsonArray({r, g, b, a}));
  206         }
  207     }
  208 
  209     for (auto &key : radare2UnusedOptions) {
  210         theme.remove(key);
  211     }
  212 
  213     // manualy converting instead of using QJsonObject::fromVariantMap because
  214     // Qt < 5.6 QJsonValue.fromVariant doesn't expect QVariant to already contain
  215     // QJson values like QJsonArray. https://github.com/qt/qtbase/commit/26237f0a2d8db80024b601f676bbce54d483e672
  216     QJsonObject obj;
  217     for (auto it = theme.begin(); it != theme.end(); it++) {
  218         auto &value = it.value();
  219         if (value.canConvert<QJsonArray>()) {
  220             obj[it.key()] = it.value().value<QJsonArray>();
  221         } else {
  222             obj[it.key()] = QJsonValue::fromVariant(value);
  223         }
  224 
  225     }
  226 
  227     return QJsonDocument(obj);
  228 }
  229 
  230 QString ColorThemeWorker::deleteTheme(const QString &themeName) const
  231 {
  232     if (!isCustomTheme(themeName)) {
  233         return tr("You can not delete standard radare2 color themes.");
  234     }
  235     if (!isThemeExist(themeName)) {
  236         return tr("Theme <b>%1</b> does not exist.").arg(themeName);
  237     }
  238 
  239     QFile file(QDir(customR2ThemesLocationPath).filePath(themeName));
  240     if (file.isWritable()) {
  241         return tr("You have no permission to write to <b>%1</b>")
  242                 .arg(QFileInfo(file).filePath());
  243     }
  244     if (!file.open(QFile::ReadOnly)) {
  245         return tr("File <b>%1</b> can not be opened.")
  246                 .arg(QFileInfo(file).filePath());
  247     }
  248     if (!file.remove()) {
  249         return tr("File <b>%1</b> can not be removed.")
  250                 .arg(QFileInfo(file).filePath());
  251     }
  252     return "";
  253 }
  254 
  255 QString ColorThemeWorker::importTheme(const QString& file) const
  256 {
  257     QFileInfo src(file);
  258      if (!src.exists()) {
  259          return tr("File <b>%1</b> does not exist.").arg(file);
  260      }
  261 
  262     bool ok;
  263     bool isTheme = isFileTheme(file, &ok);
  264     if (!ok) {
  265         return tr("File <b>%1</b> could not be opened. "
  266                   "Please make sure you have access to it and try again.")
  267                 .arg(file);
  268     } else if (!isTheme) {
  269         return tr("File <b>%1</b> is not a Cutter color theme").arg(file);
  270     }
  271 
  272     QString name = src.fileName();
  273     if (isThemeExist(name)) {
  274         return tr("A color theme named <b>%1</b> already exists.").arg(name);
  275     }
  276 
  277     if (QFile::copy(file, QDir(customR2ThemesLocationPath).filePath(name))) {
  278          return "";
  279      } else {
  280          return tr("Error occurred during importing. "
  281                    "Please make sure you have an access to "
  282                    "the directory <b>%1</b> and try again.")
  283                  .arg(src.dir().path());
  284     }
  285 }
  286 
  287 QString ColorThemeWorker::renameTheme(const QString& themeName, const QString& newName) const
  288 {
  289     if (isThemeExist(newName)) {
  290          return tr("A color theme named <b>\"%1\"</b> already exists.").arg(newName);
  291      }
  292 
  293      if (!isCustomTheme(themeName)) {
  294          return tr("You can not rename standard radare2 themes.");
  295      }
  296 
  297      QDir dir = customR2ThemesLocationPath;
  298      bool ok = QFile::rename(dir.filePath(themeName), dir.filePath(newName));
  299      if (!ok) {
  300          return tr("Something went wrong during renaming. "
  301                    "Please make sure you have access to the directory <b>\"%1\"</b>.").arg(dir.path());
  302      }
  303      return "";
  304 }
  305 
  306 bool ColorThemeWorker::isFileTheme(const QString& filePath, bool* ok) const
  307 {
  308     QFile f(filePath);
  309     if (!f.open(QFile::ReadOnly)) {
  310         *ok = false;
  311         return false;
  312     }
  313 
  314     const QString colors = "black|red|white|green|magenta|yellow|cyan|blue|gray|none";
  315     QString options = (Core()->cmdj("ecj").object().keys() << cutterSpecificOptions)
  316                       .join('|')
  317                       .replace(".", "\\.");
  318 
  319     QString pattern = QString("((ec\\s+(%1)\\s+(((rgb:|#)[0-9a-fA-F]{3,8})|(%2))))\\s*").arg(options).arg(colors);
  320     // The below construct mimics the behaviour of QRegexP::exactMatch(), which was here before
  321     QRegularExpression regexp("\\A(?:" + pattern + ")\\z");
  322 
  323     for (auto &line : QString(f.readAll()).split('\n', QString::SkipEmptyParts)) {
  324         line.replace("#~", "ec ");
  325         if (!line.isEmpty() && !regexp.match(line).hasMatch()) {
  326             *ok = true;
  327             return false;
  328         }
  329     }
  330 
  331     *ok = true;
  332     return true;
  333 }
  334 
  335 QStringList ColorThemeWorker::customThemes() const
  336 {
  337     QStringList themes = Core()->getColorThemes();
  338     QStringList ret;
  339     for (int i = 0; i < themes.size(); i++) {
  340         if (isCustomTheme(themes[i])) {
  341             ret.push_back(themes[i]);
  342         }
  343     }
  344     return ret;
  345 }