diff --git a/src/server/src/extensions/clipboard/clipboard-extension.cpp b/src/server/src/extensions/clipboard/clipboard-extension.cpp index a1996424c..1fd5d537f 100644 --- a/src/server/src/extensions/clipboard/clipboard-extension.cpp +++ b/src/server/src/extensions/clipboard/clipboard-extension.cpp @@ -6,6 +6,8 @@ #include "extensions/clipboard/clipboard-history-command.hpp" #include "service-registry.hpp" +bool ClipboardExtension::preservePinnedOnClear = false; + class ClipboardClearCommand : public BuiltinCallbackCommand { QString id() const override { return "clear"; } QString name() const override { return "Clear Current Clipboard Data"; } @@ -38,10 +40,11 @@ class ClearClipboardHistoryCommand : public BuiltinCallbackCommand { auto ctx = ctrl->context(); auto clipman = ctx->services->clipman(); auto toast = ctx->services->toastService(); + bool preservePinned = ClipboardExtension::preservePinnedOnClear; ctx->navigation->confirmAlert("Are you sure?", "Your clipboard history will be gone forever :(", - [clipman, toast]() { - if (!clipman->removeAllSelections()) { + [clipman, toast, preservePinned]() { + if (!clipman->removeAllSelections(preservePinned)) { toast->failure("Failed to clear clipboard history"); return; } @@ -60,8 +63,9 @@ ClipboardExtension::ClipboardExtension() { void ClipboardExtension::initialized(const QJsonObject &preferences) const { auto clipman = ServiceRegistry::instance()->clipman(); bool const eraseOnStartup = preferences.value("eraseOnStartup").toBool(); + preservePinnedOnClear = preferences.value("preservePinnedOnClear").toBool(false); - if (eraseOnStartup) { clipman->removeAllSelections(); } + if (eraseOnStartup) { clipman->removeAllSelections(preservePinnedOnClear); } } void ClipboardExtension::preferenceValuesChanged(const QJsonObject &value) const { @@ -70,6 +74,7 @@ void ClipboardExtension::preferenceValuesChanged(const QJsonObject &value) const clipman->setMonitoring(value.value("monitoring").toBool()); clipman->setEncryption(value.value("encryption").toBool()); clipman->setIgnorePasswords(value.value("ignorePasswords").toBool()); + preservePinnedOnClear = value.value("preservePinnedOnClear").toBool(); } std::vector ClipboardExtension::preferences() const { @@ -77,6 +82,7 @@ std::vector ClipboardExtension::preferences() const { auto monitoring = Preference::makeCheckbox("monitoring"); auto eraseOnStartup = Preference::makeCheckbox("eraseOnStartup"); auto ignorePasswords = Preference::makeCheckbox("ignorePasswords"); + auto preservePinnedOnClear = Preference::makeCheckbox("preservePinnedOnClear"); eraseOnStartup.setTitle("Erase on startup"); eraseOnStartup.setDescription("Erase clipboard history every time the vicinae server is started"); @@ -100,5 +106,9 @@ std::vector ClipboardExtension::preferences() const { "performed while this is turned off will not be recorded."); monitoring.setDefaultValue(true); - return {monitoring, ignorePasswords, eraseOnStartup, encryption}; + preservePinnedOnClear.setTitle("Preserve pinned on clear"); + preservePinnedOnClear.setDescription("Keep pinned items when clearing clipboard history"); + preservePinnedOnClear.setDefaultValue(false); + + return {monitoring, ignorePasswords, eraseOnStartup, encryption, preservePinnedOnClear}; } diff --git a/src/server/src/extensions/clipboard/clipboard-extension.hpp b/src/server/src/extensions/clipboard/clipboard-extension.hpp index 9a8af08d7..5868db2b9 100644 --- a/src/server/src/extensions/clipboard/clipboard-extension.hpp +++ b/src/server/src/extensions/clipboard/clipboard-extension.hpp @@ -18,4 +18,6 @@ class ClipboardExtension : public BuiltinCommandRepository { void preferenceValuesChanged(const QJsonObject &value) const override; ClipboardExtension(); + + static bool preservePinnedOnClear; }; diff --git a/src/server/src/extensions/clipboard/history/clipboard-history-actions.hpp b/src/server/src/extensions/clipboard/history/clipboard-history-actions.hpp index fece4ec52..e71e8d505 100644 --- a/src/server/src/extensions/clipboard/history/clipboard-history-actions.hpp +++ b/src/server/src/extensions/clipboard/history/clipboard-history-actions.hpp @@ -5,6 +5,7 @@ #include "services/clipboard/clipboard-service.hpp" #include "services/toast/toast-service.hpp" #include "ui/alert/alert.hpp" +#include "extensions/clipboard/clipboard-extension.hpp" class PasteClipboardSelection : public PasteToFocusedWindowAction { QString m_id; @@ -99,7 +100,8 @@ class RemoveAllSelectionsAction : public AbstractAction { alert->setConfirmCallback([ctx]() { auto toast = ctx->services->toastService(); auto clipman = ctx->services->clipman(); - if (clipman->removeAllSelections()) { + bool preservePinned = ClipboardExtension::preservePinnedOnClear; + if (clipman->removeAllSelections(preservePinned)) { toast->success("All selections were removed"); } else { toast->failure("Failed to remove all selections"); diff --git a/src/server/src/services/clipboard/clipboard-db.cpp b/src/server/src/services/clipboard/clipboard-db.cpp index add562e7f..aa28e2af6 100644 --- a/src/server/src/services/clipboard/clipboard-db.cpp +++ b/src/server/src/services/clipboard/clipboard-db.cpp @@ -191,6 +191,37 @@ bool ClipboardDatabase::removeAll() { query.exec("DELETE FROM selection"); } +bool ClipboardDatabase::removeAll(bool preservePinned) { + QSqlQuery query(m_db); + + if (preservePinned) { + return query.exec("DELETE FROM selection_fts") && + query.exec("DELETE FROM data_offer WHERE selection_id IN (SELECT id FROM selection WHERE " + "pinned_at IS NULL)") && + query.exec("DELETE FROM selection WHERE pinned_at IS NULL"); + } + + return removeAll(); +} + +std::vector ClipboardDatabase::getOfferIdsToDelete(bool preservePinned) { + std::vector offerIds; + QSqlQuery query(m_db); + + if (preservePinned) { + query.exec( + "SELECT id FROM data_offer WHERE selection_id IN (SELECT id FROM selection WHERE pinned_at IS NULL)"); + } else { + query.exec("SELECT id FROM data_offer"); + } + + while (query.next()) { + offerIds.emplace_back(query.value(0).toString()); + } + + return offerIds; +} + std::vector ClipboardDatabase::removeSelection(const QString &selectionId) { if (!m_db.transaction()) { return {}; } diff --git a/src/server/src/services/clipboard/clipboard-db.hpp b/src/server/src/services/clipboard/clipboard-db.hpp index 91baa5114..3e66230a5 100644 --- a/src/server/src/services/clipboard/clipboard-db.hpp +++ b/src/server/src/services/clipboard/clipboard-db.hpp @@ -91,6 +91,8 @@ class ClipboardDatabase { const ClipboardListSettings &opts = {}) const; bool removeAll(); + bool removeAll(bool preservePinned); + std::vector getOfferIdsToDelete(bool preservePinned); bool setKeywords(const QString &id, const QString &keywords); std::optional retrieveKeywords(const QString &id); diff --git a/src/server/src/services/clipboard/clipboard-service.cpp b/src/server/src/services/clipboard/clipboard-service.cpp index d0c6b35e4..d5ab495ee 100644 --- a/src/server/src/services/clipboard/clipboard-service.cpp +++ b/src/server/src/services/clipboard/clipboard-service.cpp @@ -598,6 +598,30 @@ bool ClipboardService::removeAllSelections() { return true; } +bool ClipboardService::removeAllSelections(bool preservePinned) { + ClipboardDatabase db; + + auto offerIds = db.getOfferIdsToDelete(preservePinned); + + if (!db.removeAll(preservePinned)) { + qWarning() << "Failed to remove clipboard selections"; + return false; + } + + if (!preservePinned) { + fs::remove_all(m_dataDir); + fs::create_directories(m_dataDir); + } else { + for (const auto &offerId : offerIds) { + fs::remove(m_dataDir / offerId.toStdString()); + } + } + + emit allSelectionsRemoved(); + + return true; +} + AbstractClipboardServer *ClipboardService::clipboardServer() const { return m_clipboardServer.get(); } ClipboardService::ClipboardService(const std::filesystem::path &path) { diff --git a/src/server/src/services/clipboard/clipboard-service.hpp b/src/server/src/services/clipboard/clipboard-service.hpp index de490c0b4..4a1f7bfb9 100644 --- a/src/server/src/services/clipboard/clipboard-service.hpp +++ b/src/server/src/services/clipboard/clipboard-service.hpp @@ -79,6 +79,7 @@ class ClipboardService : public QObject, public NonCopyable { static Clipboard::ReadContent readContent(); bool removeAllSelections(); + bool removeAllSelections(bool preservePinned); std::optional retrieveKeywords(const QString &id); bool setKeywords(const QString &id, const QString &keywords);