diff --git a/flameshot.pro b/flameshot.pro index 56637adf..7572644a 100644 --- a/flameshot.pro +++ b/flameshot.pro @@ -74,6 +74,9 @@ SOURCES += src/main.cpp\ src/utils/confighandler.cpp \ src/core/controller.cpp \ src/utils/systemnotification.cpp \ + src/cli/commandlineparser.cpp \ + src/cli/commandoption.cpp \ + src/cli/commandargument.cpp HEADERS += \ src/capture/buttonhandler.h \ @@ -112,6 +115,9 @@ HEADERS += \ src/utils/confighandler.h \ src/core/controller.h \ src/utils/systemnotification.h \ + src/cli/commandlineparser.h \ + src/cli/commandoption.h \ + src/cli/commandargument.h RESOURCES += \ graphics.qrc diff --git a/src/cli/commandargument.cpp b/src/cli/commandargument.cpp new file mode 100644 index 00000000..6b6b3b81 --- /dev/null +++ b/src/cli/commandargument.cpp @@ -0,0 +1,54 @@ +// Copyright 2017 Alejandro Sirgo Rica +// +// This file is part of Flameshot. +// +// Flameshot is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Flameshot is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Flameshot. If not, see . + +#include "commandargument.h" + +CommandArgument::CommandArgument() { + +} + +CommandArgument::CommandArgument(const QString &name, + const QString &description) : + m_name(name), m_description(description) +{ + +} + +void CommandArgument::setName(const QString &name) { + m_name = name; +} + +QString CommandArgument::name() const { + return m_name; +} + +void CommandArgument::setDescription(const QString &description) { + m_description = description; +} + +QString CommandArgument::description() const { + return m_description; +} + +bool CommandArgument::isRoot() const { + return m_name.isEmpty() && m_description.isEmpty(); +} + +bool CommandArgument::operator ==(const CommandArgument &arg) const { + return m_description == arg.m_description + && m_name == arg.m_name; +} diff --git a/src/cli/commandargument.h b/src/cli/commandargument.h new file mode 100644 index 00000000..821452d0 --- /dev/null +++ b/src/cli/commandargument.h @@ -0,0 +1,45 @@ +// Copyright 2017 Alejandro Sirgo Rica +// +// This file is part of Flameshot. +// +// Flameshot is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Flameshot is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Flameshot. If not, see . + +#ifndef COMMANDARGUMENT_H +#define COMMANDARGUMENT_H + +#include + +class CommandArgument +{ +public: + CommandArgument(); + explicit CommandArgument(const QString &name, const QString &description); + + void setName(const QString &name); + QString name() const; + + void setDescription(const QString &description); + QString description() const; + + bool isRoot() const; + + bool operator ==(const CommandArgument &arg) const; + +private: + QString m_name; + QString m_description; + +}; + +#endif // COMMANDARGUMENT_H diff --git a/src/cli/commandlineparser.cpp b/src/cli/commandlineparser.cpp new file mode 100644 index 00000000..4acfb9e2 --- /dev/null +++ b/src/cli/commandlineparser.cpp @@ -0,0 +1,403 @@ +// Copyright 2017 Alejandro Sirgo Rica +// +// This file is part of Flameshot. +// +// Flameshot is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Flameshot is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Flameshot. If not, see . + +#include "commandlineparser.h" +#include +#include + +CommandLineParser::CommandLineParser() : + m_description(qApp->applicationName()) +{ + +} + +namespace { + +QTextStream out(stdout); +QTextStream err(stderr); + +auto versionOption = CommandOption({"v", "version"}, + "Displays version information"); +auto helpOption = CommandOption({"h", "help"}, + "Displays this help"); + +inline QStringList addDashToOptionNames(const QStringList &names) { + QStringList dashedNames; + for (QString name: names) { + // prepend "-" to single character options, and "--" to the others + QString dashedName = (name.length() == 1) ? + QString("-%1").arg(name) : + QString("--%1").arg(name); + dashedNames << dashedName; + } + return dashedNames; +} + +QString optionsToString(const QList &options, + const QList &arguments) { + int size = 0; // track the largest size + QStringList dashedOptionList; + // save the dashed options and its size in order to print the description + // of every option at the same horizontal character position. + for (auto const &option: options) { + QStringList dashedOptions = addDashToOptionNames(option.names()); + QString joinedDashedOptions = dashedOptions.join(", "); + if(!option.valueName().isEmpty()) { + joinedDashedOptions += QString(" <%1>") + .arg(option.valueName()); + } + if (joinedDashedOptions.length() > size) { + size = joinedDashedOptions.length(); + } + dashedOptionList << joinedDashedOptions; + } + // check the lenght of the arguments + for (auto const &arg: arguments) { + if(arg.name().length() > size) + size = arg.name().length(); + } + // generate the text + QString result; + if(!dashedOptionList.isEmpty()) { + result += "Options:\n"; + for (int i = 0; i < options.length(); ++i) { + result += QString(" %1 %2\n") + .arg(dashedOptionList.at(i).leftJustified(size, ' ')) + .arg(options.at(i).description()); + } + if(!arguments.isEmpty()) { + result += "\n"; + } + } + if (!arguments.isEmpty()) { + result += "Arguments:\n"; + } + for (int i = 0; i < arguments.length(); ++i) { + result += QString(" %1 %2\n") + .arg(arguments.at(i).name().leftJustified(size, ' ')) + .arg(arguments.at(i).description()); + } + return result; +} + +} // namespace + +bool CommandLineParser::processArgs(const QStringList &args, + QStringList::const_iterator &actualIt, + Node * &actualNode) +{ + QString argument = *actualIt; + bool ok = true; + if (actualNode->subNodes.contains(argument)) { + actualNode = &(*actualNode->subNodes.find(argument)); + auto nextArg = actualNode->argument; + m_foundArgs.append(nextArg); + // check next is help + ++actualIt; + ok = processIfOptionIsHelp(args, actualIt, actualNode); + --actualIt; + } else { + ok = false; + out << QString("'%1' is not a valid argument.").arg(argument); + } + return ok; +} + +bool CommandLineParser::processOptions(const QStringList &args, + QStringList::const_iterator &actualIt, + Node *const actualNode) +{ + QString arg = *actualIt; + bool ok = true; + // track values + int equalsPos = arg.indexOf("="); + QString valueStr; + if (equalsPos != -1) { + valueStr = arg.mid(equalsPos +1); // right + arg = arg.mid(0, equalsPos); // left + } + // check format -x --xx... + bool isDoubleDashed = arg.startsWith("--"); + ok = isDoubleDashed ? arg.length() > 3 : + arg.length() == 2; + if(!ok) { + out << QString("the option %1 has a wrong format.").arg(arg); + return ok; + } + arg = isDoubleDashed ? + arg.remove(0, 2) : + arg.remove(0, 1); + // get option + auto optionIt = actualNode->options.end(); + for (const QStringList &sl: actualNode->options.keys()) { + if (sl.contains(arg)) { + optionIt = actualNode->options.find(sl); + break; + } + } + if (optionIt == actualNode->options.end()) { + QString argName = actualNode->argument.name(); + if (argName.isEmpty()) { + argName = qApp->applicationName(); + } + out << QString("the option '%1' is not a valid option " + "for the argument '%2'.").arg(arg) + .arg(argName); + ok = false; + return ok; + } + // check presence of values + CommandOption option = *optionIt; + bool requiresValue = !(option.valueName().isEmpty()); + if (!requiresValue && equalsPos != -1) { + out << QString("the option '%1' contains a '=' and it doesn't " + "require a value.").arg(arg); + ok = false; + return ok; + } else if (requiresValue && valueStr.isEmpty()) { + // find in the next + if (actualIt+1 != args.cend()) { + ++actualIt; + } else { + out << QString("Expected value after the option '%1'.").arg(arg); + ok = false; + return ok; + } + valueStr = *actualIt; + } + // check the value correctness + if (requiresValue) { + ok = option.checkValue(valueStr); + if (!ok) { + QString err = option.errorMsg(); + if (!err.endsWith(".")) + err += "."; + out << err; + return ok; + } + option.setValue(valueStr); + } + m_foundOptions.append(option); + return ok; +} + +bool CommandLineParser::parse(const QStringList &args) { + m_foundArgs.clear(); + m_foundOptions.clear(); + bool ok = true; + Node *actualNode = &m_parseTree; + auto it = ++args.cbegin(); + // check version option + QStringList dashedVersion = addDashToOptionNames(versionOption.names()); + if (m_withVersion && args.length() > 1 && + dashedVersion.contains(args.at(1))) + { + if (args.length() == 2) { + printVersion(); + m_foundOptions << versionOption; + } else { + out << "Invalid arguments after the version option."; + ok = false; + } + return ok; + + } + // check help option + ok = processIfOptionIsHelp(args, it, actualNode); + // process the other args + for (; it != args.cend() && ok; ++it) { + const QString &value = *it; + if (value.startsWith("-")) { + ok = processOptions(args, it, actualNode); + + } else { + ok = processArgs(args, it, actualNode); + } + } + if (!ok && !m_generalErrorMessage.isEmpty()) { + out << QString(" %1\n").arg(m_generalErrorMessage); + } + return ok; +} + +CommandOption CommandLineParser::addVersionOption() { + m_withVersion = true; + return versionOption; +} + +CommandOption CommandLineParser::addHelpOption() { + m_withHelp = true; + return helpOption; +} + +bool CommandLineParser::AddArgument(const CommandArgument &arg, + const CommandArgument &parent) +{ + bool res = true; + Node *n = findParent(parent); + if (n == nullptr) { + res = false; + } else { + Node child; + child.argument = arg; + n->subNodes.insert(child.argument.name(), child); + } + return res; +} + +bool CommandLineParser::AddOption(const CommandOption &option, + const CommandArgument &parent) +{ + bool res = true; + Node *n = findParent(parent); + if (n == nullptr) { + res = false; + } else { + n->options.insert(option.names(), option); + } + return res; +} + +bool CommandLineParser::AddOptions(const QList &options, + const CommandArgument &parent) +{ + bool res = true; + for (auto const &option: options) { + if (!AddOption(option, parent)) { + res = false; + break; + } + } + return res; +} + +void CommandLineParser::setGeneralErrorMessage(const QString &msg) { + m_generalErrorMessage = msg; +} + +void CommandLineParser::setDescription(const QString &description) { + m_description = description; +} + +bool CommandLineParser::isSet(const CommandArgument &arg) const { + return m_foundArgs.contains(arg); +} + + +bool CommandLineParser::isSet(const CommandOption &option) const { + return m_foundOptions.contains(option); +} + +QString CommandLineParser::value(const CommandOption &option) const { + QString value; + for (const CommandOption &fOption: m_foundOptions) { + if (option == fOption) { + value = fOption.value(); + break; + } + } + return value; +} + +void CommandLineParser::printVersion() { + out << "Flameshot " << qApp->applicationVersion() << "\nCompiled with QT " + << static_cast(QT_VERSION_STR) << "\n"; +} + +void CommandLineParser::printHelp(QStringList args, const Node *node) { + args.removeLast(); // remove the help, it's always the last + QString helpText; + // add usage info + QString argName = node->argument.name(); + if (argName.isEmpty()) { + argName = qApp->applicationName(); + } + QString argText = node->subNodes.isEmpty() ? "" : "[arguments]"; + helpText += QString("Usage: %1 [%2-options] %3\n\n").arg(args.join(" ")) + .arg(argName).arg(argText); + // add command options and subarguments + QList subArgs; + for (const Node &n: node->subNodes) + subArgs.append(n.argument); + auto modifiedOptions = node->options.values(); + if (m_withHelp) + modifiedOptions << helpOption; + if (m_withVersion && node == &m_parseTree) { + modifiedOptions << versionOption; + } + helpText += optionsToString(modifiedOptions, subArgs); + // print it + out << helpText; +} + +CommandLineParser::Node* CommandLineParser::findParent( + const CommandArgument &parent) +{ + if (parent == CommandArgument()) { + return &m_parseTree; + } + //find the parent in the subNodes recursively + Node *res = nullptr; + for (auto i = m_parseTree.subNodes.begin(); + i != m_parseTree.subNodes.end(); ++i) + { + res = recursiveParentSearch(parent, *i); + if (res != nullptr) { + break; + } + } + return res; +} + +CommandLineParser::Node* CommandLineParser::recursiveParentSearch( + const CommandArgument &parent, Node &node) const +{ + Node * res = nullptr; + if (node.argument == parent) { + res = &node; + } else { + for (auto i = node.subNodes.begin(); i != node.subNodes.end(); ++i){ + res = recursiveParentSearch(parent, *i); + if (res != nullptr) { + break; + } + } + } + return res; +} + +bool CommandLineParser::processIfOptionIsHelp( + const QStringList &args, + QStringList::const_iterator &actualIt, + Node * &actualNode) +{ + bool ok = true; + auto dashedHelpNames = addDashToOptionNames(helpOption.names()); + if (m_withHelp && actualIt != args.cend() && + dashedHelpNames.contains(*actualIt)) + { + if (actualIt+1 == args.cend()) { + m_foundOptions << helpOption; + printHelp(args, actualNode); + actualIt++; + } else { + out << "Invalid arguments after the help option."; + ok = false; + } + } + return ok; +} diff --git a/src/cli/commandlineparser.h b/src/cli/commandlineparser.h new file mode 100644 index 00000000..6ab0d887 --- /dev/null +++ b/src/cli/commandlineparser.h @@ -0,0 +1,94 @@ +// Copyright 2017 Alejandro Sirgo Rica +// +// This file is part of Flameshot. +// +// Flameshot is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Flameshot is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Flameshot. If not, see . + +#ifndef COMMANDLINEPARSER_H +#define COMMANDLINEPARSER_H + +#include "src/cli/commandargument.h" +#include "src/cli/commandoption.h" +#include + +class CommandLineParser +{ +public: + CommandLineParser(); + + bool parse(const QStringList &args); + + CommandArgument rootArgument() const { return CommandArgument(); } + + CommandOption addVersionOption(); + CommandOption addHelpOption(); + + bool AddArgument(const CommandArgument &arg, + const CommandArgument &parent = CommandArgument()); + + bool AddOption(const CommandOption &option, + const CommandArgument &parent = CommandArgument()); + + bool AddOptions(const QList &options, + const CommandArgument &parent = CommandArgument()); + + void setGeneralErrorMessage(const QString &msg); + void setDescription(const QString &description); + + bool isSet(const CommandArgument &arg) const; + bool isSet(const CommandOption &option) const; + QString value(const CommandOption &option) const; + +private: + bool m_withHelp = false; + bool m_withVersion = false; + QString m_description; + QString m_generalErrorMessage; + + struct Node { + Node(const CommandArgument &arg) : argument(arg) {} + Node() {} + bool operator==(const Node &n) const { + return argument == n.argument && + options == n.options && + subNodes == n.subNodes; + } + CommandArgument argument; + QMap options; + QMap subNodes; + }; + + Node m_parseTree; + QList m_foundOptions; + QList m_foundArgs; + + // helper functions + inline void printVersion(); + void printHelp(QStringList args, const Node *node); + Node* findParent(const CommandArgument &parent); + Node* recursiveParentSearch(const CommandArgument &parent, + Node &node) const; + inline bool processIfOptionIsHelp(const QStringList &args, + QStringList::const_iterator &actualIt, + Node * &actualNode); + inline bool processArgs(const QStringList &args, + QStringList::const_iterator &actualIt, + Node * &actualNode); + bool processOptions(const QStringList &args, + QStringList::const_iterator &actualIt, + Node *const actualNode); + +}; + +#endif // COMMANDLINEPARSER_H diff --git a/src/cli/commandoption.cpp b/src/cli/commandoption.cpp new file mode 100644 index 00000000..8e4b208f --- /dev/null +++ b/src/cli/commandoption.cpp @@ -0,0 +1,100 @@ +// Copyright 2017 Alejandro Sirgo Rica +// +// This file is part of Flameshot. +// +// Flameshot is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Flameshot is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Flameshot. If not, see . + +#include "commandoption.h" + +CommandOption::CommandOption(const QString &name, const QString &description, + const QString &valueName, + const QString &defaultValue) : + m_names(name), m_description(description), m_valueName(valueName), + m_value(defaultValue) +{ + m_checker = [](QString const&){ return true; }; +} + +CommandOption::CommandOption(const QStringList &names, + const QString &description, + const QString &valueName, + const QString &defaultValue) : + m_names(names), m_description(description), m_valueName(valueName), + m_value(defaultValue) +{ + m_checker = [](QString const&) -> bool { return true; }; +} + +void CommandOption::setName(const QString &name) { + m_names = QStringList() << name; +} + +void CommandOption::setNames(const QStringList &names) { + m_names = names; +} + +QStringList CommandOption::names() const { + return m_names; +} + +void CommandOption::setValueName(const QString &name) { + m_valueName = name; +} + +QString CommandOption::valueName() const { + return m_valueName; +} + +void CommandOption::setValue(const QString &value) { + if (m_valueName.isEmpty()) { + m_valueName = "value"; + } + m_value = value; +} + +QString CommandOption::value() const { + return m_value; +} + +void CommandOption::addChecker(const function checker, + const QString &errMsg) +{ + m_checker = checker; + m_errorMsg = errMsg; +} + +bool CommandOption::checkValue(const QString &value) const { + return m_checker(value); +} + +QString CommandOption::description() const +{ + return m_description; +} + +void CommandOption::setDescription(const QString &description) +{ + m_description = description; +} + +QString CommandOption::errorMsg() const { + return m_errorMsg; +} + +bool CommandOption::operator ==(const CommandOption &option) const +{ + return m_description == option.m_description + && m_names == option.m_names + && m_valueName == option.m_valueName; +} diff --git a/src/cli/commandoption.h b/src/cli/commandoption.h new file mode 100644 index 00000000..514fb571 --- /dev/null +++ b/src/cli/commandoption.h @@ -0,0 +1,68 @@ +// Copyright 2017 Alejandro Sirgo Rica +// +// This file is part of Flameshot. +// +// Flameshot is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Flameshot is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Flameshot. If not, see . + +#ifndef COMMANDOPTION_H +#define COMMANDOPTION_H + +#include +#include + +using std::function; + +class CommandOption +{ +public: + CommandOption(const QString &name, const QString &description, + const QString &valueName = QString(), + const QString &defaultValue = QString()); + + CommandOption(const QStringList &names, const QString &description, + const QString &valueName = QString(), + const QString &defaultValue = QString()); + + void setName(const QString &name); + void setNames(const QStringList &names); + QStringList names() const; + + void setValueName(const QString &name); + QString valueName() const; + + void setValue(const QString &value); + QString value() const; + + void addChecker(const function checker, const QString &errMsg); + bool checkValue(const QString &value) const; + + QString description() const; + void setDescription(const QString &description); + + QString errorMsg() const; + + bool operator==(const CommandOption &option) const; + +private: + QStringList m_names; + QString m_description; + QString m_valueName; + QString m_value; + + function m_checker; + QString m_errorMsg; + +}; + +#endif // COMMANDOPTION_H diff --git a/src/main.cpp b/src/main.cpp index 8ccc65f8..4192626a 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -20,12 +20,11 @@ #include "src/core/flameshotdbusadapter.h" #include "src/utils/filenamehandler.h" #include "src/utils/confighandler.h" +#include "src/cli/commandlineparser.h" #include #include -#include #include #include -#include #include int main(int argc, char *argv[]) { @@ -60,175 +59,125 @@ int main(int argc, char *argv[]) { app.setApplicationName("flameshot"); app.setOrganizationName("Dharkael"); app.setApplicationVersion(qApp->applicationVersion()); - QCommandLineParser parser; + CommandLineParser parser; // Add description - parser.setApplicationDescription( + parser.setDescription( "Powerfull yet simple to use screenshot software."); - // Command descriptions - QString fullDescription = "Capture the entire desktop."; - QString guiDescription = "Start a manual capture in GUI mode."; - QString configDescription = "Configure flameshot."; - // Positional alguments - parser.addPositionalArgument("mode", "full\t" + fullDescription + "\n"+ - "gui\t" + guiDescription + "\n" + - "config\t" + configDescription, - "mode [mode_options]"); + parser.setGeneralErrorMessage("See 'flameshot --help'."); + // Arguments + CommandArgument fullArgument("full", "Capture the entire desktop."); + CommandArgument guiArgument("gui", "Start a manual capture in GUI mode."); + CommandArgument configArgument("config", "Configure flameshot."); - // Add options - parser.addHelpOption(); - parser.addVersionOption(); - // Initial parse --------------------------------- - parser.parse(app.arguments()); - QTextStream out(stdout); - - // CLI options - QCommandLineOption pathOption( + // Options + CommandOption pathOption( {"p", "path"}, "Path where the capture will be saved", "path"); - QCommandLineOption clipboardOption( + CommandOption clipboardOption( {"c", "clipboard"}, "Save the capture to the clipboard"); - QCommandLineOption delayOption( + CommandOption delayOption( {"d", "delay"}, "Delay time in milliseconds", "milliseconds"); - QCommandLineOption filenameOption( - "filename", + CommandOption filenameOption( + {"f", "filename"}, "Set the filename pattern", "pattern"); - QCommandLineOption trayOption( - "trayicon", + CommandOption trayOption( + {"t", "trayicon"}, "Enable or disable the trayicon", "bool"); - QCommandLineOption showHelpOption( - "showhelp", + CommandOption showHelpOption( + {"s", "showhelp"}, "Show the help message in the capture mode", "bool"); - QCommandLineOption mainColorOption( - "maincolor", + CommandOption mainColorOption( + {"m", "maincolor"}, "Define the main UI color", - "color code"); - QCommandLineOption contrastColorOption( - "contrastcolor", + "color-code"); + CommandOption contrastColorOption( + {"k", "contrastcolor"}, "Define the contrast UI color", - "color code"); - // add here the names of the options without required values after the tag - QStringList optionsWithoutValue = QStringList() - << clipboardOption.names(); - // Second parse ---------------------------------- + "color-code"); - /* Detect undesired elements - * This is a very hacky solution to filter undesired arguments. - * I may consider changing to a better cli parsing library for - * this kind of command structure. - */ - auto args = app.arguments().mid(1); // ignore the first - QStringList commandList{"gui", "full", "config"}; - auto i = args.cbegin(); - QString val = (*i); - bool ok = commandList.contains(val); - // check first - for (++i; i != args.cend(); ++i) { - if (!ok) break; - val = (*i); - if(val.startsWith("-")) { - // skip next when - // - the parameter is not in the format -flag=100 - // - there are more elements to check - // - it's a flag and it requires a value - bool skipNext = (!val.contains("=") && i+1 != args.cend() - && !optionsWithoutValue.contains(val.remove("-"))); - if (skipNext) ++i; - } else { // not a flag - ok = false; - } + // Add checkers + auto colorChecker = [&parser](const QString &colorCode) -> bool { + QColor parsedColor(colorCode); + return parsedColor.isValid() && parsedColor.alphaF() == 1.0; + }; + QString colorErr = "Invalid color, " + "this flag supports the following formats:\n" + "- #RGB (each of R, G, and B is a single hex digit)\n" + "- #RRGGBB\n- #RRRGGGBBB\n" + "- #RRRRGGGGBBBB\n" + "- Named colors like 'blue' or 'red'\n" + "You may need to escape the '#' sign as in '\\#FFF'"; + + auto delayChecker = [&parser](const QString &delayValue) -> bool { + int value = delayValue.toInt(); + return value >= 0; + }; + QString delayErr = "Ivalid delay, it must be higher than 0"; + + auto pathChecker = [&parser](const QString &pathValue) -> bool { + return QDir(pathValue).exists(); + }; + QString pathErr = "Ivalid path, it must be a real path in the system"; + + auto booleanChecker = [&parser](const QString &value) -> bool { + return value == "true" || value == "false"; + }; + QString booleanErr = "Ivalid value, it must be defined as 'true' or 'false'"; + + contrastColorOption.addChecker(colorChecker, colorErr); + mainColorOption.addChecker(colorChecker, colorErr); + delayOption.addChecker(delayChecker, delayErr); + pathOption.addChecker(pathChecker, pathErr); + trayOption.addChecker(booleanChecker, booleanErr); + showHelpOption.addChecker(booleanChecker, booleanErr); + + // Relationships + parser.AddArgument(guiArgument); + parser.AddArgument(fullArgument); + parser.AddArgument(configArgument); + auto helpOption = parser.addHelpOption(); + auto versionOption = parser.addVersionOption(); + parser.AddOptions({ pathOption, delayOption }, guiArgument); + parser.AddOptions({ pathOption, clipboardOption, delayOption }, fullArgument); + parser.AddOptions({ filenameOption, trayOption, showHelpOption, + mainColorOption, contrastColorOption }, configArgument); + // Parse + if (!parser.parse(app.arguments())) + return 0; + + // PROCESS DATA + //-------------- + if (parser.isSet(helpOption) || parser.isSet(versionOption)) { } - - // obtain the command - QString command; - if (ok && parser.positionalArguments().count() > 0) { - command = parser.positionalArguments().first(); - } - - // GUI - if (command == "gui") { - // Description - parser.clearPositionalArguments(); - parser.addPositionalArgument( - "gui", guiDescription, "gui [gui_options]"); - parser.addOptions({ pathOption, delayOption }); - parser.process(app); - - // paramenters - QString pathValue; - if (parser.isSet(pathOption)) { - pathValue = QString::fromStdString(parser.value("path").toStdString()); - if (!QDir(pathValue).exists()) { - qWarning().noquote() << "Invalid path."; - return 0; - } - } - int delay = 0; - if (parser.isSet(delayOption)) { - delay = parser.value("delay").toInt(); - if (delay < 0) { - qWarning().noquote() << "Invalid negative delay."; - return 0; - } - } + else if (parser.isSet(guiArgument)) { // GUI + QString pathValue = parser.value(pathOption); + int delay = parser.value(delayOption).toInt(); // Send message QDBusMessage m = QDBusMessage::createMethodCall("org.dharkael.Flameshot", "/", "", "graphicCapture"); m << pathValue << delay; QDBusConnection::sessionBus().call(m); - } - // FULL - else if (command == "full") { - // Description - parser.clearPositionalArguments(); - parser.addPositionalArgument( - "full", fullDescription, "full [full_options]"); - parser.addOptions({ pathOption, clipboardOption, delayOption }); - parser.process(app); - - // paramenters - QString pathValue; - if (parser.isSet(pathOption)) { - pathValue = QString::fromStdString(parser.value("path").toStdString()); - if (!QDir(pathValue).exists()) { - qWarning().noquote() << "Invalid path."; - return 0; - } - } - int delay = 0; - if (parser.isSet(delayOption)) { - delay = parser.value("delay").toInt(); - if (delay < 0) { - qWarning().noquote() << "Invalid negative delay."; - return 0; - } - } + else if (parser.isSet(fullArgument)) { // FULL + QString pathValue = parser.value(pathOption); + int delay = parser.value(delayOption).toInt(); + bool toClipboard = parser.isSet(clipboardOption); // Send message QDBusMessage m = QDBusMessage::createMethodCall("org.dharkael.Flameshot", "/", "", "fullScreen"); - m << pathValue << parser.isSet("clipboard") << delay; + m << pathValue << toClipboard << delay; QDBusConnection::sessionBus().call(m); - } - // CONFIG - else if (command == "config") { - // Description - parser.clearPositionalArguments(); - parser.addPositionalArgument( - "config", configDescription, "config [config_options]"); - parser.addOptions({ filenameOption, trayOption, showHelpOption, - mainColorOption, contrastColorOption }); - parser.process(app); - + else if (parser.isSet(configArgument)) { // CONFIG bool filename = parser.isSet(filenameOption); bool tray = parser.isSet(trayOption); bool help = parser.isSet(showHelpOption); @@ -241,9 +190,9 @@ int main(int argc, char *argv[]) { QString newFilename(parser.value(filenameOption)); config.setFilenamePattern(newFilename); FileNameHandler fh; - out << "The new pattern is '" << newFilename - << "'\nParsed pattern example: " - << fh.getParsedPattern() << "\n"; + qInfo().noquote() << QString("The new pattern is '%1'\n" + "Parsed pattern example: %2").arg(newFilename) + .arg(fh.getParsedPattern()); } if (tray) { QDBusMessage m = QDBusMessage::createMethodCall("org.dharkael.Flameshot", @@ -265,30 +214,12 @@ int main(int argc, char *argv[]) { if (mainColor) { QString colorCode = parser.value(mainColorOption); QColor parsedColor(colorCode); - if (parsedColor.isValid()) { - config.setUIMainColor(parsedColor); - } else { - out << "Invalid main color, " - "this flag supports the following formats:\n" - "- RGB (each of R, G, and B is a single hex digit)" - "- RRGGBB\n- AARRGGBB\n- RRRGGGBBB\n" - "- RRRRGGGGBBBB\n" - "- named colors like 'blue' or 'red'"; - } + config.setUIMainColor(parsedColor); } if (contrastColor) { QString colorCode = parser.value(contrastColorOption); QColor parsedColor(colorCode); - if (parsedColor.isValid()) { - config.setUIContrastColor(parsedColor); - } else { - out << "Invalid contrast color, " - "this flag supports the following formats:\n" - "- RGB (each of R, G, and B is a single hex digit)" - "- RRGGBB\n- AARRGGBB\n- RRRGGGBBB\n" - "- RRRRGGGGBBBB\n" - "- named colors like 'blue' or 'red'"; - } + config.setUIContrastColor(parsedColor); } // Open gui when no options @@ -297,9 +228,6 @@ int main(int argc, char *argv[]) { "/", "", "openConfig"); QDBusConnection::sessionBus().call(m); } - } else { - qWarning().noquote() << "Invalid command, see 'flameshot --help'."; - parser.process(app.arguments()); } return 0; }