diff --git a/CMakeLists.txt b/CMakeLists.txt index 0bd77cd..e326b3a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -55,6 +55,7 @@ endif() if (BUILD_DBUS_INTERFACE OR (NOT ANDROID AND NOT DEFINED BUILD_DBUS_INTERFACE)) find_package(KF5DBusAddons ${KF5_MIN_VERSION} REQUIRED) + find_package(KF5WindowSystem ${KF5_MIN_VERSION} REQUIRED) set(ENABLE_DBUS_INTERFACE ON) endif() diff --git a/src/app/CMakeLists.txt b/src/app/CMakeLists.txt index 4b14b6d..e144e9d 100644 --- a/src/app/CMakeLists.txt +++ b/src/app/CMakeLists.txt @@ -12,4 +12,12 @@ set(keysmith_SRCS ) add_library(keysmith_lib STATIC ${keysmith_SRCS}) -target_link_libraries(keysmith_lib Qt5::Core Qt5::Gui Qt5::Concurrent Qt5::Qml KF5::I18n model_lib account_lib) +target_link_libraries(keysmith_lib + Qt5::Core Qt5::Gui Qt5::Qml Qt5::Concurrent + KF5::I18n + model_lib account_lib +) + +if (ENABLE_DBUS_INTERFACE) + target_link_libraries(keysmith_lib KF5::DBusAddons KF5::WindowSystem) +endif() diff --git a/src/app/cli.cpp b/src/app/cli.cpp index 85f49c8..bb04b61 100644 --- a/src/app/cli.cpp +++ b/src/app/cli.cpp @@ -12,6 +12,14 @@ #include #include +#ifdef ENABLE_DBUS_INTERFACE +#include +#include +#include +#include +#include +#endif + KEYSMITH_LOGGER(logger, ".app.cli") namespace app @@ -165,8 +173,62 @@ namespace app return false; } - (new InitialFlow(m_keysmith))->run(parser); + if (state->initialFlowDone()) { + (new ExternalCommandLineFlow(m_keysmith))->run(parser); + } else { + (new InitialFlow(m_keysmith))->run(parser); + } return true; } + +#ifdef ENABLE_DBUS_INTERFACE + + static QWindow * getMainWindow(QGuiApplication *app) + { + if (!app) { + qCDebug(logger) << "Cannot find a valid main window without a QGuiApplication"; + return nullptr; + } + + const auto windows = app->topLevelWindows(); + for (auto *window: windows) { + if (window && window->type() == Qt::Window) { + return window; + } + } + qCDebug(logger) << "Unable to find main window for QGuiApplication:" << app; + return nullptr; + } + + void Proxy::handleDBusActivation(const QStringList &arguments, const QString &workingDirectory) + { + Q_UNUSED(workingDirectory); + qCInfo(logger) << "Handling Keysmith activation request"; + + auto *s = sender(); + Q_ASSERT_X(s, Q_FUNC_INFO, "should be triggered with a valid sender()"); + + auto *svc = qobject_cast(s); + Q_ASSERT_X(svc, Q_FUNC_INFO, "should be triggered by a KDBusService instance"); + + QCommandLineParser cliParser; + bool parseOk = parseCommandLine(cliParser, arguments); + if (!proxy(cliParser, parseOk)) { + qCDebug(logger) << "Rejected command line arguments"; + svc->setExitValue(1); + } + + const auto mainWindow = getMainWindow(m_app); + if (!mainWindow) { + qCWarning(logger) << "Unable to activate Keysmith main window: unable to find it"; + svc->setExitValue(1); + return; + } + + qCDebug(logger) << "Activating Keysmith main window"; + KStartupInfo::setNewStartupId(mainWindow, KStartupInfo::startupId()); + KWindowSystem::activateWindow(mainWindow->winId()); + } +#endif } diff --git a/src/app/cli.h b/src/app/cli.h index 6ae6eef..221eeb1 100644 --- a/src/app/cli.h +++ b/src/app/cli.h @@ -8,6 +8,8 @@ #include "keysmith.h" #include "../model/input.h" +#include "../keysmith-features.h" + #include #include #include @@ -70,6 +72,10 @@ namespace app explicit Proxy(QGuiApplication *app, QObject *parent = nullptr); bool enable(Keysmith *keysmith); bool proxy(const QCommandLineParser &parser, bool parsedOk); +#ifdef ENABLE_DBUS_INTERFACE + public Q_SLOTS: + void handleDBusActivation(const QStringList &arguments, const QString &workingDirectory); +#endif private Q_SLOTS: void disable(void); private: diff --git a/src/app/flows_p.cpp b/src/app/flows_p.cpp index bf77856..38e6d54 100644 --- a/src/app/flows_p.cpp +++ b/src/app/flows_p.cpp @@ -204,4 +204,65 @@ namespace app flowStateOf(m_app)->setFlowRunning(false); QTimer::singleShot(0, this, &QObject::deleteLater); } + + ExternalCommandLineFlow::ExternalCommandLineFlow(Keysmith *app) : + QObject(app), m_app(app), m_input(new model::AccountInput(this)) + { + Q_ASSERT_X(app, Q_FUNC_INFO, "should have a Keysmith instance"); + } + + void ExternalCommandLineFlow::run(const QCommandLineParser &parser) + { + const auto argv = parser.positionalArguments(); + if (argv.isEmpty()) { + qCDebug(logger) << "No URIs to handle, nothing to do for external commandline:" << this; + QTimer::singleShot(0, this, &ExternalCommandLineFlow::deleteLater); + return; + } + + flowStateOf(m_app)->setFlowRunning(true); + overviewStateOf(m_app)->setActionsEnabled(false); + qCDebug(logger) << "Will parse given URI(s) from external commandline:" << this; + + auto job = new CommandLineAccountJob(m_input); + QObject::connect(job, &CommandLineAccountJob::newAccountProcessed, + this, &ExternalCommandLineFlow::onNewAccountProcessed); + QObject::connect(job, &CommandLineAccountJob::newAccountInvalid, + this, &ExternalCommandLineFlow::onNewAccountInvalid); + job->run(argv[0]); + } + + void ExternalCommandLineFlow::onNewAccountProcessed(void) + { + auto vm = new AddAccountViewModel(m_input, accountListOf(m_app), false, true); + QObject::connect(vm, &AddAccountViewModel::accepted, this, &ExternalCommandLineFlow::onAccepted); + QObject::connect(vm, &AddAccountViewModel::cancelled, this, &ExternalCommandLineFlow::back); + navigationFor(m_app)->push(Navigation::Page::AddAccount, vm); + } + + void ExternalCommandLineFlow::onNewAccountInvalid(void) + { + auto vm = new ErrorViewModel( + i18nc("@title:window", "Invalid account"), + i18nc("@info:label", "The account you are trying to add is invalid. Continue without adding the account."), + false + ); + QObject::connect(vm, &ErrorViewModel::dismissed, this, &ExternalCommandLineFlow::back); + navigationFor(m_app)->navigate(Navigation::Page::Error, vm); + } + + void ExternalCommandLineFlow::onAccepted(void) + { + accountListOf(m_app)->addAccount(m_input); + QTimer::singleShot(0, this, &ExternalCommandLineFlow::back); + } + + void ExternalCommandLineFlow::back(void) + { + auto vm = new AccountsOverviewViewModel(m_app); + navigationFor(m_app)->navigate(Navigation::Page::AccountsOverview, vm); + overviewStateOf(m_app)->setActionsEnabled(true); + flowStateOf(m_app)->setFlowRunning(false); + QTimer::singleShot(0, this, &QObject::deleteLater); + } } diff --git a/src/app/flows_p.h b/src/app/flows_p.h index 8ba4590..f9de2f2 100644 --- a/src/app/flows_p.h +++ b/src/app/flows_p.h @@ -51,6 +51,23 @@ namespace app Keysmith * const m_app; model::AccountInput * const m_input; }; + + class ExternalCommandLineFlow: public QObject + { + Q_OBJECT + public: + explicit ExternalCommandLineFlow(Keysmith *app); + public: + void run(const QCommandLineParser &parser); + private Q_SLOTS: + void onNewAccountInvalid(void); + void onNewAccountProcessed(void); + void back(void); + void onAccepted(void); + private: + Keysmith * const m_app; + model::AccountInput * const m_input; + }; } #endif diff --git a/src/main.cpp b/src/main.cpp index 4e321cb..7c03b0b 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -79,6 +79,7 @@ Q_DECL_EXPORT int main(int argc, char *argv[]) #ifdef ENABLE_DBUS_INTERFACE KDBusService service(KDBusService::Unique); + QObject::connect(&service, &KDBusService::activateRequested, &proxy, &app::Proxy::handleDBusActivation); #endif QQmlApplicationEngine engine;