commit 7a20e8ff2130a5c11f90e2942dcbed66a3dfe123
parent 06e61c2dfcb1b660ff24da0ff2a3f7ac0b0410dd
Author: cfillion <cfillion@users.noreply.github.com>
Date: Wed, 16 Mar 2016 15:19:53 -0400
create the Package Browser dialog
Diffstat:
15 files changed, 325 insertions(+), 13 deletions(-)
diff --git a/src/about.cpp b/src/about.cpp
@@ -192,7 +192,7 @@ 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});
diff --git a/src/browser.cpp b/src/browser.cpp
@@ -0,0 +1,151 @@
+/* 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 "index.hpp"
+#include "listview.hpp"
+#include "reapack.hpp"
+#include "resource.hpp"
+
+#include <boost/algorithm/string.hpp>
+
+using namespace std;
+
+Browser::Browser(const vector<IndexPtr> &indexes, ReaPack *reapack)
+ : Dialog(IDD_BROWSER_DIALOG), m_indexes(indexes), m_reapack(reapack),
+ m_checkFilter(false)
+{
+}
+
+void Browser::onInit()
+{
+ m_filterHandle = getControl(IDC_FILTER);
+
+ 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(""), 20},
+ {AUTO_STR("Package Name"), 382},
+ {AUTO_STR("Category"), 150},
+ {AUTO_STR("Version"), 80},
+ {AUTO_STR("Author"), 105},
+ {AUTO_STR("Type"), 70},
+ });
+
+ m_list->sortByColumn(1);
+
+ reload();
+ m_reloadTimer = startTimer(200);
+}
+
+void Browser::onCommand(const int id)
+{
+ switch(id) {
+ case IDC_SCRIPTS:
+ case IDC_EFFECTS:
+ case IDC_EXTENSIONS:
+ reload();
+ break;
+ case IDC_FILTER:
+ m_checkFilter = true;
+ break;
+ case IDC_CLEAR:
+ SetWindowText(m_filterHandle, AUTO_STR(""));
+ checkFilter();
+ break;
+ case IDOK:
+ case IDCANCEL:
+ close();
+ break;
+ }
+}
+
+void Browser::onContextMenu(HWND, const int, const int)
+{
+ (void)m_reapack;
+}
+
+void Browser::onTimer(const int id)
+{
+ if(id != m_reloadTimer || !m_checkFilter)
+ return;
+
+ checkFilter();
+}
+
+void Browser::checkFilter()
+{
+ 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;
+ reload();
+ }
+}
+
+void Browser::reload()
+{
+ InhibitControl freeze(m_list);
+
+ m_list->clear();
+
+ for(IndexPtr index : m_indexes) {
+ for(const Package *pkg : index->packages()) {
+ const Version *ver = pkg->lastVersion();
+
+ if(!match(ver))
+ continue;
+
+ m_list->addRow({"??", pkg->name(), pkg->category()->name(),
+ ver->name(), ver->displayAuthor(), pkg->displayType()});
+ }
+ }
+
+ m_list->sort();
+}
+
+bool Browser::match(const Version *ver)
+{
+ using namespace boost;
+
+ const Package *pkg = ver->package();
+
+ const auto typeIt = m_types.find(pkg->type());
+
+ if(typeIt == m_types.end() ||
+ SendMessage(typeIt->second, BM_GETCHECK, 0, 0) == BST_UNCHECKED)
+ return false;
+
+ return icontains(pkg->name(), m_filter) ||
+ icontains(pkg->category()->name(), m_filter) ||
+ icontains(ver->author(), m_filter);
+}
diff --git a/src/browser.hpp b/src/browser.hpp
@@ -0,0 +1,61 @@
+/* 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 <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(int) override;
+ void onContextMenu(HWND, int x, int y) override;
+ void onTimer(int) override;
+
+private:
+ bool match(const Version *);
+ void checkFilter();
+
+ std::vector<IndexPtr> m_indexes;
+ ReaPack *m_reapack;
+ bool m_checkFilter;
+ int m_reloadTimer;
+ std::string m_filter;
+
+ HWND m_filterHandle;
+ std::map<int, HWND> m_types;
+ ListView *m_list;
+};
+
+#endif
diff --git a/src/main.cpp b/src/main.cpp
@@ -81,6 +81,9 @@ static void menuHook(const char *name, HMENU handle, int f)
menu.addAction(AUTO_STR("&Synchronize packages"),
NamedCommandLookup("_REAPACK_SYNC"));
+ menu.addAction(AUTO_STR("&Browse packages..."),
+ NamedCommandLookup("_REAPACK_BROWSE"));
+
menu.addAction(AUTO_STR("&Clean up packages..."),
NamedCommandLookup("_REAPACK_CLEANUP"));
@@ -159,6 +162,9 @@ 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_BROWSE", "ReaPack: Browse packages...",
+ &reapack->browseAction, bind(&ReaPack::browsePackages, reapack));
+
reapack->setupAction("REAPACK_CLEANUP", "ReaPack: Clean up packages...",
&reapack->cleanupAction, bind(&ReaPack::cleanupPackages, reapack));
diff --git a/src/package.cpp b/src/package.cpp
@@ -48,6 +48,20 @@ string Package::fullName() const
return m_category ? m_category->fullName() + "/" + m_name : m_name;
}
+string Package::displayType() const
+{
+ switch(m_type) {
+ case UnknownType:
+ return "Unknown";
+ case ScriptType:
+ return "Script";
+ case ExtensionType:
+ return "Extension";
+ case EffectType:
+ return "Effect";
+ }
+}
+
Package::~Package()
{
for(const Version *ver : m_versions)
diff --git a/src/package.hpp b/src/package.hpp
@@ -42,6 +42,7 @@ public:
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/reapack.cpp b/src/reapack.cpp
@@ -18,6 +18,7 @@
#include "reapack.hpp"
#include "about.hpp"
+#include "browser.hpp"
#include "cleanup.hpp"
#include "config.hpp"
#include "errors.hpp"
@@ -62,9 +63,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(), cleanupAction(), importAction(), configAction(),
+ m_transaction(nullptr), m_progress(nullptr), m_browser(nullptr),
+ m_cleanup(nullptr), m_import(nullptr), m_manager(nullptr), m_instance(instance)
{
m_mainWindow = GetMainHwnd();
m_useRootPath = new UseRootPath(GetResourcePath());
@@ -333,6 +334,32 @@ void ReaPack::cleanupPackages()
});
}
+void ReaPack::browsePackages()
+{
+ 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.", "Browse packages", MB_OK
+ );
+ return;
+ }
+
+ const vector<Remote> &remotes = m_config->remotes()->getEnabled();
+
+ fetchIndexes(remotes, [=] (const vector<IndexPtr> &indexes) {
+ 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;
+ });
+ });
+}
+
void ReaPack::fetchIndex(const Remote &remote,
const IndexCallback &callback, HWND parent)
{
diff --git a/src/reapack.hpp b/src/reapack.hpp
@@ -28,6 +28,7 @@
#include <reaper_plugin.h>
+class Browser;
class Cleanup;
class Config;
class DownloadQueue;
@@ -50,6 +51,7 @@ public:
static const std::string BUILDTIME;
gaccel_register_t syncAction;
+ gaccel_register_t browseAction;
gaccel_register_t cleanupAction;
gaccel_register_t importAction;
gaccel_register_t configAction;
@@ -74,6 +76,7 @@ public:
void aboutSelf();
void about(const Remote &, HWND parent);
void cleanupPackages();
+ void browsePackages();
void fetchIndexes(const std::vector<Remote> &,
const IndexesCallback &, HWND = nullptr);
@@ -94,9 +97,10 @@ private:
Config *m_config;
Transaction *m_transaction;
Progress *m_progress;
- Manager *m_manager;
- Import *m_import;
+ Browser *m_browser;
Cleanup *m_cleanup;
+ Import *m_import;
+ Manager *m_manager;
REAPER_PLUGIN_HINSTANCE m_instance;
HWND m_mainWindow;
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;
diff --git a/src/resource.hpp b/src/resource.hpp
@@ -40,6 +40,7 @@
#define IDD_ABOUT_DIALOG 103
#define IDD_IMPORT_DIALOG 104
#define IDD_CLEANUP_DIALOG 105
+#define IDD_BROWSER_DIALOG 106
#define IDC_LABEL 200
#define IDC_LABEL2 201
@@ -57,5 +58,10 @@
#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
#endif
diff --git a/src/resource.rc b/src/resource.rc
@@ -90,3 +90,22 @@ BEGIN
DEFPUSHBUTTON "&Uninstall selected packages", IDOK, 176, 180, 120, 14
PUSHBUTTON "&Cancel", IDCANCEL, 299, 180, 45, 14
END
+
+IDD_BROWSER_DIALOG DIALOGEX 0, 0, 500, 250
+STYLE DIALOG_STYLE
+FONT DIALOG_FONT
+CAPTION "ReaPack: Package Browser"
+BEGIN
+ LTEXT "Filter:", IDC_LABEL, 5, 7, 20, 10
+ EDITTEXT IDC_FILTER, 25, 4, 280, 14, ES_AUTOHSCROLL
+ PUSHBUTTON "Cl&ear", IDC_CLEAR, 307, 4, 32, 14
+ LTEXT "Type:", IDC_LABEL, 360, 7, 20, 10
+ CHECKBOX "&Scripts", IDC_SCRIPTS, 380, 4, 40, 14
+ CHECKBOX "&Effects", IDC_EFFECTS, 415, 4, 40, 14
+ CHECKBOX "&Extensions", IDC_EXTENSIONS, 450, 4, 50, 14
+ CONTROL "", IDC_PACKAGES, WC_LISTVIEW, LVS_REPORT | LVS_SHOWSELALWAYS |
+ WS_BORDER | WS_TABSTOP, 5, 22, 490, 204
+ PUSHBUTTON "&OK", IDCANCEL, 368, 230, 40, 14
+ PUSHBUTTON "&Close", IDCANCEL, 411, 230, 40, 14
+ DEFPUSHBUTTON "&Apply", IDAPPLY, 454, 230, 40, 14
+END
diff --git a/src/version.cpp b/src/version.cpp
@@ -76,6 +76,14 @@ string Version::fullName() const
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,20 @@ 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(Package::UnknownType, "test").displayType());
+
+ SECTION("script")
+ REQUIRE("Script" == Package(Package::ScriptType, "test").displayType());
+
+ SECTION("extension")
+ REQUIRE("Extension" == Package(Package::ExtensionType, "test").displayType());
+
+ SECTION("effect")
+ REQUIRE("Effect" == Package(Package::EffectType, "test").displayType());
+}
+
TEST_CASE("empty package name", M) {
try {
Package pack(Package::ScriptType, string());
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
}
}