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/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)
|
||||||
{
|
{
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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; };
|
||||||
|
|
||||||
|
|
|
@ -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++;
|
||||||
|
|
Loading…
Reference in a new issue