From 6106212f5623864936e23fdf0bb8538a4aee7a98 Mon Sep 17 00:00:00 2001 From: Christian Heimlich Date: Tue, 7 Nov 2023 13:11:08 -0500 Subject: [PATCH] Simplify c-link by adding dedicated name argument This changes the path argument to explicitly a directory and obviates the need to decipher if the path is a file or directory. --- README.md | 51 ++++++++++++++------------------ app/src/command/c-link.cpp | 39 ++++++++---------------- app/src/command/c-link.h | 14 ++++----- app/src/command/c-link_linux.cpp | 5 +--- app/src/command/c-link_win.cpp | 6 ++-- app/src/controller.cpp | 1 + app/src/frontend/statusrelay.cpp | 10 +++++++ app/src/frontend/statusrelay.h | 1 + app/src/kernel/core.cpp | 12 ++++++++ app/src/kernel/core.h | 9 ++++++ app/src/kernel/driver.cpp | 1 + app/src/kernel/driver.h | 1 + 12 files changed, 80 insertions(+), 70 deletions(-) diff --git a/README.md b/README.md index 5762a4b..5171f9b 100644 --- a/README.md +++ b/README.md @@ -146,10 +146,10 @@ or --msg="I am a message" ### Global Options: - - **-h | --help | -?:** Prints usage information - - **-v | --version:** Prints the current version of the tool - - **-q | --quiet:** Silences all non-critical messages - - **-s | --silent:** Silences all messages (takes precedence over quiet mode) +- **-h | --help | -?:** Prints usage information +- **-v | --version:** Prints the current version of the tool +- **-q | --quiet:** Silences all non-critical messages +- **-s | --silent:** Silences all messages (takes precedence over quiet mode) Every command also has a corresponding help switch for command specific usage information. @@ -157,12 +157,12 @@ Every command also has a corresponding help switch for command specific usage in Many of CLIFp's commands require a game/animation to be specified, which can be done in several ways. These commands, are known as *title commands* and are noted as such below. The explanation for these common options are shown here instead of being repeated under each individually. Title Comamnd Shared Options: - - **-i | --id:** UUID of a game - - **-t | --title:** The title of a game. - - **-T | --title-strict:** Same as **-t**, but only exact matches are considered - - **-s | --subtitle:** Name of an additional-app under a game. Must be used with **-t**/**-T** - - **-S | --subtitle-strict:** Same as **-s**, but only exact matches are considered - - **-r | --random:** Selects a random title from the database. Must be followed by a library filter: `all`/`any`, `game`/`arcade`, or `animation`/`theatre` +- **-i | --id:** UUID of a game +- **-t | --title:** The title of a game. +- **-T | --title-strict:** Same as **-t**, but only exact matches are considered +- **-s | --subtitle:** Name of an additional-app under a game. Must be used with **-t**/**-T** +- **-S | --subtitle-strict:** Same as **-s**, but only exact matches are considered +- **-r | --random:** Selects a random title from the database. Must be followed by a library filter: `all`/`any`, `game`/`arcade`, or `animation`/`theatre` The **-title** and **-subtitle** options are case-insensitive and will match any title that contains the value provided; however, the provided title should match as closely as possible to how it appears within Flashpoint, as checks for close matches are limited due to technical restrictions. If more than one entry is found, a dialog window with more information will be displayed so that the intended title can be selected, though there is a limit to the number of matches. @@ -174,18 +174,11 @@ Tip: You can use **-subtitle** with an empty string (i.e. `-s ""`) to see all of **link** - Creates a shortcut to a Flashpoint title Options: - - [Title Command](#title-commands) options - - **-p | --path:** Path to new shortcut, defaults to the desktop. Path's ending with ".lnk" (Windows) or ".desktop" (Linux) will be interpreted as a named shortcut file. Any other path will be interpreted as a directory and the title will automatically be used as the filename +- [Title Command](#title-commands) options +- **-p | --path:** Path to directory in which to place the shortcut. Prompts if not provided. +- **-n | --name:** Name of the shortcut. Defaults to the name of the title Notes: - - - On Linux, when providing a full shortcut path via the **--path** switch, the filename component is re-interpreted as the shortcut's display name and the actual filename is set automatically. - - For example, when specifying: - - CLIFp link -p "~/Desktop/Cool Name.desktop" ... - - the display name of the desktop entry will be set to "Cool Name". - On some Linux desktop environments (i.e. GNOME) the shortcut might need to manually be set to "trusted" in order to be used and displayed correctly after it is created. This option is usually available in the file's right-click context menu. -------------------------------------------------------------------------------- @@ -194,7 +187,7 @@ Notes: Options: - - [Title Command](#title-commands) options +- [Title Command](#title-commands) options -------------------------------------------------------------------------------- @@ -202,15 +195,15 @@ Options: Options: - - [Title Command](#title-commands) options +- [Title Command](#title-commands) options -------------------------------------------------------------------------------- **run** - Start Flashpoint's webserver and then execute the provided application Options: - - **-a | --app:** Relative (to Flashpoint Directory) path of application to launch - - **-p | --param:** Command-line parameters to use when starting the application +- **-a | --app:** Relative (to Flashpoint Directory) path of application to launch +- **-p | --param:** Command-line parameters to use when starting the application Requires: **-a** @@ -235,8 +228,8 @@ See http://www.robvanderwoude.com/escapechars.php for more information. **share** - Generates a URL for starting a Flashpoint title that can be shared to other users. Options: - - [Title Command](#title-commands) options - - **-u | --universal:** Creates a standard HTTPS link that utilizes a redirect page. May be easier to share on some platforms. + - [Title Command](#title-commands) options + - **-u | --universal:** Creates a standard HTTPS link that utilizes a redirect page. May be easier to share on some platforms. - **-c | --configure:** Registers CLIFp as the default handler for "flashpoint" protocol links. - **-C | --unconfigure:** Removes CLIFp as the default handler for "flashpoint" protocol links. @@ -250,9 +243,9 @@ Notes: **show** - Display a message or extra folder Options: - - **-m | --msg:** Displays an pop-up dialog with the supplied message. Used primarily for some additional apps - - **-e | --extra:** Opens an explorer window to the specified extra. Used primarily for some additional apps - - **-h | --help | -?:** Prints command specific usage information + - **-m | --msg:** Displays an pop-up dialog with the supplied message. Used primarily for some additional apps + - **-e | --extra:** Opens an explorer window to the specified extra. Used primarily for some additional apps + - **-h | --help | -?:** Prints command specific usage information Requires: **-m** or **-e** diff --git a/app/src/command/c-link.cpp b/app/src/command/c-link.cpp index 93438f3..d6e0489 100644 --- a/app/src/command/c-link.cpp +++ b/app/src/command/c-link.cpp @@ -54,7 +54,7 @@ Qx::Error CLink::perform() // Get database Fp::Db* database = mCore.fpInstall().database(); - // Get entry (also confirms that ID is present in database) + // Get entry (also confirms that ID is present in database, which is why we do this even if a custom name is set) std::variant entry_v; Fp::DbError dbError = database->getEntry(entry_v, shortcutId); if(dbError.isValid()) @@ -66,7 +66,7 @@ Qx::Error CLink::perform() if(std::holds_alternative(entry_v)) { Fp::Game game = std::get(entry_v); - shortcutName = Qx::kosherizeFileName(game.title()); + shortcutName = game.title(); } else if(std::holds_alternative(entry_v)) { @@ -82,38 +82,28 @@ Qx::Error CLink::perform() Q_ASSERT(std::holds_alternative(entry_v)); Fp::Game parent = std::get(entry_v); - shortcutName = Qx::kosherizeFileName(parent.title() + u" ("_s + addApp.name() + u")"_s); + shortcutName = parent.title() + u" ("_s + addApp.name() + u")"_s; } else qCritical("Invalid variant state for std::variant."); + // Override shortcut name with user input + if(mParser.isSet(CL_OPTION_NAME)) + shortcutName = mParser.value(CL_OPTION_NAME); + // Get shortcut path if(mParser.isSet(CL_OPTION_PATH)) - { - QFileInfo inputPathInfo(mParser.value(CL_OPTION_PATH)); - if(inputPathInfo.suffix() == shortcutExtension()) // Path is file - { - mCore.logEvent(NAME, LOG_EVENT_FILE_PATH); - shortcutDir = inputPathInfo.absoluteDir(); - shortcutName = inputPathInfo.baseName(); - } - else // Path is directory - { - mCore.logEvent(NAME, LOG_EVENT_DIR_PATH); - shortcutDir = QDir(inputPathInfo.absoluteFilePath()); - } - } + shortcutDir = mParser.value(CL_OPTION_PATH); else { mCore.logEvent(NAME, LOG_EVENT_NO_PATH); // Prompt user for path - Core::SaveFileRequest sfr{ + Core::ExistingDirRequest edr{ .caption = DIAG_CAPTION, - .dir = QDir::homePath() + u"/Desktop/"_s + shortcutName, - .filter = u"Shortcuts (*. "_s + shortcutExtension() + u")"_s + .dir = QStandardPaths::writableLocation(QStandardPaths::DesktopLocation) }; - QString selectedPath = mCore.requestSaveFilePath(sfr); + QString selectedPath = mCore.requestExistingDirPath(edr); if(selectedPath.isEmpty()) { @@ -122,13 +112,8 @@ Qx::Error CLink::perform() } else { - if(!selectedPath.endsWith(u"."_s + shortcutExtension(), Qt::CaseInsensitive)) - selectedPath += u"."_s + shortcutExtension(); - mCore.logEvent(NAME, LOG_EVENT_SEL_PATH.arg(QDir::toNativeSeparators(selectedPath))); - QFileInfo pathInfo(selectedPath); - shortcutDir = pathInfo.absoluteDir(); - shortcutName = pathInfo.baseName(); + shortcutDir = selectedPath; } } diff --git a/app/src/command/c-link.h b/app/src/command/c-link.h index 3779dce..70778af 100644 --- a/app/src/command/c-link.h +++ b/app/src/command/c-link.h @@ -60,8 +60,6 @@ class CLink : public TitleCommand static inline const QString DIAG_CAPTION = u"Select a shortcut destination..."_s; // Logging - Messages - static inline const QString LOG_EVENT_FILE_PATH = u"Shortcut path provided is for a file"_s; - static inline const QString LOG_EVENT_DIR_PATH = u"Shortcut path provided is for a folder"_s; static inline const QString LOG_EVENT_NO_PATH = u"No shortcut path provided, user will be prompted"_s; static inline const QString LOG_EVENT_SEL_PATH = u"Shortcut path selected: %1"_s; static inline const QString LOG_EVENT_DIAG_CANCEL = u"Shortcut path selection canceled."_s; @@ -71,14 +69,17 @@ class CLink : public TitleCommand // Command line option strings static inline const QString CL_OPT_PATH_S_NAME = u"p"_s; static inline const QString CL_OPT_PATH_L_NAME = u"path"_s; - static inline const QString CL_OPT_PATH_DESC = u"Path to new shortcut. Path's ending with "".lnk""//"".desktop"" will be interpreted as a named shortcut file. " - "Any other path will be interpreted as a directory and the title will automatically be used " - "as the filename"_s; + static inline const QString CL_OPT_PATH_DESC = u"Path to a directory for the new shortcut"_s; + + static inline const QString CL_OPT_NAME_S_NAME = u"n"_s; + static inline const QString CL_OPT_NAME_L_NAME = u"name"_s; + static inline const QString CL_OPT_NAME_DESC = u"Name of the shortcut. Defaults to the name of the title"_s; // Command line options static inline const QCommandLineOption CL_OPTION_PATH{{CL_OPT_PATH_S_NAME, CL_OPT_PATH_L_NAME}, CL_OPT_PATH_DESC, u"path"_s}; // Takes value + static inline const QCommandLineOption CL_OPTION_NAME{{CL_OPT_NAME_S_NAME, CL_OPT_NAME_L_NAME}, CL_OPT_NAME_DESC, u"name"_s}; // Takes value - static inline const QList CL_OPTIONS_SPECIFIC{&CL_OPTION_PATH}; + static inline const QList CL_OPTIONS_SPECIFIC{&CL_OPTION_PATH, &CL_OPTION_NAME}; static inline const QSet CL_OPTIONS_REQUIRED{}; public: @@ -93,7 +94,6 @@ class CLink : public TitleCommand //-Instance Functions------------------------------------------------------------------------------------------------------ private: Qx::Error createShortcut(const QString& name, const QDir& dir, QUuid id); - QString shortcutExtension() const; protected: QList options() override; diff --git a/app/src/command/c-link_linux.cpp b/app/src/command/c-link_linux.cpp index e4aeea6..9fb1c24 100644 --- a/app/src/command/c-link_linux.cpp +++ b/app/src/command/c-link_linux.cpp @@ -34,8 +34,7 @@ Qx::Error CLink::createShortcut(const QString& name, const QDir& dir, QUuid id) ade.setComment(u"Generated by "_s PROJECT_SHORT_NAME " " PROJECT_VERSION_STR); // Create entry - QString filename = u"org.flashpoint.clifp."_s + id.toString(QUuid::WithoutBraces) + - '.' + shortcutExtension(); + QString filename = u"org.flashpoint.clifp."_s + id.toString(QUuid::WithoutBraces) + u".desktop"_s; QString fullEntryPath = dir.absoluteFilePath(filename); Qx::IoOpReport writeReport = Qx::DesktopEntry::writeToDisk(fullEntryPath, &ade); @@ -51,5 +50,3 @@ Qx::Error CLink::createShortcut(const QString& name, const QDir& dir, QUuid id) // Return success return CLinkError(); } - -QString CLink::shortcutExtension() const { return u"desktop"_s; }; diff --git a/app/src/command/c-link_win.cpp b/app/src/command/c-link_win.cpp index 543adb6..0999d70 100644 --- a/app/src/command/c-link_win.cpp +++ b/app/src/command/c-link_win.cpp @@ -16,6 +16,8 @@ //Private: Qx::Error CLink::createShortcut(const QString& name, const QDir& dir, QUuid id) { + QString filename = Qx::kosherizeFileName(name); + // Create shortcut properties Qx::ShortcutProperties sp; sp.target = CLIFP_PATH; @@ -23,7 +25,7 @@ Qx::Error CLink::createShortcut(const QString& name, const QDir& dir, QUuid id) sp.comment = name; // Create shortcut - QString fullShortcutPath = dir.absolutePath() + '/' + name + '.' + shortcutExtension(); + QString fullShortcutPath = dir.absolutePath() + '/' + filename + u".lnk"_s; Qx::SystemError shortcutError = Qx::createShortcut(fullShortcutPath, sp); // Check for creation failure @@ -38,5 +40,3 @@ Qx::Error CLink::createShortcut(const QString& name, const QDir& dir, QUuid id) // Return success return CLinkError(); } - -QString CLink::shortcutExtension() const { return u"lnk"_s; }; diff --git a/app/src/controller.cpp b/app/src/controller.cpp index 77fa4cc..1ab58e7 100644 --- a/app/src/controller.cpp +++ b/app/src/controller.cpp @@ -45,6 +45,7 @@ Controller::Controller(QObject* parent) : connect(driver, &Driver::message, &mStatusRelay, &StatusRelay::messageHandler, Qt::BlockingQueuedConnection); // Allows optional blocking connect(driver, &Driver::blockingErrorOccurred, &mStatusRelay, &StatusRelay::blockingErrorHandler, Qt::BlockingQueuedConnection); connect(driver, &Driver::saveFileRequested, &mStatusRelay, &StatusRelay::saveFileRequestHandler, Qt::BlockingQueuedConnection); + connect(driver, &Driver::existingDirRequested, &mStatusRelay, &StatusRelay::existingDirectoryRequestHandler, Qt::BlockingQueuedConnection); connect(driver, &Driver::itemSelectionRequested, &mStatusRelay, &StatusRelay::itemSelectionRequestHandler, Qt::BlockingQueuedConnection); connect(driver, &Driver::questionAnswerRequested, &mStatusRelay, &StatusRelay::questionAnswerRequestHandler, Qt::BlockingQueuedConnection); diff --git a/app/src/frontend/statusrelay.cpp b/app/src/frontend/statusrelay.cpp index 50c7abe..9cd24ba 100644 --- a/app/src/frontend/statusrelay.cpp +++ b/app/src/frontend/statusrelay.cpp @@ -110,6 +110,16 @@ void StatusRelay::saveFileRequestHandler(QSharedPointer file, Core::Sav qFatal("No response argument provided!"); } +void StatusRelay::existingDirectoryRequestHandler(QSharedPointer dir, Core::ExistingDirRequest request) +{ + if(dir) + { + *dir = QFileDialog::getExistingDirectory(nullptr, request.caption, request.dir, request.options); + } + else + qFatal("No response argument provided!"); +} + void StatusRelay::itemSelectionRequestHandler(QSharedPointer item, const Core::ItemSelectionRequest& request) { if(item) diff --git a/app/src/frontend/statusrelay.h b/app/src/frontend/statusrelay.h index 41f9a21..9894afa 100644 --- a/app/src/frontend/statusrelay.h +++ b/app/src/frontend/statusrelay.h @@ -51,6 +51,7 @@ public slots: void blockingErrorHandler(QSharedPointer response, Core::BlockingError blockingError); void messageHandler(const Message& message); void saveFileRequestHandler(QSharedPointer file, Core::SaveFileRequest request); + void existingDirectoryRequestHandler(QSharedPointer dir, Core::ExistingDirRequest request); void itemSelectionRequestHandler(QSharedPointer item, const Core::ItemSelectionRequest& request); void clipboardUpdateRequestHandler(const QString& text); void questionAnswerRequestHandler(QSharedPointer response, const QString& question); diff --git a/app/src/kernel/core.cpp b/app/src/kernel/core.cpp index 04d3fc1..4f7c9b2 100644 --- a/app/src/kernel/core.cpp +++ b/app/src/kernel/core.cpp @@ -911,6 +911,18 @@ QString Core::requestSaveFilePath(const SaveFileRequest& request) return *file; } +QString Core::requestExistingDirPath(const ExistingDirRequest& request) +{ + // Response holder + QSharedPointer dir = QSharedPointer::create(); + + // Emit and get response + emit existingDirRequested(dir, request); + + // Return response + return *dir; +} + QString Core::requestItemSelection(const ItemSelectionRequest& request) { // Response holder diff --git a/app/src/kernel/core.h b/app/src/kernel/core.h index 70189b8..f134043 100644 --- a/app/src/kernel/core.h +++ b/app/src/kernel/core.h @@ -117,6 +117,13 @@ class Core : public QObject QFileDialog::Options options; }; + struct ExistingDirRequest + { + QString caption; + QString dir; + QFileDialog::Options options = QFileDialog::ShowDirsOnly; + }; + struct ItemSelectionRequest { QString caption; @@ -310,6 +317,7 @@ class Core : public QObject int postBlockingError(QString src, Qx::Error error, bool log = true, QMessageBox::StandardButtons bs = QMessageBox::Ok, QMessageBox::StandardButton def = QMessageBox::NoButton); void postMessage(const Message& msg); QString requestSaveFilePath(const SaveFileRequest& request); + QString requestExistingDirPath(const ExistingDirRequest& request); QString requestItemSelection(const ItemSelectionRequest& request); void requestClipboardUpdate(const QString& text); bool requestQuestionAnswer(const QString& question); @@ -337,6 +345,7 @@ class Core : public QObject void errorOccurred(const Core::Error& error); void blockingErrorOccurred(QSharedPointer response, const Core::BlockingError& blockingError); void saveFileRequested(QSharedPointer file, const Core::SaveFileRequest& request); + void existingDirRequested(QSharedPointer dir, const Core::ExistingDirRequest& request); void itemSelectionRequested(QSharedPointer item, const Core::ItemSelectionRequest& request); void message(const Message& message); void clipboardUpdateRequested(const QString& text); diff --git a/app/src/kernel/driver.cpp b/app/src/kernel/driver.cpp index 2915aed..b215947 100644 --- a/app/src/kernel/driver.cpp +++ b/app/src/kernel/driver.cpp @@ -61,6 +61,7 @@ void Driver::init() connect(mCore, &Core::blockingErrorOccurred, this, &Driver::blockingErrorOccurred); connect(mCore, &Core::message, this, &Driver::message); connect(mCore, &Core::saveFileRequested, this, &Driver::saveFileRequested); + connect(mCore, &Core::existingDirRequested, this, &Driver::existingDirRequested); connect(mCore, &Core::itemSelectionRequested, this, &Driver::itemSelectionRequested); connect(mCore, &Core::clipboardUpdateRequested, this, &Driver::clipboardUpdateRequested); connect(mCore, &Core::questionAnswerRequested, this, &Driver::questionAnswerRequested); diff --git a/app/src/kernel/driver.h b/app/src/kernel/driver.h index 217fd0f..b9b22e8 100644 --- a/app/src/kernel/driver.h +++ b/app/src/kernel/driver.h @@ -152,6 +152,7 @@ public slots: void blockingErrorOccurred(QSharedPointer response, const Core::BlockingError& blockingError); void message(const Message& message); void saveFileRequested(QSharedPointer file, const Core::SaveFileRequest& request); + void existingDirRequested(QSharedPointer dir, const Core::ExistingDirRequest& request); void itemSelectionRequested(QSharedPointer item, const Core::ItemSelectionRequest& request); void clipboardUpdateRequested(const QString& text); void questionAnswerRequested(QSharedPointer response, const QString& question);