mirror of
https://github.com/fergalmoran/flameshot.git
synced 2026-03-27 02:20:06 +00:00
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:
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");
|
||||
}
|
||||
Reference in New Issue
Block a user