reapack

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

commit 952e33b95b6298d0d72ef2fd8423cd907c040080
parent 0ec9ec63fb0c953cfd9fe429a55af114ef8addba
Author: cfillion <cfillion@users.noreply.github.com>
Date:   Wed, 24 Aug 2016 19:45:58 -0400

prompt to uninstall obsolete packages when synchronizing

Closes #8

Diffstat:
Msrc/config.cpp | 6+++++-
Msrc/config.hpp | 1+
Msrc/manager.cpp | 16++++++++++++++--
Msrc/manager.hpp | 1+
Asrc/query.cpp | 97+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/query.hpp | 49+++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/reapack.cpp | 6++++++
Msrc/registry.hpp | 12++++++++++++
Msrc/resource.hpp | 2++
Msrc/resource.rc | 16++++++++++++++++
Msrc/transaction.cpp | 21+++++++++++++++++++++
Msrc/transaction.hpp | 4++++
12 files changed, 228 insertions(+), 3 deletions(-)

diff --git a/src/config.cpp b/src/config.cpp @@ -33,6 +33,7 @@ static const auto_char *VERSION_KEY = AUTO_STR("version"); static const auto_char *INSTALL_GRP = AUTO_STR("install"); static const auto_char *AUTOINSTALL_KEY = AUTO_STR("autoinstall"); static const auto_char *PRERELEASES_KEY = AUTO_STR("prereleases"); +static const auto_char *PROMPTOBSOLETE_KEY = AUTO_STR("promptobsolete"); static const auto_char *BROWSER_GRP = AUTO_STR("browser"); static const auto_char *SHOWDESCS_KEY = AUTO_STR("showdescs"); @@ -62,7 +63,7 @@ Config::Config() void Config::resetOptions() { - install = {false, false}; + install = {false, false, true}; browser = {true, ""}; network = {"", true}; } @@ -133,6 +134,8 @@ void Config::read(const Path &path) AUTOINSTALL_KEY, install.autoInstall) > 0; install.bleedingEdge = getUInt(INSTALL_GRP, PRERELEASES_KEY, install.bleedingEdge) > 0; + install.promptObsolete = getUInt(INSTALL_GRP, + PROMPTOBSOLETE_KEY, install.promptObsolete) > 0; browser.showDescs = getUInt(BROWSER_GRP, SHOWDESCS_KEY, browser.showDescs) > 0; @@ -153,6 +156,7 @@ void Config::write() setUInt(INSTALL_GRP, AUTOINSTALL_KEY, install.autoInstall); setUInt(INSTALL_GRP, PRERELEASES_KEY, install.bleedingEdge); + setUInt(INSTALL_GRP, PROMPTOBSOLETE_KEY, install.promptObsolete); setUInt(BROWSER_GRP, SHOWDESCS_KEY, browser.showDescs); setString(BROWSER_GRP, STATE_KEY, browser.state); diff --git a/src/config.hpp b/src/config.hpp @@ -28,6 +28,7 @@ class Path; struct InstallOpts { bool autoInstall; bool bleedingEdge; + bool promptObsolete; }; struct BrowserOpts { diff --git a/src/manager.cpp b/src/manager.cpp @@ -30,8 +30,8 @@ using namespace std; enum { ACTION_ENABLE = 80, ACTION_DISABLE, ACTION_UNINSTALL, ACTION_ABOUT, ACTION_REFRESH, ACTION_COPYURL, ACTION_SELECT, ACTION_UNSELECT, - ACTION_AUTOINSTALL, ACTION_BLEEDINGEDGE, ACTION_NETCONFIG, - ACTION_RESETCONFIG }; + ACTION_AUTOINSTALL, ACTION_BLEEDINGEDGE, ACTION_PROMPTOBSOLETE, + ACTION_NETCONFIG, ACTION_RESETCONFIG }; Manager::Manager(ReaPack *reapack) : Dialog(IDD_CONFIG_DIALOG), @@ -97,6 +97,9 @@ void Manager::onCommand(const int id, int) case ACTION_BLEEDINGEDGE: toggle(m_bleedingEdge, m_config->install.bleedingEdge); break; + case ACTION_PROMPTOBSOLETE: + toggle(m_promptObsolete, m_config->install.promptObsolete); + break; case ACTION_NETCONFIG: setupNetwork(); break; @@ -379,6 +382,11 @@ void Manager::options() if(m_bleedingEdge.value_or(m_config->install.bleedingEdge)) menu.check(index); + index = menu.addAction( + AUTO_STR("Prompt to uninstall obsolete packages"), ACTION_PROMPTOBSOLETE); + if(m_promptObsolete.value_or(m_config->install.promptObsolete)) + menu.check(index); + menu.addAction(AUTO_STR("&Network settings..."), ACTION_NETCONFIG); menu.addSeparator(); @@ -429,6 +437,9 @@ bool Manager::apply() if(m_bleedingEdge) m_config->install.bleedingEdge = m_bleedingEdge.value(); + if(m_promptObsolete) + m_config->install.promptObsolete = m_promptObsolete.value(); + for(const auto &pair : m_enableOverrides) { const Remote &remote = pair.first; const bool enable = pair.second; @@ -453,6 +464,7 @@ void Manager::reset() m_uninstall.clear(); m_autoInstall = boost::none; m_bleedingEdge = boost::none; + m_promptObsolete = boost::none; m_changes = 0; disable(m_apply); diff --git a/src/manager.hpp b/src/manager.hpp @@ -75,6 +75,7 @@ private: std::map<Remote, bool> m_enableOverrides; std::set<Remote> m_uninstall; boost::optional<bool> m_autoInstall; + boost::optional<bool> m_promptObsolete; boost::optional<bool> m_bleedingEdge; }; diff --git a/src/query.cpp b/src/query.cpp @@ -0,0 +1,97 @@ +/* ReaPack: Package manager for REAPER + * Copyright (C) 2015-2016 Christian Fillion + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "query.hpp" + +#include "encoding.hpp" +#include "listview.hpp" +#include "menu.hpp" +#include "package.hpp" +#include "resource.hpp" + +#include <sstream> + +using namespace std; + +enum { ACTION_SELECT_ALL = 300, ACTION_UNSELECT_ALL }; + +ObsoleteQuery::ObsoleteQuery(vector<Registry::Entry> *entries, bool *enable) + : Dialog(IDD_QUERY_DIALOG), m_entries(entries), m_enable(enable) +{ +} + +void ObsoleteQuery::onInit() +{ + Dialog::onInit(); + + m_enableCtrl = getControl(IDC_ENABLE); + m_okBtn = getControl(IDOK); + + m_list = createControl<ListView>(IDC_LIST, ListView::Columns{ + {AUTO_STR("Package"), 550} + }); + + m_list->onSelect([=] { setEnabled(m_list->hasSelection(), m_okBtn); }); + m_list->onContextMenu([=] (Menu &menu, int) { + menu.addAction(AUTO_STR("Select &all"), ACTION_SELECT_ALL); + menu.addAction(AUTO_STR("&Unselect all"), ACTION_UNSELECT_ALL); + return true; + }); + + for(const Registry::Entry &entry : *m_entries) { + ostringstream stream; + stream << entry.remote << '/' << entry.category << '/' + << Package::displayName(entry.package, entry.description); + m_list->addRow({make_autostring(stream.str())}); + } + +#ifdef LVSCW_AUTOSIZE_USEHEADER + m_list->resizeColumn(m_list->columnCount() - 1, LVSCW_AUTOSIZE_USEHEADER); +#endif + + SendMessage(m_enableCtrl, BM_SETCHECK, BST_CHECKED, 0); + + disable(m_okBtn); +} + +void ObsoleteQuery::onCommand(const int id, int event) +{ + switch(id) { + case IDOK: + prepare(); + break; + case IDC_ENABLE: + *m_enable = SendMessage(m_enableCtrl, BM_GETCHECK, 0, 0) == BST_CHECKED; + break; + case ACTION_SELECT_ALL: + m_list->selectAll(); + break; + case ACTION_UNSELECT_ALL: + m_list->unselectAll(); + break; + } + + Dialog::onCommand(id, event); +} + +void ObsoleteQuery::prepare() +{ + vector<Registry::Entry> selected; + for(int index : m_list->selection()) + selected.emplace_back(m_entries->at(index)); + m_entries->swap(selected); +} diff --git a/src/query.hpp b/src/query.hpp @@ -0,0 +1,49 @@ +/* ReaPack: Package manager for REAPER + * Copyright (C) 2015-2016 Christian Fillion + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef REAPACK_QUERY_HPP +#define REAPACK_QUERY_HPP + +#include "dialog.hpp" + +#include "registry.hpp" + +#include <vector> + +class Config; +class ListView; + +class ObsoleteQuery : public Dialog { +public: + ObsoleteQuery(std::vector<Registry::Entry> *, bool *enable); + +protected: + void onInit() override; + void onCommand(int, int) override; + +private: + void prepare(); + + std::vector<Registry::Entry> *m_entries; + bool *m_enable; + + HWND m_enableCtrl; + HWND m_okBtn; + ListView *m_list; +}; + +#endif diff --git a/src/reapack.cpp b/src/reapack.cpp @@ -25,6 +25,7 @@ #include "index.hpp" #include "manager.hpp" #include "progress.hpp" +#include "query.hpp" #include "report.hpp" #include "transaction.hpp" @@ -441,6 +442,11 @@ Transaction *ReaPack::setupTransaction() Dialog::Show<Report>(m_instance, m_mainWindow, *m_tx->receipt()); }); + m_tx->setObsoleteHandler([=] (vector<Registry::Entry> &entries) { + return Dialog::Show<ObsoleteQuery>(m_instance, m_progress->handle(), + &entries, &m_config->install.promptObsolete) == IDOK; + }); + m_tx->setCleanupHandler([=] { delete m_tx; m_tx = nullptr; diff --git a/src/registry.hpp b/src/registry.hpp @@ -39,6 +39,7 @@ public: bool pinned; operator bool() const { return id != 0; } + bool operator==(const Entry &o) const { return id == o.id; } }; struct File { @@ -83,4 +84,15 @@ private: size_t m_savePoint; }; +namespace std +{ + template<> struct hash<Registry::Entry> + { + std::size_t operator()(const Registry::Entry &e) const + { + return std::hash<int64_t>()(e.id); + } + }; +} + #endif diff --git a/src/resource.hpp b/src/resource.hpp @@ -41,6 +41,7 @@ #define IDD_IMPORT_DIALOG 104 #define IDD_BROWSER_DIALOG 105 #define IDD_NETCONF_DIALOG 106 +#define IDD_QUERY_DIALOG 107 #define IDC_LABEL 200 #define IDC_LABEL2 201 @@ -67,5 +68,6 @@ #define IDC_PROXY 229 #define IDC_VERIFYPEER 230 #define IDC_SCREENSHOT 231 +#define IDC_ENABLE 232 #endif diff --git a/src/resource.rc b/src/resource.rc @@ -115,3 +115,19 @@ BEGIN DEFPUSHBUTTON "&OK", IDOK, 132, 50, 40, 14 PUSHBUTTON "&Cancel", IDCANCEL, 175, 50, 40, 14 END + +IDD_QUERY_DIALOG DIALOGEX 0, 0, 350, 200 +STYLE DIALOG_STYLE +FONT DIALOG_FONT +CAPTION "ReaPack Query" +BEGIN + LTEXT "The following packages were removed from their repositories and are no longer available:", + IDC_LABEL, 5, 5, 340, 10 + GROUPBOX "Obsolete packages", IDC_GROUPBOX, 2, 18, 346, 160 + CONTROL "", IDC_LIST, WC_LISTVIEW, LVS_REPORT | LVS_SHOWSELALWAYS | + LVS_NOCOLUMNHEADER | WS_BORDER | WS_TABSTOP, 7, 29, 336, 146 + CHECKBOX "&Prompt when obsolete packages are found", + IDC_ENABLE, 5, 181, 201, 14, BS_AUTOCHECKBOX | WS_TABSTOP + DEFPUSHBUTTON "&Uninstall selected", IDOK, 212, 181, 85, 14 + PUSHBUTTON "&Ignore", IDCANCEL, 300, 181, 45, 14 +END diff --git a/src/transaction.cpp b/src/transaction.cpp @@ -74,6 +74,13 @@ void Transaction::synchronize(const Remote &remote, for(const Package *pkg : ri->packages()) synchronize(pkg, opts); + + if(m_config->install.promptObsolete) { + for(const Registry::Entry &entry : m_registry.getEntries(ri->name())) { + if(!ri->find(entry.category, entry.package)) + m_obsolete.insert(entry); + } + } }); } @@ -187,6 +194,20 @@ bool Transaction::runTasks() if(!commitTasks()) return false; + if(m_config->install.promptObsolete && !m_obsolete.empty()) { + vector<Registry::Entry> selected; + selected.insert(selected.end(), m_obsolete.begin(), m_obsolete.end()); + m_obsolete.clear(); + + if(m_promptObsolete(selected)) { + if(m_taskQueues.empty()) + m_taskQueues.push({}); + + for(const auto &entry : selected) + m_taskQueues.back().push(make_shared<UninstallTask>(entry, this)); + } + } + while(!m_taskQueues.empty()) { m_registry.savepoint(); diff --git a/src/transaction.hpp b/src/transaction.hpp @@ -43,11 +43,13 @@ class Transaction { public: typedef boost::signals2::signal<void ()> VoidSignal; typedef std::function<void()> CleanupHandler; + typedef std::function<bool(std::vector<Registry::Entry> &)> ObsoleteHandler; Transaction(Config *); void onFinish(const VoidSignal::slot_type &slot) { m_onFinish.connect(slot); } void setCleanupHandler(const CleanupHandler &cb) { m_cleanupHandler = cb; } + void setObsoleteHandler(const ObsoleteHandler &cb) { m_promptObsolete = cb; } void synchronize(const Remote &, boost::optional<bool> forceAutoInstall = boost::none); @@ -97,6 +99,7 @@ private: std::unordered_set<std::string> m_syncedRemotes; std::unordered_set<std::string> m_inhibited; + std::unordered_set<Registry::Entry> m_obsolete; DownloadQueue m_downloadQueue; TaskQueue m_currentQueue; @@ -106,6 +109,7 @@ private: VoidSignal m_onFinish; CleanupHandler m_cleanupHandler; + ObsoleteHandler m_promptObsolete; }; #endif