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:
@@ -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
@@ -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;
|
||||
};
|
||||
|
||||
@@ -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
|
||||
@@ -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)
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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
413
src/utils/valuehandler.cpp
Normal 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
197
src/utils/valuehandler.h
Normal 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;
|
||||
};
|
||||
Reference in New Issue
Block a user