reapack

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

commit facf79a5f51684ec8a1d20ecd72be2d77e4daf1c
parent 16ace30d78acfa34dd6ffc49dfaa746fb4b9cfd3
Author: cfillion <cfillion@users.noreply.github.com>
Date:   Wed,  5 Oct 2016 01:41:12 -0400

support multiple action list sections for scripts

Diffstat:
Msrc/about.cpp | 4++--
Msrc/index_v1.cpp | 12++++++++++--
Msrc/registry.cpp | 6+++---
Msrc/registry.hpp | 2+-
Msrc/source.cpp | 38+++++++++++++++++++++++++++++++++++++-
Msrc/source.hpp | 16+++++++++++++---
Msrc/task.cpp | 6++----
Msrc/transaction.cpp | 51++++++++++++++++++++++++++++++++++-----------------
Mtest/index_v1.cpp | 14++++++++++++--
Atest/indexes/v1/ReaPack/cache/explicit_sections.xml | 9+++++++++
Mtest/indexes/v1/ReaPack/cache/valid_index.xml | 2+-
Mtest/registry.cpp | 8++++----
Mtest/source.cpp | 44+++++++++++++++++++++++++++++++++++++++++---
13 files changed, 169 insertions(+), 43 deletions(-)

diff --git a/src/about.cpp b/src/about.cpp @@ -323,7 +323,7 @@ void AboutIndexDelegate::initInstalledFiles() for(const Registry::File &file : allFiles) { stream << file.path.join(); - if(file.main) + if(file.sections) // is this file registered in the action list? stream << '*'; stream << "\r\n"; } @@ -493,7 +493,7 @@ void AboutPackageDelegate::updateList(const int index) for(const Source *src : ver->sources()) { string actionList; - if(src->isMain() && src->type() == Package::ScriptType) + if(src->sections() && src->type() == Package::ScriptType) actionList = "Yes"; else actionList = "No"; diff --git a/src/index_v1.cpp b/src/index_v1.cpp @@ -19,6 +19,7 @@ #include "errors.hpp" +#include <sstream> #include <WDL/tinyxml/tinyxml.h> using namespace std; @@ -175,6 +176,9 @@ void LoadSourceV1(TiXmlElement *node, Version *ver) const char *file = node->Attribute("file"); if(!file) file = ""; + const char *main = node->Attribute("main"); + if(!main) main = ""; + const char *url = node->GetText(); if(!url) url = ""; @@ -184,8 +188,12 @@ void LoadSourceV1(TiXmlElement *node, Version *ver) src->setPlatform(platform); src->setTypeOverride(Package::getType(type)); - if(node->Attribute("main")) - src->setMain(true); + int sections = 0; + string section; + istringstream mainStream(main); + while(getline(mainStream, section, '\x20')) + sections |= Source::getSection(section.c_str()); + src->setSections(sections); if(ver->addSource(src)) ptr.release(); diff --git a/src/registry.cpp b/src/registry.cpp @@ -178,7 +178,7 @@ auto Registry::push(const Version *ver, vector<Path> *conflicts) -> Entry m_insertFile->bind(1, entryId); m_insertFile->bind(2, path.join('/')); - m_insertFile->bind(3, src->isMain()); + m_insertFile->bind(3, src->sections()); m_insertFile->bind(4, src->typeOverride()); try { @@ -259,7 +259,7 @@ auto Registry::getFiles(const Entry &entry) const -> vector<File> m_getFiles->bind(1, entry.id); m_getFiles->exec([&] { File file{m_getFiles->stringColumn(0)}; - file.main = m_getFiles->boolColumn(1); + file.sections = m_getFiles->intColumn(1); file.type = static_cast<Package::Type>(m_getFiles->intColumn(2)); if(!file.type) // < v1.0rc2 @@ -281,7 +281,7 @@ auto Registry::getMainFiles(const Entry &entry) const -> vector<File> vector<File> mainFiles; copy_if(allFiles.begin(), allFiles.end(), - back_inserter(mainFiles), [&](const File &f) { return f.main; }); + back_inserter(mainFiles), [&](const File &f) { return f.sections; }); return mainFiles; } diff --git a/src/registry.hpp b/src/registry.hpp @@ -44,7 +44,7 @@ public: struct File { Path path; - bool main; + int sections; Package::Type type; bool operator<(const File &o) const { return path < o.path; } diff --git a/src/source.cpp b/src/source.cpp @@ -20,10 +20,24 @@ #include "errors.hpp" #include "index.hpp" +#include <boost/algorithm/string.hpp> + using namespace std; +auto Source::getSection(const char *name) -> Section +{ + if(!strcmp(name, "main")) + return MainSection; + else if(!strcmp(name, "midi_editor")) + return MIDIEditorSection; + else if(!strcmp(name, "true")) + return ImplicitSection; + else + return UnknownSection; +} + Source::Source(const string &file, const string &url, const Version *ver) - : m_type(Package::UnknownType), m_file(file), m_url(url), m_main(false), + : m_type(Package::UnknownType), m_file(file), m_url(url), m_sections(0), m_version(ver) { if(m_url.empty()) @@ -56,6 +70,28 @@ const string &Source::file() const throw reapack_error("empty source file name and no package"); } +void Source::setSections(int sections) +{ + if(sections == ImplicitSection) { + // for compatibility with v1.0 + + const Package *pkg = package(); + const Category *cat = pkg ? pkg->category() : nullptr; + if(!cat) + throw reapack_error("cannot resolve implicit section: category is unset"); + + string category = Path(cat->name()).first(); + boost::algorithm::to_lower(category); + + if(category == "midi editor") + sections = MIDIEditorSection; + else + sections = MainSection; + } + + m_sections = sections; +} + string Source::fullName() const { if(!m_version) diff --git a/src/source.hpp b/src/source.hpp @@ -27,6 +27,16 @@ class Version; class Source { public: + enum Section { + UnknownSection = 0, + MainSection = 1<<0, + MIDIEditorSection = 1<<1, + + ImplicitSection = -1, // for compatibility with v1.0 + }; + + static Section getSection(const char *); + Source(const std::string &file, const std::string &url, const Version * = nullptr); @@ -38,8 +48,8 @@ public: Package::Type type() const; const std::string &file() const; const std::string &url() const { return m_url; } - void setMain(bool main) { m_main = main; } - bool isMain() const { return m_main; } + void setSections(int); + int sections() const { return m_sections; } const Version *version() const { return m_version; } const Package *package() const; @@ -52,7 +62,7 @@ private: Package::Type m_type; std::string m_file; std::string m_url; - bool m_main; + int m_sections; const Version *m_version; }; diff --git a/src/task.cpp b/src/task.cpp @@ -121,8 +121,7 @@ void InstallTask::commit() if(FS::remove(file.path)) tx()->receipt()->addRemoval(file.path); - if(file.main) - tx()->registerFile({false, m_oldEntry, file}); + tx()->registerFile({false, m_oldEntry, file}); } InstallTicket::Type type; @@ -179,8 +178,7 @@ void UninstallTask::commit() else tx()->receipt()->addError({FS::lastError(), file.path.join()}); - if(file.main) - tx()->registerFile({false, m_entry, file}); + tx()->registerFile({false, m_entry, file}); } tx()->registry()->forget(m_entry); diff --git a/src/transaction.cpp b/src/transaction.cpp @@ -25,8 +25,6 @@ #include "remote.hpp" #include "task.hpp" -#include <boost/algorithm/string.hpp> - #include <reaper_plugin_functions.h> using namespace std; @@ -308,26 +306,45 @@ void Transaction::registerQueued() } } -void Transaction::registerScript(const HostTicket &reg, const bool isLast) +void Transaction::registerScript(const HostTicket &reg, const bool isLastCall) { - enum Section { MainSection = 0, MidiEditorSection = 32060 }; + static const map<Source::Section, int> sectionMap{ + {Source::MainSection, 0}, + {Source::MIDIEditorSection, 32060}, + }; - if(!AddRemoveReaScript) - return; // do nothing if REAPER < v5.12 + if(!AddRemoveReaScript || !reg.file.sections) + return; // do nothing if REAPER < v5.12 and skip non-main files - Section section; - string category = Path(reg.entry.category).first(); - boost::algorithm::to_lower(category); + const string &fullPath = Path::prefixRoot(reg.file.path).join(); - if(category == "midi editor") - section = MidiEditorSection; - else - section = MainSection; + vector<int> sections; - const string &fullPath = Path::prefixRoot(reg.file.path).join(); - if(!AddRemoveReaScript(reg.add, section, fullPath.c_str(), isLast) && reg.add) { - m_receipt.addError({"This script could not be registered in REAPER.", - reg.file.path.join()}); + for(const auto &pair : sectionMap) { + if(reg.file.sections & pair.first) + sections.push_back(pair.second); + } + + assert(!sections.empty()); // is a section missing in sectionMap? + + bool enableError = reg.add; + auto it = sections.begin(); + + while(true) { + const int section = *it++; + const bool isLastSection = it == sections.end(); + + int id = AddRemoveReaScript(reg.add, section, fullPath.c_str(), + isLastCall && isLastSection); + + if(!id && enableError) { + m_receipt.addError({"This script could not be registered in REAPER.", + reg.file.path.join()}); + enableError = false; + } + + if(isLastSection) + break; } } diff --git a/test/index_v1.cpp b/test/index_v1.cpp @@ -174,13 +174,13 @@ TEST_CASE("full index", M) { const Source *source1 = ver->source(0); REQUIRE(source1->platform() == Platform::GenericPlatform); REQUIRE(source1->file() == "test.lua"); - REQUIRE(source1->isMain()); + REQUIRE(source1->sections() == Source::MainSection); REQUIRE(source1->url() == "https://google.com/"); const Source *source2 = ver->source(1); REQUIRE(source2->platform() == Platform::GenericPlatform); REQUIRE(source2->file() == "background.png"); - REQUIRE_FALSE(source2->isMain()); + REQUIRE_FALSE(source2->sections() == Source::MainSection); REQUIRE(source2->url() == "http://cfillion.tk/"); } @@ -293,3 +293,13 @@ TEST_CASE("read package description", M) { CHECK(ri->packages().size() == 1); REQUIRE(ri->category(0)->package(0)->description() == "From the New World"); } + +TEST_CASE("read multiple sections", M) { + UseRootPath root(RIPATH); + + IndexPtr ri = Index::load("explicit_sections"); + + CHECK(ri->packages().size() == 1); + REQUIRE(ri->category(0)->package(0)->version(0)->source(0)->sections() + == (Source::MainSection | Source::MIDIEditorSection)); +} diff --git a/test/indexes/v1/ReaPack/cache/explicit_sections.xml b/test/indexes/v1/ReaPack/cache/explicit_sections.xml @@ -0,0 +1,9 @@ +<index version="1"> + <category name="MIDI Editor"> + <reapack name="packname" type="script"> + <version name="1.0"> + <source main=" main midi_editor HELLO ">https://google.com/</source> + </version> + </reapack> + </category> +</index> diff --git a/test/indexes/v1/ReaPack/cache/valid_index.xml b/test/indexes/v1/ReaPack/cache/valid_index.xml @@ -2,7 +2,7 @@ <category name="Category Name"> <reapack name="Hello World.lua" type="script"> <version name="1.0"> - <source platform="all" file="test.lua" main="true">https://google.com/</source> + <source platform="all" file="test.lua" main="main">https://google.com/</source> <source platform="all" file="background.png">http://cfillion.tk/</source> <changelog>Fixed a division by zero error.</changelog> </version> diff --git a/test/registry.cpp b/test/registry.cpp @@ -92,7 +92,7 @@ TEST_CASE("get file list", M) { const vector<Registry::File> &files = reg.getFiles(reg.getEntry(&pkg)); REQUIRE(files.size() == 1); REQUIRE(files[0].path == src->targetPath()); - REQUIRE(files[0].main == false); + REQUIRE(files[0].sections == 0); REQUIRE(files[0].type == pkg.type()); } @@ -170,12 +170,12 @@ TEST_CASE("get main files", M) { REQUIRE((reg.getMainFiles({})).empty()); Source *main1 = new Source({}, "url", &ver); - main1->setMain(true); + main1->setSections(Source::MIDIEditorSection); main1->setTypeOverride(Package::EffectType); ver.addSource(main1); Source *main2 = new Source({}, "url", &ver); // duplicate file ignored - main2->setMain(true); + main2->setSections(Source::MainSection); main2->setTypeOverride(Package::EffectType); ver.addSource(main2); @@ -184,7 +184,7 @@ TEST_CASE("get main files", M) { const vector<Registry::File> &current = reg.getMainFiles(entry); REQUIRE(current.size() == 1); REQUIRE(current[0].path == main1->targetPath()); - REQUIRE(current[0].main == true); + REQUIRE(current[0].sections == Source::MIDIEditorSection); REQUIRE(current[0].type == Package::EffectType); } diff --git a/test/source.cpp b/test/source.cpp @@ -55,12 +55,50 @@ TEST_CASE("empty source file name and no package", M) { } } +TEST_CASE("parse file section", M) { + REQUIRE(-1 == Source::getSection("true")); + REQUIRE(0 == Source::getSection("hello")); + REQUIRE(Source::MainSection == Source::getSection("main")); + REQUIRE(Source::MIDIEditorSection == Source::getSection("midi_editor")); +} + TEST_CASE("main source", M) { Source source("filename", "url"); - REQUIRE_FALSE(source.isMain()); + REQUIRE(source.sections() == 0); + + source.setSections(Source::MainSection | Source::MIDIEditorSection); + REQUIRE(source.sections() == (Source::MainSection | Source::MIDIEditorSection)); +} + +TEST_CASE("implicit source section") { + SECTION("main") { + Category cat("Category Name"); + Package pack(Package::UnknownType, "package name", &cat); + Version ver("1.0", &pack); + + Source source("filename", "url", &ver); + source.setSections(Source::ImplicitSection); + REQUIRE(source.sections() == Source::MainSection); + } - source.setMain(true); - REQUIRE(source.isMain()); + SECTION("midi editor") { + Category cat("midi Editor/somthing else"); + Package pack(Package::UnknownType, "package name", &cat); + Version ver("1.0", &pack); + + Source source("filename", "url", &ver); + source.setSections(Source::ImplicitSection); + REQUIRE(source.sections() == Source::MIDIEditorSection); + } + + SECTION("no category") { + Source source("filename", "url"); + try { + source.setSections(Source::ImplicitSection); + FAIL(); // should throw (or crash if buggy, but not do nothing) + } + catch(const reapack_error &) {} + } } TEST_CASE("empty source url", M) {