diff --git a/src/GUI/QmlAircraftInfo.cxx b/src/GUI/QmlAircraftInfo.cxx index c4a32298e..3bfec4828 100644 --- a/src/GUI/QmlAircraftInfo.cxx +++ b/src/GUI/QmlAircraftInfo.cxx @@ -30,7 +30,7 @@ public: globals->packageRoot()->addDelegate(this); } - ~Delegate() + ~Delegate() override { globals->packageRoot()->removeDelegate(this); } @@ -135,6 +135,19 @@ static AircraftStateVec readAircraftStates(const SGPath& setXMLPath) return result; } +static SGPropertyNode_ptr readAircraftAuthors(const SGPath& setXMLPath) +{ + SGPropertyNode_ptr root(new SGPropertyNode); + try { + readProperties(setXMLPath, root); + } catch (sg_exception&) { + return {}; // malformed include or XML, just bail + } + + const auto authors = root->getNode("sim/authors"); + return authors; +} + QString humanNameFromStateTag(const std::string& tag) { if (tag == "approach") return QObject::tr("On approach"); @@ -338,11 +351,37 @@ QString QmlAircraftInfo::description() const QString QmlAircraftInfo::authors() const { + SGPropertyNode_ptr structuredAuthors; if (_item) { - return resolveItem()->authors; + std::string path = pathOnDisk().toUtf8().toStdString(); + structuredAuthors = readAircraftAuthors(SGPath::fromUtf8(path)); + if (!structuredAuthors) + return resolveItem()->authors; } else if (_package) { - std::string authors = _package->getLocalisedProp("author", _variant); - return QString::fromStdString(authors); + if (_package->properties()->hasChild("authors")) { + structuredAuthors = _package->properties()->getChild("authors"); + } else { + std::string authors = _package->getLocalisedProp("author", _variant); + return QString::fromStdString(authors); + } + } + + if (structuredAuthors) { + // build formatted HTML based on authors data + QString html = "<ul>\n"; + for (auto a : structuredAuthors->getChildren("author")) { + html += "<li>"; + html += a->getStringValue("name"); + if (a->hasChild("nick")) { + html += QStringLiteral(" '") + QString::fromStdString(a->getStringValue("nick")) + QStringLiteral("'"); + } + if (a->hasChild("description")) { + html += QStringLiteral(" - <i>") + QString::fromStdString(a->getStringValue("description")) + QStringLiteral("</i>"); + } + html += "</li>\n"; + } + html += "<ul>\n"; + return html; } return {}; diff --git a/src/Main/util.cxx b/src/Main/util.cxx index b1ba57034..f20bae1f8 100644 --- a/src/Main/util.cxx +++ b/src/Main/util.cxx @@ -179,5 +179,31 @@ SGPath fgValidatePath (const SGPath& path, bool write) return SGPath(); } -// end of util.cxx +std::string generateAuthorsText(SGPropertyNode* authors) +{ + std::string result; + for (auto a : authors->getChildren("author")) { + const std::string name = a->getStringValue("name"); + if (name.empty()) + continue; + if (!result.empty()) + result += ", "; + result += a->getStringValue("name"); + } + return result; +} + +std::string flightgear::getAircraftAuthorsText() +{ + const auto authorsNode = fgGetNode("sim/authors"); + if (authorsNode) { + // we have structured authors data + return generateAuthorsText(authorsNode); + } + + // if we hit this point, there is no strucutred authors data + return fgGetString("/sim/author"); +} + +// end of util.cxx diff --git a/src/Main/util.hxx b/src/Main/util.hxx index e75c28462..c6816ef7d 100644 --- a/src/Main/util.hxx +++ b/src/Main/util.hxx @@ -54,4 +54,15 @@ SGPath fgValidatePath(const SGPath& path, bool write); */ void fgInitAllowedPaths(); +namespace flightgear +{ + /** + * @brief getAircraftAuthorsText - get the aircraft authors as a single + * string value. This will combine the new (structured) authors data if + * present, otherwise just return the old plain string + * @return + */ + std::string getAircraftAuthorsText(); +} + #endif // __UTIL_HXX diff --git a/src/Viewer/splash.cxx b/src/Viewer/splash.cxx index 79152af1d..88cfa9f29 100644 --- a/src/Viewer/splash.cxx +++ b/src/Viewer/splash.cxx @@ -50,12 +50,13 @@ #include <Main/fg_props.hxx> #include <Main/fg_os.hxx> #include <Main/locale.hxx> +#include <Main/util.hxx> #include "splash.hxx" #include "renderer.hxx" #include <sstream> -const char* LICENSE_URL_TEXT = "Licensed under the GNU GPL. See http://www.flightgear.org for more information"; +static const char* LICENSE_URL_TEXT = "Licensed under the GNU GPL. See http://www.flightgear.org for more information"; class SplashScreenUpdateCallback : public osg::NodeCallback { public: @@ -134,7 +135,7 @@ void SplashScreen::createNodes() geode->addDrawable(geometry); if (_legacySplashScreenMode) { - addText(geode, osg::Vec2(0.025, 0.025), 0.03, + addText(geode, osg::Vec2(0.025f, 0.025f), 0.03, std::string("FlightGear ") + fgGetString("/sim/version/flightgear") + std::string(" ") + std::string(LICENSE_URL_TEXT), osgText::Text::LEFT_TOP, @@ -145,15 +146,15 @@ void SplashScreen::createNodes() // order here is important so we can re-write first item with the // startup tip. - addText(geode, osg::Vec2(0.025, 0.15), 0.03, LICENSE_URL_TEXT, + addText(geode, osg::Vec2(0.025f, 0.15f), 0.03, LICENSE_URL_TEXT, osgText::Text::LEFT_TOP, nullptr, 0.6); - addText(geode, osg::Vec2(0.025, 0.025), 0.10, std::string("FlightGear ") + fgGetString("/sim/version/flightgear"), osgText::Text::LEFT_TOP); + addText(geode, osg::Vec2(0.025f, 0.025f), 0.10, std::string("FlightGear ") + fgGetString("/sim/version/flightgear"), osgText::Text::LEFT_TOP); if (!_aircraftLogoVertexArray) { - addText(geode, osg::Vec2(0.025, 0.935), 0.10, + addText(geode, osg::Vec2(0.025f, 0.935f), 0.10, fgGetString("/sim/description"), osgText::Text::LEFT_BOTTOM, nullptr, @@ -161,8 +162,9 @@ void SplashScreen::createNodes() _items.back().maxLineCount = 1; } - addText(geode, osg::Vec2(0.025, 0.940), 0.03, - fgGetString("/sim/author"), + const auto authors = flightgear::getAircraftAuthorsText(); + addText(geode, osg::Vec2(0.025f, 0.940f), 0.03, + authors, osgText::Text::LEFT_TOP, nullptr, 0.6); @@ -170,13 +172,13 @@ void SplashScreen::createNodes() _items.back().maxHeightFraction = 0.055; } - addText(geode, osg::Vec2(0.975, 0.935), 0.03, + addText(geode, osg::Vec2(0.975f, 0.935f), 0.03, "loading status", osgText::Text::RIGHT_BOTTOM, fgGetNode("/sim/startup/splash-progress-text", true), 0.4); - addText(geode, osg::Vec2(0.975, 0.975), 0.03, + addText(geode, osg::Vec2(0.975f, 0.975f), 0.03, "spinner", osgText::Text::RIGHT_BOTTOM, fgGetNode("/sim/startup/splash-progress-spinner", true)); @@ -196,13 +198,13 @@ void SplashScreen::createNodes() _splashSpinnerVertexArray = new osg::Vec3Array; for (int i=0; i < 8; ++i) { - _splashSpinnerVertexArray->push_back(osg::Vec3(0.0, 0.0, -0.1)); + _splashSpinnerVertexArray->push_back(osg::Vec3(0.0f, 0.0f, -0.1f)); } geometry->setVertexArray(_splashSpinnerVertexArray); // QColor buttonColor(27, 122, 211); colorArray = new osg::Vec4Array; - colorArray->push_back(osg::Vec4(27 / 255.0, 122 / 255.0, 211 / 255.0, 0.75)); + colorArray->push_back(osg::Vec4(27 / 255.0f, 122 / 255.0f, 211 / 255.0f, 0.75f)); geometry->setColorArray(colorArray); geometry->setColorBinding(osg::Geometry::BIND_OVERALL); geometry->addPrimitiveSet(new osg::DrawArrays(GL_QUADS, 0, 8));