commit 8f8f55d69b86f2004317b606d143dd6138c3300d
parent 0560ce9892f7103c3706c7222ee338bb64860d1e
Author: cfillion <cfillion@users.noreply.github.com>
Date: Thu, 17 Mar 2016 22:23:40 -0400
implement actions in the package browser
Diffstat:
6 files changed, 314 insertions(+), 76 deletions(-)
diff --git a/src/browser.cpp b/src/browser.cpp
@@ -30,20 +30,33 @@
using namespace std;
-enum Action { ACTION_HISTORY = 300, ACTION_ABOUT };
+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_currentEntry(nullptr)
+ m_checkFilter(false), m_currentIndex(-1)
{
}
void Browser::onInit()
{
+ m_action = getControl(IDC_ACTION);
m_filterHandle = getControl(IDC_FILTER);
m_display = getControl(IDC_DISPLAY);
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("Uninstalled"));
@@ -59,24 +72,26 @@ void Browser::onInit()
SendMessage(pair.second, BM_SETCHECK, BST_CHECKED, 0);
m_list = createControl<ListView>(IDC_PACKAGES, ListView::Columns{
- {AUTO_STR(""), 20},
- {AUTO_STR("Package Name"), 382},
+ {AUTO_STR(""), 22},
+ {AUTO_STR("Package Name"), 380},
{AUTO_STR("Category"), 150},
{AUTO_STR("Version"), 80},
{AUTO_STR("Author"), 105},
{AUTO_STR("Type"), 70},
});
- m_list->onActivate([=] { history(entryAt(m_list->itemUnderMouse())); });
-
+ m_list->onActivate([=] { history(m_list->itemUnderMouse()); });
m_list->sortByColumn(1);
reload();
+
m_filterTimer = startTimer(200);
}
void Browser::onCommand(const int id)
{
+ namespace arg = std::placeholders;
+
switch(id) {
case IDC_DISPLAY:
case IDC_SCRIPTS:
@@ -99,19 +114,53 @@ void Browser::onCommand(const int id)
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));
+ break;
+ case ACTION_REINSTALL:
+ reinstall(m_currentIndex);
+ break;
+ case ACTION_REINSTALL_ALL:
+ selectionDo(bind(&Browser::reinstall, this, arg::_1));
+ break;
+ case ACTION_UNINSTALL:
+ uninstall(m_currentIndex);
+ break;
+ case ACTION_UNINSTALL_ALL:
+ selectionDo(bind(&Browser::uninstall, this, arg::_1));
+ break;
case ACTION_HISTORY:
- history(m_currentEntry);
+ history(m_currentIndex);
break;
case ACTION_ABOUT:
- about(m_currentEntry);
+ about(m_currentIndex);
+ break;
+ case ACTION_RESET_ALL:
+ selectionDo(bind(&Browser::resetAction, this, arg::_1));
break;
case IDOK:
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())
@@ -119,71 +168,102 @@ void Browser::onContextMenu(HWND target, const int x, const int y)
SetFocus(m_list->handle());
- Menu menu;
-
- if(const Entry *entry = entryAt(m_list->itemUnderMouse()))
- entryMenu(menu, *entry);
+ m_currentIndex = m_list->itemUnderMouse();
+ const Entry *entry = getEntry(m_currentIndex);
- menu.show(x, y, handle());
-}
+ if(!entry)
+ return;
-void Browser::entryMenu(Menu &menu, const Entry &entry)
-{
- m_currentEntry = &entry;
+ Menu menu;
- if(entry.test(InstalledFlag)) {
- if(entry.test(OutOfDateFlag)) {
+ if(entry->test(InstalledFlag)) {
+ if(entry->test(OutOfDateFlag)) {
auto_char installLabel[255] = {};
auto_snprintf(installLabel, sizeof(installLabel), AUTO_STR("&Update to v%s"),
- make_autostring(entry.latest->name()).c_str());
+ make_autostring(entry->latest->name()).c_str());
- menu.addAction(installLabel, 0);
+ 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());
+ make_autostring(entry->regEntry.versionName).c_str());
- menu.setEnabled(!entry.test(ObsoleteFlag),
- menu.addAction(reinstallLabel, 0));
+ 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());
+ make_autostring(entry->latest->name()).c_str());
- menu.addAction(installLabel, 0);
+ 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))
+ if(entry->test(ObsoleteFlag))
menu.disable(versionIndex);
else {
- const auto &versions = entry.package->versions();
- for(const Version *ver : versions | boost::adaptors::reversed)
- versionMenu.addAction(make_autostring(ver->name()).c_str(), 0);
+ const auto &versions = entry->package->versions();
+ int verIndex = 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);
+ }
}
- menu.setEnabled(entry.test(InstalledFlag),
- menu.addAction(AUTO_STR("&Uninstall"), 0));
+ 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.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));
+ 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::onTimer(const int id)
+void Browser::selectionMenu()
{
- if(id == m_filterTimer)
- checkFilter();
+ RECT rect;
+ GetWindowRect(m_action, &rect);
+
+ const Entry *entry = nullptr;
+ if(m_list->selectionSize() == 1)
+ entry = getEntry(m_list->currentIndex());
+
+ Menu menu;
+
+ menu.addAction("&Install/update selection", ACTION_LATEST_ALL);
+ menu.addAction("&Reinstall selection", ACTION_REINSTALL_ALL);
+ menu.addAction("&Uninstall selection", ACTION_UNINSTALL_ALL);
+ menu.addAction("&Clear queued action", ACTION_RESET_ALL);
+
+ if(!m_list->hasSelection())
+ menu.disableAll();
+
+ menu.show(rect.left, rect.bottom - 1, handle());
}
void Browser::checkFilter()
@@ -210,25 +290,13 @@ void Browser::reload()
try {
Registry reg(Path::prefixRoot(Path::REGISTRY));
- m_currentEntry = nullptr;
+ m_currentIndex = -1;
+ m_actions.clear();
m_entries.clear();
for(IndexPtr index : m_indexes) {
- for(const Package *pkg : index->packages()) {
- const Version *latest = pkg->lastVersion();
- const Registry::Entry ®Entry = reg.getEntry(pkg);
- int flags = 0;
-
- if(regEntry.id) {
- flags |= InstalledFlag;
- if(regEntry.versionCode < latest->code())
- flags |= OutOfDateFlag;
- }
- else
- flags |= UninstalledFlag;
-
- m_entries.push_back({flags, regEntry, pkg, latest});
- }
+ 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())) {
@@ -255,6 +323,33 @@ void Browser::reload()
}
}
+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);
@@ -299,17 +394,19 @@ string Browser::getValue(const Column col, const Entry &entry) const
string display;
switch(col) {
- case StateColumn:
+ 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';
+ display += 'i';
+
+ if(hasAction(&entry))
+ display += isTarget(&entry, nullptr) ? 'U' : 'I';
return display;
+ }
case NameColumn:
return pkg ? pkg->name() : regEntry.package;
case CategoryColumn:
@@ -339,12 +436,16 @@ bool Browser::match(const Entry &entry) const
{
using namespace boost;
- enum Display { All, Installed, OutOfDate, Uninstalled, Obsolete };
+ enum Display { All, Queued, Installed, OutOfDate, Uninstalled, Obsolete };
Display display = (Display)SendMessage(m_display, CB_GETCURSEL, 0, 0);
switch(display) {
case All:
break;
+ case Queued:
+ if(!hasAction(&entry))
+ return false;
+ break;
case Installed:
if(!entry.test(InstalledFlag))
return false;
@@ -380,7 +481,7 @@ bool Browser::match(const Entry &entry) const
icontains(author, m_filter);
}
-auto Browser::entryAt(const int listIndex) const -> const Entry *
+auto Browser::getEntry(const int listIndex) const -> const Entry *
{
if(listIndex < 0 || listIndex >= (int)m_visibleEntries.size())
return nullptr;
@@ -388,14 +489,99 @@ auto Browser::entryAt(const int listIndex) const -> const Entry *
return &m_entries[m_visibleEntries[listIndex]];
}
-void Browser::history(const Entry *entry) const
+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 Entry *entry) const
+void Browser::about(const int index) const
{
- if(entry)
+ if(const Entry *entry = getEntry(index))
m_reapack->about(getValue(RemoteColumn, *entry), handle());
}
+
+void Browser::installLatest(const int index)
+{
+ const Entry *entry = getEntry(index);
+
+ if(entry && entry->latest && entry->latest != entry->current)
+ setAction(index, entry->latest);
+}
+
+void Browser::reinstall(const int index)
+{
+ const Entry *entry = getEntry(index);
+
+ if(entry && entry->current)
+ setAction(index, entry->current);
+}
+
+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 Entry *entry = getEntry(index);
+
+ if(entry && entry->test(InstalledFlag))
+ setAction(index, nullptr);
+}
+
+void Browser::resetAction(const int index)
+{
+ const Entry *entry = getEntry(index);
+ const auto it = m_actions.find(entry);
+
+ if(it != m_actions.end()) {
+ m_actions.erase(it);
+ m_list->replaceRow(index, makeRow(*entry));
+ }
+}
+
+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 Entry *entry = getEntry(index);
+
+ if(isTarget(entry, target))
+ resetAction(index);
+ else if(entry) {
+ m_actions[entry] = target;
+ m_list->replaceRow(index, makeRow(*entry));
+ }
+}
+
+void Browser::selectionDo(const std::function<void (int)> &func)
+{
+ for(const int index : m_list->selection())
+ func(index);
+}
+
+void Browser::apply()
+{
+}
diff --git a/src/browser.hpp b/src/browser.hpp
@@ -23,6 +23,7 @@
#include "listview.hpp"
#include "registry.hpp"
+#include <functional>
#include <map>
#include <memory>
#include <string>
@@ -30,7 +31,6 @@
class Index;
class ListView;
-class Menu;
class ReaPack;
class Version;
@@ -60,6 +60,7 @@ private:
Registry::Entry regEntry;
const Package *package;
const Version *latest;
+ const Version *current;
bool test(Flag f) const { return flags & f; }
};
@@ -74,31 +75,45 @@ private:
RemoteColumn,
};
+ 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 *entryAt(int) const;
- void entryMenu(Menu &, const Entry &);
-
- void history(const Entry *) const;
- void about(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 *);
+ void selectionDo(const std::function<void (int)> &);
+ void apply();
+
+ void installLatest(int index);
+ void reinstall(int index);
+ void installVersion(int index, size_t verIndex);
+ void uninstall(int index);
+ 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;
- const Entry *m_currentEntry;
+ 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;
};
#endif
diff --git a/src/menu.cpp b/src/menu.cpp
@@ -118,10 +118,42 @@ void Menu::setEnabled(const bool enabled, const UINT index)
MENUITEMINFO mii{};
mii.cbSize = sizeof(MENUITEMINFO);
- GetMenuItemInfo(m_handle, index, true, &mii);
+ 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::check(const UINT index)
+{
+ MENUITEMINFO mii{};
+ mii.cbSize = sizeof(MENUITEMINFO);
+
+ if(!GetMenuItemInfo(m_handle, index, true, &mii))
+ return;
+
+ mii.fMask |= MIIM_STATE;
+ mii.fState |= MFS_CHECKED;
+
+ SetMenuItemInfo(m_handle, index, true, &mii);
+}
+
+void Menu::checkRadio(const UINT index)
+{
+ MENUITEMINFO mii{};
+ mii.cbSize = sizeof(MENUITEMINFO);
+
+ if(!GetMenuItemInfo(m_handle, index, true, &mii))
+ return;
+
+ mii.fMask |= MIIM_TYPE;
+ mii.fType |= MFT_RADIOCHECK;
+
+ mii.fMask |= MIIM_STATE;
+ mii.fState |= MFS_CHECKED;
+
+ SetMenuItemInfo(m_handle, index, true, &mii);
+}
diff --git a/src/menu.hpp b/src/menu.hpp
@@ -31,20 +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 enableAll();
- void enable(unsigned int);
+ void enable(UINT);
void disableAll();
- void disable(unsigned int);
- void setEnabled(bool, unsigned int);
+ void disable(UINT);
+ void setEnabled(bool, UINT);
+
+ void check(UINT);
+ void checkRadio(UINT);
private:
void append(MENUITEMINFO &);
diff --git a/src/resource.hpp b/src/resource.hpp
@@ -66,5 +66,6 @@
#define IDC_DISPLAY 228
#define IDC_SELECT 229
#define IDC_UNSELECT 230
+#define IDC_ACTION 231
#endif
diff --git a/src/resource.rc b/src/resource.rc
@@ -109,7 +109,8 @@ BEGIN
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 "&Close", IDCANCEL, 411, 230, 40, 14
+ PUSHBUTTON "&Cancel", IDCANCEL, 411, 230, 40, 14
PUSHBUTTON "&Apply", IDAPPLY, 454, 230, 40, 14
END