1 // FlightPlan.cxx - flight plan object
3 // Written by James Turner, started 2012.
5 // Copyright (C) 2012 Curtis L. Olson
7 // This program is free software; you can redistribute it and/or
8 // modify it under the terms of the GNU General Public License as
9 // published by the Free Software Foundation; either version 2 of the
10 // License, or (at your option) any later version.
12 // This program is distributed in the hope that it will be useful, but
13 // WITHOUT ANY WARRANTY; without even the implied warranty of
14 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 // General Public License for more details.
17 // You should have received a copy of the GNU General Public License
18 // along with this program; if not, write to the Free Software
19 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
25 #include "FlightPlan.hxx"
33 #include <boost/algorithm/string/case_conv.hpp>
34 #include <boost/algorithm/string.hpp>
35 #include <boost/foreach.hpp>
38 #include <simgear/structure/exception.hxx>
39 #include <simgear/misc/sg_path.hxx>
40 #include <simgear/magvar/magvar.hxx>
41 #include <simgear/timing/sg_time.hxx>
42 #include <simgear/misc/sgstream.hxx>
43 #include <simgear/misc/strutils.hxx>
44 #include <simgear/props/props_io.hxx>
47 #include <Main/globals.hxx>
48 #include "Main/fg_props.hxx"
49 #include <Navaids/procedure.hxx>
50 #include <Navaids/waypoint.hxx>
57 namespace flightgear {
59 typedef std::vector<FlightPlan::DelegateFactory*> FPDelegateFactoryVec;
60 static FPDelegateFactoryVec static_delegateFactories;
62 FlightPlan::FlightPlan() :
65 _departureRunway(NULL),
66 _destinationRunway(NULL),
72 _departureChanged = _arrivalChanged = _waypointsChanged = _currentWaypointChanged = false;
74 BOOST_FOREACH(DelegateFactory* factory, static_delegateFactories) {
75 Delegate* d = factory->createFlightPlanDelegate(this);
76 if (d) { // factory might not always create a delegate
77 d->_deleteWithPlan = true;
83 FlightPlan::~FlightPlan()
85 // delete all delegates which we own.
86 Delegate* d = _delegate;
90 if (cur->_deleteWithPlan) {
96 FlightPlan* FlightPlan::clone(const string& newIdent) const
98 FlightPlan* c = new FlightPlan();
99 c->_ident = newIdent.empty() ? _ident : newIdent;
102 // copy destination / departure data.
103 c->setDeparture(_departure);
104 c->setDeparture(_departureRunway);
107 c->setApproach(_approach);
108 } else if (_destinationRunway) {
109 c->setDestination(_destinationRunway);
110 } else if (_destination) {
111 c->setDestination(_destination);
118 c->_waypointsChanged = true;
119 for (int l=0; l < numLegs(); ++l) {
120 c->_legs.push_back(_legs[l]->cloneFor(c));
126 void FlightPlan::setIdent(const string& s)
131 string FlightPlan::ident() const
136 FlightPlan::Leg* FlightPlan::insertWayptAtIndex(Waypt* aWpt, int aIndex)
146 if ((aIndex == -1) || (aIndex > (int) _legs.size())) {
147 index = _legs.size();
150 insertWayptsAtIndex(wps, index);
151 return legAtIndex(index);
154 void FlightPlan::insertWayptsAtIndex(const WayptVec& wps, int aIndex)
161 if ((aIndex == -1) || (aIndex > (int) _legs.size())) {
162 index = _legs.size();
165 LegVec::iterator it = _legs.begin();
168 int endIndex = index + wps.size() - 1;
169 if (_currentIndex >= endIndex) {
170 _currentIndex += wps.size();
174 BOOST_FOREACH(WayptRef wp, wps) {
175 newLegs.push_back(new Leg(this, wp));
179 _waypointsChanged = true;
180 _legs.insert(it, newLegs.begin(), newLegs.end());
184 void FlightPlan::deleteIndex(int aIndex)
187 if (aIndex < 0) { // negative indices count the the end
188 index = _legs.size() + index;
191 if ((index < 0) || (index >= numLegs())) {
192 SG_LOG(SG_AUTOPILOT, SG_WARN, "removeAtIndex with invalid index:" << aIndex);
197 _waypointsChanged = true;
199 LegVec::iterator it = _legs.begin();
205 if (_currentIndex == index) {
206 // current waypoint was removed
207 _currentWaypointChanged = true;
208 } else if (_currentIndex > index) {
209 --_currentIndex; // shift current index down if necessary
215 void FlightPlan::clear()
218 _waypointsChanged = true;
219 _currentWaypointChanged = true;
220 _arrivalChanged = true;
221 _departureChanged = true;
224 BOOST_FOREACH(Leg* l, _legs) {
235 RemoveWithFlag(WayptFlag f) : flag(f), delCount(0) { }
237 int numDeleted() const { return delCount; }
239 bool operator()(FlightPlan::Leg* leg) const
241 if (leg->waypoint()->flag(flag)) {
242 std::cout << "deleting" << leg << std::endl;
252 mutable int delCount;
255 int FlightPlan::clearWayptsWithFlag(WayptFlag flag)
258 // first pass, fix up currentIndex
259 for (int i=0; i<_currentIndex; ++i) {
261 if (l->waypoint()->flag(flag)) {
266 _currentIndex -= count;
268 // now delete and remove
269 RemoveWithFlag rf(flag);
270 LegVec::iterator it = std::remove_if(_legs.begin(), _legs.end(), rf);
271 if (it == _legs.end()) {
272 return 0; // nothing was cleared, don't fire the delegate
276 _waypointsChanged = true;
278 _currentWaypointChanged = true;
281 _legs.erase(it, _legs.end());
283 return rf.numDeleted();
286 void FlightPlan::setCurrentIndex(int index)
288 if ((index < -1) || (index >= numLegs())) {
289 throw sg_range_exception("invalid leg index", "FlightPlan::setCurrentIndex");
292 if (index == _currentIndex) {
297 _currentIndex = index;
298 _currentWaypointChanged = true;
302 int FlightPlan::findWayptIndex(const SGGeod& aPos) const
304 for (int i=0; i<numLegs(); ++i) {
305 if (_legs[i]->waypoint()->matches(aPos)) {
313 FlightPlan::Leg* FlightPlan::currentLeg() const
315 if ((_currentIndex < 0) || (_currentIndex >= numLegs()))
317 return legAtIndex(_currentIndex);
320 FlightPlan::Leg* FlightPlan::previousLeg() const
322 if (_currentIndex == 0) {
326 return legAtIndex(_currentIndex - 1);
329 FlightPlan::Leg* FlightPlan::nextLeg() const
331 if ((_currentIndex < 0) || ((_currentIndex + 1) >= numLegs())) {
335 return legAtIndex(_currentIndex + 1);
338 FlightPlan::Leg* FlightPlan::legAtIndex(int index) const
340 if ((index < 0) || (index >= numLegs())) {
341 throw sg_range_exception("index out of range", "FlightPlan::legAtIndex");
347 int FlightPlan::findLegIndex(const Leg *l) const
349 for (unsigned int i=0; i<_legs.size(); ++i) {
358 void FlightPlan::setDeparture(FGAirport* apt)
360 if (apt == _departure) {
365 _departureChanged = true;
367 _departureRunway = NULL;
372 void FlightPlan::setDeparture(FGRunway* rwy)
374 if (_departureRunway == rwy) {
379 _departureChanged = true;
381 _departureRunway = rwy;
382 if (rwy->airport() != _departure) {
383 _departure = rwy->airport();
389 void FlightPlan::setSID(SID* sid, const std::string& transition)
396 _departureChanged = true;
398 _sidTransition = transition;
402 void FlightPlan::setSID(Transition* trans)
409 if (trans->parent()->type() != PROCEDURE_SID)
410 throw sg_exception("FlightPlan::setSID: transition does not belong to a SID");
412 setSID((SID*) trans->parent(), trans->ident());
415 Transition* FlightPlan::sidTransition() const
417 if (!_sid || _sidTransition.empty()) {
421 return _sid->findTransitionByName(_sidTransition);
424 void FlightPlan::setDestination(FGAirport* apt)
426 if (apt == _destination) {
431 _arrivalChanged = true;
433 _destinationRunway = NULL;
434 setSTAR((STAR*)NULL);
439 void FlightPlan::setDestination(FGRunway* rwy)
441 if (_destinationRunway == rwy) {
446 _arrivalChanged = true;
447 _destinationRunway = rwy;
448 if (_destination != rwy->airport()) {
449 _destination = rwy->airport();
450 setSTAR((STAR*)NULL);
456 void FlightPlan::setSTAR(STAR* star, const std::string& transition)
463 _arrivalChanged = true;
465 _starTransition = transition;
469 void FlightPlan::setSTAR(Transition* trans)
472 setSTAR((STAR*) NULL);
476 if (trans->parent()->type() != PROCEDURE_STAR)
477 throw sg_exception("FlightPlan::setSTAR: transition does not belong to a STAR");
479 setSTAR((STAR*) trans->parent(), trans->ident());
482 Transition* FlightPlan::starTransition() const
484 if (!_star || _starTransition.empty()) {
488 return _star->findTransitionByName(_starTransition);
491 void FlightPlan::setApproach(flightgear::Approach *app)
493 if (_approach == app) {
498 _arrivalChanged = true;
501 // keep runway + airport in sync
502 if (_destinationRunway != _approach->runway()) {
503 _destinationRunway = _approach->runway();
506 if (_destination != _destinationRunway->airport()) {
507 _destination = _destinationRunway->airport();
513 bool FlightPlan::save(const SGPath& path)
515 SG_LOG(SG_IO, SG_INFO, "Saving route to " << path.str());
517 SGPropertyNode_ptr d(new SGPropertyNode);
518 d->setIntValue("version", 2);
521 d->setStringValue("departure/airport", _departure->ident());
523 d->setStringValue("departure/sid", _sid->ident());
526 if (_departureRunway) {
527 d->setStringValue("departure/runway", _departureRunway->ident());
532 d->setStringValue("destination/airport", _destination->ident());
534 d->setStringValue("destination/star", _star->ident());
538 d->setStringValue("destination/approach", _approach->ident());
541 //d->setStringValue("destination/transition", destination->getStringValue("transition"));
543 if (_destinationRunway) {
544 d->setStringValue("destination/runway", _destinationRunway->ident());
549 SGPropertyNode* routeNode = d->getChild("route", 0, true);
550 for (unsigned int i=0; i<_legs.size(); ++i) {
551 Waypt* wpt = _legs[i]->waypoint();
552 wpt->saveAsNode(routeNode->getChild("wp", i, true));
553 } // of waypoint iteration
554 writeProperties(path.str(), d, true /* write-all */);
556 } catch (sg_exception& e) {
557 SG_LOG(SG_IO, SG_ALERT, "Failed to save flight-plan '" << path.str() << "'. " << e.getMessage());
562 bool FlightPlan::load(const SGPath& path)
566 SG_LOG(SG_IO, SG_ALERT, "Failed to load flight-plan '" << path.str()
567 << "'. The file does not exist.");
571 SGPropertyNode_ptr routeData(new SGPropertyNode);
572 SG_LOG(SG_IO, SG_INFO, "going to read flight-plan from:" << path.str());
577 readProperties(path.str(), routeData);
578 } catch (sg_exception& ) {
579 // if XML parsing fails, the file might be simple textual list of waypoints
580 Status = loadPlainTextRoute(path);
582 _waypointsChanged = true;
585 if (routeData.valid())
588 int version = routeData->getIntValue("version", 1);
590 loadVersion1XMLRoute(routeData);
591 } else if (version == 2) {
592 loadVersion2XMLRoute(routeData);
594 throw sg_io_exception("unsupported XML route version");
597 } catch (sg_exception& e) {
598 SG_LOG(SG_IO, SG_ALERT, "Failed to load flight-plan '" << e.getOrigin()
599 << "'. " << e.getMessage());
608 void FlightPlan::loadXMLRouteHeader(SGPropertyNode_ptr routeData)
611 SGPropertyNode* dep = routeData->getChild("departure");
613 string depIdent = dep->getStringValue("airport");
614 setDeparture((FGAirport*) fgFindAirportID(depIdent));
616 if (dep->hasChild("runway")) {
617 setDeparture(_departure->getRunwayByIdent(dep->getStringValue("runway")));
620 if (dep->hasChild("sid")) {
621 setSID(_departure->findSIDWithIdent(dep->getStringValue("sid")));
623 // departure->setStringValue("transition", dep->getStringValue("transition"));
628 SGPropertyNode* dst = routeData->getChild("destination");
630 setDestination((FGAirport*) fgFindAirportID(dst->getStringValue("airport")));
632 if (dst->hasChild("runway")) {
633 setDestination(_destination->getRunwayByIdent(dst->getStringValue("runway")));
636 if (dst->hasChild("star")) {
637 setSTAR(_destination->findSTARWithIdent(dst->getStringValue("star")));
640 if (dst->hasChild("approach")) {
641 setApproach(_destination->findApproachWithIdent(dst->getStringValue("approach")));
645 // destination->setStringValue("transition", dst->getStringValue("transition"));
649 SGPropertyNode* alt = routeData->getChild("alternate");
651 //alternate->setStringValue(alt->getStringValue("airport"));
655 SGPropertyNode* crs = routeData->getChild("cruise");
657 // cruise->setDoubleValue("speed-kts", crs->getDoubleValue("speed-kts"));
658 // cruise->setDoubleValue("mach", crs->getDoubleValue("mach"));
659 // cruise->setDoubleValue("altitude-ft", crs->getDoubleValue("altitude-ft"));
660 } // of cruise data loading
664 void FlightPlan::loadVersion2XMLRoute(SGPropertyNode_ptr routeData)
666 loadXMLRouteHeader(routeData);
670 SGPropertyNode_ptr routeNode = routeData->getChild("route", 0);
671 for (int i=0; i<routeNode->nChildren(); ++i) {
672 SGPropertyNode_ptr wpNode = routeNode->getChild("wp", i);
673 Leg* l = new Leg(this, Waypt::createFromProperties(NULL, wpNode));
675 } // of route iteration
676 _waypointsChanged = true;
679 void FlightPlan::loadVersion1XMLRoute(SGPropertyNode_ptr routeData)
681 loadXMLRouteHeader(routeData);
685 SGPropertyNode_ptr routeNode = routeData->getChild("route", 0);
686 for (int i=0; i<routeNode->nChildren(); ++i) {
687 SGPropertyNode_ptr wpNode = routeNode->getChild("wp", i);
688 Leg* l = new Leg(this, parseVersion1XMLWaypt(wpNode));
690 } // of route iteration
691 _waypointsChanged = true;
694 WayptRef FlightPlan::parseVersion1XMLWaypt(SGPropertyNode* aWP)
697 if (!_legs.empty()) {
698 lastPos = _legs.back()->waypoint()->position();
699 } else if (_departure) {
700 lastPos = _departure->geod();
704 string ident(aWP->getStringValue("ident"));
705 if (aWP->hasChild("longitude-deg")) {
706 // explicit longitude/latitude
707 w = new BasicWaypt(SGGeod::fromDeg(aWP->getDoubleValue("longitude-deg"),
708 aWP->getDoubleValue("latitude-deg")), ident, NULL);
711 string nid = aWP->getStringValue("navid", ident.c_str());
712 FGPositionedRef p = FGPositioned::findClosestWithIdent(nid, lastPos);
714 throw sg_io_exception("bad route file, unknown navid:" + nid);
717 SGGeod pos(p->geod());
718 if (aWP->hasChild("offset-nm") && aWP->hasChild("offset-radial")) {
719 double radialDeg = aWP->getDoubleValue("offset-radial");
720 // convert magnetic radial to a true radial!
721 radialDeg += magvarDegAt(pos);
722 double offsetNm = aWP->getDoubleValue("offset-nm");
724 SGGeodesy::direct(p->geod(), radialDeg, offsetNm * SG_NM_TO_METER, pos, az2);
727 w = new BasicWaypt(pos, ident, NULL);
730 double altFt = aWP->getDoubleValue("altitude-ft", -9999.9);
731 if (altFt > -9990.0) {
732 w->setAltitude(altFt, RESTRICT_AT);
738 bool FlightPlan::loadPlainTextRoute(const SGPath& path)
741 sg_gzifstream in(path.str().c_str());
743 throw sg_io_exception("Cannot open file for reading.");
749 getline(in, line, '\n');
750 // trim CR from end of line, if found
751 if (line[line.size() - 1] == '\r') {
752 line.erase(line.size() - 1, 1);
755 line = simgear::strutils::strip(line);
756 if (line.empty() || (line[0] == '#')) {
757 continue; // ignore empty/comment lines
760 WayptRef w = waypointFromString(line);
762 throw sg_io_exception("Failed to create waypoint from line '" + line + "'.");
765 _legs.push_back(new Leg(this, w));
766 } // of line iteration
767 } catch (sg_exception& e) {
768 SG_LOG(SG_IO, SG_ALERT, "Failed to load route from: '" << path.str() << "'. " << e.getMessage());
776 double FlightPlan::magvarDegAt(const SGGeod& pos) const
778 double jd = globals->get_time_params()->getJD();
779 return sgGetMagVar(pos, jd) * SG_RADIANS_TO_DEGREES;
782 WayptRef FlightPlan::waypointFromString(const string& tgt )
784 string target(boost::to_upper_copy(tgt));
789 RouteRestriction altSetting = RESTRICT_NONE;
791 size_t pos = target.find( '@' );
792 if ( pos != string::npos ) {
793 altFt = atof( target.c_str() + pos + 1 );
794 target = target.substr( 0, pos );
795 if ( !strcmp(fgGetString("/sim/startup/units"), "meter") )
796 altFt *= SG_METER_TO_FEET;
797 altSetting = RESTRICT_AT;
801 pos = target.find( ',' );
802 if ( pos != string::npos ) {
803 double lon = atof( target.substr(0, pos).c_str());
804 double lat = atof( target.c_str() + pos + 1);
806 char ew = (lon < 0.0) ? 'W' : 'E';
807 char ns = (lat < 0.0) ? 'S' : 'N';
808 snprintf(buf, 32, "%c%03d%c%03d", ew, (int) fabs(lon), ns, (int)fabs(lat));
810 wpt = new BasicWaypt(SGGeod::fromDeg(lon, lat), buf, NULL);
811 if (altSetting != RESTRICT_NONE) {
812 wpt->setAltitude(altFt, altSetting);
819 // route is empty, use current position
820 basePosition = globals->get_aircraft_position();
822 basePosition = _legs.back()->waypoint()->position();
825 string_list pieces(simgear::strutils::split(target, "/"));
826 FGPositionedRef p = FGPositioned::findClosestWithIdent(pieces.front(), basePosition);
828 SG_LOG( SG_AUTOPILOT, SG_INFO, "Unable to find FGPositioned with ident:" << pieces.front());
832 double magvar = magvarDegAt(basePosition);
834 if (pieces.size() == 1) {
835 wpt = new NavaidWaypoint(p, NULL);
836 } else if (pieces.size() == 3) {
837 // navaid/radial/distance-nm notation
838 double radial = atof(pieces[1].c_str()),
839 distanceNm = atof(pieces[2].c_str());
841 wpt = new OffsetNavaidWaypoint(p, NULL, radial, distanceNm);
842 } else if (pieces.size() == 2) {
843 FGAirport* apt = dynamic_cast<FGAirport*>(p.ptr());
845 SG_LOG(SG_AUTOPILOT, SG_INFO, "Waypoint is not an airport:" << pieces.front());
849 if (!apt->hasRunwayWithIdent(pieces[1])) {
850 SG_LOG(SG_AUTOPILOT, SG_INFO, "No runway: " << pieces[1] << " at " << pieces[0]);
854 FGRunway* runway = apt->getRunwayByIdent(pieces[1]);
855 wpt = new NavaidWaypoint(runway, NULL);
856 } else if (pieces.size() == 4) {
857 // navid/radial/navid/radial notation
858 FGPositionedRef p2 = FGPositioned::findClosestWithIdent(pieces[2], basePosition);
860 SG_LOG( SG_AUTOPILOT, SG_INFO, "Unable to find FGPositioned with ident:" << pieces[2]);
864 double r1 = atof(pieces[1].c_str()),
865 r2 = atof(pieces[3].c_str());
870 bool ok = SGGeodesy::radialIntersection(p->geod(), r1, p2->geod(), r2, intersection);
872 SG_LOG(SG_AUTOPILOT, SG_INFO, "no valid intersection for:" << target);
876 std::string name = p->ident() + "-" + p2->ident();
877 wpt = new BasicWaypt(intersection, name, NULL);
881 SG_LOG(SG_AUTOPILOT, SG_INFO, "Unable to parse waypoint:" << target);
885 if (altSetting != RESTRICT_NONE) {
886 wpt->setAltitude(altFt, altSetting);
891 FlightPlan::Leg::Leg(FlightPlan* owner, WayptRef wpt) :
893 _speedRestrict(RESTRICT_NONE),
894 _altRestrict(RESTRICT_NONE),
898 throw sg_exception("can't create FlightPlan::Leg without underlying waypoint");
900 _speed = _altitudeFt = 0;
903 FlightPlan::Leg* FlightPlan::Leg::cloneFor(FlightPlan* owner) const
905 Leg* c = new Leg(owner, _waypt);
908 c->_speedRestrict = _speedRestrict;
909 c->_altitudeFt = _altitudeFt;
910 c->_altRestrict = _altRestrict;
915 FlightPlan::Leg* FlightPlan::Leg::nextLeg() const
917 return _parent->legAtIndex(index() + 1);
920 unsigned int FlightPlan::Leg::index() const
922 return _parent->findLegIndex(this);
925 int FlightPlan::Leg::altitudeFt() const
927 if (_altRestrict != RESTRICT_NONE) {
931 return _waypt->altitudeFt();
934 int FlightPlan::Leg::speed() const
936 if (_speedRestrict != RESTRICT_NONE) {
940 return _waypt->speed();
943 int FlightPlan::Leg::speedKts() const
948 double FlightPlan::Leg::speedMach() const
950 if (!isMachRestrict(_speedRestrict)) {
954 return -(_speed / 100.0);
957 RouteRestriction FlightPlan::Leg::altitudeRestriction() const
959 if (_altRestrict != RESTRICT_NONE) {
963 return _waypt->altitudeRestriction();
966 RouteRestriction FlightPlan::Leg::speedRestriction() const
968 if (_speedRestrict != RESTRICT_NONE) {
969 return _speedRestrict;
972 return _waypt->speedRestriction();
975 void FlightPlan::Leg::setSpeed(RouteRestriction ty, double speed)
978 if (isMachRestrict(ty)) {
979 _speed = (speed * -100);
985 void FlightPlan::Leg::setAltitude(RouteRestriction ty, int altFt)
991 double FlightPlan::Leg::courseDeg() const
996 double FlightPlan::Leg::distanceNm() const
998 return _pathDistance;
1001 double FlightPlan::Leg::distanceAlongRoute() const
1003 return _distanceAlongPath;
1006 void FlightPlan::rebuildLegData()
1008 _totalDistance = 0.0;
1009 int lastLeg = static_cast<int>(_legs.size()) - 1;
1010 for (int l=0; l<lastLeg; ++l) {
1011 Leg* cur = _legs[l];
1012 Leg* next = _legs[l + 1];
1014 std::pair<double, double> crsDist =
1015 next->waypoint()->courseAndDistanceFrom(cur->waypoint()->position());
1016 _legs[l]->_courseDeg = crsDist.first;
1017 _legs[l]->_pathDistance = crsDist.second * SG_METER_TO_NM;
1018 _legs[l]->_distanceAlongPath = _totalDistance;
1019 _totalDistance += crsDist.second * SG_METER_TO_NM;
1020 } // of legs iteration
1023 void FlightPlan::lockDelegate()
1025 if (_delegateLock == 0) {
1026 assert(!_departureChanged && !_arrivalChanged &&
1027 !_waypointsChanged && !_currentWaypointChanged);
1033 void FlightPlan::unlockDelegate()
1035 assert(_delegateLock > 0);
1036 if (_delegateLock > 1) {
1041 if (_departureChanged) {
1042 _departureChanged = false;
1044 _delegate->runDepartureChanged();
1048 if (_arrivalChanged) {
1049 _arrivalChanged = false;
1051 _delegate->runArrivalChanged();
1055 if (_waypointsChanged) {
1056 _waypointsChanged = false;
1059 _delegate->runWaypointsChanged();
1063 if (_currentWaypointChanged) {
1064 _currentWaypointChanged = false;
1066 _delegate->runCurrentWaypointChanged();
1073 void FlightPlan::registerDelegateFactory(DelegateFactory* df)
1075 FPDelegateFactoryVec::iterator it = std::find(static_delegateFactories.begin(),
1076 static_delegateFactories.end(), df);
1077 if (it != static_delegateFactories.end()) {
1078 throw sg_exception("duplicate delegate factory registration");
1081 static_delegateFactories.push_back(df);
1084 void FlightPlan::unregisterDelegateFactory(DelegateFactory* df)
1086 FPDelegateFactoryVec::iterator it = std::find(static_delegateFactories.begin(),
1087 static_delegateFactories.end(), df);
1088 if (it == static_delegateFactories.end()) {
1092 static_delegateFactories.erase(it);
1095 void FlightPlan::addDelegate(Delegate* d)
1097 // wrap any existing delegate(s) in the new one
1098 d->_inner = _delegate;
1102 void FlightPlan::removeDelegate(Delegate* d)
1104 if (d == _delegate) {
1105 _delegate = _delegate->_inner;
1106 } else if (_delegate) {
1107 _delegate->removeInner(d);
1111 FlightPlan::Delegate::Delegate() :
1112 _deleteWithPlan(false),
1118 FlightPlan::Delegate::~Delegate()
1123 void FlightPlan::Delegate::removeInner(Delegate* d)
1130 // replace with grand-child
1132 } else { // recurse downwards
1133 _inner->removeInner(d);
1137 void FlightPlan::Delegate::runDepartureChanged()
1139 if (_inner) _inner->runDepartureChanged();
1143 void FlightPlan::Delegate::runArrivalChanged()
1145 if (_inner) _inner->runArrivalChanged();
1149 void FlightPlan::Delegate::runWaypointsChanged()
1151 if (_inner) _inner->runWaypointsChanged();
1155 void FlightPlan::Delegate::runCurrentWaypointChanged()
1157 if (_inner) _inner->runCurrentWaypointChanged();
1158 currentWaypointChanged();
1161 } // of namespace flightgear