commit 733fc3ada8908e4bbbee9e07fe2dfd85851c4305
parent 06e61c2dfcb1b660ff24da0ff2a3f7ac0b0410dd
Author: cfillion <cfillion@users.noreply.github.com>
Date: Fri, 1 Apr 2016 19:38:57 -0400
Merge branch 'browser'
Diffstat:
38 files changed, 1230 insertions(+), 472 deletions(-)
diff --git a/src/about.cpp b/src/about.cpp
@@ -24,12 +24,12 @@
#include "menu.hpp"
#include "reapack.hpp"
#include "registry.hpp"
+#include "report.hpp"
#include "resource.hpp"
#include "richedit.hpp"
#include "tabbar.hpp"
#include <boost/algorithm/string/replace.hpp>
-#include <boost/range/adaptor/reversed.hpp>
#include <sstream>
using namespace std;
@@ -53,15 +53,8 @@ void About::onInit()
m_cats->onSelect(bind(&About::updatePackages, this));
-#ifdef _WIN32
- // dirty hacks...
- const int NAME_SIZE = 330;
-#else
- const int NAME_SIZE = 382;
-#endif
-
m_packages = createControl<ListView>(IDC_PACKAGES, ListView::Columns{
- {AUTO_STR("Name"), NAME_SIZE},
+ {AUTO_STR("Name"), 382},
{AUTO_STR("Version"), 80},
{AUTO_STR("Author"), 90},
});
@@ -80,12 +73,12 @@ void About::onInit()
populate();
#ifdef LVSCW_AUTOSIZE_USEHEADER
- m_cats->resizeColumn(0, LVSCW_AUTOSIZE_USEHEADER);
- m_packages->resizeColumn(2, LVSCW_AUTOSIZE_USEHEADER);
+ m_cats->resizeColumn(m_cats->columnCount() - 1, LVSCW_AUTOSIZE_USEHEADER);
+ m_packages->resizeColumn(m_packages->columnCount() - 1, LVSCW_AUTOSIZE_USEHEADER);
#endif
}
-void About::onCommand(const int id)
+void About::onCommand(const short id, short)
{
switch(id) {
case IDC_WEBSITE:
@@ -109,6 +102,7 @@ void About::onCommand(const int id)
openLink(m_websiteLinks[id & 0xff]);
else if(id >> 8 == IDC_DONATE)
openLink(m_donationLinks[id & 0xff]);
+ break;
}
}
@@ -192,10 +186,9 @@ void About::updatePackages()
const Version *lastVer = pkg->lastVersion();
const auto_string &name = make_autostring(pkg->name());
const auto_string &version = make_autostring(lastVer->name());
- const auto_string &author = make_autostring(lastVer->author());
+ const auto_string &author = make_autostring(lastVer->displayAuthor());
- m_packages->addRow({name, version,
- author.empty() ? AUTO_STR("Unknown") : author});
+ m_packages->addRow({name, version, author});
}
m_currentCat = catIndex;
@@ -240,8 +233,6 @@ void About::updateInstalledFiles()
stream << path.join() << "\r\n";
SetWindowText(m_installedFiles, make_autostring(stream.str()).c_str());
-
- hide(getControl(IDC_INSTALL));
}
}
@@ -249,22 +240,23 @@ void About::selectLink(const int ctrl, const std::vector<const Link *> &links)
{
const int count = (int)links.size();
- if(count > 1) {
- Menu menu;
+ m_tabs->setFocus();
- for(int i = 0; i < count; i++) {
- const string &name = boost::replace_all_copy(links[i]->name, "&", "&&");
- menu.addAction(make_autostring(name).c_str(), i | (ctrl << 8));
- }
+ if(count == 1) {
+ openLink(links.front());
+ return;
+ }
+
+ Menu menu;
- RECT rect;
- GetWindowRect(getControl(ctrl), &rect);
- menu.show(rect.left, rect.bottom - 1, handle());
+ for(int i = 0; i < count; i++) {
+ const string &name = boost::replace_all_copy(links[i]->name, "&", "&&");
+ menu.addAction(make_autostring(name).c_str(), i | (ctrl << 8));
}
- else if(count == 1)
- openLink(links.front());
- m_tabs->setFocus();
+ RECT rect;
+ GetWindowRect(getControl(ctrl), &rect);
+ menu.show(rect.left, rect.bottom - 1, handle());
}
void About::openLink(const Link *link)
@@ -283,22 +275,3 @@ void About::packageHistory()
const Package *pkg = m_packagesData->at(index);
Dialog::Show<History>(instance(), handle(), pkg);
}
-
-History::History(const Package *pkg)
- : ReportDialog(), m_package(pkg)
-{
-}
-
-void History::fillReport()
-{
- SetWindowText(handle(), AUTO_STR("Package History"));
- SetWindowText(getControl(IDC_LABEL),
- make_autostring(m_package->name()).c_str());
-
- for(const Version *ver : m_package->versions() | boost::adaptors::reversed) {
- if(stream().tellp())
- stream() << NL;
-
- printVersion(ver);
- }
-}
diff --git a/src/about.hpp b/src/about.hpp
@@ -23,12 +23,9 @@
#include <memory>
#include <vector>
-#include "report.hpp"
-
class Index;
class ListView;
class Package;
-class ReportBase;
class RichEdit;
class TabBar;
struct Link;
@@ -43,7 +40,7 @@ public:
protected:
void onInit() override;
- void onCommand(int) override;
+ void onCommand(short, short) override;
void onContextMenu(HWND, int x, int y) override;
private:
@@ -68,15 +65,4 @@ private:
const std::vector<const Package *> *m_packagesData;
};
-class History : public ReportDialog {
-public:
- History(const Package *);
-
-protected:
- void fillReport() override;
-
-private:
- const Package *m_package;
-};
-
#endif
diff --git a/src/browser.cpp b/src/browser.cpp
@@ -0,0 +1,655 @@
+/* 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 "browser.hpp"
+
+#include "encoding.hpp"
+#include "errors.hpp"
+#include "index.hpp"
+#include "menu.hpp"
+#include "reapack.hpp"
+#include "report.hpp"
+#include "resource.hpp"
+
+#include <boost/algorithm/string.hpp>
+#include <boost/range/adaptor/reversed.hpp>
+
+using namespace std;
+
+enum Action {
+ ACTION_VERSION = 80,
+ ACTION_LATEST = 300,
+ ACTION_LATEST_ALL,
+ ACTION_REINSTALL,
+ ACTION_REINSTALL_ALL,
+ ACTION_UNINSTALL,
+ ACTION_UNINSTALL_ALL,
+ ACTION_HISTORY,
+ ACTION_ABOUT,
+ ACTION_RESET_ALL,
+};
+
+Browser::Browser(const vector<IndexPtr> &indexes, ReaPack *reapack)
+ : Dialog(IDD_BROWSER_DIALOG), m_indexes(indexes), m_reapack(reapack),
+ m_checkFilter(false), m_currentIndex(-1)
+{
+}
+
+void Browser::onInit()
+{
+ m_action = getControl(IDC_ACTION);
+ m_apply = getControl(IDAPPLY);
+ m_filterHandle = getControl(IDC_FILTER);
+ m_display = getControl(IDC_DISPLAY);
+
+ disable(m_apply);
+
+ SendMessage(m_display, CB_ADDSTRING, 0, (LPARAM)AUTO_STR("All"));
+ SendMessage(m_display, CB_ADDSTRING, 0, (LPARAM)AUTO_STR("Queued"));
+ SendMessage(m_display, CB_ADDSTRING, 0, (LPARAM)AUTO_STR("Installed"));
+ SendMessage(m_display, CB_ADDSTRING, 0, (LPARAM)AUTO_STR("Out of date"));
+ SendMessage(m_display, CB_ADDSTRING, 0, (LPARAM)AUTO_STR("Obsolete"));
+ SendMessage(m_display, CB_ADDSTRING, 0, (LPARAM)AUTO_STR("Uninstalled"));
+ SendMessage(m_display, CB_SETCURSEL, 0, 0);
+
+ m_types = {
+ {Package::ScriptType, getControl(IDC_SCRIPTS)},
+ {Package::EffectType, getControl(IDC_EFFECTS)},
+ {Package::ExtensionType, getControl(IDC_EXTENSIONS)},
+ };
+
+ for(const auto &pair : m_types)
+ SendMessage(pair.second, BM_SETCHECK, BST_CHECKED, 0);
+
+ m_list = createControl<ListView>(IDC_PACKAGES, ListView::Columns{
+ {AUTO_STR(""), 23},
+ {AUTO_STR("Package Name"), 380},
+ {AUTO_STR("Category"), 150},
+ {AUTO_STR("Version"), 80},
+ {AUTO_STR("Author"), 95},
+ {AUTO_STR("Type"), 70},
+ });
+
+ m_list->onActivate([=] { history(m_list->itemUnderMouse()); });
+ m_list->sortByColumn(1);
+
+ reload();
+
+#ifdef LVSCW_AUTOSIZE_USEHEADER
+ m_list->resizeColumn(m_list->columnCount() - 1, LVSCW_AUTOSIZE_USEHEADER);
+#endif
+
+ m_filterTimer = startTimer(200);
+}
+
+void Browser::onCommand(const short id, const short event)
+{
+ namespace arg = std::placeholders;
+
+ switch(id) {
+ case IDC_DISPLAY:
+ if(event != CBN_SELCHANGE)
+ break;
+ case IDC_SCRIPTS:
+ case IDC_EFFECTS:
+ case IDC_EXTENSIONS:
+ fillList();
+ break;
+ case IDC_FILTER:
+ m_checkFilter = true;
+ break;
+ case IDC_CLEAR:
+ SetWindowText(m_filterHandle, AUTO_STR(""));
+ checkFilter();
+ break;
+ case IDC_SELECT:
+ m_list->selectAll();
+ SetFocus(m_list->handle());
+ break;
+ case IDC_UNSELECT:
+ m_list->unselectAll();
+ SetFocus(m_list->handle());
+ break;
+ case IDC_ACTION:
+ selectionMenu();
+ break;
+ case ACTION_LATEST:
+ installLatest(m_currentIndex);
+ break;
+ case ACTION_LATEST_ALL:
+ selectionDo(bind(&Browser::installLatest, this, arg::_1, false));
+ break;
+ case ACTION_REINSTALL:
+ reinstall(m_currentIndex);
+ break;
+ case ACTION_REINSTALL_ALL:
+ selectionDo(bind(&Browser::reinstall, this, arg::_1, false));
+ break;
+ case ACTION_UNINSTALL:
+ uninstall(m_currentIndex);
+ break;
+ case ACTION_UNINSTALL_ALL:
+ selectionDo(bind(&Browser::uninstall, this, arg::_1, false));
+ break;
+ case ACTION_HISTORY:
+ history(m_currentIndex);
+ break;
+ case ACTION_ABOUT:
+ about(m_currentIndex);
+ break;
+ case ACTION_RESET_ALL:
+ selectionDo(bind(&Browser::resetAction, this, arg::_1));
+ break;
+ case IDOK:
+ case IDAPPLY:
+ if(confirm()) {
+ apply();
+
+ if(id == IDAPPLY)
+ break;
+ }
+ else
+ break;
+ case IDCANCEL:
+ close();
+ break;
+ default:
+ if(id >> 8 == ACTION_VERSION)
+ installVersion(m_currentIndex, id & 0xff);
+ break;
+ }
+}
+
+void Browser::onTimer(const int id)
+{
+ if(id == m_filterTimer)
+ checkFilter();
+}
+
+void Browser::onContextMenu(HWND target, const int x, const int y)
+{
+ if(target != m_list->handle())
+ return;
+
+ SetFocus(m_list->handle());
+
+ m_currentIndex = m_list->itemUnderMouse();
+ const Entry *entry = getEntry(m_currentIndex);
+
+ if(!entry)
+ return;
+
+ Menu menu;
+
+ if(entry->test(InstalledFlag)) {
+ if(entry->test(OutOfDateFlag)) {
+ auto_char installLabel[255] = {};
+ auto_snprintf(installLabel, sizeof(installLabel), AUTO_STR("U&pdate to v%s"),
+ make_autostring(entry->latest->name()).c_str());
+
+ const UINT actionIndex = menu.addAction(installLabel, ACTION_LATEST);
+ if(isTarget(entry, entry->latest))
+ menu.check(actionIndex);
+ }
+
+ auto_char reinstallLabel[255] = {};
+ auto_snprintf(reinstallLabel, sizeof(reinstallLabel), AUTO_STR("&Reinstall v%s"),
+ make_autostring(entry->regEntry.versionName).c_str());
+
+ const UINT actionIndex = menu.addAction(reinstallLabel, ACTION_REINSTALL);
+ if(!entry->current || entry->test(ObsoleteFlag))
+ menu.disable(actionIndex);
+ else if(isTarget(entry, entry->current))
+ menu.check(actionIndex);
+ }
+ else {
+ auto_char installLabel[255] = {};
+ auto_snprintf(installLabel, sizeof(installLabel), AUTO_STR("&Install v%s"),
+ make_autostring(entry->latest->name()).c_str());
+
+ const UINT actionIndex = menu.addAction(installLabel, ACTION_LATEST);
+ if(isTarget(entry, entry->latest))
+ menu.check(actionIndex);
+ }
+
+ Menu versionMenu = menu.addMenu(AUTO_STR("Versions"));
+ const UINT versionIndex = menu.size() - 1;
+ if(entry->test(ObsoleteFlag))
+ menu.disable(versionIndex);
+ else {
+ const auto &versions = entry->package->versions();
+ int verIndex = (int)versions.size();
+ for(const Version *ver : versions | boost::adaptors::reversed) {
+ const UINT actionIndex = versionMenu.addAction(
+ make_autostring(ver->name()).c_str(), --verIndex | (ACTION_VERSION << 8));
+
+ if(hasAction(entry) ? isTarget(entry, ver) : ver == entry->current)
+ versionMenu.checkRadio(actionIndex);
+ }
+ }
+
+ const UINT uninstallIndex =
+ menu.addAction(AUTO_STR("&Uninstall"), ACTION_UNINSTALL);
+ if(!entry->test(InstalledFlag))
+ menu.disable(uninstallIndex);
+ else if(isTarget(entry, nullptr))
+ menu.check(uninstallIndex);
+
+ menu.addSeparator();
+
+ menu.setEnabled(!entry->test(ObsoleteFlag),
+ menu.addAction(AUTO_STR("Package &History"), ACTION_HISTORY));
+
+ auto_char aboutLabel[255] = {};
+ const auto_string &name = make_autostring(getValue(RemoteColumn, *entry));
+ auto_snprintf(aboutLabel, sizeof(aboutLabel),
+ AUTO_STR("&About %s..."), name.c_str());
+ menu.addAction(aboutLabel, ACTION_ABOUT);
+
+ menu.show(x, y, handle());
+}
+
+void Browser::selectionMenu()
+{
+ RECT rect;
+ GetWindowRect(m_action, &rect);
+
+ const Entry *entry = nullptr;
+ if(m_list->selectionSize() == 1)
+ entry = getEntry(m_list->currentIndex());
+
+ Menu menu;
+
+ 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);
+
+ if(!m_list->hasSelection())
+ menu.disableAll();
+
+ menu.show(rect.left, rect.bottom - 1, handle());
+}
+
+void Browser::checkFilter()
+{
+ if(!m_checkFilter)
+ return;
+
+ m_checkFilter = false;
+
+ auto_string wideFilter(4096, 0);
+ GetWindowText(m_filterHandle, &wideFilter[0], (int)wideFilter.size());
+ wideFilter.resize(wideFilter.find(AUTO_STR('\0')));
+
+ const string &filter = from_autostring(wideFilter);
+
+ if(filter != m_filter) {
+ m_filter = filter;
+ fillList();
+ }
+}
+
+void Browser::reload()
+{
+ try {
+ Registry reg(Path::prefixRoot(Path::REGISTRY));
+
+ m_currentIndex = -1;
+ m_actions.clear();
+ m_entries.clear();
+
+ for(IndexPtr index : m_indexes) {
+ for(const Package *pkg : index->packages())
+ m_entries.push_back(makeEntry(pkg, reg.getEntry(pkg)));
+
+ // obsolete packages
+ for(const Registry::Entry &entry : reg.getEntries(index->name())) {
+ const Category *cat = index->category(entry.category);
+
+ if(cat && cat->package(entry.package))
+ continue;
+
+ m_entries.push_back({InstalledFlag | ObsoleteFlag, entry});
+ }
+ }
+
+ fillList();
+ }
+ catch(const reapack_error &e) {
+ const auto_string &desc = make_autostring(e.what());
+ auto_char msg[255] = {};
+ auto_snprintf(msg, sizeof(msg),
+ AUTO_STR("ReaPack could not open its package registry.\r\n")
+ AUTO_STR("Retry later when all installation task are completed.\r\n")
+ AUTO_STR("\r\nError description: %s"),
+ desc.c_str());
+ MessageBox(handle(), msg, AUTO_STR("ReaPack"), MB_OK);
+ }
+}
+
+auto Browser::makeEntry(const Package *pkg, const Registry::Entry ®Entry)
+ -> Entry
+{
+ const Version *latest = pkg->lastVersion();
+ const Version *current = nullptr;
+
+ int flags = 0;
+
+ if(regEntry.id) {
+ flags |= InstalledFlag;
+
+ if(regEntry.versionCode < latest->code())
+ flags |= OutOfDateFlag;
+
+ for(const Version *ver : pkg->versions()) {
+ if(ver->code() == regEntry.versionCode) {
+ current = ver;
+ break;
+ }
+ }
+ }
+ else
+ flags |= UninstalledFlag;
+
+ return {flags, regEntry, pkg, latest, current};
+}
+
+void Browser::fillList()
+{
+ InhibitControl freeze(m_list);
+
+ m_list->clear();
+ m_visibleEntries.clear();
+
+ for(size_t i = 0; i < m_entries.size(); i++) {
+ const Entry &entry = m_entries[i];
+
+ if(!match(entry))
+ continue;
+
+ m_list->addRow(makeRow(entry));
+ m_visibleEntries.push_back(i);
+ }
+
+ m_list->sort();
+}
+
+ListView::Row Browser::makeRow(const Entry &entry) const
+{
+ const string &state = getValue(StateColumn, entry);
+ const string &name = getValue(NameColumn, entry);
+ const string &category = getValue(CategoryColumn, entry);
+ const string &version = getValue(VersionColumn, entry);
+ const string &author = getValue(AuthorColumn, entry);
+ const string &type = getValue(TypeColumn, entry);
+
+ return {
+ make_autostring(state), make_autostring(name), make_autostring(category),
+ make_autostring(version), make_autostring(author), make_autostring(type)
+ };
+}
+
+string Browser::getValue(const Column col, const Entry &entry) const
+{
+ const Package *pkg = entry.package;
+ const Version *ver = entry.latest;
+ const Registry::Entry ®Entry = entry.regEntry;
+
+ string display;
+
+ switch(col) {
+ case StateColumn: {
+ if(entry.test(ObsoleteFlag))
+ display += 'o';
+ else if(entry.test(OutOfDateFlag))
+ display += 'u';
+ else if(entry.test(InstalledFlag))
+ display += 'i';
+ else
+ display += '\x20';
+
+ if(hasAction(&entry))
+ display += isTarget(&entry, nullptr) ? 'U' : 'I';
+
+ return display;
+ }
+ case NameColumn:
+ return pkg ? pkg->name() : regEntry.package;
+ case CategoryColumn:
+ return pkg ? pkg->category()->name() : regEntry.category;
+ case VersionColumn:
+ if(entry.test(InstalledFlag))
+ display = regEntry.versionName;
+
+ if(ver && ver->code() != regEntry.versionCode) {
+ if(!display.empty())
+ display += '\x20';
+
+ display += '(' + ver->name() + ')';
+ }
+
+ return display;
+ case AuthorColumn:
+ return ver ? ver->displayAuthor() : regEntry.author;
+ case TypeColumn:
+ return pkg ? pkg->displayType() : Package::displayType(regEntry.type);
+ case RemoteColumn:
+ return pkg ? pkg->category()->index()->name() : regEntry.remote;
+ }
+
+ return {}; // for MSVC
+}
+
+bool Browser::match(const Entry &entry) const
+{
+ using namespace boost;
+
+ switch(getDisplay()) {
+ case All:
+ break;
+ case Queued:
+ if(!hasAction(&entry))
+ return false;
+ break;
+ case Installed:
+ if(!entry.test(InstalledFlag))
+ return false;
+ break;
+ case OutOfDate:
+ if(!entry.test(OutOfDateFlag))
+ return false;
+ break;
+ case Uninstalled:
+ if(!entry.test(UninstalledFlag))
+ return false;
+ break;
+ case Obsolete:
+ if(!entry.test(ObsoleteFlag))
+ return false;
+ break;
+ }
+
+ const Package::Type type =
+ entry.latest ? entry.package->type() : entry.regEntry.type;
+
+ const auto typeIt = m_types.find(type);
+
+ if(typeIt == m_types.end() ||
+ SendMessage(typeIt->second, BM_GETCHECK, 0, 0) == BST_UNCHECKED)
+ return false;
+
+ const string &name = getValue(NameColumn, entry);
+ const string &category = getValue(CategoryColumn, entry);
+ const string &author = getValue(AuthorColumn, entry);
+
+ return icontains(name, m_filter) || icontains(category, m_filter) ||
+ icontains(author, m_filter);
+}
+
+auto Browser::getEntry(const int listIndex) const -> const Entry *
+{
+ if(listIndex < 0 || listIndex >= (int)m_visibleEntries.size())
+ return nullptr;
+
+ return &m_entries[m_visibleEntries[listIndex]];
+}
+
+void Browser::history(const int index) const
+{
+ const Entry *entry = getEntry(index);
+
+ if(entry && entry->package)
+ Dialog::Show<History>(instance(), handle(), entry->package);
+}
+
+void Browser::about(const int index) const
+{
+ if(const Entry *entry = getEntry(index))
+ m_reapack->about(getValue(RemoteColumn, *entry), handle());
+}
+
+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);
+}
+
+void Browser::reinstall(const int index, const bool toggle)
+{
+ const Entry *entry = getEntry(index);
+
+ if(entry && entry->current)
+ setAction(index, entry->current, toggle);
+}
+
+void Browser::installVersion(const int index, const size_t verIndex)
+{
+ const Entry *entry = getEntry(index);
+ const auto versions = entry->package->versions();
+
+ if(verIndex >= versions.size())
+ return;
+
+ const Version *target = entry->package->version(verIndex);
+
+ if(target == entry->current)
+ resetAction(index);
+ else
+ setAction(index, target);
+}
+
+void Browser::uninstall(const int index, const bool toggle)
+{
+ const Entry *entry = getEntry(index);
+
+ if(entry && entry->test(InstalledFlag))
+ setAction(index, nullptr, toggle);
+}
+
+void Browser::resetAction(const int index)
+{
+ const Entry *entry = getEntry(index);
+ const auto it = m_actions.find(entry);
+
+ if(it == m_actions.end())
+ return;
+
+ m_actions.erase(it);
+
+ if(getDisplay() == Queued)
+ m_list->removeRow(index);
+ else
+ m_list->replaceRow(index, makeRow(*entry));
+
+ if(m_actions.empty())
+ disable(m_apply);
+}
+
+bool Browser::isTarget(const Entry *entry, const Version *target) const
+{
+ const auto it = m_actions.find(entry);
+
+ if(it == m_actions.end())
+ return false;
+ else
+ return it->second == target;
+}
+
+void Browser::setAction(const int index, const Version *target, const bool toggle)
+{
+ const Entry *entry = getEntry(index);
+
+ if(toggle && isTarget(entry, target))
+ resetAction(index);
+ else if(entry) {
+ m_actions[entry] = target;
+ m_list->replaceRow(index, makeRow(*entry));
+ enable(m_apply);
+ }
+}
+
+void Browser::selectionDo(const std::function<void (int)> &func)
+{
+ for(const int index : m_list->selection())
+ func(index);
+}
+
+auto Browser::getDisplay() const -> Display
+{
+ return (Display)SendMessage(m_display, CB_GETCURSEL, 0, 0);
+}
+
+bool Browser::confirm() const
+{
+ if(m_actions.empty())
+ return true;
+
+ const size_t count = m_actions.size();
+
+ auto_char msg[255] = {};
+ auto_snprintf(msg, sizeof(msg),
+ AUTO_STR("Confirm execution of %zu action%s?\n"),
+ count, count == 1 ? AUTO_STR("") : AUTO_STR("s"));
+
+ const auto_char *title = AUTO_STR("ReaPack Query");
+ const int btn = MessageBox(handle(), msg, title, MB_YESNO);
+
+ return btn == IDYES;
+}
+
+void Browser::apply()
+{
+ if(m_actions.empty())
+ return;
+
+ disable(m_apply);
+
+ for(const auto &pair : m_actions) {
+ if(pair.second)
+ m_reapack->install(pair.second);
+ else
+ m_reapack->uninstall(pair.first->regEntry);
+ }
+
+ m_actions.clear();
+ m_reapack->runTasks();
+
+ fillList(); // update state column
+}
diff --git a/src/browser.hpp b/src/browser.hpp
@@ -0,0 +1,131 @@
+/* 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_BROWSER_HPP
+#define REAPACK_BROWSER_HPP
+
+#include "dialog.hpp"
+
+#include "listview.hpp"
+#include "registry.hpp"
+
+#include <functional>
+#include <map>
+#include <memory>
+#include <string>
+#include <vector>
+
+class Index;
+class ListView;
+class ReaPack;
+class Version;
+
+typedef std::shared_ptr<const Index> IndexPtr;
+
+class Browser : public Dialog {
+public:
+ Browser(const std::vector<IndexPtr> &, ReaPack *);
+ void reload();
+
+protected:
+ void onInit() override;
+ void onCommand(short, short) override;
+ void onContextMenu(HWND, int x, int y) override;
+ void onTimer(int) override;
+
+private:
+ enum Flag {
+ UninstalledFlag = 1<<1,
+ InstalledFlag = 1<<2,
+ OutOfDateFlag = 1<<3,
+ ObsoleteFlag = 1<<4,
+ };
+
+ struct Entry {
+ int flags;
+ Registry::Entry regEntry;
+ const Package *package;
+ const Version *latest;
+ const Version *current;
+
+ bool test(Flag f) const { return (flags & f) != 0; }
+ };
+
+ enum Column {
+ StateColumn,
+ NameColumn,
+ CategoryColumn,
+ VersionColumn,
+ AuthorColumn,
+ TypeColumn,
+ RemoteColumn,
+ };
+
+ enum Display {
+ All,
+ Queued,
+ Installed,
+ OutOfDate,
+ Obsolete,
+ Uninstalled,
+ };
+
+ static Entry makeEntry(const Package *, const Registry::Entry &);
+
+ bool match(const Entry &) const;
+ void checkFilter();
+ void fillList();
+ std::string getValue(Column, const Entry &entry) const;
+ ListView::Row makeRow(const Entry &) const;
+ const Entry *getEntry(int) const;
+ void selectionMenu();
+ 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 selectionDo(const std::function<void (int)> &);
+ Display getDisplay() const;
+ bool confirm() const;
+ void apply();
+
+ void installLatest(int index, bool toggle = true);
+ 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 about(int index) const;
+
+ std::vector<IndexPtr> m_indexes;
+ ReaPack *m_reapack;
+ bool m_checkFilter;
+ int m_currentIndex;
+
+ int m_filterTimer;
+ std::string m_filter;
+ std::vector<Entry> m_entries;
+ std::vector<size_t> m_visibleEntries;
+ std::map<const Entry *, const Version *> m_actions;
+
+ HWND m_filterHandle;
+ HWND m_display;
+ std::map<int, HWND> m_types;
+ ListView *m_list;
+ HWND m_action;
+ HWND m_apply;
+};
+
+#endif
diff --git a/src/cleanup.cpp b/src/cleanup.cpp
@@ -1,174 +0,0 @@
-/* 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 "cleanup.hpp"
-
-#include "encoding.hpp"
-#include "errors.hpp"
-#include "index.hpp"
-#include "listview.hpp"
-#include "menu.hpp"
-#include "reapack.hpp"
-#include "resource.hpp"
-
-using namespace std;
-
-enum { ACTION_SELECT = 300, ACTION_UNSELECT };
-
-Cleanup::Cleanup(const std::vector<IndexPtr> &indexes, ReaPack *reapack)
- : Dialog(IDD_CLEANUP_DIALOG), m_indexes(indexes), m_reapack(reapack)
-{
-}
-
-void Cleanup::onInit()
-{
- (void)m_reapack;
- m_ok = getControl(IDOK);
- m_stateLabel = getControl(IDC_LABEL2);
-
- disable(m_ok);
-
- m_list = createControl<ListView>(IDC_LIST, ListView::Columns{
- {AUTO_STR("Name"), 560},
- });
-
- m_list->sortByColumn(0);
- m_list->onSelect(bind(&Cleanup::onSelectionChanged, this));
-
- try {
- populate();
- }
- catch(const reapack_error &e) {
- const auto_string &desc = make_autostring(e.what());
- auto_char msg[255] = {};
- auto_snprintf(msg, sizeof(msg),
- AUTO_STR("The file list is currently unavailable.\x20")
- AUTO_STR("Retry later when all installation task are completed.\r\n")
- AUTO_STR("\r\nError description: %s"),
- desc.c_str());
- MessageBox(handle(), msg, AUTO_STR("AAA"), MB_OK);
- }
-
-#ifdef LVSCW_AUTOSIZE_USEHEADER
- m_list->resizeColumn(0, LVSCW_AUTOSIZE_USEHEADER);
-#endif
-
- onSelectionChanged();
-
- if(m_list->empty())
- startTimer(10);
-}
-
-void Cleanup::onCommand(const int id)
-{
- switch(id) {
- case ACTION_SELECT:
- m_list->selectAll();
- break;
- case ACTION_UNSELECT:
- m_list->unselectAll();
- break;
- case IDOK:
- if(confirm())
- apply();
- else
- break;
- case IDCANCEL:
- close();
- break;
- }
-}
-
-void Cleanup::onContextMenu(HWND target, const int x, const int y)
-{
- if(target != m_list->handle())
- return;
-
- Menu menu;
- menu.addAction(AUTO_STR("&Select all"), ACTION_SELECT);
- menu.addAction(AUTO_STR("&Unselect all"), ACTION_UNSELECT);
- menu.show(x, y, handle());
-}
-
-void Cleanup::onTimer(const int id)
-{
- stopTimer(id);
-
- MessageBox(handle(), AUTO_STR("No obsolete package found!"),
- AUTO_STR("ReaPack"), MB_OK);
-
- close();
-}
-
-void Cleanup::populate()
-{
- Registry reg(Path::prefixRoot(Path::REGISTRY));
-
- for(IndexPtr index : m_indexes) {
- for(const Registry::Entry &entry : reg.getEntries(index->name())) {
- const Category *cat = index->category(entry.category);
-
- if(cat && cat->package(entry.package))
- continue;
-
- const string row = entry.remote + "/" + entry.category + "/" + entry.package;
- m_list->addRow({make_autostring(row)});
-
- m_entries.push_back(entry);
- }
- }
-
- m_list->sort();
-}
-
-void Cleanup::onSelectionChanged()
-{
- const int selectionSize = m_list->selectionSize();
-
- auto_char state[255] = {};
- auto_snprintf(state, sizeof(state), AUTO_STR("%d of %d package%s selected"),
- selectionSize, m_list->rowCount(),
- selectionSize == 1 ? AUTO_STR("") : AUTO_STR("s"));
-
- SetWindowText(m_stateLabel, state);
-
- setEnabled(selectionSize > 0, m_ok);
-}
-
-bool Cleanup::confirm() const
-{
- const int count = m_list->selectionSize();
-
- auto_char msg[255] = {};
- auto_snprintf(msg, sizeof(msg),
- AUTO_STR("Uninstall %d package%s?\n")
- AUTO_STR("Every file they contain will be removed from your computer."),
- count, count == 1 ? AUTO_STR("") : AUTO_STR("s"));
-
- const auto_char *title = AUTO_STR("ReaPack Query");
- const int btn = MessageBox(handle(), msg, title, MB_YESNO);
-
- return btn == IDYES;
-}
-
-void Cleanup::apply()
-{
- for(const int i : m_list->selection())
- m_reapack->uninstall(m_entries[i]);
-
- m_reapack->runTasks();
-}
diff --git a/src/cleanup.hpp b/src/cleanup.hpp
@@ -1,60 +0,0 @@
-/* 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_CLEANUP_HPP
-#define REAPACK_CLEANUP_HPP
-
-#include "dialog.hpp"
-
-#include "registry.hpp"
-
-#include <memory>
-#include <vector>
-
-class Index;
-class ListView;
-class ReaPack;
-
-typedef std::shared_ptr<const Index> IndexPtr;
-
-class Cleanup : public Dialog {
-public:
- Cleanup(const std::vector<IndexPtr> &, ReaPack *);
-
-protected:
- void onInit() override;
- void onCommand(int) override;
- void onContextMenu(HWND, int x, int y) override;
- void onTimer(int) override;
-
-private:
- void populate();
- void onSelectionChanged();
- bool confirm() const;
- void apply();
-
- std::vector<IndexPtr> m_indexes;
- ReaPack *m_reapack;
-
- HWND m_ok;
- HWND m_stateLabel;
- ListView *m_list;
-
- std::vector<Registry::Entry> m_entries;
-};
-
-#endif
diff --git a/src/config.cpp b/src/config.cpp
@@ -32,6 +32,7 @@ using namespace std;
static const char *GLOBAL_GRP = "reapack";
static const char *VERSION_KEY = "version";
+static const char *AUTOINSTALL_KEY = "autoInstall";
static const char *SIZE_KEY = "size";
@@ -46,7 +47,7 @@ static string ArrayKey(const string &key, const size_t i)
static const int BUFFER_SIZE = 2083;
Config::Config()
- : m_isFirstRun(false), m_version(0), m_remotesIniSize(0)
+ : m_isFirstRun(false), m_version(0), m_autoInstall(false), m_remotesIniSize(0)
{
}
@@ -84,6 +85,8 @@ void Config::read(const Path &path)
migrate();
+ m_autoInstall = getUInt(GLOBAL_GRP, AUTOINSTALL_KEY) > 0;
+
readRemotes();
restoreSelfRemote();
}
@@ -91,6 +94,7 @@ void Config::read(const Path &path)
void Config::write()
{
setUInt(GLOBAL_GRP, VERSION_KEY, m_version);
+ setUInt(GLOBAL_GRP, AUTOINSTALL_KEY, m_autoInstall);
writeRemotes();
}
diff --git a/src/config.hpp b/src/config.hpp
@@ -32,6 +32,8 @@ public:
void write();
bool isFirstRun() const { return m_isFirstRun; }
+ bool autoInstall() const { return m_autoInstall; }
+ void setAutoInstall(const bool enable) { m_autoInstall = enable; }
RemoteList *remotes() { return &m_remotes; }
private:
@@ -48,6 +50,7 @@ private:
std::string m_path;
bool m_isFirstRun;
size_t m_version;
+ bool m_autoInstall;
void readRemotes();
void restoreSelfRemote();
diff --git a/src/dialog.cpp b/src/dialog.cpp
@@ -71,7 +71,7 @@ WDL_DLGRET Dialog::Proc(HWND handle, UINT msg, WPARAM wParam, LPARAM lParam)
dlg->onTimer((int)wParam);
break;
case WM_COMMAND:
- dlg->onCommand(LOWORD(wParam));
+ dlg->onCommand(LOWORD(wParam), HIWORD(wParam));
break;
case WM_NOTIFY:
dlg->onNotify((LPNMHDR)lParam, lParam);
@@ -242,7 +242,7 @@ void Dialog::onTimer(int)
{
}
-void Dialog::onCommand(const int id)
+void Dialog::onCommand(const short id, short)
{
switch(id) {
case IDOK:
diff --git a/src/dialog.hpp b/src/dialog.hpp
@@ -114,8 +114,8 @@ protected:
virtual void onInit();
virtual void onShow();
virtual void onHide();
- virtual void onTimer(int);
- virtual void onCommand(int);
+ virtual void onTimer(int id);
+ virtual void onCommand(short id, short event);
virtual void onNotify(LPNMHDR, LPARAM);
virtual void onContextMenu(HWND, int x, int y);
diff --git a/src/import.cpp b/src/import.cpp
@@ -55,7 +55,7 @@ void Import::onInit()
#endif
}
-void Import::onCommand(const int id)
+void Import::onCommand(const short id, short)
{
switch(id) {
case IDOK:
diff --git a/src/import.hpp b/src/import.hpp
@@ -34,7 +34,7 @@ public:
protected:
void onInit() override;
- void onCommand(int) override;
+ void onCommand(short, short) override;
void onTimer(int) override;
private:
diff --git a/src/listview.cpp b/src/listview.cpp
@@ -48,7 +48,7 @@ void ListView::addColumn(const Column &col)
LVCOLUMN item{};
item.mask |= LVCF_WIDTH;
- item.cx = col.width;
+ item.cx = adjustWidth(col.width);
item.mask |= LVCF_TEXT;
item.pszText = const_cast<auto_char *>(col.text.c_str());
@@ -111,7 +111,7 @@ void ListView::removeRow(const int userIndex)
void ListView::resizeColumn(const int index, const int width)
{
- ListView_SetColumnWidth(handle(), index, width);
+ ListView_SetColumnWidth(handle(), index, adjustWidth(width));
}
void ListView::sort()
@@ -277,7 +277,7 @@ void ListView::handleColumnClick(LPARAM lParam)
int ListView::translate(const int userIndex) const
{
- if(m_sortColumn < 0)
+ if(m_sortColumn < 0 || userIndex < 0)
return userIndex;
for(int viewIndex = 0; viewIndex < rowCount(); viewIndex++) {
@@ -295,13 +295,27 @@ int ListView::translate(const int userIndex) const
int ListView::translateBack(const int internalIndex) const
{
- if(m_sortColumn < 0)
+ if(m_sortColumn < 0 || internalIndex < 0)
return internalIndex;
LVITEM item{};
item.iItem = internalIndex;
item.mask |= LVIF_PARAM;
- ListView_GetItem(handle(), &item);
- return (int)item.lParam;
+ if(ListView_GetItem(handle(), &item))
+ return (int)item.lParam;
+ else
+ return -1;
+}
+
+int ListView::adjustWidth(const int points)
+{
+#ifdef _WIN32
+ if(points < 1)
+ return points;
+ else
+ return (int)ceil(points * 0.863); // magic number to make pretty sizes...
+#else
+ return points;
+#endif
}
diff --git a/src/listview.hpp b/src/listview.hpp
@@ -57,6 +57,7 @@ public:
std::vector<int> selection() const;
int itemUnderMouse() const;
int rowCount() const { return (int)m_rows.size(); }
+ int columnCount() const { return m_columnSize; }
bool empty() const { return rowCount() < 1; }
void onSelect(const VoidSignal::slot_type &slot) { m_onSelect.connect(slot); }
@@ -66,6 +67,7 @@ protected:
void onNotify(LPNMHDR, LPARAM) override;
private:
+ static int adjustWidth(int);
void setExStyle(int style, bool enable);
void addColumn(const Column &);
void setSortArrow(bool);
diff --git a/src/main.cpp b/src/main.cpp
@@ -81,8 +81,8 @@ static void menuHook(const char *name, HMENU handle, int f)
menu.addAction(AUTO_STR("&Synchronize packages"),
NamedCommandLookup("_REAPACK_SYNC"));
- menu.addAction(AUTO_STR("&Clean up packages..."),
- NamedCommandLookup("_REAPACK_CLEANUP"));
+ menu.addAction(AUTO_STR("&Browse packages..."),
+ NamedCommandLookup("_REAPACK_BROWSE"));
menu.addAction(AUTO_STR("&Import a repository..."),
NamedCommandLookup("_REAPACK_IMPORT"));
@@ -159,8 +159,8 @@ extern "C" REAPER_PLUGIN_DLL_EXPORT int REAPER_PLUGIN_ENTRYPOINT(
reapack->setupAction("REAPACK_SYNC", "ReaPack: Synchronize packages",
&reapack->syncAction, bind(&ReaPack::synchronizeAll, reapack));
- reapack->setupAction("REAPACK_CLEANUP", "ReaPack: Clean up packages...",
- &reapack->cleanupAction, bind(&ReaPack::cleanupPackages, reapack));
+ reapack->setupAction("REAPACK_BROWSE", "ReaPack: Browse packages...",
+ &reapack->browseAction, bind(&ReaPack::browsePackages, reapack));
reapack->setupAction("REAPACK_IMPORT", "ReaPack: Import a repository...",
&reapack->importAction, bind(&ReaPack::importRemote, reapack));
diff --git a/src/manager.cpp b/src/manager.cpp
@@ -29,35 +29,36 @@
using namespace std;
-enum { ACTION_ENABLE = 80, ACTION_DISABLE, ACTION_UNINSTALL, ACTION_ABOUT };
+enum { ACTION_ENABLE = 80, ACTION_DISABLE, ACTION_UNINSTALL, ACTION_ABOUT,
+ ACTION_AUTOINSTALL };
Manager::Manager(ReaPack *reapack)
- : Dialog(IDD_CONFIG_DIALOG), m_reapack(reapack), m_list(0)
+ : Dialog(IDD_CONFIG_DIALOG),
+ m_reapack(reapack), m_config(reapack->config()), m_list(0)
{
}
void Manager::onInit()
{
- // It would be better to not hard-code the column sizes like that...
-#ifndef _WIN32
- const int URL_WIDTH = 360;
-#else
- const int URL_WIDTH = 300;
-#endif
-
m_apply = getControl(IDAPPLY);
disable(m_apply);
m_list = createControl<ListView>(IDC_LIST, ListView::Columns{
{AUTO_STR("Name"), 110},
- {AUTO_STR("URL"), URL_WIDTH},
+ {AUTO_STR("URL"), 350},
{AUTO_STR("State"), 60},
});
m_list->onActivate([=] { about(m_list->currentIndex()); });
+
+ refresh();
+
+#ifdef LVSCW_AUTOSIZE_USEHEADER
+ m_list->resizeColumn(m_list->columnCount() - 1, LVSCW_AUTOSIZE_USEHEADER);
+#endif
}
-void Manager::onCommand(const int id)
+void Manager::onCommand(const short id, short)
{
switch(id) {
case IDC_IMPORT:
@@ -72,6 +73,13 @@ void Manager::onCommand(const int id)
case ACTION_UNINSTALL:
uninstall();
break;
+ case ACTION_AUTOINSTALL:
+ m_autoInstall = !m_autoInstall.value_or(m_config->autoInstall());
+ enable(m_apply);
+ break;
+ case IDC_OPTIONS:
+ options();
+ break;
case IDOK:
case IDAPPLY:
if(confirm()) {
@@ -151,16 +159,12 @@ void Manager::refresh()
InhibitControl lock(m_list);
m_list->clear();
- for(const Remote &remote : *m_reapack->config()->remotes()) {
+ for(const Remote &remote : *m_config->remotes()) {
if(!m_uninstall.count(remote))
m_list->addRow(makeRow(remote));
}
m_list->sort();
-
-#ifdef LVSCW_AUTOSIZE_USEHEADER
- m_list->resizeColumn(2, LVSCW_AUTOSIZE_USEHEADER);
-#endif
}
void Manager::setRemoteEnabled(const bool enabled)
@@ -225,6 +229,22 @@ void Manager::about(const int index)
m_reapack->about(getRemote(index), handle());
}
+void Manager::options()
+{
+ RECT rect;
+ GetWindowRect(getControl(IDC_OPTIONS), &rect);
+
+ Menu menu;
+
+ const UINT index = menu.addAction(
+ AUTO_STR("&Install new packages automatically"), ACTION_AUTOINSTALL);
+
+ if(m_autoInstall.value_or(m_config->autoInstall()))
+ menu.check(index);
+
+ menu.show(rect.left, rect.bottom - 1, handle());
+}
+
bool Manager::confirm() const
{
if(m_uninstall.empty())
@@ -246,6 +266,9 @@ bool Manager::confirm() const
void Manager::apply()
{
+ if(m_autoInstall)
+ m_config->setAutoInstall(m_autoInstall.value());
+
for(const auto &pair : m_enableOverrides) {
const Remote &remote = pair.first;
const bool enable = pair.second;
@@ -259,7 +282,7 @@ void Manager::apply()
for(const Remote &remote : m_uninstall)
m_reapack->uninstall(remote);
- m_reapack->config()->write();
+ m_config->write();
m_reapack->runTasks();
}
@@ -288,5 +311,5 @@ Remote Manager::getRemote(const int index) const
const ListView::Row &row = m_list->row(index);
const string &remoteName = from_autostring(row[0]);
- return m_reapack->config()->remotes()->get(remoteName);
+ return m_config->remotes()->get(remoteName);
}
diff --git a/src/manager.hpp b/src/manager.hpp
@@ -22,9 +22,11 @@
#include "listview.hpp"
+#include <boost/optional.hpp>
#include <map>
#include <set>
+class Config;
class ReaPack;
class Remote;
@@ -36,7 +38,7 @@ public:
protected:
void onInit() override;
- void onCommand(int) override;
+ void onCommand(short, short) override;
void onContextMenu(HWND, int x, int y) override;
private:
@@ -47,6 +49,7 @@ private:
bool isRemoteEnabled(const Remote &) const;
void uninstall();
void about(int index);
+ void options();
bool confirm() const;
void apply();
@@ -54,10 +57,12 @@ private:
HWND m_apply;
ReaPack *m_reapack;
+ Config *m_config;
ListView *m_list;
std::map<Remote, bool> m_enableOverrides;
std::set<Remote> m_uninstall;
+ boost::optional<bool> m_autoInstall;
};
#endif
diff --git a/src/menu.cpp b/src/menu.cpp
@@ -17,6 +17,10 @@
#include "menu.hpp"
+#ifndef MIIM_FTYPE // for SWELL
+#define MIIM_FTYPE MIIM_TYPE
+#endif
+
Menu::Menu(HMENU handle)
: m_handle(handle), m_ownership(!handle)
{
@@ -91,9 +95,10 @@ void Menu::show(const int x, const int y, HWND parent) const
x, y, 0, parent, nullptr);
}
-void Menu::disable()
+void Menu::disableAll()
{
- setEnabled(false);
+ for(UINT i = 0; i < m_size; i++)
+ setEnabled(false, i);
}
void Menu::disable(const UINT index)
@@ -101,9 +106,10 @@ void Menu::disable(const UINT index)
setEnabled(false, index);
}
-void Menu::enable()
+void Menu::enableAll()
{
- setEnabled(true);
+ for(UINT i = 0; i < m_size; i++)
+ setEnabled(false, i);
}
void Menu::enable(const UINT index)
@@ -111,21 +117,46 @@ void Menu::enable(const UINT index)
setEnabled(true, index);
}
-void Menu::setEnabled(const bool enabled)
+void Menu::setEnabled(const bool enabled, const UINT index)
{
- for(UINT i = 0; i < m_size; i++)
- setEnabled(enabled, i);
+ MENUITEMINFO mii{};
+ mii.cbSize = sizeof(MENUITEMINFO);
+
+ if(!GetMenuItemInfo(m_handle, index, true, &mii))
+ return;
+
+ mii.fMask |= MIIM_STATE;
+ mii.fState |= enabled ? MFS_ENABLED : MFS_DISABLED;
+
+ SetMenuItemInfo(m_handle, index, true, &mii);
}
-void Menu::setEnabled(const bool enabled, const UINT index)
+void Menu::check(const UINT index)
{
MENUITEMINFO mii{};
mii.cbSize = sizeof(MENUITEMINFO);
+ mii.fMask |= MIIM_STATE;
+
+ if(!GetMenuItemInfo(m_handle, index, true, &mii))
+ return;
- GetMenuItemInfo(m_handle, index, true, &mii);
+ mii.fState |= MFS_CHECKED;
+
+ SetMenuItemInfo(m_handle, index, true, &mii);
+}
+void Menu::checkRadio(const UINT index)
+{
+ MENUITEMINFO mii{};
+ mii.cbSize = sizeof(MENUITEMINFO);
+ mii.fMask |= MIIM_FTYPE;
mii.fMask |= MIIM_STATE;
- mii.fState |= enabled ? MFS_ENABLED : MFS_DISABLED;
+
+ if(!GetMenuItemInfo(m_handle, index, true, &mii))
+ return;
+
+ mii.fType |= MFT_RADIOCHECK;
+ mii.fState |= MFS_CHECKED;
SetMenuItemInfo(m_handle, index, true, &mii);
}
diff --git a/src/menu.hpp b/src/menu.hpp
@@ -31,21 +31,23 @@ public:
Menu(HMENU handle = 0);
~Menu();
- unsigned int size() { return m_size; }
+ UINT size() { return m_size; }
bool empty() const { return m_size == 0; }
- unsigned int addAction(const auto_char *label, int commandId);
+ UINT addAction(const auto_char *label, int commandId);
void addSeparator();
Menu addMenu(const auto_char *label);
void show(int x, int y, HWND parent) const;
- void enable();
- void enable(unsigned int);
- void disable();
- void disable(unsigned int);
- void setEnabled(bool);
- void setEnabled(bool, unsigned int);
+ void enableAll();
+ void enable(UINT);
+ void disableAll();
+ void disable(UINT);
+ void setEnabled(bool, UINT);
+
+ void check(UINT);
+ void checkRadio(UINT);
private:
void append(MENUITEMINFO &);
diff --git a/src/package.cpp b/src/package.cpp
@@ -36,6 +36,22 @@ Package::Type Package::typeFor(const char *type)
return UnknownType;
}
+string Package::displayType(const Type type)
+{
+ switch(type) {
+ case UnknownType:
+ return "Unknown";
+ case ScriptType:
+ return "Script";
+ case ExtensionType:
+ return "Extension";
+ case EffectType:
+ return "Effect";
+ }
+
+ return {}; // MSVC is stupid
+}
+
Package::Package(const Type type, const string &name, Category *cat)
: m_category(cat), m_type(type), m_name(name)
{
@@ -48,6 +64,11 @@ string Package::fullName() const
return m_category ? m_category->fullName() + "/" + m_name : m_name;
}
+string Package::displayType() const
+{
+ return displayType(m_type);
+}
+
Package::~Package()
{
for(const Version *ver : m_versions)
diff --git a/src/package.hpp b/src/package.hpp
@@ -36,12 +36,14 @@ public:
};
static Type typeFor(const char *);
+ static std::string displayType(Type);
Package(const Type, const std::string &name, Category * = nullptr);
~Package();
Category *category() const { return m_category; }
Type type() const { return m_type; }
+ std::string displayType() const;
const std::string &name() const { return m_name; }
std::string fullName() const;
diff --git a/src/progress.cpp b/src/progress.cpp
@@ -38,7 +38,7 @@ void Progress::onInit()
SetWindowText(m_label, AUTO_STR("Initializing..."));
}
-void Progress::onCommand(const int id)
+void Progress::onCommand(const short id, short)
{
switch(id) {
case IDCANCEL:
diff --git a/src/progress.hpp b/src/progress.hpp
@@ -31,7 +31,7 @@ public:
protected:
void onInit() override;
- void onCommand(int) override;
+ void onCommand(short, short) override;
private:
void addDownload(Download *);
diff --git a/src/reapack.cpp b/src/reapack.cpp
@@ -18,7 +18,7 @@
#include "reapack.hpp"
#include "about.hpp"
-#include "cleanup.hpp"
+#include "browser.hpp"
#include "config.hpp"
#include "errors.hpp"
#include "filesystem.hpp"
@@ -62,9 +62,9 @@ static void CleanupTempFiles()
#endif
ReaPack::ReaPack(REAPER_PLUGIN_HINSTANCE instance)
- : syncAction(), cleanupAction(), importAction(), configAction(),
- m_transaction(nullptr), m_progress(nullptr), m_manager(nullptr),
- m_import(nullptr), m_cleanup(nullptr), m_instance(instance)
+ : syncAction(), browseAction(),importAction(), configAction(),
+ m_transaction(nullptr), m_progress(nullptr), m_browser(nullptr),
+ m_import(nullptr), m_manager(nullptr), m_instance(instance)
{
m_mainWindow = GetMainHwnd();
m_useRootPath = new UseRootPath(GetResourcePath());
@@ -147,7 +147,7 @@ void ReaPack::synchronizeAll()
return;
for(const Remote &remote : remotes)
- t->synchronize(remote);
+ t->synchronize(remote, m_config->autoInstall());
t->runTasks();
}
@@ -177,6 +177,14 @@ void ReaPack::setRemoteEnabled(const Remote &original, const bool enable)
});
}
+void ReaPack::install(const Version *ver)
+{
+ if(!hitchhikeTransaction())
+ return;
+
+ m_transaction->install(ver);
+}
+
void ReaPack::uninstall(const Remote &remote)
{
if(remote.isProtected())
@@ -252,6 +260,8 @@ void ReaPack::import(const Remote &remote)
}
else {
enable(existing);
+ runTasks();
+
m_config->write();
return;
@@ -277,7 +287,6 @@ void ReaPack::manageRemotes()
}
m_manager = Dialog::Create<Manager>(m_instance, m_mainWindow, this);
- m_manager->refresh();
m_manager->show();
m_manager->setCloseHandler([=] (INT_PTR) {
@@ -288,7 +297,12 @@ void ReaPack::manageRemotes()
void ReaPack::aboutSelf()
{
- about(m_config->remotes()->get("ReaPack"), m_mainWindow);
+ about("ReaPack", m_mainWindow);
+}
+
+void ReaPack::about(const string &remoteName, HWND parent)
+{
+ about(m_config->remotes()->get(remoteName), parent);
}
void ReaPack::about(const Remote &remote, HWND parent)
@@ -301,22 +315,25 @@ void ReaPack::about(const Remote &remote, HWND parent)
if(ret == About::InstallResult) {
enable(remote);
- if(m_transaction)
- m_transaction->synchronize(remote);
+
+ if(m_transaction) // transaction is created by enable()
+ m_transaction->synchronize(remote, true);
+
+ runTasks();
}
}, parent);
}
-void ReaPack::cleanupPackages()
+void ReaPack::browsePackages()
{
- if(m_cleanup) {
- m_cleanup->setFocus();
+ if(m_browser) {
+ m_browser->setFocus();
return;
}
else if(m_transaction) {
ShowMessageBox(
"This feature cannot be used while packages are being installed. "
- "Try again later.", "Clean up packages", MB_OK
+ "Try again later.", "Browse packages", MB_OK
);
return;
}
@@ -324,11 +341,11 @@ void ReaPack::cleanupPackages()
const vector<Remote> &remotes = m_config->remotes()->getEnabled();
fetchIndexes(remotes, [=] (const vector<IndexPtr> &indexes) {
- m_cleanup = Dialog::Create<Cleanup>(m_instance, m_mainWindow, indexes, this);
- m_cleanup->show();
- m_cleanup->setCloseHandler([=] (INT_PTR) {
- Dialog::Destroy(m_cleanup);
- m_cleanup = nullptr;
+ m_browser = Dialog::Create<Browser>(m_instance, m_mainWindow, indexes, this);
+ m_browser->show();
+ m_browser->setCloseHandler([=] (INT_PTR) {
+ Dialog::Destroy(m_browser);
+ m_browser = nullptr;
});
});
}
@@ -480,12 +497,15 @@ Transaction *ReaPack::createTransaction()
return;
LockDialog managerLock(m_manager);
- LockDialog cleanupLock(m_cleanup);
+ LockDialog cleanupLock(m_browser);
if(m_transaction->taskCount() == 0 && !receipt->hasErrors())
ShowMessageBox("Nothing to do!", "ReaPack", 0);
else
Dialog::Show<Report>(m_instance, m_mainWindow, receipt);
+
+ if(m_browser && m_transaction->taskCount() > 0)
+ m_browser->reload();
});
m_transaction->setCleanupHandler([=] {
diff --git a/src/reapack.hpp b/src/reapack.hpp
@@ -28,7 +28,7 @@
#include <reaper_plugin.h>
-class Cleanup;
+class Browser;
class Config;
class DownloadQueue;
class Import;
@@ -50,7 +50,7 @@ public:
static const std::string BUILDTIME;
gaccel_register_t syncAction;
- gaccel_register_t cleanupAction;
+ gaccel_register_t browseAction;
gaccel_register_t importAction;
gaccel_register_t configAction;
@@ -66,14 +66,16 @@ public:
void setRemoteEnabled(const Remote &, bool enable);
void enable(const Remote &r) { setRemoteEnabled(r, true); }
void disable(const Remote &r) { setRemoteEnabled(r, false); }
+ void install(const Version *);
void uninstall(const Remote &);
void uninstall(const Registry::Entry &);
void importRemote();
void import(const Remote &);
void manageRemotes();
void aboutSelf();
+ void about(const std::string &, HWND parent);
void about(const Remote &, HWND parent);
- void cleanupPackages();
+ void browsePackages();
void fetchIndexes(const std::vector<Remote> &,
const IndexesCallback &, HWND = nullptr);
@@ -94,9 +96,9 @@ private:
Config *m_config;
Transaction *m_transaction;
Progress *m_progress;
- Manager *m_manager;
+ Browser *m_browser;
Import *m_import;
- Cleanup *m_cleanup;
+ Manager *m_manager;
REAPER_PLUGIN_HINSTANCE m_instance;
HWND m_mainWindow;
diff --git a/src/registry.cpp b/src/registry.cpp
@@ -34,21 +34,22 @@ Registry::Registry(const Path &path)
// entry queries
m_insertEntry = m_db.prepare(
- "INSERT INTO entries VALUES(NULL, ?, ?, ?, ?, ?);"
+ "INSERT INTO entries(remote, category, package, type, version, author)"
+ "VALUES(?, ?, ?, ?, ?, ?);"
);
m_updateEntry = m_db.prepare(
- "UPDATE entries SET type = ?, version = ? WHERE id = ?"
+ "UPDATE entries SET type = ?, version = ?, author = ? WHERE id = ?"
);
m_findEntry = m_db.prepare(
- "SELECT id, remote, category, package, type, version FROM entries "
+ "SELECT id, remote, category, package, type, version, author FROM entries "
"WHERE remote = ? AND category = ? AND package = ? "
"LIMIT 1"
);
m_allEntries = m_db.prepare(
- "SELECT id, category, package, type, version "
+ "SELECT id, category, package, type, version, author "
"FROM entries WHERE remote = ?"
);
m_forgetEntry = m_db.prepare("DELETE FROM entries WHERE id = ?");
@@ -90,6 +91,7 @@ void Registry::migrate()
" package TEXT NOT NULL,"
" type INTEGER NOT NULL,"
" version TEXT NOT NULL,"
+ " author TEXT NOT NULL,"
" UNIQUE(remote, category, package)"
");"
@@ -106,7 +108,8 @@ void Registry::migrate()
// current schema version
break;
default:
- throw reapack_error("database was created with a newer version of ReaPack");
+ throw reapack_error(
+ "The package registry was created by a newer version of ReaPack");
}
m_db.commit();
@@ -133,7 +136,8 @@ auto Registry::push(const Version *ver, vector<Path> *conflicts) -> Entry
if(entryId) {
m_updateEntry->bind(1, pkg->type());
m_updateEntry->bind(2, ver->name());
- m_updateEntry->bind(3, entryId);
+ m_updateEntry->bind(3, ver->displayAuthor());
+ m_updateEntry->bind(4, entryId);
m_updateEntry->exec();
}
else {
@@ -142,6 +146,7 @@ auto Registry::push(const Version *ver, vector<Path> *conflicts) -> Entry
m_insertEntry->bind(3, pkg->name());
m_insertEntry->bind(4, pkg->type());
m_insertEntry->bind(5, ver->name());
+ m_insertEntry->bind(6, ver->displayAuthor());
m_insertEntry->exec();
entryId = m_db.lastInsertId();
@@ -180,18 +185,13 @@ auto Registry::push(const Version *ver, vector<Path> *conflicts) -> Entry
else {
release();
return {entryId, ri->name(), cat->name(),
- pkg->name(), pkg->type(), ver->code()};
+ pkg->name(), pkg->type(), ver->name(), ver->code(), ver->displayAuthor()};
}
}
auto Registry::getEntry(const Package *pkg) const -> Entry
{
- int id = 0;
- string remote;
- string category;
- string package;
- Package::Type type = Package::UnknownType;
- Version::Code version = 0;
+ Entry entry{};
const Category *cat = pkg->category();
const Index *ri = cat->index();
@@ -203,17 +203,19 @@ auto Registry::getEntry(const Package *pkg) const -> Entry
m_findEntry->exec([&] {
int col = 0;
- id = m_findEntry->intColumn(col++);
- remote = m_findEntry->stringColumn(col++);
- category = m_findEntry->stringColumn(col++);
- package = m_findEntry->stringColumn(col++);
- type = static_cast<Package::Type>(m_findEntry->intColumn(col++));
- Version::parse(m_findEntry->stringColumn(col++), &version);
+ entry.id = m_findEntry->intColumn(col++);
+ entry.remote = m_findEntry->stringColumn(col++);
+ entry.category = m_findEntry->stringColumn(col++);
+ entry.package = m_findEntry->stringColumn(col++);
+ entry.type = static_cast<Package::Type>(m_findEntry->intColumn(col++));
+ entry.versionName = m_findEntry->stringColumn(col++);
+ Version::parse(entry.versionName, &entry.versionCode);
+ entry.author = m_findEntry->stringColumn(col++);
return false;
});
- return {id, remote, category, package, type, version};
+ return entry;
}
auto Registry::getEntries(const string &remoteName) const -> vector<Entry>
@@ -224,15 +226,17 @@ auto Registry::getEntries(const string &remoteName) const -> vector<Entry>
m_allEntries->exec([&] {
int col = 0;
- const int id = m_allEntries->intColumn(col++);
- const string &category = m_allEntries->stringColumn(col++);
- const string &package = m_allEntries->stringColumn(col++);
- const Package::Type type =
- static_cast<Package::Type>(m_allEntries->intColumn(col++));
- Version::Code version = 0;
- Version::parse(m_allEntries->stringColumn(col++), &version);
-
- list.push_back({id, remoteName, category, package, type, version});
+ Entry entry{};
+ entry.id = m_allEntries->intColumn(col++);
+ entry.remote = remoteName;
+ entry.category = m_allEntries->stringColumn(col++);
+ entry.package = m_allEntries->stringColumn(col++);
+ entry.type = static_cast<Package::Type>(m_allEntries->intColumn(col++));
+ entry.versionName = m_allEntries->stringColumn(col++);
+ Version::parse(entry.versionName, &entry.versionCode);
+ entry.author = m_allEntries->stringColumn(col++);
+
+ list.push_back(entry);
return true;
});
diff --git a/src/registry.hpp b/src/registry.hpp
@@ -38,7 +38,9 @@ public:
std::string category;
std::string package;
Package::Type type;
- Version::Code version;
+ std::string versionName;
+ Version::Code versionCode;
+ std::string author;
};
Registry(const Path &path = Path());
diff --git a/src/report.cpp b/src/report.cpp
@@ -60,7 +60,7 @@ void ReportDialog::printVersion(const Version *ver)
if(!ver->author().empty())
stream() << " by " << ver->author();
- const string &date = ver->formattedDate();
+ const string &date = ver->displayTime();
if(!date.empty())
stream() << " – " << date;
@@ -92,7 +92,7 @@ void Report::fillReport()
const size_t errors = m_receipt->errors().size();
stream()
- << installs << " new packages, "
+ << installs << " installed packages, "
<< updates << " updates, "
<< removals << " removed files and "
<< errors << " errors"
@@ -111,7 +111,7 @@ void Report::fillReport()
printErrors();
if(installs)
- printNewPackages();
+ printInstalls();
if(updates)
printUpdates();
@@ -120,9 +120,9 @@ void Report::fillReport()
printRemovals();
}
-void Report::printNewPackages()
+void Report::printInstalls()
{
- printHeader("New packages");
+ printHeader("Installed packages");
for(const InstallTicket &ticket : m_receipt->installs())
stream() << ticket.version->fullName() << NL;
@@ -132,21 +132,24 @@ void Report::printUpdates()
{
printHeader("Updates");
+ const auto start = stream().tellp();
+
for(const InstallTicket &ticket : m_receipt->updates()) {
const Package *pkg = ticket.version->package();
const Registry::Entry ®Entry = ticket.regEntry;
const VersionSet &versions = pkg->versions();
+ if(stream().tellp() != start)
+ stream() << NL;
+
stream() << pkg->fullName() << ':' << NL;
for(const Version *ver : versions | boost::adaptors::reversed) {
- if(ver->code() <= regEntry.version)
+ if(ver->code() <= regEntry.versionCode)
break;
printVersion(ver);
}
-
- stream() << NL;
}
}
@@ -154,10 +157,14 @@ void Report::printErrors()
{
printHeader("Errors");
+ const auto start = stream().tellp();
+
for(const Receipt::Error &err : m_receipt->errors()) {
+ if(stream().tellp() != start)
+ stream() << NL;
+
stream() << err.title << ':' << NL;
printIndented(err.message);
- stream() << "\n";
}
}
@@ -168,3 +175,22 @@ void Report::printRemovals()
for(const Path &path : m_receipt->removals())
stream() << path.join() << NL;
}
+
+History::History(const Package *pkg)
+ : ReportDialog(), m_package(pkg)
+{
+}
+
+void History::fillReport()
+{
+ SetWindowText(handle(), AUTO_STR("Package History"));
+ SetWindowText(getControl(IDC_LABEL),
+ make_autostring(m_package->name()).c_str());
+
+ for(const Version *ver : m_package->versions() | boost::adaptors::reversed) {
+ if(stream().tellp())
+ stream() << NL;
+
+ printVersion(ver);
+ }
+}
diff --git a/src/report.hpp b/src/report.hpp
@@ -22,6 +22,7 @@
#include <sstream>
+class Package;
class Receipt;
class Version;
@@ -53,7 +54,7 @@ protected:
void fillReport() override;
private:
- void printNewPackages();
+ void printInstalls();
void printUpdates();
void printErrors();
void printRemovals();
@@ -61,4 +62,15 @@ private:
const Receipt *m_receipt;
};
+class History : public ReportDialog {
+public:
+ History(const Package *);
+
+protected:
+ void fillReport() override;
+
+private:
+ const Package *m_package;
+};
+
#endif
diff --git a/src/resource.hpp b/src/resource.hpp
@@ -39,7 +39,7 @@
#define IDD_CONFIG_DIALOG 102
#define IDD_ABOUT_DIALOG 103
#define IDD_IMPORT_DIALOG 104
-#define IDD_CLEANUP_DIALOG 105
+#define IDD_BROWSER_DIALOG 105
#define IDC_LABEL 200
#define IDC_LABEL2 201
@@ -57,5 +57,15 @@
#define IDC_PACKAGES 220
#define IDC_GROUPBOX 221
#define IDC_URL 222
+#define IDC_SCRIPTS 223
+#define IDC_EFFECTS 224
+#define IDC_EXTENSIONS 225
+#define IDC_FILTER 226
+#define IDC_CLEAR 227
+#define IDC_DISPLAY 228
+#define IDC_SELECT 229
+#define IDC_UNSELECT 230
+#define IDC_ACTION 231
+#define IDC_OPTIONS 232
#endif
diff --git a/src/resource.rc b/src/resource.rc
@@ -34,6 +34,7 @@ BEGIN
CONTROL "", IDC_LIST, WC_LISTVIEW, LVS_REPORT | LVS_SHOWSELALWAYS |
WS_BORDER | WS_TABSTOP, 5, 18, 320, 136
PUSHBUTTON "&Import...", IDC_IMPORT, 5, 160, 45, 14
+ PUSHBUTTON "&Options...", IDC_OPTIONS, 53, 160, 45, 14
DEFPUSHBUTTON "&OK", IDOK, 198, 160, 40, 14
PUSHBUTTON "&Cancel", IDCANCEL, 241, 160, 40, 14
PUSHBUTTON "&Apply", IDAPPLY, 284, 160, 40, 14
@@ -59,7 +60,7 @@ BEGIN
WS_VSCROLL | ES_MULTILINE | ES_READONLY | NOT WS_TABSTOP
PUSHBUTTON "&Website", IDC_WEBSITE, 5, 250, 45, 14
PUSHBUTTON "&Donate...", IDC_DONATE, 54, 250, 45, 14
- PUSHBUTTON "&Install this repository", IDC_INSTALL, 286, 250, 120, 14
+ PUSHBUTTON "&Install/update this repository", IDC_INSTALL, 286, 250, 120, 14
DEFPUSHBUTTON "&Close", IDOK, 409, 250, 45, 14
END
@@ -77,16 +78,26 @@ BEGIN
PUSHBUTTON "&Cancel", IDCANCEL, 244, 50, 40, 14
END
-IDD_CLEANUP_DIALOG DIALOGEX 0, 0, 350, 200
+IDD_BROWSER_DIALOG DIALOGEX 0, 0, 500, 250
STYLE DIALOG_STYLE
FONT DIALOG_FONT
-CAPTION "ReaPack: Clean up packages"
+CAPTION "ReaPack: Package Browser"
BEGIN
- LTEXT "The following packages could not be found in any enabled the repositories:",
- IDC_LABEL, 5, 5, 340, 10
- CONTROL "", IDC_LIST, WC_LISTVIEW, LVS_REPORT | LVS_SHOWSELALWAYS |
- LVS_NOCOLUMNHEADER | WS_BORDER | WS_TABSTOP, 5, 18, 340, 156
- LTEXT "", IDC_LABEL2, 5, 183, 150, 10
- DEFPUSHBUTTON "&Uninstall selected packages", IDOK, 176, 180, 120, 14
- PUSHBUTTON "&Cancel", IDCANCEL, 299, 180, 45, 14
+ LTEXT "Filter:", IDC_LABEL, 5, 7, 20, 10
+ EDITTEXT IDC_FILTER, 25, 4, 200, 14, ES_AUTOHSCROLL | WS_TABSTOP
+ PUSHBUTTON "Cl&ear", IDC_CLEAR, 227, 4, 32, 14
+ LTEXT "Display:", IDC_LABEL2, 266, 7, 30, 10
+ COMBOBOX IDC_DISPLAY, 294, 5, 55, 54, CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
+ LTEXT "Type:", IDC_LABEL3, 356, 7, 20, 10
+ CHECKBOX "&Scripts", IDC_SCRIPTS, 376, 4, 35, 14, BS_AUTOCHECKBOX | WS_TABSTOP
+ CHECKBOX "&Effects", IDC_EFFECTS, 413, 4, 35, 14, BS_AUTOCHECKBOX | WS_TABSTOP
+ CHECKBOX "&Extensions", IDC_EXTENSIONS, 450, 4, 50, 14, BS_AUTOCHECKBOX | WS_TABSTOP
+ CONTROL "", IDC_PACKAGES, WC_LISTVIEW, LVS_REPORT | LVS_SHOWSELALWAYS |
+ WS_BORDER | WS_TABSTOP, 5, 22, 490, 204
+ PUSHBUTTON "&Select all", IDC_SELECT, 5, 230, 45, 14
+ PUSHBUTTON "&Unselect all", IDC_UNSELECT, 53, 230, 45, 14
+ PUSHBUTTON "&Action...", IDC_ACTION, 101, 230, 45, 14
+ DEFPUSHBUTTON "&OK", IDOK, 368, 230, 40, 14
+ PUSHBUTTON "&Cancel", IDCANCEL, 411, 230, 40, 14
+ PUSHBUTTON "&Apply", IDAPPLY, 454, 230, 40, 14
END
diff --git a/src/transaction.cpp b/src/transaction.cpp
@@ -49,7 +49,7 @@ Transaction::Transaction()
if(m_installQueue.empty())
finish();
else
- installQueued();
+ runTasks();
});
}
@@ -61,11 +61,10 @@ Transaction::~Transaction()
delete m_registry;
}
-void Transaction::synchronize(const Remote &remote, const bool isUserAction)
+void Transaction::synchronize(const Remote &remote, const bool autoInstall)
{
- // show the report dialog even if no task are ran
- if(isUserAction)
- m_receipt.setEnabled(true);
+ // show the report dialog or "nothing to do" even if no task are ran
+ m_receipt.setEnabled(true);
fetchIndex(remote, [=] {
IndexPtr ri;
@@ -87,12 +86,29 @@ void Transaction::synchronize(const Remote &remote, const bool isUserAction)
m_registry->savepoint();
for(const Package *pkg : ri->packages())
- install(pkg->lastVersion());
+ synchronize(pkg, autoInstall);
m_registry->restore();
});
}
+void Transaction::synchronize(const Package *pkg, const bool autoInstall)
+{
+ const auto ®Entry = m_registry->getEntry(pkg);
+
+ if(!regEntry.id && !autoInstall)
+ return;
+
+ const Version *ver = pkg->lastVersion();
+
+ if(regEntry.versionCode == ver->code()) {
+ if(allFilesExists(ver->files()))
+ return; // latest version is really installed, nothing to do here!
+ }
+
+ install(ver, regEntry);
+}
+
void Transaction::fetchIndex(const Remote &remote, const IndexCallback &cb)
{
// add the callback to the list, and start the download if it's the first one
@@ -127,19 +143,20 @@ void Transaction::saveIndex(Download *dl, const string &name)
void Transaction::install(const Version *ver)
{
- const Package *pkg = ver->package();
- const Registry::Entry ®Entry = m_registry->getEntry(pkg);
+ install(ver, m_registry->getEntry(ver->package()));
+}
- InstallTicket::Type type = InstallTicket::Install;
+void Transaction::install(const Version *ver,
+ const Registry::Entry ®Entry)
+{
+ m_receipt.setEnabled(true);
- if(regEntry.id) {
- if(regEntry.version == ver->code()) {
- if(allFilesExists(ver->files()))
- return; // latest version is really installed, nothing to do here!
- }
- else if(regEntry.version < ver->code())
- type = InstallTicket::Upgrade;
- }
+ InstallTicket::Type type;
+
+ if(regEntry.id && regEntry.versionCode < ver->code())
+ type = InstallTicket::Upgrade;
+ else
+ type = InstallTicket::Install;
// prevent file conflicts (don't worry, the registry push is reverted later)
try {
@@ -163,23 +180,13 @@ void Transaction::install(const Version *ver)
}
// all green! (pronounce with a japanese accent)
- IndexPtr ri = pkg->category()->index()->shared_from_this();
+ IndexPtr ri = ver->package()->category()->index()->shared_from_this();
if(!m_indexes.count(ri))
m_indexes.insert(ri);
m_installQueue.push({type, ver, regEntry});
}
-void Transaction::installQueued()
-{
- while(!m_installQueue.empty()) {
- installTicket(m_installQueue.front());
- m_installQueue.pop();
- }
-
- runTasks();
-}
-
void Transaction::installTicket(const InstallTicket &ticket)
{
const Version *ver = ticket.version;
@@ -312,6 +319,11 @@ void Transaction::addTask(Task *task)
void Transaction::runTasks()
{
+ while(!m_installQueue.empty()) {
+ installTicket(m_installQueue.front());
+ m_installQueue.pop();
+ }
+
while(!m_taskQueue.empty()) {
m_taskQueue.front()->start();
m_taskQueue.pop();
diff --git a/src/transaction.hpp b/src/transaction.hpp
@@ -56,7 +56,7 @@ public:
void onFinish(const VoidSignal::slot_type &slot) { m_onFinish.connect(slot); }
void setCleanupHandler(const CleanupHandler &cb) { m_cleanupHandler = cb; }
- void synchronize(const Remote &, bool userAction = true);
+ void synchronize(const Remote &, bool autoInstall);
void install(const Version *);
void uninstall(const Remote &);
void uninstall(const Registry::Entry &);
@@ -78,7 +78,8 @@ private:
void fetchIndex(const Remote &, const IndexCallback &cb);
void saveIndex(Download *, const std::string &remoteName);
- void installQueued();
+ void synchronize(const Package *, bool autoInstall);
+ void install(const Version *, const Registry::Entry &);
void installTicket(const InstallTicket &);
void addTask(Task *);
diff --git a/src/version.cpp b/src/version.cpp
@@ -72,10 +72,18 @@ Version::~Version()
string Version::fullName() const
{
- const string fName = "v" + m_name;
+ const string fName = 'v' + m_name;
return m_package ? m_package->fullName() + " " + fName : fName;
}
+string Version::displayAuthor() const
+{
+ if(m_author.empty())
+ return "Unknown";
+ else
+ return m_author;
+}
+
void Version::addSource(Source *source)
{
const Source::Platform p = source->platform();
@@ -135,7 +143,7 @@ void Version::setTime(const char *iso8601)
m_time.tm_mday = day;
}
-string Version::formattedDate() const
+string Version::displayTime() const
{
if(m_time.tm_year == 0)
return {};
diff --git a/src/version.hpp b/src/version.hpp
@@ -44,10 +44,11 @@ public:
void setAuthor(const std::string &author) { m_author = author; }
const std::string &author() const { return m_author; }
+ std::string displayAuthor() const;
void setTime(const char *iso8601);
const std::tm &time() const { return m_time; }
- std::string formattedDate() const;
+ std::string displayTime() const;
void setChangelog(const std::string &);
const std::string &changelog() const { return m_changelog; }
diff --git a/test/package.cpp b/test/package.cpp
@@ -26,6 +26,28 @@ TEST_CASE("package type from string", M) {
REQUIRE(Package::typeFor("effect") == Package::EffectType);
}
+TEST_CASE("package type to string", M) {
+ SECTION("unknown") {
+ REQUIRE("Unknown" == Package::displayType(Package::UnknownType));
+ REQUIRE("Unknown" == Package(Package::UnknownType, "test").displayType());
+ }
+
+ SECTION("script") {
+ REQUIRE("Script" == Package::displayType(Package::ScriptType));
+ REQUIRE("Script" == Package(Package::ScriptType, "test").displayType());
+ }
+
+ SECTION("extension") {
+ REQUIRE("Extension" == Package::displayType(Package::ExtensionType));
+ REQUIRE("Extension" == Package(Package::ExtensionType, "test").displayType());
+ }
+
+ SECTION("effect") {
+ REQUIRE("Effect" == Package::displayType(Package::EffectType));
+ REQUIRE("Effect" == Package(Package::EffectType, "test").displayType());
+ }
+}
+
TEST_CASE("empty package name", M) {
try {
Package pack(Package::ScriptType, string());
diff --git a/test/registry.cpp b/test/registry.cpp
@@ -18,6 +18,7 @@ static const char *M = "[registry]";
Category cat("Category Name", &ri); \
Package pkg(Package::ScriptType, "Hello", &cat); \
Version *ver = new Version("1.0", &pkg); \
+ ver->setAuthor("John Doe"); \
Source *src = new Source(Source::GenericPlatform, "file", "url", ver); \
ver->addSource(src); \
pkg.addVersion(ver);
@@ -29,10 +30,10 @@ TEST_CASE("query uninstalled package", M) {
const Registry::Entry &res = reg.getEntry(&pkg);
REQUIRE(res.id == 0);
- REQUIRE(res.version == 0);
+ REQUIRE(res.versionCode == 0);
}
-TEST_CASE("query installed pacakge", M) {
+TEST_CASE("query installed package", M) {
MAKE_PACKAGE
Registry reg;
@@ -43,7 +44,9 @@ TEST_CASE("query installed pacakge", M) {
REQUIRE(entry.category == "Category Name");
REQUIRE(entry.package == "Hello");
REQUIRE(entry.type == Package::ScriptType);
- REQUIRE(entry.version == Version("1.0").code());
+ REQUIRE(entry.versionName == "1.0");
+ REQUIRE(entry.versionCode == Version("1.0").code());
+ REQUIRE(entry.author == "John Doe");
const Registry::Entry &selectEntry = reg.getEntry(&pkg);
REQUIRE(selectEntry.id == entry.id);
@@ -51,7 +54,9 @@ TEST_CASE("query installed pacakge", M) {
REQUIRE(selectEntry.category == entry.category);
REQUIRE(selectEntry.package == entry.package);
REQUIRE(selectEntry.type == entry.type);
- REQUIRE(selectEntry.version == entry.version);
+ REQUIRE(selectEntry.versionName == entry.versionName);
+ REQUIRE(selectEntry.versionCode == entry.versionCode);
+ REQUIRE(selectEntry.author == entry.author);
}
TEST_CASE("bump version", M) {
@@ -65,11 +70,13 @@ TEST_CASE("bump version", M) {
pkg.addVersion(ver2);
const Registry::Entry &entry1 = reg.getEntry(&pkg);
- REQUIRE(entry1.version == Version("1.0").code());
+ REQUIRE(entry1.versionName == "1.0");
+ CHECK(entry1.author == "John Doe");
reg.push(ver2);
const Registry::Entry &entry2 = reg.getEntry(&pkg);
- REQUIRE(entry2.version == Version("2.0").code());
+ REQUIRE(entry2.versionName == "2.0");
+ CHECK(entry2.author == "Unknown");
REQUIRE(entry2.id == entry1.id);
}
@@ -104,7 +111,9 @@ TEST_CASE("query all packages", M) {
REQUIRE(entries[0].category == "Category Name");
REQUIRE(entries[0].package == "Hello");
REQUIRE(entries[0].type == Package::ScriptType);
- REQUIRE(entries[0].version == Version("1.0").code());
+ REQUIRE(entries[0].versionName == "1.0");
+ REQUIRE(entries[0].versionCode == Version("1.0").code());
+ REQUIRE(entries[0].author == "John Doe");
}
TEST_CASE("forget registry entry", M) {
diff --git a/test/version.cpp b/test/version.cpp
@@ -219,25 +219,25 @@ TEST_CASE("version date", M) {
CHECK(ver.time().tm_year == 0);
CHECK(ver.time().tm_mon == 0);
CHECK(ver.time().tm_mday == 0);
- CHECK(ver.formattedDate() == "");
+ CHECK(ver.displayTime() == "");
SECTION("valid") {
ver.setTime("2016-02-12T01:16:40Z");
REQUIRE(ver.time().tm_year == 2016 - 1900);
REQUIRE(ver.time().tm_mon == 2 - 1);
REQUIRE(ver.time().tm_mday == 12);
- REQUIRE(ver.formattedDate() != "");
+ REQUIRE(ver.displayTime() != "");
}
SECTION("garbage") {
ver.setTime("hello world");
REQUIRE(ver.time().tm_year == 0);
- REQUIRE(ver.formattedDate() == "");
+ REQUIRE(ver.displayTime() == "");
}
SECTION("out of range") {
ver.setTime("2016-99-99T99:99:99Z");
- ver.formattedDate(); // no crash
+ ver.displayTime(); // no crash
}
}