From 7f8455f7314b6070c188fcf8a9daa75dc3dd17f8 Mon Sep 17 00:00:00 2001 From: James Turner Date: Sun, 13 Nov 2016 12:03:44 +0000 Subject: [PATCH] Multi-thumbnail support in packages. Allow multiple thumbnails per variant / package, including tagging with types so we can require certain thumbnails in the future. --- simgear/misc/test_macros.hxx | 5 + simgear/package/CatalogTest.cxx | 39 +++++++- simgear/package/Package.cxx | 100 ++++++++++++++++---- simgear/package/Package.hxx | 40 +++++++- simgear/package/catalogTest1/catalog-v2.xml | 53 +++++++++++ simgear/package/catalogTest1/catalog.xml | 41 ++++++++ 6 files changed, 255 insertions(+), 23 deletions(-) diff --git a/simgear/misc/test_macros.hxx b/simgear/misc/test_macros.hxx index 3082eaf4..1fe545a2 100644 --- a/simgear/misc/test_macros.hxx +++ b/simgear/misc/test_macros.hxx @@ -35,4 +35,9 @@ exit(1); \ } +#define SG_TEST_FAIL(msg) \ + std::cerr << "failure:" << msg; \ + exit(1); + + #endif // of SG_MISC_TEST_MACROS_HXX diff --git a/simgear/package/CatalogTest.cxx b/simgear/package/CatalogTest.cxx index c4ca4575..8881fd3a 100644 --- a/simgear/package/CatalogTest.cxx +++ b/simgear/package/CatalogTest.cxx @@ -150,6 +150,43 @@ int parseTest() COMPARE(p2->qualifiedId(), "org.flightgear.test.catalog1.c172p"); COMPARE(p2->description(), "A plane made by Cessna"); + pkg::Package::ThumbnailVec thumbs = p2->thumbnailsForVariant(0); + COMPARE(thumbs.size(), 3); + + auto index = std::find_if(thumbs.begin(), thumbs.end(), [](const pkg::Package::Thumbnail& t) + { return (t.type == pkg::Package::Thumbnail::Type::EXTERIOR); }); + VERIFY(index != thumbs.end()); + COMPARE(index->path, "thumb-exterior.png"); + COMPARE(index->url, "http://foo.bar.com/thumb-exterior.png"); + VERIFY(index->type == pkg::Package::Thumbnail::Type::EXTERIOR); + + index = std::find_if(thumbs.begin(), thumbs.end(), [](const pkg::Package::Thumbnail& t) + { return (t.type == pkg::Package::Thumbnail::Type::PANEL); }); + VERIFY(index != thumbs.end()); + COMPARE(index->path, "thumb-panel.png"); + COMPARE(index->url, "http://foo.bar.com/thumb-panel.png"); + VERIFY(index->type == pkg::Package::Thumbnail::Type::PANEL); + +// test variants + try { + p2->indexOfVariant("fofofo"); + SG_TEST_FAIL("lookup of non-existant variant did not throw"); + } catch (sg_exception& e) { + // expected + } + + unsigned int skisVariant = p2->indexOfVariant("c172p-skis"); + VERIFY(skisVariant > 0); + + pkg::Package::ThumbnailVec thumbs2 = p2->thumbnailsForVariant(skisVariant); + COMPARE(thumbs2.size(), 2); + + index = std::find_if(thumbs2.begin(), thumbs2.end(), [](const pkg::Package::Thumbnail& t) + { return (t.type == pkg::Package::Thumbnail::Type::EXTERIOR); }); + VERIFY(index != thumbs2.end()); + COMPARE(index->path, "thumb-exterior-skis.png"); + COMPARE(index->url, "http://foo.bar.com/thumb-exterior-skis.png"); + VERIFY(index->type == pkg::Package::Thumbnail::Type::EXTERIOR); // test filtering / searching too @@ -454,7 +491,7 @@ int main(int argc, char* argv[]) parseTest(); testInstallPackage(&cl); - + testUninstall(&cl); testRemoveCatalog(&cl); diff --git a/simgear/package/Package.cxx b/simgear/package/Package.cxx index c8848d68..70ceb1ae 100644 --- a/simgear/package/Package.cxx +++ b/simgear/package/Package.cxx @@ -21,7 +21,7 @@ #include #include -#include +#include #include #include @@ -29,7 +29,7 @@ #include namespace simgear { - + namespace pkg { Package::Package(const SGPropertyNode* aProps, CatalogRef aCatalog) : @@ -103,7 +103,7 @@ bool Package::matches(const SGPropertyNode* aFilter) const else SG_LOG(SG_GENERAL, SG_WARN, "unknown filter term:" << filter_name); } // of filter props iteration - + return true; } @@ -127,7 +127,7 @@ InstallRef Package::install() if (ins) { return ins; } - + // start a new install ins = new Install(this, pathOnDisk()); m_catalog->root()->scheduleToUpdate(ins); @@ -190,10 +190,10 @@ unsigned int Package::revision() const if (!m_props) { return 0; } - + return m_props->getIntValue("revision"); } - + std::string Package::name() const { return m_props->getStringValue("name"); @@ -203,7 +203,7 @@ size_t Package::fileSizeBytes() const { return m_props->getIntValue("file-size-bytes"); } - + std::string Package::description() const { return getLocalisedProp("description"); @@ -213,7 +213,7 @@ string_set Package::tags() const { return m_tags; } - + SGPropertyNode* Package::properties() const { return m_props.ptr(); @@ -225,7 +225,7 @@ string_list Package::thumbnailUrls() const if (!m_props) { return r; } - + BOOST_FOREACH(SGPropertyNode* dl, m_props->getChildren("thumbnail")) { r.push_back(dl->getStringValue()); } @@ -238,20 +238,20 @@ string_list Package::thumbnails() const if (!m_props) { return r; } - + BOOST_FOREACH(SGPropertyNode* dl, m_props->getChildren("thumbnail-path")) { r.push_back(dl->getStringValue()); } return r; } - + string_list Package::downloadUrls() const { string_list r; if (!m_props) { return r; } - + BOOST_FOREACH(SGPropertyNode* dl, m_props->getChildren("url")) { r.push_back(dl->getStringValue()); } @@ -272,41 +272,41 @@ std::string Package::getLocalisedString(const SGPropertyNode* aRoot, const char* return localeRoot->getStringValue(aName); } } - + return aRoot->getStringValue(aName); } PackageList Package::dependencies() const { PackageList result; - + BOOST_FOREACH(SGPropertyNode* dep, m_props->getChildren("depends")) { std::string depName = dep->getStringValue("id"); unsigned int rev = dep->getIntValue("revision", 0); - + // prefer local hangar package if possible, in case someone does something // silly with naming. Of course flightgear's aircraft search doesn't know // about hangars, so names still need to be unique. PackageRef depPkg = m_catalog->getPackageById(depName); - if (!depPkg) { + if (!depPkg) { Root* rt = m_catalog->root(); depPkg = rt->getPackageById(depName); if (!depPkg) { throw sg_exception("Couldn't satisfy dependency of " + id() + " : " + depName); } } - + if (depPkg->revision() < rev) { throw sg_range_exception("Couldn't find suitable revision of " + depName); } - + // forbid recursive dependency graphs, we don't need that level // of complexity for aircraft resources assert(depPkg->dependencies() == PackageList()); - + result.push_back(depPkg); } - + return result; } @@ -338,6 +338,24 @@ std::string Package::nameForVariant(const std::string& vid) const throw sg_exception("Unknow variant +" + vid + " in package " + id()); } +unsigned int Package::indexOfVariant(const std::string& vid) const +{ + if (vid == id()) { + return 0; + } + + unsigned int result = 1; + for (SGPropertyNode* var : m_props->getChildren("variant")) { + if (var->getStringValue("id") == vid) { + return result; + } + + result++; + } + + throw sg_exception("Unknow variant " + vid + " in package " + id()); +} + std::string Package::nameForVariant(const unsigned int vIndex) const { if (vIndex == 0) @@ -351,6 +369,48 @@ std::string Package::nameForVariant(const unsigned int vIndex) const throw sg_exception("Unknow variant in package " + id()); } +Package::ThumbnailVec Package::thumbnailsForVariant(unsigned int vIndex) const +{ + if (vIndex == 0) { + return thumbnailsFromProps(m_props); + } + + SGPropertyNode_ptr var = m_props->getChild("variant", vIndex - 1); + if (!var) { + throw sg_exception("Unknow variant in package " + id()); + } + + return thumbnailsFromProps(var); +} + +Package::Thumbnail::Type thumbnailTypeFromString(const std::string& s) +{ + if (s == "exterior") return Package::Thumbnail::Type::EXTERIOR; + if (s == "interior") return Package::Thumbnail::Type::INTERIOR; + if (s == "panel") return Package::Thumbnail::Type::PANEL; + return Package::Thumbnail::Type::UNKNOWN; +} + +Package::Thumbnail::Thumbnail(const std::string& aUrl, const std::string& aPath, Type aType) : + url(aUrl), + path(aPath), + type(aType) +{ +} + +Package::ThumbnailVec Package::thumbnailsFromProps(const SGPropertyNode_ptr& ptr) const +{ + ThumbnailVec result; + + for (auto thumbNode : ptr->getChildren("thumbnail")) { + Thumbnail t(thumbNode->getStringValue("url"), + thumbNode->getStringValue("path"), + thumbnailTypeFromString(thumbNode->getStringValue("type"))); + result.push_back(t); + } + + return result; +} } // of namespace pkg diff --git a/simgear/package/Package.hxx b/simgear/package/Package.hxx index 840a22cd..0f1a82bf 100644 --- a/simgear/package/Package.hxx +++ b/simgear/package/Package.hxx @@ -80,7 +80,12 @@ public: * Fully-qualified ID, including our catalog'd ID */ std::string qualifiedVariantId(const unsigned int variantIndex) const; - + + /** + * + */ + unsigned int indexOfVariant(const std::string& vid) const; + /** * human-readable name - note this is probably not localised, * although this is not ruled out for the future. @@ -133,6 +138,35 @@ public: * thumbnail file paths within the package on disk */ string_list thumbnails() const; + + /** + * information about a thumbnail + */ + struct Thumbnail { + enum class Type + { + UNKNOWN, + PANEL, + INTERIOR, + EXTERIOR + + // NIGHT / GROUND as modifiers? does this add any + // actual value for GUIs? + }; + + Thumbnail(const std::string& url, const std::string& path, Type ty = Type::UNKNOWN); + + std::string url; + std::string path; + Type type = Type::UNKNOWN; + }; + + typedef std::vector ThumbnailVec; + + /** + * retrieve all the thumbnails for a variant + */ + ThumbnailVec thumbnailsForVariant(unsigned int vIndex) const; /** * Packages we depend upon. @@ -159,7 +193,9 @@ private: void updateFromProps(const SGPropertyNode* aProps); std::string getLocalisedString(const SGPropertyNode* aRoot, const char* aName) const; - + + ThumbnailVec thumbnailsFromProps(const SGPropertyNode_ptr& ptr) const; + SGPropertyNode_ptr m_props; std::string m_id; string_set m_tags; diff --git a/simgear/package/catalogTest1/catalog-v2.xml b/simgear/package/catalogTest1/catalog-v2.xml index a66ac7a5..87190d92 100644 --- a/simgear/package/catalogTest1/catalog-v2.xml +++ b/simgear/package/catalogTest1/catalog-v2.xml @@ -48,6 +48,23 @@ 10 + + exterior + thumb-exterior.png + http://foo.bar.com/thumb-exterior.png + + + + panel + thumb-panel.png + http://foo.bar.com/thumb-panel.png + + + + thumb-something.png + http://foo.bar.com/thumb-something.png + + c172p-2d-panel C172 with 2d panel only @@ -56,11 +73,35 @@ c172p-floats C172 with floats + + + exterior + thumb-exterior-floats.png + http://foo.bar.com/thumb-exterior-floats.png + + + + panel + thumb-panel.png + http://foo.bar.com/thumb-panel.png + c172p-skis C172 with skis + + + exterior + thumb-exterior-skis.png + http://foo.bar.com/thumb-exterior-skis.png + + + + panel + thumb-panel.png + http://foo.bar.com/thumb-panel.png + ec0e2ffdf98d6a5c05c77445e5447ff5 @@ -99,6 +140,18 @@ a94ca5704f305b90767f40617d194ed6 http://localhost:2000/catalogTest1/b737.tar.gz + + + exterior + thumb-exterior.png + http://foo.bar.com/thumb-exterior.png + + + + panel + thumb-panel.png + http://foo.bar.com/thumb-panel.png + diff --git a/simgear/package/catalogTest1/catalog.xml b/simgear/package/catalogTest1/catalog.xml index 5da1400f..ab8ab4f1 100644 --- a/simgear/package/catalogTest1/catalog.xml +++ b/simgear/package/catalogTest1/catalog.xml @@ -48,6 +48,23 @@ 10 + + exterior + thumb-exterior.png + http://foo.bar.com/thumb-exterior.png + + + + panel + thumb-panel.png + http://foo.bar.com/thumb-panel.png + + + + thumb-something.png + http://foo.bar.com/thumb-something.png + + c172p-2d-panel C172 with 2d panel only @@ -56,11 +73,35 @@ c172p-floats C172 with floats + + + exterior + thumb-exterior-floats.png + http://foo.bar.com/thumb-exterior-floats.png + + + + panel + thumb-panel.png + http://foo.bar.com/thumb-panel.png + c172p-skis C172 with skis + + + exterior + thumb-exterior-skis.png + http://foo.bar.com/thumb-exterior-skis.png + + + + panel + thumb-panel.png + http://foo.bar.com/thumb-panel.png + ec0e2ffdf98d6a5c05c77445e5447ff5