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:
parent
4f96039e70
commit
30cfbd1f91
7 changed files with 121 additions and 0 deletions
|
@ -29,7 +29,9 @@
|
|||
#include <simgear/nasal/cppbind/NasalHash.hxx>
|
||||
#include <simgear/nasal/naref.h>
|
||||
#include <simgear/props/props.hxx>
|
||||
#include <simgear/props/props_io.hxx>
|
||||
|
||||
#include <Main/fg_props.hxx>
|
||||
#include <Main/globals.hxx>
|
||||
#include <Scripting/NasalSys.hxx>
|
||||
|
||||
|
@ -266,6 +268,22 @@ int Addon::getLoadSequenceNumber() const
|
|||
void Addon::setLoadSequenceNumber(int 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::ostringstream oss;
|
||||
|
@ -311,9 +329,81 @@ Addon Addon::fromAddonDir(const SGPath& addonPath)
|
|||
addon.setSupportUrl(std::move(metadata.supportUrl));
|
||||
addon.setCodeRepositoryUrl(std::move(metadata.codeRepositoryUrl));
|
||||
|
||||
SGPath menuFile = addonPath / "addon-menubar-items.xml";
|
||||
|
||||
if (menuFile.exists()) {
|
||||
addon.setMenubarNodes(readMenubarItems(menuFile));
|
||||
}
|
||||
|
||||
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
|
||||
void Addon::setupGhost(nasal::Hash& addonsModule)
|
||||
{
|
||||
|
|
|
@ -173,6 +173,12 @@ public:
|
|||
// Get all non-empty URLs pertaining to this add-on
|
||||
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
|
||||
std::string str() const;
|
||||
|
||||
|
@ -186,6 +192,10 @@ private:
|
|||
static SGPath getMetadataFile(const SGPath& addonPath);
|
||||
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
|
||||
// register two add-ons with the same id in a given FlightGear session.
|
||||
std::string _id;
|
||||
|
@ -226,6 +236,8 @@ private:
|
|||
std::string _triggerProperty;
|
||||
// Semantics explained above
|
||||
int _loadSequenceNumber = -1;
|
||||
|
||||
std::vector<SGPropertyNode_ptr> _menubarNodes;
|
||||
};
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, const Addon& addon);
|
||||
|
|
|
@ -255,6 +255,14 @@ SGPropertyNode_ptr AddonManager::addonNode(const string& addonId) const
|
|||
return getAddon(addonId)->getAddonNode();
|
||||
}
|
||||
|
||||
void
|
||||
AddonManager::addAddonMenusToFGMenubar() const
|
||||
{
|
||||
for (const auto& addon: _registeredAddons) {
|
||||
addon->addToFGMenubar();
|
||||
}
|
||||
}
|
||||
|
||||
} // of namespace addons
|
||||
|
||||
} // of namespace flightgear
|
||||
|
|
|
@ -86,6 +86,10 @@ public:
|
|||
// Base node pertaining to the add-on in the Global Property Tree
|
||||
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:
|
||||
// Constructor called from createInstance() only
|
||||
explicit AddonManager() = default;
|
||||
|
|
|
@ -56,6 +56,7 @@ class error;
|
|||
class error_loading_config_file;
|
||||
class no_metadata_file_found;
|
||||
class error_loading_metadata_file;
|
||||
class error_loading_menubar_items_file;
|
||||
class duplicate_registration_attempt;
|
||||
class fg_version_too_old;
|
||||
class fg_version_too_recent;
|
||||
|
|
|
@ -50,6 +50,9 @@ class no_metadata_file_found : public error
|
|||
class error_loading_metadata_file : public error
|
||||
{ using error::error; };
|
||||
|
||||
class error_loading_menubar_items_file : public error
|
||||
{ using error::error; };
|
||||
|
||||
class duplicate_registration_attempt : public error
|
||||
{ using error::error; };
|
||||
|
||||
|
|
|
@ -585,6 +585,9 @@ int fgMainInit( int argc, char **argv )
|
|||
SG_LOG(SG_GENERAL, SG_INFO,
|
||||
"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.
|
||||
fgOSInit(&argc, argv);
|
||||
_bootstrap_OSInit++;
|
||||
|
|
Loading…
Reference in a new issue