1
0
Fork 0

Add-ons: load add-on-specific menu bar items from addon-menubar-items.xml

If an add-on has a file named addon-menubar-items.xml in its base
directory, load it and add its items to the FG menubar.

Logically, fgStartNewReset() should call
flightgear::addons::AddonManager::instance()->addAddonMenusToFGMenubar()
in order to re-add the items, however doing so would cause the
add-on-specific menus to be added one more time on every reset, because
for some reason, commit 45ea8b5daa added
the PRESERVE attribute to /sim/menubar (apparently to preserve the state
of menu entries upon reset?).

Note: the addon-menubar-items.xml files are reloaded during reset,
      however the menu bar doesn't reflect this, since adding the
      reloaded items to the menu bar in fgStartNewReset() would cause
      the add-on-specific menus to appear several times in the menu bar,
      as explained above.
This commit is contained in:
Florent Rougon 2018-02-27 01:17:30 +01:00
parent 4f96039e70
commit 30cfbd1f91
7 changed files with 121 additions and 0 deletions

View file

@ -29,7 +29,9 @@
#include <simgear/nasal/cppbind/NasalHash.hxx> #include <simgear/nasal/cppbind/NasalHash.hxx>
#include <simgear/nasal/naref.h> #include <simgear/nasal/naref.h>
#include <simgear/props/props.hxx> #include <simgear/props/props.hxx>
#include <simgear/props/props_io.hxx>
#include <Main/fg_props.hxx>
#include <Main/globals.hxx> #include <Main/globals.hxx>
#include <Scripting/NasalSys.hxx> #include <Scripting/NasalSys.hxx>
@ -266,6 +268,22 @@ int Addon::getLoadSequenceNumber() const
void Addon::setLoadSequenceNumber(int num) void Addon::setLoadSequenceNumber(int num)
{ _loadSequenceNumber = num; } { _loadSequenceNumber = num; }
std::vector<SGPropertyNode_ptr> Addon::getMenubarNodes() const
{ return _menubarNodes; }
void Addon::setMenubarNodes(const std::vector<SGPropertyNode_ptr>& menubarNodes)
{ _menubarNodes = menubarNodes; }
void Addon::addToFGMenubar() const
{
SGPropertyNode* menuRootNode = fgGetNode("/sim/menubar/default", true);
for (const auto& node: getMenubarNodes()) {
SGPropertyNode* childNode = menuRootNode->addChild("menu");
::copyProperties(node.ptr(), childNode);
}
}
std::string Addon::str() const std::string Addon::str() const
{ {
std::ostringstream oss; std::ostringstream oss;
@ -311,9 +329,81 @@ Addon Addon::fromAddonDir(const SGPath& addonPath)
addon.setSupportUrl(std::move(metadata.supportUrl)); addon.setSupportUrl(std::move(metadata.supportUrl));
addon.setCodeRepositoryUrl(std::move(metadata.codeRepositoryUrl)); addon.setCodeRepositoryUrl(std::move(metadata.codeRepositoryUrl));
SGPath menuFile = addonPath / "addon-menubar-items.xml";
if (menuFile.exists()) {
addon.setMenubarNodes(readMenubarItems(menuFile));
}
return addon; return addon;
} }
// Static method
std::vector<SGPropertyNode_ptr>
Addon::readMenubarItems(const SGPath& menuFile)
{
SGPropertyNode rootNode;
try {
readProperties(menuFile, &rootNode);
} catch (const sg_exception &e) {
throw errors::error_loading_menubar_items_file(
"unable to load add-on menu bar items from file '" +
menuFile.utf8Str() + "': " + e.getFormattedMessage());
}
// Check the 'meta' section
SGPropertyNode *metaNode = rootNode.getChild("meta");
if (metaNode == nullptr) {
throw errors::error_loading_menubar_items_file(
"no /meta node found in add-on menu bar items file '" +
menuFile.utf8Str() + "'");
}
// Check the file type
SGPropertyNode *fileTypeNode = metaNode->getChild("file-type");
if (fileTypeNode == nullptr) {
throw errors::error_loading_menubar_items_file(
"no /meta/file-type node found in add-on menu bar items file '" +
menuFile.utf8Str() + "'");
}
string fileType = fileTypeNode->getStringValue();
if (fileType != "FlightGear add-on menu bar items") {
throw errors::error_loading_menubar_items_file(
"Invalid /meta/file-type value for add-on menu bar items file '" +
menuFile.utf8Str() + "': '" + fileType + "' "
"(expected 'FlightGear add-on menu bar items')");
}
// Check the format version
SGPropertyNode *fmtVersionNode = metaNode->getChild("format-version");
if (fmtVersionNode == nullptr) {
throw errors::error_loading_menubar_items_file(
"no /meta/format-version node found in add-on menu bar items file '" +
menuFile.utf8Str() + "'");
}
int formatVersion = fmtVersionNode->getIntValue();
if (formatVersion != 1) {
throw errors::error_loading_menubar_items_file(
"unknown format version in add-on menu bar items file '" +
menuFile.utf8Str() + "': " + std::to_string(formatVersion));
}
SG_LOG(SG_GENERAL, SG_DEBUG,
"Loaded add-on menu bar items from '" << menuFile.utf8Str() + "'");
SGPropertyNode *menubarItemsNode = rootNode.getChild("menubar-items");
std::vector<SGPropertyNode_ptr> res;
if (menubarItemsNode != nullptr) {
res = menubarItemsNode->getChildren("menu");
}
return res;
}
// Static method // Static method
void Addon::setupGhost(nasal::Hash& addonsModule) void Addon::setupGhost(nasal::Hash& addonsModule)
{ {

View file

@ -173,6 +173,12 @@ public:
// Get all non-empty URLs pertaining to this add-on // Get all non-empty URLs pertaining to this add-on
std::multimap<UrlType, QualifiedUrl> getUrls() const; std::multimap<UrlType, QualifiedUrl> getUrls() const;
// Getter and setter for the menu bar item nodes of the add-on
std::vector<SGPropertyNode_ptr> getMenubarNodes() const;
void setMenubarNodes(const std::vector<SGPropertyNode_ptr>& menubarNodes);
// Add the menus defined in addon-menubar-items.xml to /sim/menubar/default
void addToFGMenubar() const;
// Simple string representation // Simple string representation
std::string str() const; std::string str() const;
@ -186,6 +192,10 @@ private:
static SGPath getMetadataFile(const SGPath& addonPath); static SGPath getMetadataFile(const SGPath& addonPath);
SGPath getMetadataFile() const; SGPath getMetadataFile() const;
// Read all menus from addon-menubar-items.xml (under the add-on base path)
static std::vector<SGPropertyNode_ptr>
readMenubarItems(const SGPath& menuFile);
// The add-on identifier, in reverse DNS style. The AddonManager refuses to // The add-on identifier, in reverse DNS style. The AddonManager refuses to
// register two add-ons with the same id in a given FlightGear session. // register two add-ons with the same id in a given FlightGear session.
std::string _id; std::string _id;
@ -226,6 +236,8 @@ private:
std::string _triggerProperty; std::string _triggerProperty;
// Semantics explained above // Semantics explained above
int _loadSequenceNumber = -1; int _loadSequenceNumber = -1;
std::vector<SGPropertyNode_ptr> _menubarNodes;
}; };
std::ostream& operator<<(std::ostream& os, const Addon& addon); std::ostream& operator<<(std::ostream& os, const Addon& addon);

View file

@ -255,6 +255,14 @@ SGPropertyNode_ptr AddonManager::addonNode(const string& addonId) const
return getAddon(addonId)->getAddonNode(); return getAddon(addonId)->getAddonNode();
} }
void
AddonManager::addAddonMenusToFGMenubar() const
{
for (const auto& addon: _registeredAddons) {
addon->addToFGMenubar();
}
}
} // of namespace addons } // of namespace addons
} // of namespace flightgear } // of namespace flightgear

View file

@ -86,6 +86,10 @@ public:
// Base node pertaining to the add-on in the Global Property Tree // Base node pertaining to the add-on in the Global Property Tree
SGPropertyNode_ptr addonNode(const std::string& addonId) const; SGPropertyNode_ptr addonNode(const std::string& addonId) const;
// Add the 'menu' nodes defined by each registered add-on to
// /sim/menubar/default
void addAddonMenusToFGMenubar() const;
private: private:
// Constructor called from createInstance() only // Constructor called from createInstance() only
explicit AddonManager() = default; explicit AddonManager() = default;

View file

@ -56,6 +56,7 @@ class error;
class error_loading_config_file; class error_loading_config_file;
class no_metadata_file_found; class no_metadata_file_found;
class error_loading_metadata_file; class error_loading_metadata_file;
class error_loading_menubar_items_file;
class duplicate_registration_attempt; class duplicate_registration_attempt;
class fg_version_too_old; class fg_version_too_old;
class fg_version_too_recent; class fg_version_too_recent;

View file

@ -50,6 +50,9 @@ class no_metadata_file_found : public error
class error_loading_metadata_file : public error class error_loading_metadata_file : public error
{ using error::error; }; { using error::error; };
class error_loading_menubar_items_file : public error
{ using error::error; };
class duplicate_registration_attempt : public error class duplicate_registration_attempt : public error
{ using error::error; }; { using error::error; };

View file

@ -585,6 +585,9 @@ int fgMainInit( int argc, char **argv )
SG_LOG(SG_GENERAL, SG_INFO, SG_LOG(SG_GENERAL, SG_INFO,
"EmbeddedResourceManager: selected locale '" << locale << "'"); "EmbeddedResourceManager: selected locale '" << locale << "'");
// Copy the property nodes for the menus added by registered add-ons
addons::AddonManager::instance()->addAddonMenusToFGMenubar();
// Initialize the Window/Graphics environment. // Initialize the Window/Graphics environment.
fgOSInit(&argc, argv); fgOSInit(&argc, argv);
_bootstrap_OSInit++; _bootstrap_OSInit++;