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;
}