static FGNasalSys* nasalSys = 0;
+// Listener class for loading Nasal modules on demand
+class FGNasalModuleListener : public SGPropertyChangeListener
+{
+public:
+ FGNasalModuleListener(SGPropertyNode* node);
+
+ virtual void valueChanged(SGPropertyNode* node);
+
+private:
+ SGPropertyNode_ptr _node;
+};
+
+FGNasalModuleListener::FGNasalModuleListener(SGPropertyNode* node) : _node(node)
+{
+}
+
+void FGNasalModuleListener::valueChanged(SGPropertyNode*)
+{
+ if (_node->getBoolValue("enabled",false)&&
+ !_node->getBoolValue("loaded",true))
+ {
+ nasalSys->loadPropertyScripts(_node);
+ }
+}
+
// Read and return file contents in a single buffer. Note use of
// stat() to get the file size. This is a win32 function, believe it
SGPropertyNode* nasal = globals->get_props()->getNode("nasal");
if(!nasal) return;
- for(int i=0; i<nasal->nChildren(); i++) {
+ for(int i=0; i<nasal->nChildren(); i++)
+ {
SGPropertyNode* n = nasal->getChild(i);
- bool is_loaded = false;
+ loadPropertyScripts(n);
+ }
+}
- const char* module = n->getName();
- if(n->hasChild("module"))
- module = n->getStringValue("module");
- if (n->getBoolValue("enabled",true))
- {
- // allow multiple files to be specified within a single
- // Nasal module tag
- int j = 0;
- SGPropertyNode *fn;
- bool file_specified = false;
- while((fn = n->getChild("file", j)) != NULL) {
- file_specified = true;
- const char* file = fn->getStringValue();
- SGPath p(file);
- if (!p.isAbsolute() || !p.exists())
- {
- p = globals->resolve_maybe_aircraft_path(file);
- }
- loadModule(p, module);
- j++;
- }
-
- const char* src = n->getStringValue("script");
- if(!n->hasChild("script")) src = 0; // Hrm...
- if(src)
- createModule(module, n->getPath().c_str(), src, strlen(src));
-
- if(!file_specified && !src)
+// Loads the scripts found under /nasal in the global tree
+void FGNasalSys::loadPropertyScripts(SGPropertyNode* n)
+{
+ bool is_loaded = false;
+
+ const char* module = n->getName();
+ if(n->hasChild("module"))
+ module = n->getStringValue("module");
+ if (n->getBoolValue("enabled",true))
+ {
+ // allow multiple files to be specified within a single
+ // Nasal module tag
+ int j = 0;
+ SGPropertyNode *fn;
+ bool file_specified = false;
+ bool ok=true;
+ while((fn = n->getChild("file", j)) != NULL) {
+ file_specified = true;
+ const char* file = fn->getStringValue();
+ SGPath p(file);
+ if (!p.isAbsolute() || !p.exists())
{
- // module no longer exists - clear the archived "enable" flag
- n->setAttribute(SGPropertyNode::USERARCHIVE,false);
- SGPropertyNode* node = n->getChild("enabled",0,false);
- if (node)
- node->setAttribute(SGPropertyNode::USERARCHIVE,false);
-
- SG_LOG(SG_NASAL, SG_ALERT, "Nasal error: " <<
- "no <file> or <script> defined in " <<
- "/nasal/" << module);
+ p = globals->resolve_maybe_aircraft_path(file);
}
- else
- is_loaded = true;
+ ok &= loadModule(p, module);
+ j++;
+ }
+
+ const char* src = n->getStringValue("script");
+ if(!n->hasChild("script")) src = 0; // Hrm...
+ if(src)
+ createModule(module, n->getPath().c_str(), src, strlen(src));
+
+ if(!file_specified && !src)
+ {
+ // module no longer exists - clear the archived "enable" flag
+ n->setAttribute(SGPropertyNode::USERARCHIVE,false);
+ SGPropertyNode* node = n->getChild("enabled",0,false);
+ if (node)
+ node->setAttribute(SGPropertyNode::USERARCHIVE,false);
+
+ SG_LOG(SG_NASAL, SG_ALERT, "Nasal error: " <<
+ "no <file> or <script> defined in " <<
+ "/nasal/" << module);
}
- n->setBoolValue("loaded",is_loaded);
+ else
+ is_loaded = ok;
}
+ else
+ {
+ SGPropertyNode* enable = n->getChild("enabled");
+ if (enable)
+ {
+ FGNasalModuleListener* listener = new FGNasalModuleListener(n);
+ enable->addChangeListener(listener, false);
+ }
+ }
+ n->setBoolValue("loaded",is_loaded);
}
// Logs a runtime error, with stack trace, to the FlightGear log stream
// Reads a script file, executes it, and places the resulting
// namespace into the global namespace under the specified module
// name.
-void FGNasalSys::loadModule(SGPath file, const char* module)
+bool FGNasalSys::loadModule(SGPath file, const char* module)
{
int len = 0;
char* buf = readfile(file.c_str(), &len);
SG_LOG(SG_NASAL, SG_ALERT,
"Nasal error: could not read script file " << file.c_str()
<< " into module " << module);
- return;
+ return false;
}
- createModule(module, file.c_str(), buf, len);
+ bool ok = createModule(module, file.c_str(), buf, len);
delete[] buf;
+ return ok;
}
// Parse and run. Save the local variables namespace, as it will
// become a sub-object of globals. The optional "arg" argument can be
// used to pass an associated property node to the module, which can then
// be accessed via cmdarg(). (This is, for example, used by XML dialogs.)
-void FGNasalSys::createModule(const char* moduleName, const char* fileName,
+bool FGNasalSys::createModule(const char* moduleName, const char* fileName,
const char* src, int len,
const SGPropertyNode* cmdarg,
int argc, naRef* args)
{
naRef code = parse(fileName, src, len);
if(naIsNil(code))
- return;
+ return false;
// See if we already have a module hash to use. This allows the
// user to, for example, add functions to the built-in math
call(code, argc, args, locals);
hashset(_globals, moduleName, locals);
+ return true;
}
void FGNasalSys::deleteModule(const char* moduleName)
// Loads a nasal script from an external file and inserts it as a
// global module of the specified name.
- void loadModule(SGPath file, const char* moduleName);
+ bool loadModule(SGPath file, const char* moduleName);
// Simple hook to run arbitrary source code. Returns a bool to
// indicate successful execution. Does *not* return any Nasal
// Callbacks for command and timer bindings
virtual bool handleCommand(const SGPropertyNode* arg);
- void createModule(const char* moduleName, const char* fileName,
+ bool createModule(const char* moduleName, const char* fileName,
const char* src, int len, const SGPropertyNode* cmdarg=0,
int argc=0, naRef*args=0);
private:
friend class FGNasalScript;
friend class FGNasalListener;
+ friend class FGNasalModuleListener;
//
// FGTimer subclass for handling Nasal timer callbacks.
static int _listenerId;
void loadPropertyScripts();
+ void loadPropertyScripts(SGPropertyNode* n);
void loadScriptDirectory(simgear::Dir nasalDir);
void addModule(string moduleName, simgear::PathList scripts);
void hashset(naRef hash, const char* key, naRef val);