]> git.mxchange.org Git - flightgear.git/commitdiff
Expose FlightPlan delegates to Nasal, finally.
authorJames Turner <zakalawe@mac.com>
Sat, 12 May 2012 16:23:17 +0000 (17:23 +0100)
committerJames Turner <zakalawe@mac.com>
Sat, 12 May 2012 16:23:17 +0000 (17:23 +0100)
This will permit Nasal (e.g., FMS) to update nicely when the FlightPlan
is modified from anywhere else.

src/Autopilot/route_mgr.cxx
src/Navaids/FlightPlan.cxx
src/Navaids/FlightPlan.hxx
src/Scripting/NasalPositioned.cxx
src/Scripting/NasalSys.cxx
src/Scripting/NasalSys.hxx

index dedb68aaa15814ef5b786e669b0494e3c264862f..ffb9dc306235f50fec29189059fa957ca9742fa7 100644 (file)
@@ -326,12 +326,13 @@ void FGRouteMgr::init() {
   wpn->getChild("eta", 0, true);
   
   _pathNode = fgGetNode(RM "file-path", 0, true);
-  setFlightPlan(new FlightPlan());
 }
 
 
 void FGRouteMgr::postinit()
 {
+  setFlightPlan(new FlightPlan());
+  
   SGPath path(_pathNode->getStringValue());
   if (!path.isNull()) {
     SG_LOG(SG_AUTOPILOT, SG_INFO, "loading flight-plan from: " << path.str());
@@ -402,12 +403,13 @@ void FGRouteMgr::setFlightPlan(FlightPlan* plan)
   }
   
   if (_plan) {
+    _plan->removeDelegate(this);
     delete _plan;
     active->setBoolValue(false);
   }
   
   _plan = plan;
-  _plan->setDelegate(this);
+  _plan->addDelegate(this);
   
   _flightplanChanged->fireValueChanged();
   
index 7d7233be43b1485bb00c6f26a1bfdfa9ea99e58d..5630bea67eb12ce6b5b6a1448ad96a3e9499ce58 100644 (file)
@@ -55,6 +55,9 @@ using std::fstream;
 
 namespace flightgear {
 
+typedef std::vector<FlightPlan::DelegateFactory*> FPDelegateFactoryVec;
+static FPDelegateFactoryVec static_delegateFactories;
+  
 FlightPlan::FlightPlan() :
   _currentIndex(-1),
   _departureRunway(NULL),
@@ -64,12 +67,26 @@ FlightPlan::FlightPlan() :
   _approach(NULL),
   _delegate(NULL)
 {
-  
+  BOOST_FOREACH(DelegateFactory* factory, static_delegateFactories) {
+    Delegate* d = factory->createFlightPlanDelegate(this);
+    if (d) { // factory might not always create a delegate
+      d->_deleteWithPlan = true;
+      addDelegate(d);
+    }
+  }
 }
   
 FlightPlan::~FlightPlan()
 {
-  
+// delete all delegates which we own.
+  Delegate* d = _delegate;
+  while (d) {
+    Delegate* cur = d;
+    d = d->_inner;
+    if (cur->_deleteWithPlan) {
+      delete cur;
+    }
+  }
 }
   
 FlightPlan* FlightPlan::clone(const string& newIdent) const
@@ -986,7 +1003,29 @@ void FlightPlan::rebuildLegData()
   } // of legs iteration
 }
   
-void FlightPlan::setDelegate(Delegate* d)
+void FlightPlan::registerDelegateFactory(DelegateFactory* df)
+{
+  FPDelegateFactoryVec::iterator it = std::find(static_delegateFactories.begin(),
+                                                static_delegateFactories.end(), df);
+  if (it != static_delegateFactories.end()) {
+    throw  sg_exception("duplicate delegate factory registration");
+  }
+  
+  static_delegateFactories.push_back(df);
+}
+  
+void FlightPlan::unregisterDelegateFactory(DelegateFactory* df)
+{
+  FPDelegateFactoryVec::iterator it = std::find(static_delegateFactories.begin(),
+                                                static_delegateFactories.end(), df);
+  if (it == static_delegateFactories.end()) {
+    return;
+  }
+  
+  static_delegateFactories.erase(it);
+}
+  
+void FlightPlan::addDelegate(Delegate* d)
 {
   // wrap any existing delegate(s) in the new one
   d->_inner = _delegate;
@@ -1003,6 +1042,7 @@ void FlightPlan::removeDelegate(Delegate* d)
 }
   
 FlightPlan::Delegate::Delegate() :
+  _deleteWithPlan(false),
   _inner(NULL)
 {
   
index b46dae03c7a11230b2bd8f8b2713dc592642a421..4558d16ddbcf6a4ed42100f74e5a11a256071230 100644 (file)
@@ -100,7 +100,7 @@ public:
   {
   public:
     virtual ~Delegate();
-    
+        
     virtual void departureChanged() { }
     virtual void arrivalChanged() { }
     virtual void waypointsChanged() { }
@@ -120,6 +120,7 @@ public:
     
     friend class FlightPlan;
     
+    bool _deleteWithPlan;
     Delegate* _inner;
   };
   
@@ -207,7 +208,20 @@ public:
    */
   WayptRef waypointFromString(const std::string& target);
   
-  void setDelegate(Delegate* d);
+  /**
+   * abstract interface for creating delegates automatically when a
+   * flight-plan is created or loaded
+   */
+  class DelegateFactory
+  {
+  public:
+    virtual Delegate* createFlightPlanDelegate(FlightPlan* fp) = 0;
+  };
+  
+  static void registerDelegateFactory(DelegateFactory* df);
+  static void unregisterDelegateFactory(DelegateFactory* df);
+  
+  void addDelegate(Delegate* d);
   void removeDelegate(Delegate* d);
 private:
   
index ff7546bfc2db6cfbab434cda9d17c9decd3a7de2..1baf07d0463407fa478f8c73d860d99c246d5922 100644 (file)
@@ -1308,6 +1308,16 @@ static naRef f_airport_getApproach(naContext c, naRef me, int argc, naRef* args)
   return ghostForProcedure(c, apt->findApproachWithIdent(ident));
 }
 
+static naRef f_airport_toString(naContext c, naRef me, int argc, naRef* args)
+{
+  FGAirport* apt = airportGhost(me);
+  if (!apt) {
+    naRuntimeError(c, "airport.tostring called on non-airport object");
+  }
+  
+  return stringToNasal(c, "an airport " + apt->ident());
+}
+
 // Returns vector of data hash for navaid of a <type>, nil on error
 // navaids sorted by ascending distance 
 // navinfo([<lat>,<lon>],[<type>],[<id>])
@@ -1617,6 +1627,108 @@ static naRef f_route(naContext c, naRef me, int argc, naRef* args)
   return naNil();
 }
 
+class NasalFPDelegate : public FlightPlan::Delegate
+{
+public:
+  NasalFPDelegate(FlightPlan* fp, FGNasalSys* sys, naRef ins) :
+    _nasal(sys),
+    _plan(fp),
+    _instance(ins)
+  {
+    SG_LOG(SG_NASAL, SG_INFO, "created Nasal delegate for " << fp);
+    _gcSaveKey = _nasal->gcSave(ins);
+  }
+  
+  virtual ~NasalFPDelegate()
+  {
+    SG_LOG(SG_NASAL, SG_INFO, "destroying Nasal delegate for " << _plan);
+    _nasal->gcRelease(_gcSaveKey);
+  }
+  
+  virtual void departureChanged()
+  {
+    callDelegateMethod("departureChanged");
+  }
+  
+  virtual void arrivalChanged()
+  {
+    callDelegateMethod("arrivalChanged");
+  }
+  
+  virtual void waypointsChanged()
+  {
+    callDelegateMethod("waypointsChanged");
+  }
+  
+  virtual void currentWaypointChanged()
+  {
+    callDelegateMethod("currentWaypointChanged");
+  }
+private:
+  
+  void callDelegateMethod(const char* method)
+  {
+    naRef f;
+    naMember_cget(_nasal->context(), _instance, method, &f);
+    if (naIsNil(f)) {
+      return; // no method on the delegate
+    }
+    
+    naRef arg[1];
+    arg[0] = ghostForFlightPlan(_nasal->context(), _plan);
+    _nasal->callMethod(f, _instance, 1, arg, naNil());
+  }
+  
+  FGNasalSys* _nasal;
+  FlightPlan* _plan;
+  naRef _instance;
+  int _gcSaveKey;
+};
+
+class NasalFPDelegateFactory : public FlightPlan::DelegateFactory
+{
+public:
+  NasalFPDelegateFactory(naRef code)
+  {
+    _nasal = (FGNasalSys*) globals->get_subsystem("nasal");
+    _func = code;
+    _gcSaveKey = _nasal->gcSave(_func);
+  }
+  
+  ~NasalFPDelegateFactory()
+  {
+    _nasal->gcRelease(_gcSaveKey);
+  }
+  
+  virtual FlightPlan::Delegate* createFlightPlanDelegate(FlightPlan* fp)
+  {
+    naRef args[1];
+    args[0] = ghostForFlightPlan(_nasal->context(), fp);
+    naRef instance = _nasal->call(_func, 1, args, naNil());
+    if (naIsNil(instance)) {
+      return NULL;
+    }
+    
+    return new NasalFPDelegate(fp, _nasal, instance);
+  }
+private:
+  FGNasalSys* _nasal;
+  naRef _func;
+  int _gcSaveKey;
+};
+
+static naRef f_registerFPDelegate(naContext c, naRef me, int argc, naRef* args)
+{
+  if ((argc < 1) || !naIsFunc(args[0])) {
+    naRuntimeError(c, "non-function argument to registerFlightPlanDelegate");
+  }
+  
+  NasalFPDelegateFactory* factory = new NasalFPDelegateFactory(args[0]);
+  FlightPlan::registerDelegateFactory(factory);
+  
+  return naNil();
+}
+
 static WayptRef wayptFromArg(naRef arg)
 {
   WayptRef r = wayptGhost(arg);
@@ -2077,6 +2189,7 @@ static struct { const char* name; naCFunction func; } funcs[] = {
   { "findNavaidsByID", f_findNavaidsByIdent },
   { "findFixesByID", f_findFixesByIdent },
   { "flightplan", f_route },
+  { "registerFlightPlanDelegate", f_registerFPDelegate },
   { "createWP", f_createWP },
   { "createWPFrom", f_createWPFrom },
   { "airwaysRoute", f_airwaySearch },
@@ -2104,6 +2217,7 @@ naRef initNasalPositioned(naRef globals, naContext c, naRef gcSave)
     hashset(c, airportPrototype, "getSid", naNewFunc(c, naNewCCode(c, f_airport_getSid)));
     hashset(c, airportPrototype, "getStar", naNewFunc(c, naNewCCode(c, f_airport_getStar)));
     hashset(c, airportPrototype, "getIAP", naNewFunc(c, naNewCCode(c, f_airport_getApproach)));
+    hashset(c, airportPrototype, "tostring", naNewFunc(c, naNewCCode(c, f_airport_toString)));
   
     flightplanPrototype = naNewHash(c);
     hashset(c, gcSave, "flightplanProto", flightplanPrototype);
index 73bb2e25a1dcb306e3b918e05ecdff2bd165246d..1c3ec31cb2265a5e8f437d3f828ba697f23abad1 100644 (file)
@@ -99,18 +99,24 @@ FGNasalSys::FGNasalSys()
     _callCount = 0;
 }
 
+naRef FGNasalSys::call(naRef code, int argc, naRef* args, naRef locals)
+{
+  return callMethod(code, naNil(), argc, args, locals);
+}
+
 // Does a naCall() in a new context.  Wrapped here to make lock
 // tracking easier.  Extension functions are called with the lock, but
 // we have to release it before making a new naCall().  So rather than
 // drop the lock in every extension function that might call back into
 // Nasal, we keep a stack depth counter here and only unlock/lock
 // around the naCall if it isn't the first one.
-naRef FGNasalSys::call(naRef code, int argc, naRef* args, naRef locals)
+
+naRef FGNasalSys::callMethod(naRef code, naRef self, int argc, naRef* args, naRef locals)
 {
     naContext ctx = naNewContext();
     if(_callCount) naModUnlock();
     _callCount++;
-    naRef result = naCall(ctx, code, argc, args, naNil(), locals);
+    naRef result = naCall(ctx, code, argc, args, self, locals);
     if(naGetError(ctx))
         logError(ctx);
     _callCount--;
index b564bb354d8d711975ceab5a7a40a4d589ac4704..5ca64f109ff18b006b9da2ffdd9cb6f0a6bd3ed0 100644 (file)
@@ -111,11 +111,29 @@ public:
     void deleteModule(const char* moduleName);
 
     naRef call(naRef code, int argc, naRef* args, naRef locals);
+  
+    naRef callMethod(naRef code, naRef self, int argc, naRef* args, naRef locals);
+  
     naRef propNodeGhost(SGPropertyNode* handle);
 
     void registerToLoad(FGNasalModelData* data)   { _loadList.push(data);}
     void registerToUnload(FGNasalModelData* data) { _unloadList.push(data);}
 
+    // can't call this 'globals' due to naming clash
+    naRef nasalGlobals() const
+    { return _globals; }
+  
+    naContext context() const
+    { return _context; }
+  
+    // This mechanism is here to allow naRefs to be passed to
+    // locations "outside" the interpreter.  Normally, such a
+    // reference would be garbage collected unexpectedly.  By passing
+    // it to gcSave and getting a key/handle, it can be cached in a
+    // globals.__gcsave hash.  Be sure to release it with gcRelease
+    // when done.
+    int gcSave(naRef r);
+    void gcRelease(int key);
 private:
     friend class FGNasalScript;
     friend class FGNasalListener;
@@ -151,15 +169,6 @@ private:
     naRef parse(const char* filename, const char* buf, int len);
     naRef genPropsModule();
 
-    // This mechanism is here to allow naRefs to be passed to
-    // locations "outside" the interpreter.  Normally, such a
-    // reference would be garbage collected unexpectedly.  By passing
-    // it to gcSave and getting a key/handle, it can be cached in a
-    // globals.__gcsave hash.  Be sure to release it with gcRelease
-    // when done.
-    int gcSave(naRef r);
-    void gcRelease(int key);
-
     naContext _context;
     naRef _globals;