1 #include "Configuration.h"
2 #include <QJsonObject>
3 #include <QJsonArray>
4 #include <QDir>
5 #include <QFontDatabase>
6 #include <QFile>
7 #include <QApplication>
8 
9 #ifdef CUTTER_ENABLE_KSYNTAXHIGHLIGHTING
10 #include <KSyntaxHighlighting/repository.h>
11 #include <KSyntaxHighlighting/theme.h>
12 #include <KSyntaxHighlighting/definition.h>
13 #endif
14 
15 #include "common/ColorThemeWorker.h"
16 #include "common/SyntaxHighlighter.h"
17 #include "common/ResourcePaths.h"
18 
19 /* Map with names of themes associated with its color palette
20  * (Dark or Light), so for dark interface themes will be shown only Dark color themes
21  * and for light - only light ones.
22  */
23 const QHash<QString, ColorFlags> Configuration::relevantThemes = {
24     { "ayu", DarkFlag },
25     { "consonance", DarkFlag },
26     { "darkda", DarkFlag },
27     { "onedark", DarkFlag },
28     { "solarized", DarkFlag },
29     { "zenburn", DarkFlag },
30     { "cutter", LightFlag },
31     { "dark", LightFlag },
32     { "matrix", LightFlag },
33     { "tango", LightFlag },
34     { "white", LightFlag }
35 };
36 static const QString DEFAULT_LIGHT_COLOR_THEME = "cutter";
37 static const QString DEFAULT_DARK_COLOR_THEME = "ayu";
38 
39 
40 const QHash<QString, QHash<ColorFlags, QColor>> Configuration::cutterOptionColors = {
41     { "gui.cflow",                 { { DarkFlag,  QColor(0xff, 0xff, 0xff) },
42                                      { LightFlag, QColor(0x00, 0x00, 0x00) }} },
43     { "gui.dataoffset",            { { DarkFlag,  QColor(0xff, 0xff, 0xff) },
44                                      { LightFlag, QColor(0x00, 0x00, 0x00) }} },
45     { "gui.imports",               { { DarkFlag,  QColor(0x32, 0x8c, 0xff) },
46                                      { LightFlag, QColor(0x32, 0x8c, 0xff) }} },
47     { "gui.item_invalid",          { { DarkFlag,  QColor(0x9b, 0x9b, 0x9b) },
48                                      { LightFlag, QColor(0x9b, 0x9b, 0x9b) }} },
49     { "gui.main",                  { { DarkFlag,  QColor(0x00, 0x80, 0x00) },
50                                      { LightFlag, QColor(0x00, 0x80, 0x00) }} },
51     { "gui.item_unsafe",           { { DarkFlag,  QColor(0xff, 0x81, 0x7b) },
52                                      { LightFlag, QColor(0xff, 0x81, 0x7b) }} },
53     { "gui.navbar.seek",           { { DarkFlag,  QColor(0xe9, 0x56, 0x56) },
54                                      { LightFlag, QColor(0xff, 0x00, 0x00) }} },
55     { "gui.navbar.pc",             { { DarkFlag,  QColor(0x42, 0xee, 0xf4) },
56                                      { LightFlag, QColor(0x42, 0xee, 0xf4) }} },
57     { "gui.navbar.code",           { { DarkFlag,  QColor(0x82, 0xc8, 0x6f) },
58                                      { LightFlag, QColor(0x68, 0xe5, 0x45) }} },
59     { "gui.navbar.str",            { { DarkFlag,  QColor(0x6f, 0x86, 0xd8) },
60                                      { LightFlag, QColor(0x45, 0x68, 0xe5) }} },
61     { "gui.navbar.sym",            { { DarkFlag,  QColor(0xdd, 0xa3, 0x68) },
62                                      { LightFlag, QColor(0xe5, 0x96, 0x45) }} },
63     { "gui.navbar.empty",          { { DarkFlag,  QColor(0x64, 0x64, 0x64) },
64                                      { LightFlag, QColor(0xdc, 0xec, 0xf5) }} },
65     { "gui.breakpoint_background", { { DarkFlag,  QColor(0x8c, 0x4c, 0x4c) },
66                                      { LightFlag, QColor(0xe9, 0x8f, 0x8f) }} },
67     { "gui.overview.node",         { { DarkFlag,  QColor(0x64, 0x64, 0x64) },
68                                      { LightFlag, QColor(0xf5, 0xfa, 0xff) }} },
69     { "gui.tooltip.background",    { { DarkFlag,  QColor(0x2a, 0x2c, 0x2e) },
70                                      { LightFlag, QColor(0xfa, 0xfc, 0xfe) }} },
71     { "gui.tooltip.foreground",    { { DarkFlag,  QColor(0xfa, 0xfc, 0xfe) },
72                                      { LightFlag, QColor(0x2a, 0x2c, 0x2e) }} },
73     { "gui.border",                { { DarkFlag,  QColor(0x64, 0x64, 0x64) },
74                                      { LightFlag, QColor(0x91, 0xc8, 0xfa) }} },
75     { "gui.background",            { { DarkFlag,  QColor(0x25, 0x28, 0x2b) },
76                                      { LightFlag, QColor(0xff, 0xff, 0xff) }} },
77     { "gui.alt_background",        { { DarkFlag,  QColor(0x1c, 0x1f, 0x24) },
78                                      { LightFlag, QColor(0xf5, 0xfa, 0xff) }} },
79     { "gui.disass_selected",       { { DarkFlag,  QColor(0x1f, 0x22, 0x28) },
80                                      { LightFlag, QColor(0xff, 0xff, 0xff) }} },
81     { "lineHighlight",             { { DarkFlag,  QColor(0x15, 0x1d, 0x1d, 0x96) },
82                                      { LightFlag, QColor(0xd2, 0xd2, 0xff, 0x96) }} },
83     { "wordHighlight",             { { DarkFlag,  QColor(0x34, 0x3a, 0x47, 0xff) },
84                                      { LightFlag, QColor(0xb3, 0x77, 0xd6, 0x3c) }} },
85     { "highlightPC",               { { DarkFlag,  QColor(0x57, 0x1a, 0x07) },
86                                      { LightFlag, QColor(0xd6, 0xff, 0xd2) }} },
87     { "gui.overview.fill",         { { DarkFlag,  QColor(0xff, 0xff, 0xff, 0x28) },
88                                      { LightFlag, QColor(0xaf, 0xd9, 0xea, 0x41) }} },
89     { "gui.overview.border",       { { DarkFlag,  QColor(0x63, 0xda, 0xe8, 0x32) },
90                                      { LightFlag, QColor(0x63, 0xda, 0xe8, 0x32) }} },
91     { "gui.navbar.err",            { { DarkFlag,  QColor(0x03, 0xaa, 0xf5) },
92                                      { LightFlag, QColor(0x03, 0xaa, 0xf5) }} }
93 };
94 
95 Configuration *Configuration::mPtr = nullptr;
96 
97 /**
98  * @brief All asm.* options saved as settings. Values are the default values.
99  */
100 static const QHash<QString, QVariant> asmOptions = {
101     { "asm.esil",           false },
102     { "asm.pseudo",         false },
103     { "asm.offset",         true },
104     { "asm.xrefs",          false },
105     { "asm.indent",         false },
106     { "asm.describe",       false },
107     { "asm.slow",           true },
108     { "asm.lines",          true },
109     { "asm.lines.fcn",      true },
110     { "asm.flags.offset",   false },
111     { "asm.emu",            false },
112     { "emu.str",            false},
113     { "asm.cmt.right",      true },
114     { "asm.cmt.col",        35 },
115     { "asm.var.summary",    false },
116     { "asm.bytes",          false },
117     { "asm.size",           false },
118     { "asm.bytes.space",    false },
119     { "asm.lbytes",         true },
120     { "asm.nbytes",         10 },
121     { "asm.syntax",         "intel" },
122     { "asm.ucase",          false },
123     { "asm.bb.line",        false },
124     { "asm.capitalize",     false },
125     { "asm.var.sub",        true },
126     { "asm.sub.varonly",    true },
127     { "asm.tabs",           8 },
128     { "asm.tabs.off",       5 },
129     { "asm.marks",          false },
130     { "asm.refptr",         false },
131     { "asm.flags.real",     true },
132     { "asm.reloff",         false },
133     { "asm.reloff.flags",   false },
134     { "esil.breakoninvalid",true },
135     { "graph.offset",       false}
136 };
137 
138 
Configuration()139 Configuration::Configuration() : QObject(), nativePalette(qApp->palette())
140 {
141     mPtr = this;
142     if (!s.isWritable()) {
143         QMessageBox::critical(nullptr,
144                               tr("Critical!"),
145                               tr("!!! Settings are not writable! Make sure you have a write access to \"%1\"")
146                               .arg(s.fileName())
147                              );
148     }
149 #ifdef CUTTER_ENABLE_KSYNTAXHIGHLIGHTING
150     kSyntaxHighlightingRepository = nullptr;
151 #endif
152 }
153 
instance()154 Configuration *Configuration::instance()
155 {
156     if (!mPtr)
157         mPtr = new Configuration();
158     return mPtr;
159 }
160 
loadInitial()161 void Configuration::loadInitial()
162 {
163     setInterfaceTheme(getInterfaceTheme());
164     setColorTheme(getColorTheme());
165     applySavedAsmOptions();
166 
167 #ifdef CUTTER_ENABLE_KSYNTAXHIGHLIGHTING
168     kSyntaxHighlightingRepository = new KSyntaxHighlighting::Repository();
169 #endif
170 }
171 
getDirProjects()172 QString Configuration::getDirProjects()
173 {
174     auto projectsDir = s.value("dir.projects").toString();
175     if (projectsDir.isEmpty()) {
176         projectsDir = Core()->getConfig("dir.projects");
177         setDirProjects(projectsDir);
178     }
179 
180     return QDir::toNativeSeparators(projectsDir);
181 }
182 
setDirProjects(const QString & dir)183 void Configuration::setDirProjects(const QString &dir)
184 {
185     s.setValue("dir.projects", QDir::toNativeSeparators(dir));
186 }
187 
getRecentFolder()188 QString Configuration::getRecentFolder()
189 {
190     QString recentFolder = s.value("dir.recentFolder", QDir::homePath()).toString();
191 
192     return QDir::toNativeSeparators(recentFolder);
193 }
194 
setRecentFolder(const QString & dir)195 void Configuration::setRecentFolder(const QString &dir)
196 {
197     s.setValue("dir.recentFolder", QDir::toNativeSeparators(dir));
198 }
199 
200 /**
201  * @brief Configuration::setFilesTabLastClicked
202  * Set the new file dialog last clicked tab
203  * @param lastClicked
204  */
setNewFileLastClicked(int lastClicked)205 void Configuration::setNewFileLastClicked(int lastClicked)
206 {
207     s.setValue("newFileLastClicked", lastClicked);
208 }
209 
getNewFileLastClicked()210 int Configuration::getNewFileLastClicked()
211 {
212     return s.value("newFileLastClicked").toInt();
213 }
214 
resetAll()215 void Configuration::resetAll()
216 {
217     // Don't reset all r2 vars, that currently breaks a bunch of stuff.
218     // settingsFile.remove()+loadInitials() should reset all settings configurable using Cutter GUI.
219     //Core()->cmdRaw("e-");
220 
221     Core()->setSettings();
222     // Delete the file so no extra configuration is in it.
223     QFile settingsFile(s.fileName());
224     settingsFile.remove();
225     s.clear();
226 
227     loadInitial();
228     emit fontsUpdated();
229 }
230 
getAutoUpdateEnabled() const231 bool Configuration::getAutoUpdateEnabled() const
232 {
233     return s.value("autoUpdateEnabled", false).toBool();
234 }
235 
setAutoUpdateEnabled(bool au)236 void Configuration::setAutoUpdateEnabled(bool au)
237 {
238     s.setValue("autoUpdateEnabled", au);
239 }
240 
241 /**
242  * @brief get the current Locale set in Cutter's user configuration
243  * @return a QLocale object describes user's current locale
244  */
getCurrLocale() const245 QLocale Configuration::getCurrLocale() const
246 {
247     return s.value("locale", QLocale().system()).toLocale();
248 }
249 
250 /**
251  * @brief sets Cutter's locale
252  * @param l - a QLocale object describes the locate to configure
253  */
setLocale(const QLocale & l)254 void Configuration::setLocale(const QLocale &l)
255 {
256     s.setValue("locale", l);
257 }
258 
259 /**
260  * @brief set Cutter's interface language by a given locale name
261  * @param language - a string represents the name of a locale language
262  * @return true on success
263  */
setLocaleByName(const QString & language)264 bool Configuration::setLocaleByName(const QString &language)
265 {
266     const auto &allLocales = QLocale::matchingLocales(QLocale::AnyLanguage, QLocale::AnyScript,
267                                                       QLocale::AnyCountry);
268 
269     for (auto &it : allLocales) {
270         if (QString::compare(it.nativeLanguageName(), language, Qt::CaseInsensitive) == 0) {
271             setLocale(it);
272             return true;
273         }
274     }
275     return false;
276 }
277 
windowColorIsDark()278 bool Configuration::windowColorIsDark()
279 {
280     ColorFlags currentThemeColorFlags = getCurrentTheme()->flag;
281     if (currentThemeColorFlags == ColorFlags::LightFlag) {
282         return false;
283     } else if (currentThemeColorFlags == ColorFlags::DarkFlag) {
284         return true;
285     }
286     return nativeWindowIsDark();
287 }
288 
nativeWindowIsDark()289 bool Configuration::nativeWindowIsDark()
290 {
291     const QPalette &palette = qApp->palette();
292     auto windowColor = palette.color(QPalette::Window).toRgb();
293     return (windowColor.red() + windowColor.green() + windowColor.blue()) < 382;
294 }
295 
loadNativeStylesheet()296 void Configuration::loadNativeStylesheet()
297 {
298     /* Load Qt Theme */
299     QFile f(":native/native.qss");
300     if (!f.exists()) {
301         qWarning() << "Can't find Native theme stylesheet.";
302     } else {
303         f.open(QFile::ReadOnly | QFile::Text);
304         QTextStream ts(&f);
305         QString stylesheet = ts.readAll();
306         qApp->setStyleSheet(stylesheet);
307     }
308 
309     qApp->setPalette(nativePalette);
310     /* Some widgets does not change its palette when QApplication changes one
311      * so this loop force all widgets do this, but all widgets take palette from
312      * QApplication::palette() when they are created so line above is necessary too.
313      */
314     for (auto widget : qApp->allWidgets()) {
315         widget->setPalette(nativePalette);
316     }
317 }
318 
319 /**
320  * @brief Loads the Light theme of Cutter and modify special theme colors
321  */
loadLightStylesheet()322 void Configuration::loadLightStylesheet()
323 {
324     /* Load Qt Theme */
325     QFile f(":lightstyle/light.qss");
326     if (!f.exists()) {
327         qWarning() << "Can't find Light theme stylesheet.";
328     } else {
329         f.open(QFile::ReadOnly | QFile::Text);
330         QTextStream ts(&f);
331         QString stylesheet = ts.readAll();
332 
333         QPalette p = qApp->palette();
334         p.setColor(QPalette::Text, Qt::black);
335         qApp->setPalette(p);
336 
337         qApp->setStyleSheet(stylesheet);
338     }
339 }
340 
loadDarkStylesheet()341 void Configuration::loadDarkStylesheet()
342 {
343     /* Load Qt Theme */
344     QFile f(":qdarkstyle/style.qss");
345     if (!f.exists()) {
346         qWarning() << "Can't find Dark theme stylesheet.";
347     } else {
348         f.open(QFile::ReadOnly | QFile::Text);
349         QTextStream ts(&f);
350         QString stylesheet = ts.readAll();
351 #ifdef Q_OS_MACX
352         // see https://github.com/ColinDuquesnoy/QDarkStyleSheet/issues/22#issuecomment-96179529
353         stylesheet += "QDockWidget::title"
354                       "{"
355                       "    background-color: #31363b;"
356                       "    text-align: center;"
357                       "    height: 12px;"
358                       "}";
359 #endif
360         QPalette p = qApp->palette();
361         p.setColor(QPalette::Text, Qt::white);
362         qApp->setPalette(p);
363         qApp->setStyleSheet(stylesheet);
364     }
365 }
366 
loadMidnightStylesheet()367 void Configuration::loadMidnightStylesheet()
368 {
369     /* Load Qt Theme */
370     QFile f(":midnight/style.css");
371     if (!f.exists()) {
372         qWarning() << "Can't find Midnight theme stylesheet.";
373     } else {
374         f.open(QFile::ReadOnly | QFile::Text);
375         QTextStream ts(&f);
376         QString stylesheet = ts.readAll();
377 
378         QPalette p = qApp->palette();
379         p.setColor(QPalette::Text, Qt::white);
380         qApp->setPalette(p);
381 
382         qApp->setStyleSheet(stylesheet);
383     }
384 }
385 
getBaseFont() const386 const QFont Configuration::getBaseFont() const
387 {
388     QFont font = s.value("font", QFont("Inconsolata", 11)).value<QFont>();
389     return font;
390 }
391 
getFont() const392 const QFont Configuration::getFont() const
393 {
394   QFont font = getBaseFont();
395   font.setPointSizeF(font.pointSizeF() * getZoomFactor());
396   return font;
397 }
398 
setFont(const QFont & font)399 void Configuration::setFont(const QFont &font)
400 {
401     s.setValue("font", font);
402     emit fontsUpdated();
403 }
404 
refreshFont()405 void Configuration::refreshFont()
406 {
407     emit fontsUpdated();
408 }
409 
getZoomFactor() const410 qreal Configuration::getZoomFactor() const {
411   qreal fontZoom = s.value("zoomFactor", 1.0).value<qreal>();
412   return qMax(fontZoom, 0.1);
413 }
414 
setZoomFactor(qreal zoom)415 void Configuration::setZoomFactor(qreal zoom) {
416   s.setValue("zoomFactor", qMax(zoom, 0.1));
417   emit fontsUpdated();
418 }
419 
getLastThemeOf(const CutterInterfaceTheme & currInterfaceTheme) const420 QString Configuration::getLastThemeOf(const CutterInterfaceTheme &currInterfaceTheme) const
421 {
422     return s.value("lastThemeOf." + currInterfaceTheme.name,
423                    Config()->getColorTheme()).toString();
424 }
425 
setInterfaceTheme(int theme)426 void Configuration::setInterfaceTheme(int theme)
427 {
428     if (theme >= cutterInterfaceThemesList().size() ||
429             theme < 0) {
430         theme = 0;
431     }
432     s.setValue("ColorPalette", theme);
433 
434     CutterInterfaceTheme interfaceTheme = cutterInterfaceThemesList()[theme];
435 
436     if (interfaceTheme.name == "Native") {
437         loadNativeStylesheet();
438     } else if (interfaceTheme.name == "Dark") {
439         loadDarkStylesheet();
440     } else if (interfaceTheme.name == "Midnight") {
441         loadMidnightStylesheet();
442     } else if (interfaceTheme.name == "Light") {
443         loadLightStylesheet();
444     } else {
445         loadNativeStylesheet();
446     }
447 
448     for (auto it = cutterOptionColors.cbegin(); it != cutterOptionColors.cend(); it++) {
449         setColor(it.key(), it.value()[interfaceTheme.flag]);
450     }
451 
452     adjustColorThemeDarkness();
453 
454     emit interfaceThemeChanged();
455     emit colorsUpdated();
456 #ifdef CUTTER_ENABLE_KSYNTAXHIGHLIGHTING
457     emit kSyntaxHighlightingThemeChanged();
458 #endif
459 }
460 
getCurrentTheme()461 const CutterInterfaceTheme *Configuration::getCurrentTheme()
462 {
463     int i = getInterfaceTheme();
464     if (i < 0 || i >= cutterInterfaceThemesList().size()) {
465         i = 0;
466         setInterfaceTheme(i);
467     }
468     return &cutterInterfaceThemesList()[i];
469 }
470 
471 #ifdef CUTTER_ENABLE_KSYNTAXHIGHLIGHTING
getKSyntaxHighlightingRepository()472 KSyntaxHighlighting::Repository *Configuration::getKSyntaxHighlightingRepository()
473 {
474     return kSyntaxHighlightingRepository;
475 }
476 
getKSyntaxHighlightingTheme()477 KSyntaxHighlighting::Theme Configuration::getKSyntaxHighlightingTheme()
478 {
479     auto repo = getKSyntaxHighlightingRepository();
480     if (!repo) {
481         return KSyntaxHighlighting::Theme();
482     }
483     return repo->defaultTheme(
484         getCurrentTheme()->flag & DarkFlag
485         ? KSyntaxHighlighting::Repository::DefaultTheme::DarkTheme
486         : KSyntaxHighlighting::Repository::DefaultTheme::LightTheme);
487 }
488 #endif
489 
createSyntaxHighlighter(QTextDocument * document)490 QSyntaxHighlighter *Configuration::createSyntaxHighlighter(QTextDocument *document)
491 {
492 #ifdef CUTTER_ENABLE_KSYNTAXHIGHLIGHTING
493     auto syntaxHighlighter = new SyntaxHighlighter(document);
494     auto repo = getKSyntaxHighlightingRepository();
495     if (repo) {
496         syntaxHighlighter->setDefinition(repo->definitionForName("C"));
497     }
498     return syntaxHighlighter;
499 #else
500     return new FallbackSyntaxHighlighter(document);
501 #endif
502 }
503 
getLogoFile()504 QString Configuration::getLogoFile()
505 {
506     return windowColorIsDark()
507            ? QString(":/img/cutter_white_plain.svg")
508            : QString(":/img/cutter_plain.svg");
509 }
510 
511 /**
512  * @brief Configuration::setColor sets the local Cutter configuration color
513  * @param name Color Name
514  * @param color The color you want to set
515  */
setColor(const QString & name,const QColor & color)516 void Configuration::setColor(const QString &name, const QColor &color)
517 {
518     s.setValue("colors." + name, color);
519 }
520 
setLastThemeOf(const CutterInterfaceTheme & currInterfaceTheme,const QString & theme)521 void Configuration::setLastThemeOf(const CutterInterfaceTheme &currInterfaceTheme, const QString &theme)
522 {
523     s.setValue("lastThemeOf." + currInterfaceTheme.name, theme);
524 }
525 
getColor(const QString & name) const526 const QColor Configuration::getColor(const QString &name) const
527 {
528     if (s.contains("colors." + name)) {
529         return s.value("colors." + name).value<QColor>();
530     } else {
531         return s.value("colors.other").value<QColor>();
532     }
533 }
534 
setColorTheme(const QString & theme)535 void Configuration::setColorTheme(const QString &theme)
536 {
537     if (theme == "default") {
538         Core()->cmdRaw("ecd");
539         s.setValue("theme", "default");
540     } else {
541         Core()->cmdRaw(QStringLiteral("eco %1").arg(theme));
542         s.setValue("theme", theme);
543     }
544 
545     QJsonObject colorTheme = ThemeWorker().getTheme(theme).object();
546     for (auto it = colorTheme.constBegin(); it != colorTheme.constEnd(); it++) {
547         QJsonArray rgb = it.value().toArray();
548         if (rgb.size() != 4) {
549             continue;
550         }
551         setColor(it.key(), QColor(rgb[0].toInt(), rgb[1].toInt(), rgb[2].toInt(), rgb[3].toInt()));
552     }
553 
554     emit colorsUpdated();
555 }
556 
adjustColorThemeDarkness()557 void Configuration::adjustColorThemeDarkness()
558 {
559     bool windowIsDark = windowColorIsDark();
560     int windowDarkness = windowIsDark ? DarkFlag : LightFlag;
561     int currentColorThemeDarkness = colorThemeDarkness(getColorTheme());
562 
563     if ((currentColorThemeDarkness & windowDarkness) == 0) {
564         setColorTheme(windowIsDark ? DEFAULT_DARK_COLOR_THEME : DEFAULT_LIGHT_COLOR_THEME);
565     }
566 }
567 
colorThemeDarkness(const QString & colorTheme) const568 int Configuration::colorThemeDarkness(const QString &colorTheme) const
569 {
570     auto flags = relevantThemes.find(colorTheme);
571     if (flags != relevantThemes.end()) {
572         return static_cast<int>(*flags);
573     }
574     return DarkFlag | LightFlag;
575 }
576 
resetToDefaultAsmOptions()577 void Configuration::resetToDefaultAsmOptions()
578 {
579     for (auto it = asmOptions.cbegin(); it != asmOptions.cend(); it++) {
580         setConfig(it.key(), it.value());
581     }
582 }
583 
applySavedAsmOptions()584 void Configuration::applySavedAsmOptions()
585 {
586     for (auto it = asmOptions.cbegin(); it != asmOptions.cend(); it++) {
587         Core()->setConfig(it.key(), s.value(it.key(), it.value()));
588     }
589 }
590 
cutterInterfaceThemesList()591 const QList<CutterInterfaceTheme>& Configuration::cutterInterfaceThemesList()
592 {
593     static const QList<CutterInterfaceTheme> list = {
594         { "Native", Configuration::nativeWindowIsDark() ? DarkFlag : LightFlag },
595         { "Dark",   DarkFlag },
596         { "Midnight", DarkFlag },
597         { "Light",  LightFlag }
598     };
599     return list;
600 }
601 
getConfigVar(const QString & key)602 QVariant Configuration::getConfigVar(const QString &key)
603 {
604     QHash<QString, QVariant>::const_iterator it = asmOptions.find(key);
605     if (it != asmOptions.end()) {
606         switch (it.value().type()) {
607         case QVariant::Type::Bool:
608             return Core()->getConfigb(key);
609         case QVariant::Type::Int:
610             return Core()->getConfigi(key);
611         default:
612             return Core()->getConfig(key);
613         }
614     }
615     return QVariant();
616 }
617 
618 
getConfigBool(const QString & key)619 bool Configuration::getConfigBool(const QString &key)
620 {
621     return getConfigVar(key).toBool();
622 }
623 
getConfigInt(const QString & key)624 int Configuration::getConfigInt(const QString &key)
625 {
626     return getConfigVar(key).toInt();
627 }
628 
getConfigString(const QString & key)629 QString Configuration::getConfigString(const QString &key)
630 {
631     return getConfigVar(key).toString();
632 }
633 
634 /**
635  * @brief Configuration::setConfig
636  * Set radare2 configuration value (e.g. "asm.lines")
637  * @param key
638  * @param value
639  */
setConfig(const QString & key,const QVariant & value)640 void Configuration::setConfig(const QString &key, const QVariant &value)
641 {
642     if (asmOptions.contains(key)) {
643         s.setValue(key, value);
644     }
645 
646     Core()->setConfig(key, value);
647 }
648 
649 /**
650  * @brief this function will gather and return available translation for Cutter
651  * @return a list of all available translations
652  */
getAvailableTranslations()653 QStringList Configuration::getAvailableTranslations()
654 {
655     const auto &trDirs = Cutter::getTranslationsDirectories();
656 
657     QSet<QString> fileNamesSet;
658     for (const auto &trDir : trDirs) {
659         QDir dir(trDir);
660         if (!dir.exists()) {
661             continue;
662         }
663         const QStringList &currTrFileNames = dir.entryList(QStringList("cutter_*.qm"), QDir::Files,
664                                                            QDir::Name);
665         for (const auto &trFile : currTrFileNames) {
666             fileNamesSet << trFile;
667         }
668     }
669 
670     QStringList fileNames = fileNamesSet.values();
671     std::sort(fileNames.begin(), fileNames.end());
672     QStringList languages;
673     QString currLanguageName;
674     auto allLocales = QLocale::matchingLocales(QLocale::AnyLanguage, QLocale::AnyScript,
675                                                QLocale::AnyCountry);
676 
677     for (auto i : fileNames) {
678         QString localeName = i.mid(sizeof("cutter_") - 1, 2);
679         for (auto j : allLocales) {
680             if (j.name().startsWith(localeName)) {
681                 currLanguageName = j.nativeLanguageName();
682                 currLanguageName = currLanguageName.at(0).toUpper() +
683                                    currLanguageName.right(currLanguageName.length() - 1);
684                 languages << currLanguageName;
685                 break;
686             }
687         }
688     }
689     return languages << QLatin1String("English");
690 }
691 
692 /**
693  * @brief check if this is the first time Cutter's is executed on this computer
694  * @return true if this is first execution; otherwise returns false.
695  */
isFirstExecution()696 bool Configuration::isFirstExecution()
697 {
698     // check if a variable named firstExecution existed in the configuration
699     if (s.contains("firstExecution")) {
700         return false;
701     } else {
702         s.setValue("firstExecution", false);
703         return true;
704     }
705 }
706 
getSelectedDecompiler()707 QString Configuration::getSelectedDecompiler()
708 {
709     return s.value("selectedDecompiler").toString();
710 }
711 
setSelectedDecompiler(const QString & id)712 void Configuration::setSelectedDecompiler(const QString &id)
713 {
714     s.setValue("selectedDecompiler", id);
715 }
716 
getDecompilerAutoRefreshEnabled()717 bool Configuration::getDecompilerAutoRefreshEnabled()
718 {
719     return s.value("decompilerAutoRefresh", true).toBool();
720 }
721 
setDecompilerAutoRefreshEnabled(bool enabled)722 void Configuration::setDecompilerAutoRefreshEnabled(bool enabled)
723 {
724     s.setValue("decompilerAutoRefresh", enabled);
725 }
726 
enableDecompilerAnnotationHighlighter(bool useDecompilerHighlighter)727 void Configuration::enableDecompilerAnnotationHighlighter(bool useDecompilerHighlighter)
728 {
729     s.setValue("decompilerAnnotationHighlighter", useDecompilerHighlighter);
730     emit colorsUpdated();
731 }
732 
isDecompilerAnnotationHighlighterEnabled()733 bool Configuration::isDecompilerAnnotationHighlighterEnabled()
734 {
735     return s.value("decompilerAnnotationHighlighter", true).value<bool>();
736 }
737 
getBitmapTransparentState()738 bool Configuration::getBitmapTransparentState()
739 {
740     return s.value("bitmapGraphExportTransparency", false).value<bool>();
741 }
742 
getBitmapExportScaleFactor()743 double Configuration::getBitmapExportScaleFactor()
744 {
745     return s.value("bitmapGraphExportScale", 1.0).value<double>();
746 }
747 
setBitmapTransparentState(bool inputValueGraph)748 void Configuration::setBitmapTransparentState(bool inputValueGraph)
749 {
750     s.setValue("bitmapGraphExportTransparency", inputValueGraph);
751 }
752 
setBitmapExportScaleFactor(double inputValueGraph)753 void Configuration::setBitmapExportScaleFactor(double inputValueGraph)
754 {
755     s.setValue("bitmapGraphExportScale", inputValueGraph);
756 }
757 
setGraphSpacing(QPoint blockSpacing,QPoint edgeSpacing)758 void Configuration::setGraphSpacing(QPoint blockSpacing, QPoint edgeSpacing)
759 {
760     s.setValue("graph.blockSpacing", blockSpacing);
761     s.setValue("graph.edgeSpacing", edgeSpacing);
762 }
763 
getGraphBlockSpacing()764 QPoint Configuration::getGraphBlockSpacing()
765 {
766     return s.value("graph.blockSpacing", QPoint(20, 40)).value<QPoint>();
767 }
768 
getGraphEdgeSpacing()769 QPoint Configuration::getGraphEdgeSpacing()
770 {
771     return s.value("graph.edgeSpacing", QPoint(10, 10)).value<QPoint>();
772 }
773 
setOutputRedirectionEnabled(bool enabled)774 void Configuration::setOutputRedirectionEnabled(bool enabled)
775 {
776     this->outputRedirectEnabled = enabled;
777 }
778 
getOutputRedirectionEnabled() const779 bool Configuration::getOutputRedirectionEnabled() const
780 {
781     return outputRedirectEnabled;
782 }
783 
getGraphBlockEntryOffset()784 bool Configuration::getGraphBlockEntryOffset()
785 {
786     return s.value("graphBlockEntryOffset", true).value<bool>();
787 }
788 
setGraphBlockEntryOffset(bool enabled)789 void Configuration::setGraphBlockEntryOffset(bool enabled)
790 {
791     s.setValue("graphBlockEntryOffset", enabled);
792 }