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