add command line parser

Support open files via command line.
This commit is contained in:
Le Tan 2021-03-17 22:04:29 +08:00
parent 2bad915583
commit c506b3e1e7
10 changed files with 227 additions and 35 deletions

View File

@ -0,0 +1,55 @@
#include "commandlineoptions.h"
#include <QCommandLineParser>
#include <QCommandLineOption>
#include <QCoreApplication>
#include <QDebug>
#define TR(x) QCoreApplication::translate("main", (x))
CommandLineOptions::ParseResult CommandLineOptions::parse(const QStringList &p_arguments)
{
QCommandLineParser parser;
parser.setApplicationDescription(TR("A pleasant note-taking platform."));
const auto helpOpt = parser.addHelpOption();
const auto versionOpt = parser.addVersionOption();
// Positional arguments.
parser.addPositionalArgument("paths", TR("Files or folders to open."));
const QCommandLineOption verboseOpt("verbose", TR("Print more logs."));
parser.addOption(verboseOpt);
// WebEngine options.
// No need to handle them. Just add them to the parser to avoid parse error.
QCommandLineOption webRemoteDebuggingPortOpt("remote-debugging-port",
TR("WebEngine remote debugging port."),
"port_number");
webRemoteDebuggingPortOpt.setFlags(QCommandLineOption::HiddenFromHelp);
parser.addOption(webRemoteDebuggingPortOpt);
if (!parser.parse(p_arguments)) {
m_errorMsg = parser.errorText();
return ParseResult::Error;
}
// Handle results.
m_helpText = parser.helpText();
if (parser.isSet(helpOpt)) {
return ParseResult::HelpRequested;
}
if (parser.isSet(versionOpt)) {
return ParseResult::VersionRequested;
}
// Position arguments.
const auto args = parser.positionalArguments();
m_pathsToOpen = args;
if (parser.isSet(verboseOpt)) {
m_verbose = true;
}
return ParseResult::Ok;
}

30
src/commandlineoptions.h Normal file
View File

@ -0,0 +1,30 @@
#ifndef COMMANDLINEOPTIONS_H
#define COMMANDLINEOPTIONS_H
#include <QStringList>
class CommandLineOptions
{
public:
enum ParseResult
{
Ok,
Error,
VersionRequested,
HelpRequested
};
CommandLineOptions() = default;
ParseResult parse(const QStringList &p_arguments);
QString m_errorMsg;
QString m_helpText;
QStringList m_pathsToOpen;
bool m_verbose = false;
};
#endif // COMMANDLINEOPTIONS_H

View File

@ -229,7 +229,7 @@ QString ConfigMgr::getConfigFilePath(Source p_src) const
QString configPath; QString configPath;
switch (p_src) { switch (p_src) {
case Source::Default: case Source::Default:
configPath = QStringLiteral(":/vnotex/data/core/") + c_configFileName; configPath = getDefaultConfigFilePath();
break; break;
case Source::App: case Source::App:
@ -255,9 +255,13 @@ QString ConfigMgr::getConfigFilePath(Source p_src) const
return configPath; return configPath;
} }
QString ConfigMgr::getDefaultConfigFilePath()
{
return QStringLiteral(":/vnotex/data/core/") + c_configFileName;
}
QSharedPointer<ConfigMgr::Settings> ConfigMgr::getSettings(Source p_src) const QSharedPointer<ConfigMgr::Settings> ConfigMgr::getSettings(Source p_src) const
{ {
return ConfigMgr::Settings::fromFile(getConfigFilePath(p_src)); return ConfigMgr::Settings::fromFile(getConfigFilePath(p_src));
} }
@ -433,3 +437,18 @@ QString ConfigMgr::getDocumentOrHomePath()
return docHomePath; return docHomePath;
} }
QString ConfigMgr::getApplicationVersion()
{
static QString appVersion;
if (appVersion.isEmpty()) {
auto defaultSettings = ConfigMgr::Settings::fromFile(getDefaultConfigFilePath());
const auto &defaultObj = defaultSettings->getJson();
auto metaDataObj = defaultObj.value(QStringLiteral("metadata")).toObject();
appVersion = metaDataObj.value(QStringLiteral("version")).toString();
}
return appVersion;
}

View File

@ -104,6 +104,8 @@ namespace vnotex
static QString getDocumentOrHomePath(); static QString getDocumentOrHomePath();
static QString getApplicationVersion();
static const QString c_orgName; static const QString c_orgName;
static const QString c_appName; static const QString c_appName;
@ -129,6 +131,8 @@ namespace vnotex
// Update it if in need. // Update it if in need.
void checkAppConfig(); void checkAppConfig();
static QString getDefaultConfigFilePath();
QScopedPointer<MainConfig> m_config;; QScopedPointer<MainConfig> m_config;;
// Session config. // Session config.

View File

@ -12,6 +12,8 @@ using namespace vnotex;
const QString SingleInstanceGuard::c_serverName = "vnote"; const QString SingleInstanceGuard::c_serverName = "vnote";
const QChar SingleInstanceGuard::c_stringListSeparator = '>';
SingleInstanceGuard::SingleInstanceGuard() SingleInstanceGuard::SingleInstanceGuard()
{ {
qInfo() << "guarding is on"; qInfo() << "guarding is on";
@ -54,6 +56,14 @@ void SingleInstanceGuard::requestOpenFiles(const QStringList &p_files)
if (p_files.isEmpty()) { if (p_files.isEmpty()) {
return; return;
} }
Q_ASSERT(!m_online);
if (!m_client || m_client->state() != QLocalSocket::ConnectedState) {
qWarning() << "failed to request open files" << m_client->errorString();
return ;
}
sendRequest(m_client.data(), OpCode::OpenFiles, p_files.join(c_stringListSeparator));
} }
void SingleInstanceGuard::requestShow() void SingleInstanceGuard::requestShow()
@ -157,6 +167,7 @@ void SingleInstanceGuard::receiveCommand(QLocalSocket *p_socket)
inStream.setDevice(p_socket); inStream.setDevice(p_socket);
inStream.setVersion(QDataStream::Qt_5_12); inStream.setVersion(QDataStream::Qt_5_12);
while (p_socket->bytesAvailable() > 0) {
if (m_command.m_opCode == OpCode::Null) { if (m_command.m_opCode == OpCode::Null) {
// Relies on the fact that QDataStream serializes a quint32 into // Relies on the fact that QDataStream serializes a quint32 into
// sizeof(quint32) bytes. // sizeof(quint32) bytes.
@ -174,7 +185,7 @@ void SingleInstanceGuard::receiveCommand(QLocalSocket *p_socket)
return; return;
} }
qDebug() << "op code" << m_command.m_opCode << m_command.m_size; qDebug() << "op code" << m_command.m_opCode << m_command.m_size << p_socket->bytesAvailable();
switch (m_command.m_opCode) { switch (m_command.m_opCode) {
case OpCode::Show: case OpCode::Show:
@ -182,9 +193,23 @@ void SingleInstanceGuard::receiveCommand(QLocalSocket *p_socket)
emit showRequested(); emit showRequested();
break; break;
case OpCode::OpenFiles:
{
Q_ASSERT(m_command.m_size != 0);
QString payload;
inStream >> payload;
const auto files = payload.split(c_stringListSeparator);
emit openFilesRequested(files);
break;
}
default: default:
qWarning() << "unknown op code" << m_command.m_opCode; qWarning() << "unknown op code" << m_command.m_opCode;
break; m_command.clear();
return;
}
m_command.clear();
} }
} }

View File

@ -41,7 +41,8 @@ namespace vnotex
enum OpCode enum OpCode
{ {
Null = 0, Null = 0,
Show Show,
OpenFiles
}; };
struct Command struct Command
@ -78,6 +79,8 @@ namespace vnotex
Command m_command; Command m_command;
static const QString c_serverName; static const QString c_serverName;
static const QChar c_stringListSeparator;
}; };
} // ns vnotex } // ns vnotex

View File

@ -21,6 +21,7 @@
#include <QWebEngineSettings> #include <QWebEngineSettings>
#include <core/exception.h> #include <core/exception.h>
#include <widgets/messageboxhelper.h> #include <widgets/messageboxhelper.h>
#include "commandlineoptions.h"
using namespace vnotex; using namespace vnotex;
@ -28,6 +29,8 @@ void loadTranslators(QApplication &p_app);
void initWebEngineSettings(); void initWebEngineSettings();
void showMessageOnCommandLineIfAvailable(const QString &p_msg);
int main(int argc, char *argv[]) int main(int argc, char *argv[])
{ {
QTextCodec *codec = QTextCodec::codecForName("UTF8"); QTextCodec *codec = QTextCodec::codecForName("UTF8");
@ -76,18 +79,44 @@ int main(int argc, char *argv[])
app.setApplicationName(ConfigMgr::c_appName); app.setApplicationName(ConfigMgr::c_appName);
app.setOrganizationName(ConfigMgr::c_orgName); app.setOrganizationName(ConfigMgr::c_orgName);
app.setApplicationVersion(ConfigMgr::getApplicationVersion());
}
CommandLineOptions cmdOptions;
switch (cmdOptions.parse(app.arguments())) {
case CommandLineOptions::Ok:
break;
case CommandLineOptions::Error:
showMessageOnCommandLineIfAvailable(cmdOptions.m_errorMsg);
return -1;
case CommandLineOptions::VersionRequested:
{
auto versionStr = QString("%1 %2").arg(app.applicationName()).arg(app.applicationVersion());
showMessageOnCommandLineIfAvailable(versionStr);
return 0;
}
case CommandLineOptions::HelpRequested:
Q_FALLTHROUGH();
default:
showMessageOnCommandLineIfAvailable(cmdOptions.m_helpText);
return 0;
} }
// Guarding. // Guarding.
SingleInstanceGuard guard; SingleInstanceGuard guard;
bool canRun = guard.tryRun(); bool canRun = guard.tryRun();
if (!canRun) { if (!canRun) {
guard.requestOpenFiles(cmdOptions.m_pathsToOpen);
guard.requestShow(); guard.requestShow();
return 0; return 0;
} }
try { try {
app.setApplicationVersion(ConfigMgr::getInst().getConfig().getVersion()); ConfigMgr::getInst();
} catch (Exception &e) { } catch (Exception &e) {
MessageBoxHelper::notify(MessageBoxHelper::Critical, MessageBoxHelper::notify(MessageBoxHelper::Critical,
MainWindow::tr("%1 failed to start.").arg(ConfigMgr::c_appName), MainWindow::tr("%1 failed to start.").arg(ConfigMgr::c_appName),
@ -98,7 +127,7 @@ int main(int argc, char *argv[])
} }
// Init logger after app info is set. // Init logger after app info is set.
Logger::init(false); Logger::init(cmdOptions.m_verbose);
qInfo() << QString("%1 (v%2) started at %3 (%4)").arg(ConfigMgr::c_appName, qInfo() << QString("%1 (v%2) started at %3 (%4)").arg(ConfigMgr::c_appName,
app.applicationVersion(), app.applicationVersion(),
@ -112,8 +141,6 @@ int main(int argc, char *argv[])
qWarning() << "versions of the built and linked OpenSSL mismatch, network may not work"; qWarning() << "versions of the built and linked OpenSSL mismatch, network may not work";
} }
// TODO: parse command line options.
// Should set the correct locale before VNoteX::getInst(). // Should set the correct locale before VNoteX::getInst().
loadTranslators(app); loadTranslators(app);
@ -131,8 +158,10 @@ int main(int argc, char *argv[])
QObject::connect(&guard, &SingleInstanceGuard::showRequested, QObject::connect(&guard, &SingleInstanceGuard::showRequested,
&window, &MainWindow::showMainWindow); &window, &MainWindow::showMainWindow);
QObject::connect(&guard, &SingleInstanceGuard::openFilesRequested,
&window, &MainWindow::openFiles);
window.kickOffOnStart(); window.kickOffOnStart(cmdOptions.m_pathsToOpen);
int ret = app.exec(); int ret = app.exec();
if (ret == RESTART_EXIT_CODE) { if (ret == RESTART_EXIT_CODE) {
@ -208,3 +237,13 @@ void initWebEngineSettings()
auto settings = QWebEngineSettings::defaultSettings(); auto settings = QWebEngineSettings::defaultSettings();
settings->setAttribute(QWebEngineSettings::LocalContentCanAccessRemoteUrls, true); settings->setAttribute(QWebEngineSettings::LocalContentCanAccessRemoteUrls, true);
} }
void showMessageOnCommandLineIfAvailable(const QString &p_msg)
{
#if defined(Q_OS_WIN)
MessageBoxHelper::notify(MessageBoxHelper::Information,
QString("<pre>%1</pre>").arg(p_msg));
#else
printf("%s\n", qPrintable(p_msg));
#endif
}

View File

@ -30,6 +30,7 @@ ICON = data/core/icons/vnote.icns
TRANSLATIONS += data/core/translations/vnote_zh_CN.ts TRANSLATIONS += data/core/translations/vnote_zh_CN.ts
SOURCES += \ SOURCES += \
commandlineoptions.cpp \
main.cpp main.cpp
INCLUDEPATH *= $$PWD INCLUDEPATH *= $$PWD
@ -137,3 +138,6 @@ unix:!macx {
INSTALLS += extraresource INSTALLS += extraresource
message("VNote will be installed in prefix $${PREFIX}") message("VNote will be installed in prefix $${PREFIX}")
} }
HEADERS += \
commandlineoptions.h

View File

@ -70,8 +70,9 @@ MainWindow::~MainWindow()
m_viewArea = nullptr; m_viewArea = nullptr;
} }
void MainWindow::kickOffOnStart() void MainWindow::kickOffOnStart(const QStringList &p_paths)
{ {
QTimer::singleShot(300, [this, p_paths]() {
VNoteX::getInst().initLoad(); VNoteX::getInst().initLoad();
emit mainWindowStarted(); emit mainWindowStarted();
@ -79,6 +80,16 @@ void MainWindow::kickOffOnStart()
emit layoutChanged(); emit layoutChanged();
demoWidget(); demoWidget();
openFiles(p_paths);
});
}
void MainWindow::openFiles(const QStringList &p_files)
{
for (const auto &file : p_files) {
emit VNoteX::getInst().openFileRequested(file, QSharedPointer<FileOpenParameters>::create());
}
} }
void MainWindow::setupUI() void MainWindow::setupUI()

View File

@ -33,7 +33,7 @@ namespace vnotex
MainWindow(const MainWindow &) = delete; MainWindow(const MainWindow &) = delete;
void operator=(const MainWindow &) = delete; void operator=(const MainWindow &) = delete;
void kickOffOnStart(); void kickOffOnStart(const QStringList &p_paths);
void resetStateAndGeometry(); void resetStateAndGeometry();
@ -53,6 +53,8 @@ namespace vnotex
void quitApp(); void quitApp();
void openFiles(const QStringList &p_files);
signals: signals:
void mainWindowStarted(); void mainWindowStarted();