diff --git a/simgear/package/CatalogTest.cxx b/simgear/package/CatalogTest.cxx index 426bfd88..fd90ab86 100644 --- a/simgear/package/CatalogTest.cxx +++ b/simgear/package/CatalogTest.cxx @@ -173,6 +173,8 @@ int parseTest() SG_CHECK_EQUAL(thumb.path, "exterior.png"); // test variants + SG_CHECK_EQUAL(p2->parentIdForVariant(0), std::string()); + try { p2->indexOfVariant("fofofo"); SG_TEST_FAIL("lookup of non-existant variant did not throw"); @@ -186,6 +188,8 @@ int parseTest() unsigned int skisVariant = p2->indexOfVariant("c172p-skis"); SG_VERIFY(skisVariant > 0); + SG_CHECK_EQUAL(p2->parentIdForVariant(skisVariantFull), "c172p"); + SG_CHECK_EQUAL(skisVariant, skisVariantFull); SG_CHECK_EQUAL(p2->getLocalisedProp("description", skisVariant), @@ -195,6 +199,7 @@ int parseTest() unsigned int floatsVariant = p2->indexOfVariant("c172p-floats"); SG_VERIFY(floatsVariant > 0); + SG_CHECK_EQUAL(p2->parentIdForVariant(floatsVariant), "c172p"); SG_CHECK_EQUAL(p2->getLocalisedProp("description", floatsVariant), "A plane with floats"); @@ -215,6 +220,19 @@ int parseTest() SG_CHECK_EQUAL(thumb2.url, "http://foo.bar.com/thumb-floats.png"); SG_CHECK_EQUAL(thumb2.path, "thumb-floats.png"); +// test multiple primary + unsigned int rVariant = p2->indexOfVariant("c172r"); + SG_VERIFY(rVariant > 0); + + SG_CHECK_EQUAL(p2->parentIdForVariant(rVariant), std::string()); + + unsigned int rFloatVariant = p2->indexOfVariant("c172r-floats"); + SG_VERIFY(rFloatVariant > 0); + SG_CHECK_EQUAL(p2->parentIdForVariant(rFloatVariant), std::string("c172r")); + + string_list primaries = {"c172p", "c172r"}; + SG_VERIFY(p2->primaryVariants() == primaries); + // test filtering / searching too string_set tags(p2->tags()); SG_CHECK_EQUAL(tags.size(), 4); diff --git a/simgear/package/Package.cxx b/simgear/package/Package.cxx index b5361eb9..17772f94 100644 --- a/simgear/package/Package.cxx +++ b/simgear/package/Package.cxx @@ -291,7 +291,7 @@ string_list Package::downloadUrls() const return r; } - BOOST_FOREACH(SGPropertyNode* dl, m_props->getChildren("url")) { + for (auto dl : m_props->getChildren("url")) { r.push_back(dl->getStringValue()); } return r; @@ -412,7 +412,38 @@ SGPropertyNode_ptr Package::propsForVariant(const unsigned int vIndex, const cha return m_props; } - throw sg_exception("Unknow variant in package " + id()); + throw sg_exception("Unknown variant in package " + id()); +} + +std::string Package::parentIdForVariant(unsigned int variantIndex) const +{ + const std::string parentId = propsForVariant(variantIndex)->getStringValue("variant-of"); + if ((variantIndex == 0) || (parentId == "_package_")) { + return std::string(); + } + + if (parentId.empty()) { + // this is a variant without a variant-of, so assume its parent is + // the first primary + return m_variants.front(); + } + + assert(indexOfVariant(parentId) >= 0); + return parentId; +} + +string_list Package::primaryVariants() const +{ + string_list result; + for (unsigned int v = 0; v < m_variants.size(); ++v) { + const auto pr = parentIdForVariant(v); + if (pr.empty()) { + result.push_back(m_variants.at(v)); + } + } + assert(!result.empty()); + assert(result.front() == id()); + return result; } Package::Thumbnail Package::thumbnailForVariant(unsigned int vIndex) const diff --git a/simgear/package/Package.hxx b/simgear/package/Package.hxx index 54dd4667..bbb9582c 100644 --- a/simgear/package/Package.hxx +++ b/simgear/package/Package.hxx @@ -62,15 +62,25 @@ public: existingInstall(const InstallCallback& cb = InstallCallback()) const; bool isInstalled() const; - + + /** + * package ID + */ std::string id() const; /** - * Variant IDs. Note the primary ID will always be included as - * variants()[0], to simplify enumerating all variants + * Variant IDs */ string_list variants() const; + /** + * All variants without a parent, i.e top-level variants in this package. + * Often this is a single-element list matching id() above, but when + * packages contain multiple primary aircraft, this will have multiple + * elements. + */ + string_list primaryVariants() const; + /** * Fully-qualified ID, including our catalog'd ID */ @@ -89,6 +99,8 @@ public: /** * human-readable name - note this is probably not localised, * although this is not ruled out for the future. + * + * Deprecated - please use nameForVariant */ std::string name() const; @@ -101,6 +113,9 @@ public: /** * syntactic sugar to get the localised description + * + * Deprecated - please use getLocalisedProp to get the variant-specific + * description. */ std::string description() const; @@ -182,6 +197,15 @@ public: * same as the primary ID, depending on the aircraft author */ std::string dirName() const; + + /** + * Return the parent variant of a variant. This will be the emtpy string if + * the variant is primary (top-level), otherwise the local (non-qualified) + * ID. This allows establishing a heirarchy of variants within the package. + * Note at present most code assumes a maxiumum two-level deep heirarchy + * (parents and children) + */ + std::string parentIdForVariant(unsigned int variantIndex) const; private: SGPath pathOnDisk() const; diff --git a/simgear/package/catalogTest1/catalog.xml b/simgear/package/catalogTest1/catalog.xml index b226ca3d..26fb7025 100644 --- a/simgear/package/catalogTest1/catalog.xml +++ b/simgear/package/catalogTest1/catalog.xml @@ -99,6 +99,8 @@ C172 with skis A plane with skis + c172p + exterior thumb-exterior-skis.png @@ -112,6 +114,30 @@ + + c172r + C172R + Equally good version of the C172 + _package_ + + panel + thumb-panel.png + http://foo.bar.com/thumb-panel.png + + + + + c172r-floats + C172R-floats + Equally good version of the C172 with floats + c172r + + panel + thumb-panel.png + http://foo.bar.com/thumb-panel.png + + + ec0e2ffdf98d6a5c05c77445e5447ff5 http://localhost:2000/catalogTest1/c172p.zip