Implement config checking (#1859)

* Add error handling functions to ConfigHandler

Refurbished functions setValue and value which were previously unused.
These functions now set/get a setting with error handling.
Currently recognizes only errors recognizable by QSettings.

* Make use of value and setValue in ConfigHandler

Signed-off-by: Haris Gušić <harisgusic.dev@gmail.com>

* Add checker for unrecognized general options

Extraneous config options in [General] will be reported as errors.
Added some placeholder functions to be implemented in future commits.

* Introduce keysFromGroup function

Signed-off-by: Haris Gušić <harisgusic.dev@gmail.com>

* Check shortcut names for duplicates

Signed-off-by: Haris Gušić <harisgusic.dev@gmail.com>

* Fix notification spam

Signed-off-by: Haris Gušić <harisgusic.dev@gmail.com>

* Implement shortcut conflict checking

Signed-off-by: Haris Gušić <harisgusic.dev@gmail.com>

* Fix reading of fallbacks on error

If there is a config error, some values would not be loaded correctly.
Using the newly implemented function ConfigHandler::contains instead of
QSettings::contains solves this issue.

These changes reveal u bug that causes a crash on startup.

Signed-off-by: Haris Gušić <harisgusic.dev@gmail.com>

* Fix crashes introduced in previous commit

Because ConfigHandler is a dependency of most other classes,
calling functions from those classes inside ConfigHandler caused
infinite recursions in some cases.

Signed-off-by: Haris Gušić <harisgusic.dev@gmail.com>

* Add config file watcher

Signed-off-by: Haris Gušić <harisgusic.dev@gmail.com>

* Add missing config options

Signed-off-by: Haris Gušić <harisgusic.dev@gmail.com>

* Fix bug in shortcut conflict detection

Signed-off-by: Haris Gušić <harisgusic.dev@gmail.com>

* Add error resolved notification

Signed-off-by: Haris Gušić <harisgusic.dev@gmail.com>

* Add GUI error message overlay

Signed-off-by: Haris Gušić <harisgusic.dev@gmail.com>

* Add indicator in config window

Signed-off-by: Haris Gušić <harisgusic.dev@gmail.com>

* Use ConfigHandler::fileChanged in ConfigWindow

Signed-off-by: Haris Gušić <harisgusic.dev@gmail.com>

* Fix watcher sometimes not firing

Signed-off-by: Haris Gušić <harisgusic.dev@gmail.com>

* Improve config file watching performance

Signed-off-by: Haris Gušić <harisgusic.dev@gmail.com>

* Add new way to handle config

This is only a fundamental implementation. Future commits will replace
everything with this new paradigm.

Signed-off-by: Haris Gušić <harisgusic.dev@gmail.com>

* Fix getButtons and related functions

Also refactored related code to use QList instead of QVector because
QSettings does not work well with QVector.

Signed-off-by: Haris Gušić <harisgusic.dev@gmail.com>

* Make good use of the new way

* Implement proper checking for basic types

Everything is covered, apart from KeySequence.

* Move fallback path to ExistingDir value handler

Signed-off-by: Haris Gušić <harisgusic.dev@gmail.com>

* Use consistent naming scheme in ConfigHandler

Signed-off-by: Haris Gušić <harisgusic.dev@gmail.com>

* Implement config getters/setters via macro

Signed-off-by: Haris Gušić <harisgusic.dev@gmail.com>

* Surround text with tr and clang-format

Signed-off-by: Haris Gušić <harisgusic.dev@gmail.com>

* Fix colors being saved obfuscated

Signed-off-by: Haris Gušić <harisgusic.dev@gmail.com>

* Add ValueHandler::represenation

Signed-off-by: Haris Gušić <harisgusic.dev@gmail.com>

* Move ValueHandler to separate files

Signed-off-by: Haris Gušić <harisgusic.dev@gmail.com>

* confighandler.cpp: rename macro CUSTOM to OPTION

Signed-off-by: Haris Gušić <harisgusic.dev@gmail.com>

* Fix bug with shortcut conflict checker

Signed-off-by: Haris Gušić <harisgusic.dev@gmail.com>

* Update docs and fix setAllTheButtons

Signed-off-by: Haris Gušić <harisgusic.dev@gmail.com>

* Handle filenamePattern properly

Signed-off-by: Haris Gušić <harisgusic.dev@gmail.com>

* Fix failing build due to wrong function name

Signed-off-by: Haris Gušić <harisgusic.dev@gmail.com>

* Fix QSet error due to Qt version mismatch

Signed-off-by: Haris Gušić <harisgusic.dev@gmail.com>

* Replace QSharedPointer::get with data for older Qt versions

Signed-off-by: Haris Gušić <harisgusic.dev@gmail.com>

* Fix failing build on MacOS and ubuntu 18.04

Signed-off-by: Haris Gušić <harisgusic.dev@gmail.com>

* Add column headers to recognizedGeneralOptions map

Signed-off-by: Haris Gušić <harisgusic.dev@gmail.com>

* Fix ubuntu 18.04 error

Signed-off-by: Haris Gušić <harisgusic.dev@gmail.com>

* Fix false positive when shortcuts empty

Signed-off-by: Haris Gušić <harisgusic.dev@gmail.com>

* Fix wrong shortcut group prefix

Signed-off-by: Haris Gušić <harisgusic.dev@gmail.com>

* Implement proper shortcut checking

Signed-off-by: Haris Gušić <harisgusic.dev@gmail.com>

* Add shortcut map in ConfigHandler

Signed-off-by: Haris Gušić <harisgusic.dev@gmail.com>

* Move ConfigShortcuts functions to ShortcutsWidget

Signed-off-by: Haris Gušić <harisgusic.dev@gmail.com>

* Fix minor bugs

Signed-off-by: Haris Gušić <harisgusic.dev@gmail.com>

* Add fallback scheme: Pictures, HOME, TMP

Signed-off-by: Haris Gušić <harisgusic.dev@gmail.com>

* Add config --check CLI option

Signed-off-by: Haris Gušić <harisgusic.dev@gmail.com>

* Add config error log to GUI

Signed-off-by: Haris Gušić <harisgusic.dev@gmail.com>

* Rename ValueHandler::description to expected

* Convert Qt's #AARRGGBB to #RRGGBBAA and vice versa

Signed-off-by: Haris Gušić <harisgusic.dev@gmail.com>

* Remove obsolete `saveAfterCopyPath`

Signed-off-by: Haris Gušić <harisgusic.dev@gmail.com>

* Fix errors in example config

Also added an additional ; in front of actual comments to differentiate
them from commented options.

Signed-off-by: Haris Gušić <harisgusic.dev@gmail.com>

* Allow special value 'picker' in userColors

Signed-off-by: Haris Gušić <harisgusic.dev@gmail.com>

* Allow only name, #RRGGBB, and #RRGGBBAA color formats

Signed-off-by: Haris Gušić <harisgusic.dev@gmail.com>
This commit is contained in:
Haris Gušić
2021-09-15 18:56:01 +02:00
committed by GitHub
parent a7e88e60de
commit d1428889b9
35 changed files with 1665 additions and 859 deletions

View File

@@ -5,7 +5,7 @@ target_sources(
filenamehandler.h
screengrabber.h
systemnotification.h
configshortcuts.h
valuehandler.h
request.h
strfparse.h
)
@@ -16,6 +16,7 @@ target_sources(
screengrabber.cpp
confighandler.cpp
systemnotification.cpp
valuehandler.cpp
screenshotsaver.cpp
dbusutils.cpp
globalvalues.cpp
@@ -24,7 +25,6 @@ target_sources(
pathinfo.cpp
colorutils.cpp
history.cpp
configshortcuts.cpp
strfparse.cpp
request.cpp
)

File diff suppressed because it is too large Load Diff

View File

@@ -9,121 +9,141 @@
#include <QVariant>
#include <QVector>
class ConfigHandler
class QFileSystemWatcher;
class ValueHandler;
template<class T>
class QSharedPointer;
class QTextStream;
/**
* Declare and implement a getter for a config option. `KEY` is the option key
* as it appears in the config file, `TYPE` is the C++ type. At the same time
* `KEY` is the name of the generated getter function.
*/
#define CONFIG_GETTER(KEY, TYPE) \
TYPE KEY() { return value(QStringLiteral(#KEY)).value<TYPE>(); }
/**
* Declare and implement a setter for a config option. `FUNC` is the name of the
* generated function, `KEY` is the option key as it appears in the config file
* and `TYPE` is the C++ type.
*/
#define CONFIG_SETTER(FUNC, KEY, TYPE) \
void FUNC(const TYPE& value) \
{ \
setValue(QStringLiteral(#KEY), QVariant::fromValue(value)); \
}
/**
* Combines the functionality of `CONFIG_GETTER` and `CONFIG_SETTER`. `GETFUNC`
* is simultaneously the name of the getter function and the option key as it
* appears in the config file. `SETFUNC` is the name of the setter function.
* `TYPE` is the C++ type of the value.
*/
#define CONFIG_GETTER_SETTER(GETFUNC, SETFUNC, TYPE) \
CONFIG_GETTER(GETFUNC, TYPE) \
CONFIG_SETTER(SETFUNC, GETFUNC, TYPE)
class ConfigHandler : public QObject
{
Q_OBJECT
public:
explicit ConfigHandler();
explicit ConfigHandler(bool skipInitialErrorCheck = false);
QVector<CaptureToolButton::ButtonType> getButtons();
void setButtons(const QVector<CaptureToolButton::ButtonType>&);
static ConfigHandler* getInstance();
QVector<QColor> getUserColors();
// Definitions of getters and setters for config options
// Some special cases are implemented regularly, without the macro
// NOTE: When adding new options, make sure to add an entry in
// recognizedGeneralOptions in the cpp file.
CONFIG_GETTER_SETTER(userColors, setUserColors, QVector<QColor>);
CONFIG_GETTER_SETTER(savePath, setSavePath, QString)
CONFIG_GETTER_SETTER(savePathFixed, setSavePathFixed, bool)
CONFIG_GETTER_SETTER(uiColor, setUiColor, QColor)
CONFIG_GETTER_SETTER(contrastUiColor, setContrastUiColor, QColor)
CONFIG_GETTER_SETTER(drawColor, setDrawColor, QColor)
CONFIG_GETTER_SETTER(fontFamily, setFontFamily, QString)
CONFIG_GETTER_SETTER(showHelp, setShowHelp, bool)
CONFIG_GETTER_SETTER(showSidePanelButton, setShowSidePanelButton, bool)
CONFIG_GETTER_SETTER(showDesktopNotification,
setShowDesktopNotification,
bool)
CONFIG_GETTER_SETTER(filenamePattern, setFilenamePattern, QString)
CONFIG_GETTER_SETTER(disabledTrayIcon, setDisabledTrayIcon, bool)
CONFIG_GETTER_SETTER(drawThickness, setDrawThickness, int)
CONFIG_GETTER_SETTER(drawFontSize, setDrawFontSize, int)
CONFIG_GETTER_SETTER(keepOpenAppLauncher, setKeepOpenAppLauncher, bool)
CONFIG_GETTER_SETTER(checkForUpdates, setCheckForUpdates, bool)
CONFIG_GETTER_SETTER(showStartupLaunchMessage,
setShowStartupLaunchMessage,
bool)
CONFIG_GETTER_SETTER(contrastOpacity, setContrastOpacity, int)
CONFIG_GETTER_SETTER(copyAndCloseAfterUpload,
setCopyAndCloseAfterUpload,
bool)
CONFIG_GETTER_SETTER(historyConfirmationToDelete,
setHistoryConfirmationToDelete,
bool)
CONFIG_GETTER_SETTER(uploadHistoryMax, setUploadHistoryMax, int)
CONFIG_GETTER_SETTER(saveAfterCopy, setSaveAfterCopy, bool)
CONFIG_GETTER_SETTER(copyPathAfterSave, setCopyPathAfterSave, bool)
CONFIG_GETTER_SETTER(useJpgForClipboard, setUseJpgForClipboard, bool)
CONFIG_GETTER_SETTER(ignoreUpdateToVersion,
setIgnoreUpdateToVersion,
QString)
CONFIG_GETTER_SETTER(undoLimit, setUndoLimit, int)
CONFIG_GETTER_SETTER(buttons,
setButtons,
QList<CaptureToolButton::ButtonType>)
QString savePath();
void setSavePath(const QString&);
bool savePathFixed();
void setSavePathFixed(bool);
QColor uiMainColorValue();
void setUIMainColor(const QColor&);
QColor uiContrastColorValue();
void setUIContrastColor(const QColor&);
QColor drawColorValue();
void setDrawColor(const QColor&);
void setFontFamily(const QString&);
const QString& fontFamily();
bool showHelpValue();
void setShowHelp(const bool);
bool showSidePanelButtonValue();
void setShowSidePanelButton(const bool);
bool desktopNotificationValue();
void setDesktopNotification(const bool);
QString filenamePatternDefault();
QString filenamePatternValue();
void setFilenamePattern(const QString&);
bool disabledTrayIconValue();
void setDisabledTrayIcon(const bool);
int drawThicknessValue();
void setDrawThickness(const int);
int drawFontSizeValue();
void setDrawFontSize(const int);
bool keepOpenAppLauncherValue();
void setKeepOpenAppLauncher(const bool);
bool checkForUpdates();
void setCheckForUpdates(const bool);
bool verifyLaunchFile();
bool startupLaunchValue();
// SPECIAL CASES
bool startupLaunch();
void setStartupLaunch(const bool);
bool showStartupLaunchMessage();
void setShowStartupLaunchMessage(const bool);
int contrastOpacityValue();
void setContrastOpacity(const int);
bool copyAndCloseAfterUploadEnabled();
void setCopyAndCloseAfterUploadEnabled(const bool);
bool historyConfirmationToDelete();
void setHistoryConfirmationToDelete(const bool save);
int uploadHistoryMaxSizeValue();
void setUploadHistoryMaxSize(const int);
bool saveAfterCopyValue();
void setSaveAfterCopy(const bool);
bool copyPathAfterSaveEnabled();
void setCopyPathAfterSaveEnabled(const bool);
bool useJpgForClipboard() const;
void setUseJpgForClipboard(const bool);
void setSaveAsFileExtension(const QString& extension);
QString getSaveAsFileExtension();
void setDefaultSettings();
QString saveAsFileExtension();
CONFIG_SETTER(setSaveAsFileExtension, setSaveAsFileExtension, QString)
void setAllTheButtons();
void setIgnoreUpdateToVersion(const QString& text);
QString ignoreUpdateToVersion();
void setUndoLimit(int value);
int undoLimit();
bool setShortcut(const QString&, const QString&);
const QString& shortcut(const QString&);
// DEFAULTS
QString filenamePatternDefault();
void setDefaultSettings();
QString configFilePath() const;
void setValue(const QString& group,
const QString& key,
const QVariant& value);
QVariant& value(const QString& group, const QString& key);
// GENERIC GETTERS AND SETTERS
bool setShortcut(const QString&, const QString&);
QString shortcut(const QString&);
void setValue(const QString& key, const QVariant& value);
QVariant value(const QString& key) const;
// INFO
const QSet<QString>& recognizedGeneralOptions() const;
const QSet<QString>& recognizedShortcutNames() const;
QSet<QString> keysFromGroup(const QString& group) const;
// ERROR HANDLING
bool checkForErrors(QTextStream* log = nullptr) const;
bool checkUnrecognizedSettings(QTextStream* log = nullptr) const;
bool checkShortcutConflicts(QTextStream* log = nullptr) const;
bool checkSemantics(QTextStream* log = nullptr) const;
void checkAndHandleError() const;
void setErrorState(bool error) const;
bool hasError() const;
QString errorMessage() const;
signals:
void error() const;
void errorResolved() const;
void fileChanged() const;
private:
QString m_strRes;
QVariant m_varRes;
QSettings m_settings;
QVector<QStringList> m_shortcuts;
mutable QSettings m_settings;
bool normalizeButtons(QVector<int>&);
static bool m_hasError, m_errorCheckPending, m_skipNextErrorCheck;
static QSharedPointer<QFileSystemWatcher> m_configWatcher;
QVector<CaptureToolButton::ButtonType> fromIntToButton(
const QVector<int>& l);
QVector<int> fromButtonToInt(
const QVector<CaptureToolButton::ButtonType>& l);
void ensureFileWatched() const;
QSharedPointer<ValueHandler> valueHandler(const QString& key) const;
void assertKeyRecognized(const QString& key) const;
bool isShortcut(const QString& key) const;
QString baseName(QString key) const;
};

View File

@@ -1,26 +0,0 @@
#ifndef CONFIGSHORTCUTS_H
#define CONFIGSHORTCUTS_H
#include "src/widgets/capture/capturetoolbutton.h"
#include <QKeySequence>
#include <QString>
#include <QStringList>
#include <QVector>
class ConfigShortcuts
{
public:
ConfigShortcuts();
const QVector<QStringList>& captureShortcutsDefault(
const QVector<CaptureToolButton::ButtonType>& buttons);
const QKeySequence& captureShortcutDefault(const QString& buttonType);
private:
QVector<QStringList> m_shortcuts;
QKeySequence m_ks;
void addShortcut(const QString& shortcutName, const QString& description);
};
#endif // CONFIGSHORTCUTS_H

View File

@@ -27,7 +27,7 @@ FileNameHandler::FileNameHandler(QObject* parent)
QString FileNameHandler::parsedPattern()
{
return parseFilename(ConfigHandler().filenamePatternValue());
return parseFilename(ConfigHandler().filenamePattern());
}
QString FileNameHandler::parseFilename(const QString& name)

View File

@@ -57,7 +57,7 @@ const QList<QString>& History::history()
QDir::Files,
QDir::Time);
int cnt = 0;
int max = ConfigHandler().uploadHistoryMaxSizeValue();
int max = ConfigHandler().uploadHistoryMax();
m_thumbs.clear();
foreach (QString fileName, images) {
if (++cnt <= max) {

View File

@@ -58,7 +58,7 @@ void ScreenshotSaver::saveToClipboard(const QPixmap& capture)
{
// If we are able to properly save the file, save the file and copy to
// clipboard.
if ((ConfigHandler().saveAfterCopyValue()) &&
if ((ConfigHandler().saveAfterCopy()) &&
(!ConfigHandler().savePath().isEmpty())) {
saveToFilesystem(capture,
ConfigHandler().savePath(),
@@ -126,7 +126,7 @@ QString ScreenshotSaver::ShowSaveFileDialog(QWidget* parent,
mimeTypeList.append(mimeType);
dialog.setMimeTypeFilters(mimeTypeList);
QString suffix = ConfigHandler().getSaveAsFileExtension();
QString suffix = ConfigHandler().saveAsFileExtension();
QString defaultMimeType =
QMimeDatabase().mimeTypeForFile("image" + suffix).name();
dialog.selectMimeTypeFilter(defaultMimeType);
@@ -179,7 +179,7 @@ bool ScreenshotSaver::saveToFilesystemGUI(const QPixmap& capture)
QString msg = QObject::tr("Capture saved as ") + savePath;
if (config.copyPathAfterSaveEnabled()) {
if (config.copyPathAfterSave()) {
msg =
QObject::tr("Capture is saved and copied to the clipboard as ") +
savePath;
@@ -190,7 +190,7 @@ bool ScreenshotSaver::saveToFilesystemGUI(const QPixmap& capture)
Controller::getInstance()->sendCaptureSaved(
m_id, QFileInfo(savePath).canonicalFilePath());
if (config.copyPathAfterSaveEnabled()) {
if (config.copyPathAfterSave()) {
QApplication::clipboard()->setText(savePath);
}

View File

@@ -35,7 +35,7 @@ void SystemNotification::sendMessage(const QString& text,
const QString& savePath,
const int timeout)
{
if (!ConfigHandler().desktopNotificationValue()) {
if (!ConfigHandler().showDesktopNotification()) {
return;
}

413
src/utils/valuehandler.cpp Normal file
View File

@@ -0,0 +1,413 @@
#include "valuehandler.h"
#include "confighandler.h"
#include <QColor>
#include <QFileInfo>
#include <QStandardPaths>
#include <QVariant>
// VALUE HANDLER
QVariant ValueHandler::value(const QVariant& val)
{
if (!val.isValid() || !check(val)) {
return fallback();
} else {
return process(val);
}
}
QVariant ValueHandler::fallback()
{
return QVariant();
}
QVariant ValueHandler::representation(const QVariant& val)
{
return val.toString();
}
QString ValueHandler::expected()
{
return {};
}
QVariant ValueHandler::process(const QVariant& val)
{
return val;
}
// BOOL
Bool::Bool(bool def)
: m_def(def)
{}
bool Bool::check(const QVariant& val)
{
QString str = val.toString();
if (str != "true" && str != "false") {
return false;
}
return true;
}
QVariant Bool::fallback()
{
return m_def;
}
QString Bool::expected()
{
return QStringLiteral("true or false");
}
// STRING
String::String(const QString& def)
: m_def(def)
{}
bool String::check(const QVariant&)
{
return true;
}
QVariant String::fallback()
{
return m_def;
}
QString String::expected()
{
return QStringLiteral("string");
}
// COLOR
Color::Color(const QColor& def)
: m_def(def)
{}
bool Color::check(const QVariant& val)
{
QString str = val.toString();
// Disable #RGB, #RRRGGGBBB and #RRRRGGGGBBBB formats that QColor supports
return QColor::isValidColor(str) &&
(str[0] != '#' ||
(str.length() != 4 && str.length() != 10 && str.length() != 13));
}
QVariant Color::process(const QVariant& val)
{
QString str = val.toString();
QColor color(str);
if (str.length() == 9 && str[0] == '#') {
// Convert #RRGGBBAA (flameshot) to #AARRGGBB (QColor)
int blue = color.blue();
color.setBlue(color.green());
color.setGreen(color.red());
color.setRed(color.alpha());
color.setAlpha(blue);
}
return color;
}
QVariant Color::fallback()
{
return m_def;
}
QVariant Color::representation(const QVariant& val)
{
QString str = val.toString();
QColor color(str);
if (str.length() == 9 && str[0] == '#') {
// Convert #AARRGGBB (QColor) to #RRGGBBAA (flameshot)
int alpha = color.alpha();
color.setAlpha(color.red());
color.setRed(color.green());
color.setGreen(color.blue());
color.setBlue(alpha);
}
return color.name();
}
QString Color::expected()
{
return QStringLiteral("color name or hex value");
}
// BOUNDED INT
BoundedInt::BoundedInt(int min, int max, int def)
: m_min(min)
, m_max(max)
, m_def(def)
{}
bool BoundedInt::check(const QVariant& val)
{
QString str = val.toString();
bool conversionOk;
int num = str.toInt(&conversionOk);
return conversionOk && (m_max < m_min || num <= m_max);
}
QVariant BoundedInt::fallback()
{
return m_def;
}
QString BoundedInt::expected()
{
return QStringLiteral("number between %1 and %2").arg(m_min).arg(m_max);
}
// LOWER BOUNDED INT
LowerBoundedInt::LowerBoundedInt(int min, int def)
: m_min(min)
, m_def(def)
{}
bool LowerBoundedInt::check(const QVariant& val)
{
QString str = val.toString();
bool conversionOk;
int num = str.toInt(&conversionOk);
return conversionOk && num >= m_min;
}
QVariant LowerBoundedInt::fallback()
{
return m_def;
}
QString LowerBoundedInt::expected()
{
return QStringLiteral("number >= %1").arg(m_min);
}
// KEY SEQUENCE
KeySequence::KeySequence(const QKeySequence& fallback)
: m_fallback(fallback)
{}
bool KeySequence::check(const QVariant& val)
{
QString str = val.toString();
if (!str.isEmpty() && QKeySequence(str).toString().isEmpty()) {
return false;
}
return true;
}
QVariant KeySequence::fallback()
{
return m_fallback;
}
QString KeySequence::expected()
{
return QStringLiteral("keyboard shortcut");
}
// EXISTING DIR
bool ExistingDir::check(const QVariant& val)
{
if (!val.canConvert(QVariant::String) || val.toString().isEmpty()) {
return false;
}
QFileInfo info(val.toString());
return info.isDir() && info.exists();
}
QVariant ExistingDir::fallback()
{
using SP = QStandardPaths;
for (auto location :
{ SP::PicturesLocation, SP::HomeLocation, SP::TempLocation }) {
QString path = SP::writableLocation(location);
if (QFileInfo(path).isDir()) {
return path;
}
}
return {};
}
QString ExistingDir::expected()
{
return QStringLiteral("existing directory");
}
// FILENAME PATTERN
bool FilenamePattern::check(const QVariant&)
{
return true;
}
QVariant FilenamePattern::fallback()
{
return ConfigHandler().filenamePatternDefault();
}
QVariant FilenamePattern::process(const QVariant& val)
{
QString str = val.toString();
return !str.isEmpty() ? val : fallback();
}
QString FilenamePattern::expected()
{
return QStringLiteral("please edit using the GUI");
}
// BUTTON LIST
using BType = CaptureToolButton::ButtonType;
using BList = QList<BType>;
bool ButtonList::check(const QVariant& val)
{
using CTB = CaptureToolButton;
auto allButtons = CTB::getIterableButtonTypes();
for (int btn : val.value<QList<int>>()) {
if (!allButtons.contains(static_cast<CTB::ButtonType>(btn))) {
return false;
}
}
return true;
}
// Helper
void sortButtons(BList& buttons)
{
std::sort(buttons.begin(), buttons.end(), [](BType a, BType b) {
return CaptureToolButton::getPriorityByButton(a) <
CaptureToolButton::getPriorityByButton(b);
});
}
QVariant ButtonList::process(const QVariant& val)
{
QList<int> intButtons = val.value<QList<int>>();
auto buttons = ButtonList::fromIntList(intButtons);
sortButtons(buttons);
return QVariant::fromValue(buttons);
}
QVariant ButtonList::fallback()
{
auto buttons = CaptureToolButton::getIterableButtonTypes();
buttons.removeOne(CaptureToolButton::TYPE_SIZEDECREASE);
buttons.removeOne(CaptureToolButton::TYPE_SIZEINCREASE);
sortButtons(buttons);
return QVariant::fromValue(buttons);
}
QVariant ButtonList::representation(const QVariant& val)
{
auto intList = toIntList(val.value<BList>());
normalizeButtons(intList);
return QVariant::fromValue(intList);
}
QString ButtonList::expected()
{
return QStringLiteral("please don't edit by hand");
}
QList<CaptureToolButton::ButtonType> ButtonList::fromIntList(
const QList<int>& l)
{
QList<CaptureToolButton::ButtonType> buttons;
buttons.reserve(l.size());
for (auto const i : l)
buttons << static_cast<CaptureToolButton::ButtonType>(i);
return buttons;
}
QList<int> ButtonList::toIntList(const QList<CaptureToolButton::ButtonType>& l)
{
QList<int> buttons;
buttons.reserve(l.size());
for (auto const i : l)
buttons << static_cast<int>(i);
return buttons;
}
bool ButtonList::normalizeButtons(QList<int>& buttons)
{
QList<int> listTypesInt =
toIntList(CaptureToolButton::getIterableButtonTypes());
bool hasChanged = false;
for (int i = 0; i < buttons.size(); i++) {
if (!listTypesInt.contains(buttons.at(i))) {
buttons.removeAt(i);
hasChanged = true;
}
}
return hasChanged;
}
// USER COLORS
bool UserColors::check(const QVariant& val)
{
if (!val.isValid()) {
return true;
}
if (!val.canConvert(QVariant::StringList)) {
return false;
}
for (const QString& str : val.toStringList()) {
if (!QColor::isValidColor(str) && str != "picker") {
return false;
}
}
return true;
}
QVariant UserColors::process(const QVariant& val)
{
QStringList strColors = val.toStringList();
if (strColors.isEmpty()) {
return fallback();
}
QVector<QColor> colors;
colors.reserve(strColors.size());
for (const QString& str : strColors) {
if (str != "picker") {
colors.append(QColor(str));
} else {
colors.append(QColor());
}
}
return QVariant::fromValue(colors);
}
QVariant UserColors::fallback()
{
return QVariant::fromValue(QVector<QColor>{ Qt::darkRed,
Qt::red,
Qt::yellow,
Qt::green,
Qt::darkGreen,
Qt::cyan,
Qt::blue,
Qt::magenta,
Qt::darkMagenta,
QColor() });
}
QString UserColors::expected()
{
return QStringLiteral("list of colors separated by comma");
}

197
src/utils/valuehandler.h Normal file
View File

@@ -0,0 +1,197 @@
#pragma once
#include "src/widgets/capture/capturetoolbutton.h"
#include <QColor>
#include <QList>
#include <QString>
class QVariant;
/**
* @brief Handles the value of a configuration option (abstract class).
*
* Each configuration option is represented as a `QVariant`. If the option was
* not specified in a config file, the `QVariant` will be invalid.
*
* Each option will usually be handled in three different ways:
* - have its value checked for semantic errors (type, format, etc).
* @see ValueHandler::check
* - have its value (that was taken from the config file) adapted for proper
* use.
* @see ValueHandler::value
* - provided a fallback value in case: the config does not explicitly specify
* it, or the config contains an error and is globally falling back to
* defaults.
* @see ValueHandler::fallback.
* - some options may want to be stored in the config file in a different way
* than the default one provided by `QVariant`.
* @see ValueHandler::representation
*
* @note Please see the documentation of the functions to learn when you should
* override each.
*
*/
class ValueHandler
{
public:
/**
* @brief Check the value semantically.
* @param val The value that was read from the config file
* @return Whether the value is correct
* @note The function should presume that `val.isValid()` is true.
*/
virtual bool check(const QVariant& val) = 0;
/**
* @brief Adapt the value for proper use.
* @param val The value that was read from the config file
* @return The modified value
*
* If the value is invalid (unspecified in the config) or does not pass
* `check`, the fallback will be returned. Otherwise the value is processed
* by `process` and then returned.
*
* @note Cannot be overriden
* @see fallback, process
*/
QVariant value(const QVariant& val);
/**
* @brief Fallback value (default value).
*/
virtual QVariant fallback();
/**
* @brief Return the representaion of the value in the config file.
*
* Override this if you want to write the value in a different format than
* the one provided by `QVariant`.
*/
virtual QVariant representation(const QVariant& val);
/**
* @brief The expected value (descriptive).
* Used when reporting configuration errors.
*/
virtual QString expected();
protected:
/**
* @brief Process a value, presuming it is a valid `QVariant`.
* @param val The value that was read from the config file
* @return The processed value
* @note You will usually want to override this. In rare cases, you may want
* to override `value`.
*/
virtual QVariant process(const QVariant& val);
};
class Bool : public ValueHandler
{
public:
Bool(bool def);
bool check(const QVariant& val) override;
QVariant fallback() override;
QString expected() override;
private:
bool m_def;
};
class String : public ValueHandler
{
public:
String(const QString& def);
bool check(const QVariant&) override;
QVariant fallback() override;
QString expected() override;
private:
QString m_def;
};
class Color : public ValueHandler
{
public:
Color(const QColor& def);
bool check(const QVariant& val) override;
QVariant process(const QVariant& val) override;
QVariant fallback() override;
QVariant representation(const QVariant& val) override;
QString expected() override;
private:
QColor m_def;
};
class BoundedInt : public ValueHandler
{
public:
BoundedInt(int min, int max, int def);
bool check(const QVariant& val) override;
virtual QVariant fallback() override;
QString expected() override;
private:
int m_min, m_max, m_def;
};
class LowerBoundedInt : public ValueHandler
{
public:
LowerBoundedInt(int min, int def);
bool check(const QVariant& val) override;
QVariant fallback() override;
QString expected() override;
private:
int m_min, m_def;
};
class KeySequence : public ValueHandler
{
public:
KeySequence(const QKeySequence& fallback = {});
bool check(const QVariant& val) override;
QVariant fallback() override;
QString expected() override;
private:
QKeySequence m_fallback;
};
class ExistingDir : public ValueHandler
{
bool check(const QVariant& val) override;
QVariant fallback() override;
QString expected() override;
};
class FilenamePattern : public ValueHandler
{
bool check(const QVariant&) override;
QVariant fallback() override;
QVariant process(const QVariant&) override;
QString expected() override;
};
class ButtonList : public ValueHandler
{
public:
bool check(const QVariant& val) override;
QVariant process(const QVariant& val) override;
QVariant fallback() override;
QVariant representation(const QVariant& val) override;
QString expected() override;
// UTILITY FUNCTIONS
static QList<CaptureToolButton::ButtonType> fromIntList(const QList<int>&);
static QList<int> toIntList(const QList<CaptureToolButton::ButtonType>& l);
static bool normalizeButtons(QList<int>& buttons);
};
class UserColors : public ValueHandler
{
bool check(const QVariant& val) override;
QVariant process(const QVariant& val) override;
QVariant fallback() override;
QString expected() override;
};