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:
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