reapack

Package manager for REAPER
Log | Files | Refs | Submodules | README | LICENSE

commit 8aca0bfe1c3626ee33b06afc5e65c6688fbf1a25
parent d401c43528d5a28cdeddb1b65cbbe36670ddbf1e
Author: cfillion <cfillion@users.noreply.github.com>
Date:   Wed,  4 May 2016 21:39:12 -0400

Merge branch 'pin-version'

Diffstat:
Msrc/browser.cpp | 197+++++++++++++++++++++++++++++++++++++++++++++++++++----------------------------
Msrc/browser.hpp | 25++++++++++++++++---------
Msrc/progress.cpp | 2+-
Msrc/receipt.hpp | 11++++++++++-
Msrc/registry.cpp | 19+++++++++++++++----
Msrc/registry.hpp | 5++++-
Msrc/task.hpp | 10++++++++++
Msrc/transaction.cpp | 43+++++++++++++++++++++++++------------------
Msrc/transaction.hpp | 11+----------
Mtest/registry.cpp | 17+++++++++++++++++
10 files changed, 226 insertions(+), 114 deletions(-)

diff --git a/src/browser.cpp b/src/browser.cpp @@ -43,6 +43,7 @@ enum Action { ACTION_REINSTALL_ALL, ACTION_UNINSTALL, ACTION_UNINSTALL_ALL, + ACTION_PIN, ACTION_CONTENTS, ACTION_HISTORY, ACTION_ABOUT, @@ -138,6 +139,9 @@ void Browser::onCommand(const int id, const int event) case ACTION_UNINSTALL_ALL: selectionDo(bind(&Browser::uninstall, this, arg::_1, false)); break; + case ACTION_PIN: + togglePin(m_currentIndex); + break; case ACTION_CONTENTS: contents(m_currentIndex); break; @@ -148,7 +152,7 @@ void Browser::onCommand(const int id, const int event) about(m_currentIndex); break; case ACTION_RESET_ALL: - selectionDo(bind(&Browser::resetAction, this, arg::_1)); + selectionDo(bind(&Browser::resetTarget, this, arg::_1)); break; case ACTION_REFRESH: refresh(true); @@ -201,7 +205,7 @@ void Browser::onContextMenu(HWND target, const int x, const int y) menu.addAction(AUTO_STR("&Install/update selection"), ACTION_LATEST_ALL); menu.addAction(AUTO_STR("&Reinstall selection"), ACTION_REINSTALL_ALL); menu.addAction(AUTO_STR("&Uninstall selection"), ACTION_UNINSTALL_ALL); - menu.addAction(AUTO_STR("&Clear queued action"), ACTION_RESET_ALL); + menu.addAction(AUTO_STR("&Clear queued actions"), ACTION_RESET_ALL); menu.addSeparator(); } @@ -220,7 +224,7 @@ void Browser::onContextMenu(HWND target, const int x, const int y) make_autostring(entry->latest->name()).c_str()); const UINT actionIndex = menu.addAction(installLabel, ACTION_LATEST); - if(isTarget(entry, entry->latest)) + if(entry->target && *entry->target == entry->latest) menu.check(actionIndex); } @@ -232,7 +236,7 @@ void Browser::onContextMenu(HWND target, const int x, const int y) const UINT actionIndex = menu.addAction(reinstallLabel, ACTION_REINSTALL); if(!entry->current || entry->test(ObsoleteFlag)) menu.disable(actionIndex); - else if(isTarget(entry, entry->current)) + else if(entry->target && *entry->target == entry->current) menu.check(actionIndex); } else { @@ -242,7 +246,7 @@ void Browser::onContextMenu(HWND target, const int x, const int y) make_autostring(entry->latest->name()).c_str()); const UINT actionIndex = menu.addAction(installLabel, ACTION_LATEST); - if(isTarget(entry, entry->latest)) + if(entry->target && *entry->target == entry->latest) menu.check(actionIndex); } @@ -257,9 +261,8 @@ void Browser::onContextMenu(HWND target, const int x, const int y) const UINT actionIndex = versionMenu.addAction( make_autostring(ver->name()).c_str(), --verIndex | (ACTION_VERSION << 8)); - const bool isAction = hasAction(entry); - if(isAction ? isTarget(entry, ver) : ver == entry->current) { - if(isAction && ver != entry->latest) + if(entry->target ? *entry->target == ver : ver == entry->current) { + if(entry->target && ver != entry->latest) menu.check(versionMenuIndex); versionMenu.checkRadio(actionIndex); @@ -271,11 +274,21 @@ void Browser::onContextMenu(HWND target, const int x, const int y) menu.addAction(AUTO_STR("&Uninstall"), ACTION_UNINSTALL); if(!entry->test(InstalledFlag)) menu.disable(uninstallIndex); - else if(isTarget(entry, nullptr)) + else if(entry->target && *entry->target == nullptr) menu.check(uninstallIndex); menu.addSeparator(); + const UINT pinIndex = menu.addAction( + AUTO_STR("&Pin current version"), ACTION_PIN); + + if(!entry->canPin()) + menu.disable(pinIndex); + if(entry->pin.value_or(entry->regEntry.pinned)) + menu.check(pinIndex); + + menu.addSeparator(); + menu.setEnabled(!entry->test(ObsoleteFlag), menu.addAction(AUTO_STR("Package &Contents"), ACTION_CONTENTS)); @@ -468,27 +481,34 @@ void Browser::populate() void Browser::transferActions() { - auto actionIt = m_actions.begin(); - while(actionIt != m_actions.end()) - { - const Entry &oldEntry = *actionIt->first; - const Version *target = actionIt->second; - - const auto &entryIt = find(m_entries.begin(), m_entries.end(), oldEntry); - - actionIt = m_actions.erase(actionIt); + unordered_set<Entry *> oldActions; + swap(m_actions, oldActions); + for(Entry *oldEntry : oldActions) { + const auto &entryIt = find(m_entries.begin(), m_entries.end(), *oldEntry); if(entryIt == m_entries.end()) continue; - if(target) { - const Package *pkg = entryIt->package; - if(!pkg || !(target = pkg->findVersion(*target))) - continue; + if(oldEntry->target) { + const Version *target = *oldEntry->target; + + if(target) { + const Package *pkg = entryIt->package; + if(!pkg || !(target = pkg->findVersion(*target))) + continue; + } + + entryIt->target = target; } - m_actions[&*entryIt] = target; + if(oldEntry->pin) + entryIt->pin = *oldEntry->pin; + + m_actions.insert(&*entryIt); } + + if(m_actions.empty()) + disable(m_apply); } auto Browser::makeEntry(const Package *pkg, const Registry::Entry &regEntry) @@ -580,6 +600,8 @@ string Browser::getValue(const Column col, const Entry &entry) const case StateColumn: { if(entry.test(ObsoleteFlag)) display += 'o'; + else if(entry.regEntry.pinned) + display += 'p'; else if(entry.test(OutOfDateFlag)) display += 'u'; else if(entry.test(InstalledFlag)) @@ -587,8 +609,10 @@ string Browser::getValue(const Column col, const Entry &entry) const else display += '\x20'; - if(hasAction(&entry)) - display += isTarget(&entry, nullptr) ? 'U' : 'I'; + if(entry.target) + display += *entry.target == nullptr ? 'U' : 'I'; + if(entry.pin && entry.canPin()) + display += 'P'; return display; } @@ -621,13 +645,11 @@ string Browser::getValue(const Column col, const Entry &entry) const bool Browser::match(const Entry &entry) const { - using namespace boost; - switch(currentTab()) { case All: break; case Queued: - if(!hasAction(&entry)) + if(!m_actions.count(const_cast<Entry *>(&entry))) return false; break; case Installed: @@ -661,7 +683,7 @@ bool Browser::match(const Entry &entry) const return m_filter.match(name + category + author); } -auto Browser::getEntry(const int listIndex) const -> const Entry * +auto Browser::getEntry(const int listIndex) -> Entry * { if(listIndex < 0 || listIndex >= (int)m_visibleEntries.size()) return nullptr; @@ -669,7 +691,7 @@ auto Browser::getEntry(const int listIndex) const -> const Entry * return &m_entries[m_visibleEntries[listIndex]]; } -void Browser::history(const int index) const +void Browser::history(const int index) { const Entry *entry = getEntry(index); @@ -677,7 +699,7 @@ void Browser::history(const int index) const Dialog::Show<History>(instance(), handle(), entry->package); } -void Browser::contents(const int index) const +void Browser::contents(const int index) { const Entry *entry = getEntry(index); @@ -685,7 +707,7 @@ void Browser::contents(const int index) const Dialog::Show<Contents>(instance(), handle(), entry->package); } -void Browser::about(const int index) const +void Browser::about(const int index) { if(const Entry *entry = getEntry(index)) m_reapack->about(getValue(RemoteColumn, *entry), handle()); @@ -696,7 +718,7 @@ void Browser::installLatest(const int index, const bool toggle) const Entry *entry = getEntry(index); if(entry && entry->latest && entry->latest != entry->current) - setAction(index, entry->latest, toggle); + setTarget(index, entry->latest, toggle); } void Browser::reinstall(const int index, const bool toggle) @@ -704,12 +726,15 @@ void Browser::reinstall(const int index, const bool toggle) const Entry *entry = getEntry(index); if(entry && entry->current) - setAction(index, entry->current, toggle); + setTarget(index, entry->current, toggle); } void Browser::installVersion(const int index, const size_t verIndex) { const Entry *entry = getEntry(index); + if(!entry) + return; + const auto versions = entry->package->versions(); if(verIndex >= versions.size()) @@ -718,9 +743,9 @@ void Browser::installVersion(const int index, const size_t verIndex) const Version *target = entry->package->version(verIndex); if(target == entry->current) - resetAction(index); + resetTarget(index); else - setAction(index, target); + setTarget(index, target); } void Browser::uninstall(const int index, const bool toggle) @@ -728,52 +753,74 @@ void Browser::uninstall(const int index, const bool toggle) const Entry *entry = getEntry(index); if(entry && entry->test(InstalledFlag)) - setAction(index, nullptr, toggle); + setTarget(index, nullptr, toggle); } -void Browser::resetAction(const int index) +void Browser::togglePin(const int index) { - const Entry *entry = getEntry(index); - const auto it = m_actions.find(entry); - - if(it == m_actions.end()) + Entry *entry = getEntry(index); + if(!entry) return; - m_actions.erase(it); + const bool newVal = !entry->pin.value_or(entry->regEntry.pinned); - if(currentTab() == Queued) { - m_list->removeRow(index); - m_visibleEntries.erase(m_visibleEntries.begin() + index); - updateDisplayLabel(); + if(newVal == entry->regEntry.pinned) { + entry->pin = boost::none; + if(!entry->target) + m_actions.erase(entry); + } + else { + entry->pin = newVal; + m_actions.insert(entry); } - else - m_list->replaceRow(index, makeRow(*entry)); - if(m_actions.empty()) - disable(m_apply); + updateAction(index); } -bool Browser::isTarget(const Entry *entry, const Version *target) const +void Browser::setTarget(const int index, const Version *target, const bool toggle) { - const auto it = m_actions.find(entry); + Entry *entry = getEntry(index); - if(it == m_actions.end()) - return false; - else - return it->second == target; + if(toggle && entry->target && *entry->target == target) + resetTarget(index); + else if(entry) { + entry->target = target; + m_actions.insert(entry); + updateAction(index); + } } -void Browser::setAction(const int index, const Version *target, const bool toggle) +void Browser::resetTarget(const int index) { - const Entry *entry = getEntry(index); + Entry *entry = getEntry(index); + if(!entry->target) + return; - if(toggle && isTarget(entry, target)) - resetAction(index); - else if(entry) { - m_actions[entry] = target; + entry->target = boost::none; + if(!entry->pin || !entry->canPin()) + m_actions.erase(entry); + + updateAction(index); +} + +void Browser::updateAction(const int index) +{ + Entry *entry = getEntry(index); + if(!entry) + return; + + if(currentTab() == Queued && !m_actions.count(entry)) { + m_list->removeRow(index); + m_visibleEntries.erase(m_visibleEntries.begin() + index); + updateDisplayLabel(); + } + else m_list->replaceRow(index, makeRow(*entry)); + + if(m_actions.empty()) + disable(m_apply); + else enable(m_apply); - } } void Browser::selectionDo(const function<void (int)> &func) @@ -827,16 +874,26 @@ bool Browser::apply() if(!tx) return false; - disable(m_apply); + for(Entry *entry : m_actions) { + if(entry->target) { + const Version *target = *entry->target; - for(const auto &pair : m_actions) { - if(pair.second) - tx->install(pair.second); - else - tx->uninstall(pair.first->regEntry); + if(target) + tx->install(target); + else + tx->uninstall(entry->regEntry); + + entry->target = boost::none; + } + + if(entry->pin) { + tx->setPinned(entry->package, *entry->pin); + entry->pin = boost::none; + } } m_actions.clear(); + disable(m_apply); if(!tx->runTasks()) { // this is an asynchronous transaction diff --git a/src/browser.hpp b/src/browser.hpp @@ -24,10 +24,12 @@ #include "listview.hpp" #include "registry.hpp" +#include <boost/optional.hpp> #include <functional> #include <map> #include <memory> #include <string> +#include <unordered_set> #include <vector> class Index; @@ -59,14 +61,19 @@ private: struct Entry { typedef std::tuple<std::string, std::string, std::string> Hash; + int flags; Registry::Entry regEntry; const Package *package; const Version *latest; const Version *current; + boost::optional<const Version *> target; + boost::optional<bool> pin; + Hash hash() const; bool test(Flag f) const { return (flags & f) != 0; } + bool canPin() const { return target ? *target != nullptr : test(InstalledFlag); } bool operator==(const Entry &o) const { return hash() == o.hash(); } }; @@ -98,14 +105,14 @@ private: void fillList(); std::string getValue(Column, const Entry &entry) const; ListView::Row makeRow(const Entry &) const; - const Entry *getEntry(int) const; + Entry *getEntry(int); void updateDisplayLabel(); void displayButton(); bool isFiltered(Package::Type) const; void toggleFiltered(Package::Type); - bool hasAction(const Entry *e) const { return m_actions.count(e) > 0; } - bool isTarget(const Entry *, const Version *) const; - void setAction(const int index, const Version *, bool toggle = true); + void setTarget(const int index, const Version *, bool toggle = true); + void resetTarget(int index); + void updateAction(const int index); void selectionDo(const std::function<void (int)> &); Tab currentTab() const; bool confirm() const; @@ -115,10 +122,10 @@ private: void reinstall(int index, bool toggle = true); void installVersion(int index, size_t verIndex); void uninstall(int index, bool toggle = true); - void resetAction(int index); - void history(int index) const; - void contents(int index) const; - void about(int index) const; + void togglePin(int index); + void history(int index); + void contents(int index); + void about(int index); std::vector<IndexPtr> m_indexes; ReaPack *m_reapack; @@ -131,7 +138,7 @@ private: Filter m_filter; std::vector<Entry> m_entries; std::vector<size_t> m_visibleEntries; - std::map<const Entry *, const Version *> m_actions; + std::unordered_set<Entry *> m_actions; HWND m_filterHandle; HWND m_tabs; diff --git a/src/progress.cpp b/src/progress.cpp @@ -63,7 +63,7 @@ void Progress::addDownload(Download *dl) updateProgress(); if(!isVisible()) - startTimer(10); + startTimer(100); dl->onStart([=] { m_currentName = make_autostring(dl->name()); diff --git a/src/receipt.hpp b/src/receipt.hpp @@ -22,8 +22,17 @@ #include <string> #include <vector> +#include "registry.hpp" + class Path; -struct InstallTicket; + +struct InstallTicket { + enum Type { Install, Upgrade }; + + Type type; + const Version *version; + Registry::Entry regEntry; +}; class Receipt { public: diff --git a/src/registry.cpp b/src/registry.cpp @@ -42,14 +42,15 @@ Registry::Registry(const Path &path) "UPDATE entries SET type = ?, version = ?, author = ? WHERE id = ?" ); + m_setPinned = m_db.prepare("UPDATE entries SET pinned = ? WHERE id = ?"); + m_findEntry = m_db.prepare( - "SELECT id, remote, category, package, type, version, author FROM entries " - "WHERE remote = ? AND category = ? AND package = ? " - "LIMIT 1" + "SELECT id, remote, category, package, type, version, author, pinned " + "FROM entries WHERE remote = ? AND category = ? AND package = ? LIMIT 1" ); m_allEntries = m_db.prepare( - "SELECT id, category, package, type, version, author " + "SELECT id, category, package, type, version, author, pinned " "FROM entries WHERE remote = ?" ); m_forgetEntry = m_db.prepare("DELETE FROM entries WHERE id = ?"); @@ -92,6 +93,7 @@ void Registry::migrate() " type INTEGER NOT NULL," " version TEXT NOT NULL," " author TEXT NOT NULL," + " pinned INTEGER DEFAULT 0," " UNIQUE(remote, category, package)" ");" @@ -188,6 +190,13 @@ auto Registry::push(const Version *ver, vector<Path> *conflicts) -> Entry } } +void Registry::setPinned(const Entry &entry, const bool pinned) +{ + m_setPinned->bind(1, pinned); + m_setPinned->bind(2, entry.id); + m_setPinned->exec(); +} + auto Registry::getEntry(const Package *pkg) const -> Entry { Entry entry{}; @@ -209,6 +218,7 @@ auto Registry::getEntry(const Package *pkg) const -> Entry entry.type = static_cast<Package::Type>(m_findEntry->intColumn(col++)); entry.version.tryParse(m_findEntry->stringColumn(col++)); entry.version.setAuthor(m_findEntry->stringColumn(col++)); + entry.pinned = m_findEntry->intColumn(col++) != 0; return false; }); @@ -232,6 +242,7 @@ auto Registry::getEntries(const string &remoteName) const -> vector<Entry> entry.type = static_cast<Package::Type>(m_allEntries->intColumn(col++)); entry.version.tryParse(m_allEntries->stringColumn(col++)); entry.version.setAuthor(m_allEntries->stringColumn(col++)); + entry.pinned = m_allEntries->intColumn(col++) != 0; list.push_back(entry); diff --git a/src/registry.hpp b/src/registry.hpp @@ -39,6 +39,7 @@ public: std::string package; Package::Type type; Version version; + bool pinned; operator bool() const { return id != 0; } }; @@ -50,6 +51,7 @@ public: std::set<Path> getFiles(const Entry &) const; std::string getMainFile(const Entry &) const; Entry push(const Version *, std::vector<Path> *conflicts = nullptr); + void setPinned(const Entry &, bool pinned); void forget(const Entry &); void savepoint(); void restore(); @@ -62,13 +64,14 @@ private: Database m_db; Statement *m_insertEntry; Statement *m_updateEntry; + Statement *m_setPinned; Statement *m_findEntry; Statement *m_allEntries; Statement *m_forgetEntry; - Statement *m_setMainFile; Statement *m_getFiles; Statement *m_getMainFile; + Statement *m_setMainFile; Statement *m_insertFile; Statement *m_clearFiles; Statement *m_forgetFiles; diff --git a/src/task.hpp b/src/task.hpp @@ -95,4 +95,14 @@ private: std::set<Path> m_removedFiles; }; +class DummyTask : public Task { +public: + DummyTask(Transaction *t) : Task(t) {} + +protected: + void doStart() override {} + bool doCommit() override { return true; } + void doRollback() override {} +}; + #endif diff --git a/src/transaction.cpp b/src/transaction.cpp @@ -49,8 +49,9 @@ Transaction::Transaction() finish(); }); + // run tasks after fetching indexes m_downloadQueue.onDone([=] { - if(m_installQueue.empty()) + if(m_tasks.empty()) finish(); else runTasks(); @@ -105,7 +106,7 @@ void Transaction::synchronize(const Package *pkg, const InstallOpts &opts) if(allFilesExists(latest->files())) return; // latest version is really installed, nothing to do here! } - else if(regEntry.version > *latest) + else if(regEntry.pinned || *latest < regEntry.version) return; install(latest, regEntry); @@ -186,18 +187,12 @@ void Transaction::install(const Version *ver, if(!m_indexes.count(ri)) m_indexes.insert(ri); - m_installQueue.push({type, ver, regEntry}); -} - -void Transaction::installTicket(const InstallTicket &ticket) -{ - const Version *ver = ticket.version; - const set<Path> &currentFiles = m_registry->getFiles(ticket.regEntry); + const set<Path> &currentFiles = m_registry->getFiles(regEntry); InstallTask *task = new InstallTask(ver, currentFiles, this); task->onCommit([=] { - m_receipt.addTicket(ticket); + m_receipt.addTicket({type, ver, regEntry}); m_receipt.addRemovals(task->removedFiles()); const Registry::Entry newEntry = m_registry->push(ver); @@ -209,6 +204,7 @@ void Transaction::installTicket(const InstallTicket &ticket) }); addTask(task); + m_receipt.setEnabled(true); } void Transaction::registerAll(const Remote &remote) @@ -222,6 +218,21 @@ void Transaction::registerAll(const Remote &remote) inhibit(remote); } +void Transaction::setPinned(const Package *pkg, const bool pinned) +{ + // pkg may or may not be installed yet at this point, + // waiting for the install task to be commited before querying the registry + + DummyTask *task = new DummyTask(this); + task->onCommit([=] { + const Registry::Entry &entry = m_registry->getEntry(pkg); + if(entry) + m_registry->setPinned(entry, pinned); + }); + + addTask(task); +} + void Transaction::uninstall(const Remote &remote) { inhibit(remote); @@ -261,6 +272,7 @@ void Transaction::uninstall(const Registry::Entry &entry) task->onCommit([=] { m_receipt.addRemovals(task->removedFiles()); }); addTask(task); + m_receipt.setEnabled(true); } bool Transaction::saveFile(Download *dl, const Path &path) @@ -315,8 +327,6 @@ void Transaction::addTask(Task *task) { m_tasks.push_back(task); m_taskQueue.push(task); - - m_receipt.setEnabled(true); } bool Transaction::runTasks() @@ -327,11 +337,6 @@ bool Transaction::runTasks() m_onRun(); m_onRun.disconnect_all_slots(); - while(!m_installQueue.empty()) { - installTicket(m_installQueue.front()); - m_installQueue.pop(); - } - while(!m_taskQueue.empty()) { m_taskQueue.front()->start(); m_taskQueue.pop(); @@ -349,7 +354,9 @@ bool Transaction::runTasks() void Transaction::registerInHost(const bool add, const Registry::Entry &entry) { // don't actually do anything until commit() – which will calls registerQueued - m_regQueue.push({add, entry, m_registry->getMainFile(entry)}); + const string &mainFile = m_registry->getMainFile(entry); + if(!mainFile.empty()) + m_regQueue.push({add, entry, mainFile}); } void Transaction::registerQueued() diff --git a/src/transaction.hpp b/src/transaction.hpp @@ -36,14 +36,6 @@ struct InstallOpts; typedef std::shared_ptr<const Index> IndexPtr; -struct InstallTicket { - enum Type { Install, Upgrade }; - - Type type; - const Version *version; - const Registry::Entry regEntry; -}; - struct HostTicket { bool add; Registry::Entry entry; std::string file; }; class Transaction { @@ -59,6 +51,7 @@ public: void synchronize(const Remote &, const InstallOpts &); void install(const Version *); + void setPinned(const Package *, bool pinned); void uninstall(const Remote &); void uninstall(const Registry::Entry &); void registerAll(const Remote &); @@ -81,7 +74,6 @@ private: void synchronize(const Package *, const InstallOpts &); void install(const Version *, const Registry::Entry &); - void installTicket(const InstallTicket &); void addTask(Task *); bool allFilesExists(const std::set<Path> &) const; @@ -104,7 +96,6 @@ private: DownloadQueue m_downloadQueue; std::queue<Task *> m_taskQueue; - std::queue<InstallTicket> m_installQueue; std::queue<HostTicket> m_regQueue; VoidSignal m_onRun; diff --git a/test/registry.cpp b/test/registry.cpp @@ -174,3 +174,20 @@ TEST_CASE("get main file", M) { const Registry::Entry &entry = reg.push(ver); REQUIRE(reg.getMainFile(entry) == main->targetPath().join('/')); } + +TEST_CASE("pin registry entry", M) { + MAKE_PACKAGE + + Registry reg; + reg.push(ver); + + const Registry::Entry &entry = reg.getEntry(&pkg); + REQUIRE_FALSE(entry.pinned); + + reg.setPinned(entry, true); + REQUIRE(reg.getEntry(&pkg).pinned); + REQUIRE(reg.getEntries(ri.name())[0].pinned); + + reg.setPinned(entry, false); + REQUIRE_FALSE(reg.getEntry(&pkg).pinned); +}