]> git.mxchange.org Git - flightgear.git/blob - src/Main/fg_props.cxx
816c5f543f4b664566224160c1dec0f21e1be69b
[flightgear.git] / src / Main / fg_props.cxx
1 // fg_props.cxx -- support for FlightGear properties.
2 //
3 // Written by David Megginson, started 2000.
4 //
5 // Copyright (C) 2000, 2001 David Megginson - david@megginson.com
6 //
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.
11 //
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.
16 //
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., 675 Mass Ave, Cambridge, MA 02139, USA.
20 //
21 // $Id$
22
23 #ifdef HAVE_CONFIG_H
24 #  include <simgear/compiler.h>
25 #endif
26
27 #include <simgear/misc/exception.hxx>
28
29 #include STL_IOSTREAM
30
31 #include <ATC/ATCdisplay.hxx>
32 #include <Autopilot/newauto.hxx>
33 #include <Aircraft/aircraft.hxx>
34 #include <Time/tmp.hxx>
35 #include <FDM/UIUCModel/uiuc_aircraftdir.h>
36 #ifndef FG_OLD_WEATHER
37 #  include <WeatherCM/FGLocalWeatherDatabase.h>
38 #else
39 #  include <Weather/weather.hxx>
40 #endif // FG_OLD_WEATHER
41 #include <Objects/matlib.hxx>
42
43 #include <GUI/gui.h>
44
45 #include "globals.hxx"
46 #include "fgfs.hxx"
47 #include "fg_props.hxx"
48 #include "viewmgr.hxx"
49
50 #if !defined(SG_HAVE_NATIVE_SGI_COMPILERS)
51 SG_USING_STD(istream);
52 SG_USING_STD(ostream);
53 #endif
54
55 #if !defined(FG_OLD_WEATHER)
56 static double getWindNorth ();
57 static double getWindEast ();
58 static double getWindDown ();
59 #endif // FG_OLD_WEATHER
60
61 // Allow the view to be set from two axes (i.e. a joystick hat)
62 // This needs to be in FGViewer itself, somehow.
63 static double axisLong = 0.0;
64 static double axisLat = 0.0;
65
66 static bool winding_ccw = true; // FIXME: temporary
67
68 static bool fdm_data_logging = false; // FIXME: temporary
69
70
71 /**
72  * Utility function.
73  */
74 static inline void
75 _set_view_from_axes ()
76 {
77                                 // Take no action when hat is centered
78   if ( ( axisLong <  0.01 ) &&
79        ( axisLong > -0.01 ) &&
80        ( axisLat  <  0.01 ) &&
81        ( axisLat  > -0.01 )
82      )
83     return;
84
85   double viewDir = 999;
86
87   /* Do all the quick and easy cases */
88   if (axisLong < 0) {           // Longitudinal axis forward
89     if (axisLat == axisLong)
90       viewDir = 45;
91     else if (axisLat == - axisLong)
92       viewDir = 315;
93     else if (axisLat == 0)
94       viewDir = 0;
95   } else if (axisLong > 0) {    // Longitudinal axis backward
96     if (axisLat == - axisLong)
97       viewDir = 135;
98     else if (axisLat == axisLong)
99       viewDir = 225;
100     else if (axisLat == 0)
101       viewDir = 180;
102   } else if (axisLong == 0) {   // Longitudinal axis neutral
103     if (axisLat < 0)
104       viewDir = 90;
105     else if (axisLat > 0)
106       viewDir = 270;
107     else return; /* And assertion failure maybe? */
108   }
109
110   /* Do all the difficult cases */
111   if ( viewDir > 900 )
112     viewDir = SGD_RADIANS_TO_DEGREES * atan2 ( -axisLat, -axisLong );
113   if ( viewDir < -1 ) viewDir += 360;
114
115 //  SG_LOG(SG_INPUT, SG_ALERT, "Joystick Lat=" << axisLat << "   and Long="
116 //      << axisLong << "  gave angle=" << viewDir );
117
118   globals->get_current_view()->set_goal_view_offset(viewDir*SGD_DEGREES_TO_RADIANS);
119 //   globals->get_current_view()->set_view_offset(viewDir*SGD_DEGREES_TO_RADIANS);
120 }
121
122 \f
123 ////////////////////////////////////////////////////////////////////////
124 // Default property bindings (not yet handled by any module).
125 ////////////////////////////////////////////////////////////////////////
126
127 struct LogClassMapping {
128   sgDebugClass c;
129   string name;
130   LogClassMapping(sgDebugClass cc, string nname) { c = cc; name = nname; }
131 };
132
133 LogClassMapping log_class_mappings [] = {
134   LogClassMapping(SG_NONE, "none"),
135   LogClassMapping(SG_TERRAIN, "terrain"),
136   LogClassMapping(SG_ASTRO, "astro"),
137   LogClassMapping(SG_FLIGHT, "flight"),
138   LogClassMapping(SG_INPUT, "input"),
139   LogClassMapping(SG_GL, "gl"),
140   LogClassMapping(SG_VIEW, "view"),
141   LogClassMapping(SG_COCKPIT, "cockpit"),
142   LogClassMapping(SG_GENERAL, "general"),
143   LogClassMapping(SG_MATH, "math"),
144   LogClassMapping(SG_EVENT, "event"),
145   LogClassMapping(SG_AIRCRAFT, "aircraft"),
146   LogClassMapping(SG_AUTOPILOT, "autopilot"),
147   LogClassMapping(SG_IO, "io"),
148   LogClassMapping(SG_CLIPPER, "clipper"),
149   LogClassMapping(SG_NETWORK, "network"),
150   LogClassMapping(SG_UNDEFD, "")
151 };
152
153
154 /**
155  * Get the logging classes.
156  */
157 static string
158 getLoggingClasses ()
159 {
160   sgDebugClass classes = logbuf::get_log_classes();
161   string result = "";
162   for (int i = 0; log_class_mappings[i].c != SG_UNDEFD; i++) {
163     if ((classes&log_class_mappings[i].c) > 0) {
164       if (result != (string)"")
165         result += '|';
166       result += log_class_mappings[i].name;
167     }
168   }
169   return result;
170 }
171
172
173 static void addLoggingClass (const string &name)
174 {
175   sgDebugClass classes = logbuf::get_log_classes();
176   for (int i = 0; log_class_mappings[i].c != SG_UNDEFD; i++) {
177     if (name == log_class_mappings[i].name) {
178       logbuf::set_log_classes(sgDebugClass(classes|log_class_mappings[i].c));
179       return;
180     }
181   }
182   SG_LOG(SG_GENERAL, SG_ALERT, "Unknown logging class: " << name);
183 }
184
185
186 /**
187  * Set the logging classes.
188  */
189 static void
190 setLoggingClasses (string classes)
191 {
192   logbuf::set_log_classes(SG_NONE);
193
194   if (classes == "none") {
195     SG_LOG(SG_GENERAL, SG_INFO, "Disabled all logging classes");
196     return;
197   }
198
199   if (classes == "" || classes == "all") { // default
200     logbuf::set_log_classes(SG_ALL);
201     SG_LOG(SG_GENERAL, SG_INFO, "Enabled all logging classes: "
202            << getLoggingClasses());
203     return;
204   }
205
206   string rest = classes;
207   string name = "";
208   int sep = rest.find('|');
209   while (sep > 0) {
210     name = rest.substr(0, sep);
211     rest = rest.substr(sep+1);
212     addLoggingClass(name);
213     sep = rest.find('|');
214   }
215   addLoggingClass(rest);
216   SG_LOG(SG_GENERAL, SG_INFO, "Set logging classes to "
217          << getLoggingClasses());
218 }
219
220
221 /**
222  * Get the logging priority.
223  */
224 static string
225 getLoggingPriority ()
226 {
227   switch (logbuf::get_log_priority()) {
228   case SG_BULK:
229     return "bulk";
230   case SG_DEBUG:
231     return "debug";
232   case SG_INFO:
233     return "info";
234   case SG_WARN:
235     return "warn";
236   case SG_ALERT:
237     return "alert";
238   default:
239     SG_LOG(SG_GENERAL, SG_WARN, "Internal: Unknown logging priority number: "
240            << logbuf::get_log_priority());
241     return "unknown";
242   }
243 }
244
245
246 /**
247  * Set the logging priority.
248  */
249 static void
250 setLoggingPriority (string priority)
251 {
252   if (priority == "bulk") {
253     logbuf::set_log_priority(SG_BULK);
254   } else if (priority == "debug") {
255     logbuf::set_log_priority(SG_DEBUG);
256   } else if (priority == "" || priority == "info") { // default
257     logbuf::set_log_priority(SG_INFO);
258   } else if (priority == "warn") {
259     logbuf::set_log_priority(SG_WARN);
260   } else if (priority == "alert") {
261     logbuf::set_log_priority(SG_ALERT);
262   } else {
263     SG_LOG(SG_GENERAL, SG_WARN, "Unknown logging priority " << priority);
264   }
265   SG_LOG(SG_GENERAL, SG_INFO, "Logging priority is " << getLoggingPriority());
266 }
267
268
269 #if 0
270 /**
271  * Get the pause state of the sim.
272  */
273 static bool
274 getFreeze ()
275 {
276   return globals->get_freeze();
277 }
278
279
280 /**
281  * Set the pause state of the sim.
282  */
283 static void
284 setFreeze (bool freeze)
285 {
286     globals->set_freeze(freeze);
287     if ( freeze ) {
288         // BusyCursor( 0 );
289         current_atcdisplay->CancelRepeatingMessage();
290         current_atcdisplay->RegisterRepeatingMessage("****    SIM IS FROZEN    ****    SIM IS FROZEN    ****");
291     } else {
292         // BusyCursor( 1 );
293         current_atcdisplay->CancelRepeatingMessage();
294     }
295 }
296 #endif
297
298 /**
299  * Return the current aircraft directory (UIUC) as a string.
300  */
301 static string 
302 getAircraftDir ()
303 {
304   return aircraft_dir;
305 }
306
307
308 /**
309  * Set the current aircraft directory (UIUC).
310  */
311 static void
312 setAircraftDir (string dir)
313 {
314   if (getAircraftDir() != dir) {
315     aircraft_dir = dir;
316 //     needReinit(); FIXME!!
317   }
318 }
319
320
321 /**
322  * Get the current view offset in degrees.
323  */
324 static double
325 getViewOffset ()
326 {
327   return (globals->get_current_view()
328           ->get_view_offset() * SGD_RADIANS_TO_DEGREES);
329 }
330
331
332 static void
333 setViewOffset (double offset)
334 {
335   globals->get_current_view()->set_view_offset(offset * SGD_DEGREES_TO_RADIANS);
336 }
337
338 static double
339 getGoalViewOffset ()
340 {
341   return (globals->get_current_view()
342           ->get_goal_view_offset() * SGD_RADIANS_TO_DEGREES);
343 }
344
345 static void
346 setGoalViewOffset (double offset)
347 {
348     while ( offset < 0 ) {
349         offset += 360.0;
350     }
351     while ( offset > 360.0 ) {
352         offset -= 360.0;
353     }
354     // Snap to center if we are close
355     if ( fabs(offset) < 1.0 ||  fabs(offset) > 359.0 ) {
356         offset = 0.0;
357     }
358
359     globals->get_current_view()
360         ->set_goal_view_offset(offset * SGD_DEGREES_TO_RADIANS);
361 }
362
363 /**
364  * Get the current view tilt in degrees.
365  */
366 static double
367 getViewTilt ()
368 {
369   return (globals->get_current_view()
370           ->get_view_tilt() * SGD_RADIANS_TO_DEGREES);
371 }
372
373
374 static void
375 setViewTilt (double tilt)
376 {
377   globals->get_current_view()->set_view_tilt(tilt * SGD_DEGREES_TO_RADIANS);
378 }
379
380 static double
381 getGoalViewTilt ()
382 {
383   return (globals->get_current_view()
384           ->get_goal_view_tilt() * SGD_RADIANS_TO_DEGREES);
385 }
386
387 static void
388 setGoalViewTilt (double tilt)
389 {
390     while ( tilt < 0 ) {
391         tilt += 360.0;
392     }
393     while ( tilt > 360.0 ) {
394         tilt -= 360.0;
395     }
396     // Snap to center if we are close
397     if ( fabs(tilt) < 1.0 ||  fabs(tilt) > 359.0 ) {
398         tilt = 0.0;
399     }
400
401     globals->get_current_view()
402         ->set_goal_view_tilt(tilt * SGD_DEGREES_TO_RADIANS);
403 }
404
405
406 /**
407  * Pilot position offset from CG.
408  */
409 static float
410 getPilotPositionXOffset ()
411 {
412   FGViewer * pilot_view = globals->get_viewmgr()->get_view(0);
413   float * offset = pilot_view->get_pilot_offset();
414   return offset[0];
415 }
416
417 static void
418 setPilotPositionXOffset (float x)
419 {
420   FGViewer * pilot_view = globals->get_viewmgr()->get_view(0);
421   float * offset = pilot_view->get_pilot_offset();
422   pilot_view->set_pilot_offset(x, offset[1], offset[2]);
423 }
424
425 static float
426 getPilotPositionYOffset ()
427 {
428   FGViewer * pilot_view = globals->get_viewmgr()->get_view(0);
429   float * offset = pilot_view->get_pilot_offset();
430   return offset[1];
431 }
432
433 static void
434 setPilotPositionYOffset (float y)
435 {
436   FGViewer * pilot_view = globals->get_viewmgr()->get_view(0);
437   float * offset = pilot_view->get_pilot_offset();
438   pilot_view->set_pilot_offset(offset[0], y, offset[2]);
439 }
440
441 static float
442 getPilotPositionZOffset ()
443 {
444   FGViewer * pilot_view = globals->get_viewmgr()->get_view(0);
445   float * offset = pilot_view->get_pilot_offset();
446   return offset[2];
447 }
448
449 static void
450 setPilotPositionZOffset (float z)
451 {
452   FGViewer * pilot_view = globals->get_viewmgr()->get_view(0);
453   float * offset = pilot_view->get_pilot_offset();
454   pilot_view->set_pilot_offset(offset[0], offset[1], z);
455 }
456
457
458 /**
459  * Return the current Zulu time.
460  */
461 static string 
462 getDateString ()
463 {
464   string out;
465   char buf[64];
466   struct tm * t = globals->get_time_params()->getGmt();
467   sprintf(buf, "%.4d-%.2d-%.2dT%.2d:%.2d:%.2d",
468           t->tm_year + 1900, t->tm_mon + 1, t->tm_mday,
469           t->tm_hour, t->tm_min, t->tm_sec);
470   out = buf;
471   return out;
472 }
473
474
475 /**
476  * Set the current Zulu time.
477  */
478 static void
479 setDateString (string date_string)
480 {
481   static const SGPropertyNode *cur_time_override
482         = fgGetNode("/sim/time/cur-time-override", true);
483
484   SGTime * st = globals->get_time_params();
485   struct tm * current_time = st->getGmt();
486   struct tm new_time;
487
488                                 // Scan for basic ISO format
489                                 // YYYY-MM-DDTHH:MM:SS
490   int ret = sscanf(date_string.c_str(), "%d-%d-%dT%d:%d:%d",
491                    &(new_time.tm_year), &(new_time.tm_mon),
492                    &(new_time.tm_mday), &(new_time.tm_hour),
493                    &(new_time.tm_min), &(new_time.tm_sec));
494
495                                 // Be pretty picky about this, so
496                                 // that strange things don't happen
497                                 // if the save file has been edited
498                                 // by hand.
499   if (ret != 6) {
500     SG_LOG(SG_INPUT, SG_ALERT, "Date/time string " << date_string
501            << " not in YYYY-MM-DDTHH:MM:SS format; skipped");
502     return;
503   }
504
505                                 // OK, it looks like we got six
506                                 // values, one way or another.
507   new_time.tm_year -= 1900;
508   new_time.tm_mon -= 1;
509
510                                 // Now, tell flight gear to use
511                                 // the new time.  This was far
512                                 // too difficult, by the way.
513   long int warp =
514     mktime(&new_time) - mktime(current_time) + globals->get_warp();
515   double lon = current_aircraft.fdm_state->get_Longitude();
516   double lat = current_aircraft.fdm_state->get_Latitude();
517   globals->set_warp(warp);
518   st->update(lon, lat, cur_time_override->getLongValue(), warp);
519   fgUpdateSkyAndLightingParams();
520 }
521
522 /**
523  * Return the GMT as a string.
524  */
525 static string 
526 getGMTString ()
527 {
528   string out;
529   char buf[16];
530   struct tm *t = globals->get_time_params()->getGmt();
531   sprintf(buf, " %.2d:%.2d:%.2d",
532           t->tm_hour, t->tm_min, t->tm_sec);
533   // cout << t << " " << buf << endl;
534   out = buf;
535   return out;
536 }
537
538
539 /**
540  * Get the texture rendering state.
541  */
542 static bool
543 getTextures ()
544 {
545   return (material_lib.get_step() == 0);
546 }
547
548
549 /**
550  * Set the texture rendering state.
551  */
552 static void
553 setTextures (bool textures)
554 {
555   if (textures)
556     material_lib.set_step(0);
557   else
558     material_lib.set_step(1);
559 }
560
561
562 /**
563  * Return the magnetic variation
564  */
565 static double
566 getMagVar ()
567 {
568   return globals->get_mag()->get_magvar() * SGD_RADIANS_TO_DEGREES;
569 }
570
571
572 /**
573  * Return the magnetic dip
574  */
575 static double
576 getMagDip ()
577 {
578   return globals->get_mag()->get_magdip() * SGD_RADIANS_TO_DEGREES;
579 }
580
581
582 /**
583  * Return the current heading in degrees.
584  */
585 static double
586 getHeadingMag ()
587 {
588   return current_aircraft.fdm_state->get_Psi() * SGD_RADIANS_TO_DEGREES - getMagVar();
589 }
590
591
592 /**
593  * Get the autopilot altitude lock (true=on).
594  */
595 static bool
596 getAPAltitudeLock ()
597 {
598     return (current_autopilot->get_AltitudeEnabled() &&
599             current_autopilot->get_AltitudeMode()
600             == FGAutopilot::FG_ALTITUDE_LOCK);
601 }
602
603
604 /**
605  * Set the autopilot altitude lock (true=on).
606  */
607 static void
608 setAPAltitudeLock (bool lock)
609 {
610   if (lock)
611     current_autopilot->set_AltitudeMode(FGAutopilot::FG_ALTITUDE_LOCK);
612   if (current_autopilot->get_AltitudeMode() == FGAutopilot::FG_ALTITUDE_LOCK)
613     current_autopilot->set_AltitudeEnabled(lock);
614 }
615
616
617 /**
618  * Get the autopilot target altitude in feet.
619  */
620 static double
621 getAPAltitude ()
622 {
623   return current_autopilot->get_TargetAltitude() * SG_METER_TO_FEET;
624 }
625
626
627 /**
628  * Set the autopilot target altitude in feet.
629  */
630 static void
631 setAPAltitude (double altitude)
632 {
633   current_autopilot->set_TargetAltitude( altitude * SG_FEET_TO_METER );
634 }
635
636 /**
637  * Get the autopilot altitude lock (true=on).
638  */
639 static bool
640 getAPGSLock ()
641 {
642     return (current_autopilot->get_AltitudeEnabled() &&
643             (current_autopilot->get_AltitudeMode()
644              == FGAutopilot::FG_ALTITUDE_GS1));
645 }
646
647
648 /**
649  * Set the autopilot altitude lock (true=on).
650  */
651 static void
652 setAPGSLock (bool lock)
653 {
654   if (lock)
655     current_autopilot->set_AltitudeMode(FGAutopilot::FG_ALTITUDE_GS1);
656   if (current_autopilot->get_AltitudeMode() == FGAutopilot::FG_ALTITUDE_GS1)
657     current_autopilot->set_AltitudeEnabled(lock);
658 }
659
660
661 /**
662  * Get the autopilot terrain lock (true=on).
663  */
664 static bool
665 getAPTerrainLock ()
666 {
667     return (current_autopilot->get_AltitudeEnabled() &&
668             (current_autopilot->get_AltitudeMode()
669              == FGAutopilot::FG_ALTITUDE_TERRAIN));
670 }
671
672
673 /**
674  * Set the autopilot terrain lock (true=on).
675  */
676 static void
677 setAPTerrainLock (bool lock)
678 {
679   if (lock) {
680     current_autopilot->set_AltitudeMode(FGAutopilot::FG_ALTITUDE_TERRAIN);
681     current_autopilot
682       ->set_TargetAGL(current_aircraft.fdm_state->get_Altitude_AGL() *
683                       SG_FEET_TO_METER);
684     cout << "Target AGL = "
685          << current_aircraft.fdm_state->get_Altitude_AGL() * SG_FEET_TO_METER
686          << endl;
687   }
688   if (current_autopilot->get_AltitudeMode() == FGAutopilot::FG_ALTITUDE_TERRAIN)
689     current_autopilot->set_AltitudeEnabled(lock);
690 }
691
692
693 /**
694  * Get the autopilot target altitude in feet.
695  */
696 static double
697 getAPClimb ()
698 {
699   return current_autopilot->get_TargetClimbRate() * SG_METER_TO_FEET;
700 }
701
702
703 /**
704  * Set the autopilot target altitude in feet.
705  */
706 static void
707 setAPClimb (double rate)
708 {
709     current_autopilot->set_TargetClimbRate( rate * SG_FEET_TO_METER );
710 }
711
712
713 /**
714  * Get the autopilot heading lock (true=on).
715  */
716 static bool
717 getAPHeadingLock ()
718 {
719     return
720       (current_autopilot->get_HeadingEnabled() &&
721        current_autopilot->get_HeadingMode() == DEFAULT_AP_HEADING_LOCK);
722 }
723
724
725 /**
726  * Set the autopilot heading lock (true=on).
727  */
728 static void
729 setAPHeadingLock (bool lock)
730 {
731   if (lock)
732     current_autopilot->set_HeadingMode(DEFAULT_AP_HEADING_LOCK);
733   if (current_autopilot->get_HeadingMode() == DEFAULT_AP_HEADING_LOCK)
734     current_autopilot->set_HeadingEnabled(lock);
735 }
736
737
738 /**
739  * Get the autopilot heading bug in degrees.
740  */
741 static double
742 getAPHeadingBug ()
743 {
744   return current_autopilot->get_DGTargetHeading();
745 }
746
747
748 /**
749  * Set the autopilot heading bug in degrees.
750  */
751 static void
752 setAPHeadingBug (double heading)
753 {
754   current_autopilot->set_DGTargetHeading( heading );
755 }
756
757
758 /**
759  * Get the autopilot wing leveler lock (true=on).
760  */
761 static bool
762 getAPWingLeveler ()
763 {
764     return
765       (current_autopilot->get_HeadingEnabled() &&
766        current_autopilot->get_HeadingMode() == FGAutopilot::FG_TC_HEADING_LOCK);
767 }
768
769
770 /**
771  * Set the autopilot wing leveler lock (true=on).
772  */
773 static void
774 setAPWingLeveler (bool lock)
775 {
776     if (lock)
777         current_autopilot->set_HeadingMode(FGAutopilot::FG_TC_HEADING_LOCK);
778     if (current_autopilot->get_HeadingMode() == FGAutopilot::FG_TC_HEADING_LOCK)
779       current_autopilot->set_HeadingEnabled(lock);
780 }
781
782 /**
783  * Return true if the autopilot is locked to NAV1.
784  */
785 static bool
786 getAPNAV1Lock ()
787 {
788   return
789     (current_autopilot->get_HeadingEnabled() &&
790      current_autopilot->get_HeadingMode() == FGAutopilot::FG_HEADING_NAV1);
791 }
792
793
794 /**
795  * Set the autopilot NAV1 lock.
796  */
797 static void
798 setAPNAV1Lock (bool lock)
799 {
800   if (lock)
801     current_autopilot->set_HeadingMode(FGAutopilot::FG_HEADING_NAV1);
802   if (current_autopilot->get_HeadingMode() == FGAutopilot::FG_HEADING_NAV1)
803     current_autopilot->set_HeadingEnabled(lock);
804 }
805
806 /**
807  * Get the autopilot autothrottle lock.
808  */
809 static bool
810 getAPAutoThrottleLock ()
811 {
812   return current_autopilot->get_AutoThrottleEnabled();
813 }
814
815
816 /**
817  * Set the autothrottle lock.
818  */
819 static void
820 setAPAutoThrottleLock (bool lock)
821 {
822   current_autopilot->set_AutoThrottleEnabled(lock);
823 }
824
825
826 // kludge
827 static double
828 getAPRudderControl ()
829 {
830     if (getAPHeadingLock())
831         return current_autopilot->get_TargetHeading();
832     else
833         return globals->get_controls()->get_rudder();
834 }
835
836 // kludge
837 static void
838 setAPRudderControl (double value)
839 {
840     if (getAPHeadingLock()) {
841         SG_LOG(SG_GENERAL, SG_DEBUG, "setAPRudderControl " << value );
842         value -= current_autopilot->get_TargetHeading();
843         current_autopilot->HeadingAdjust(value < 0.0 ? -1.0 : 1.0);
844     } else {
845         globals->get_controls()->set_rudder(value);
846     }
847 }
848
849 // kludge
850 static double
851 getAPElevatorControl ()
852 {
853   if (getAPAltitudeLock())
854       return current_autopilot->get_TargetAltitude();
855   else
856     return globals->get_controls()->get_elevator();
857 }
858
859 // kludge
860 static void
861 setAPElevatorControl (double value)
862 {
863     if (value != 0 && getAPAltitudeLock()) {
864         SG_LOG(SG_GENERAL, SG_DEBUG, "setAPElevatorControl " << value );
865         value -= current_autopilot->get_TargetAltitude();
866         current_autopilot->AltitudeAdjust(value < 0.0 ? 100.0 : -100.0);
867     } else {
868         globals->get_controls()->set_elevator(value);
869     }
870 }
871
872 // kludge
873 static double
874 getAPThrottleControl ()
875 {
876   if (getAPAutoThrottleLock())
877     return 0.0;                 // always resets
878   else
879     return globals->get_controls()->get_throttle(0);
880 }
881
882 // kludge
883 static void
884 setAPThrottleControl (double value)
885 {
886   if (getAPAutoThrottleLock())
887     current_autopilot->AutoThrottleAdjust(value < 0.0 ? -0.01 : 0.01);
888   else
889     globals->get_controls()->set_throttle(FGControls::ALL_ENGINES, value);
890 }
891
892
893 #if !defined(FG_OLD_WEATHER)
894
895 /**
896  * Get the current visibility (meters).
897  */
898 static double
899 getVisibility ()
900 {
901   return WeatherDatabase->getWeatherVisibility();
902 }
903
904
905 /**
906  * Set the current visibility (meters).
907  */
908 static void
909 setVisibility (double visibility)
910 {
911   WeatherDatabase->setWeatherVisibility(visibility);
912 }
913
914 /**
915  * Get the current wind north velocity (feet/second).
916  */
917 static double
918 getWindNorth ()
919 {
920   return current_aircraft.fdm_state->get_V_north_airmass();
921 }
922
923
924 /**
925  * Set the current wind north velocity (feet/second).
926  */
927 static void
928 setWindNorth (double speed)
929 {
930   current_aircraft.fdm_state
931     ->set_Velocities_Local_Airmass(speed, getWindEast(), getWindDown());
932 }
933
934
935 /**
936  * Get the current wind east velocity (feet/second).
937  */
938 static double
939 getWindEast ()
940 {
941   return current_aircraft.fdm_state->get_V_east_airmass();
942 }
943
944
945 /**
946  * Set the current wind east velocity (feet/second).
947  */
948 static void
949 setWindEast (double speed)
950 {
951   cout << "Set wind-east to " << speed << endl;
952   current_aircraft.fdm_state->set_Velocities_Local_Airmass(getWindNorth(),
953                                                            speed,
954                                                            getWindDown());
955 }
956
957
958 /**
959  * Get the current wind down velocity (feet/second).
960  */
961 static double
962 getWindDown ()
963 {
964   return current_aircraft.fdm_state->get_V_down_airmass();
965 }
966
967
968 /**
969  * Set the current wind down velocity (feet/second).
970  */
971 static void
972 setWindDown (double speed)
973 {
974   current_aircraft.fdm_state->set_Velocities_Local_Airmass(getWindNorth(),
975                                                            getWindEast(),
976                                                            speed);
977 }
978
979 #endif // FG_OLD_WEATHER
980
981 static double
982 getFOV ()
983 {
984   return globals->get_current_view()->get_fov();
985 }
986
987 static void
988 setFOV (double fov)
989 {
990   if ( fov < 180 ) {
991       globals->get_current_view()->set_fov( fov );
992   }
993 }
994
995 static long
996 getWarp ()
997 {
998   return globals->get_warp();
999 }
1000
1001 static void
1002 setWarp (long warp)
1003 {
1004   globals->set_warp(warp);
1005 }
1006
1007 static long
1008 getWarpDelta ()
1009 {
1010   return globals->get_warp_delta();
1011 }
1012
1013 static void
1014 setWarpDelta (long delta)
1015 {
1016   globals->set_warp_delta(delta);
1017 }
1018
1019 static void
1020 setViewAxisLong (double axis)
1021 {
1022   axisLong = axis;
1023 }
1024
1025 static void
1026 setViewAxisLat (double axis)
1027 {
1028   axisLat = axis;
1029 }
1030
1031 static bool
1032 getWindingCCW ()
1033 {
1034   return winding_ccw;
1035 }
1036
1037 static void
1038 setWindingCCW (bool state)
1039 {
1040   winding_ccw = state;
1041   if ( winding_ccw )
1042     glFrontFace ( GL_CCW );
1043   else
1044     glFrontFace ( GL_CW );
1045 }
1046
1047 static bool
1048 getFullScreen ()
1049 {
1050 #if defined(FX) && !defined(WIN32)
1051   return globals->get_fullscreen();
1052 #else
1053   return false;
1054 #endif
1055 }
1056
1057 static void
1058 setFullScreen (bool state)
1059 {
1060 #if defined(FX) && !defined(WIN32)
1061   globals->set_fullscreen(state);
1062 #  if defined(XMESA_FX_FULLSCREEN) && defined(XMESA_FX_WINDOW)
1063   XMesaSetFXmode( state ? XMESA_FX_FULLSCREEN : XMESA_FX_WINDOW );
1064 #  endif
1065 #endif
1066 }
1067
1068 static bool
1069 getFDMDataLogging ()
1070 {
1071   return fdm_data_logging;
1072 }
1073
1074 static void
1075 setFDMDataLogging (bool state)
1076 {
1077                                 // kludge; no getter or setter available
1078   if (state != fdm_data_logging) {
1079     fgToggleFDMdataLogging();
1080     fdm_data_logging = state;
1081   }
1082 }
1083
1084 \f
1085 ////////////////////////////////////////////////////////////////////////
1086 // Tie the properties.
1087 ////////////////////////////////////////////////////////////////////////
1088
1089 void
1090 fgInitProps ()
1091 {
1092                                 // Simulation
1093   fgTie("/sim/logging/priority", getLoggingPriority, setLoggingPriority);
1094   fgTie("/sim/logging/classes", getLoggingClasses, setLoggingClasses);
1095   // fgTie("/sim/freeze", getFreeze, setFreeze);
1096   fgTie("/sim/aircraft-dir", getAircraftDir, setAircraftDir);
1097   fgTie("/sim/view/offset-deg", getViewOffset, setViewOffset, false);
1098   fgSetArchivable("/sim/view/offset-deg");
1099   fgTie("/sim/view/goal-offset-deg", getGoalViewOffset, setGoalViewOffset, false);
1100   fgTie("/sim/view/tilt-deg", getViewTilt, setViewTilt, false);
1101   fgSetArchivable("/sim/view/tilt-deg");
1102   fgTie("/sim/view/goal-tilt-deg", getGoalViewTilt, setGoalViewTilt, false);
1103   fgSetArchivable("/sim/view/goal-offset-deg");
1104   fgTie("/sim/view/pilot/x-offset-m",
1105         getPilotPositionXOffset, setPilotPositionXOffset);
1106   fgSetArchivable("/sim/view/pilot/x-offset-m");
1107   fgTie("/sim/view/pilot/y-offset-m",
1108         getPilotPositionYOffset, setPilotPositionYOffset);
1109   fgSetArchivable("/sim/view/pilot/y-offset-m");
1110   fgTie("/sim/view/pilot/z-offset-m",
1111         getPilotPositionZOffset, setPilotPositionZOffset);
1112   fgSetArchivable("/sim/view/pilot/z-offset-m");
1113   fgTie("/sim/time/gmt", getDateString, setDateString);
1114   fgSetArchivable("/sim/time/gmt");
1115   fgTie("/sim/time/gmt-string", getGMTString);
1116   fgTie("/sim/rendering/textures", getTextures, setTextures);
1117
1118                                 // Orientation
1119   fgTie("/orientation/heading-magnetic-deg", getHeadingMag);
1120
1121                                 // Autopilot
1122   fgTie("/autopilot/locks/altitude", getAPAltitudeLock, setAPAltitudeLock);
1123   fgSetArchivable("/autopilot/locks/altitude");
1124   fgTie("/autopilot/settings/altitude-ft", getAPAltitude, setAPAltitude);
1125   fgSetArchivable("/autopilot/settings/altitude-ft");
1126   fgTie("/autopilot/locks/glide-slope", getAPGSLock, setAPGSLock);
1127   fgSetArchivable("/autopilot/locks/glide-slope");
1128   fgTie("/autopilot/locks/terrain", getAPTerrainLock, setAPTerrainLock);
1129   fgSetArchivable("/autopilot/locks/terrain");
1130   fgTie("/autopilot/settings/climb-rate-fpm", getAPClimb, setAPClimb, false);
1131   fgSetArchivable("/autopilot/settings/climb-rate-fpm");
1132   fgTie("/autopilot/locks/heading", getAPHeadingLock, setAPHeadingLock);
1133   fgSetArchivable("/autopilot/locks/heading");
1134   fgTie("/autopilot/settings/heading-bug-deg",
1135         getAPHeadingBug, setAPHeadingBug);
1136   fgSetArchivable("/autopilot/settings/heading-bug-deg");
1137   fgTie("/autopilot/locks/wing-leveler", getAPWingLeveler, setAPWingLeveler);
1138   fgSetArchivable("/autopilot/locks/wing-leveler");
1139   fgTie("/autopilot/locks/nav[0]", getAPNAV1Lock, setAPNAV1Lock);
1140   fgSetArchivable("/autopilot/locks/nav[0]");
1141   fgTie("/autopilot/locks/auto-throttle",
1142         getAPAutoThrottleLock, setAPAutoThrottleLock);
1143   fgSetArchivable("/autopilot/locks/auto-throttle");
1144   fgTie("/autopilot/control-overrides/rudder",
1145         getAPRudderControl, setAPRudderControl);
1146   fgSetArchivable("/autopilot/control-overrides/rudder");
1147   fgTie("/autopilot/control-overrides/elevator",
1148         getAPElevatorControl, setAPElevatorControl);
1149   fgSetArchivable("/autopilot/control-overrides/elevator");
1150   fgTie("/autopilot/control-overrides/throttle",
1151         getAPThrottleControl, setAPThrottleControl);
1152   fgSetArchivable("/autopilot/control-overrides/throttle");
1153
1154                                 // Environment
1155 #if !defined(FG_OLD_WEATHER)
1156   fgTie("/environment/visibility-m", getVisibility, setVisibility);
1157   fgSetArchivable("/environment/visibility-m");
1158   fgTie("/environment/wind-north-fps", getWindNorth, setWindNorth);
1159   fgSetArchivable("/environment/wind-north-fps");
1160   fgTie("/environment/wind-east-fps", getWindEast, setWindEast);
1161   fgSetArchivable("/environment/wind-east-fps");
1162   fgTie("/environment/wind-down-fps", getWindDown, setWindDown);
1163   fgSetArchivable("/environment/wind-down-fps");
1164 #endif
1165
1166   fgTie("/environment/magnetic-variation-deg", getMagVar);
1167   fgTie("/environment/magnetic-dip-deg", getMagDip);
1168
1169                                 // View
1170   fgTie("/sim/field-of-view", getFOV, setFOV);
1171   fgSetArchivable("/sim/field-of-view");
1172   fgTie("/sim/time/warp", getWarp, setWarp, false);
1173   fgTie("/sim/time/warp-delta", getWarpDelta, setWarpDelta);
1174   fgTie("/sim/view/axes/long", (double(*)())0, setViewAxisLong);
1175   fgTie("/sim/view/axes/lat", (double(*)())0, setViewAxisLat);
1176
1177                                 // Misc. Temporary junk.
1178   fgTie("/sim/temp/winding-ccw", getWindingCCW, setWindingCCW, false);
1179   fgTie("/sim/temp/full-screen", getFullScreen, setFullScreen);
1180   fgTie("/sim/temp/fdm-data-logging", getFDMDataLogging, setFDMDataLogging);
1181         
1182 }
1183
1184
1185 void
1186 fgUpdateProps ()
1187 {
1188   _set_view_from_axes();
1189 }
1190
1191
1192 \f
1193 ////////////////////////////////////////////////////////////////////////
1194 // Save and restore.
1195 ////////////////////////////////////////////////////////////////////////
1196
1197
1198 /**
1199  * Save the current state of the simulator to a stream.
1200  */
1201 bool
1202 fgSaveFlight (ostream &output)
1203 {
1204   try {
1205     writeProperties(output, globals->get_props());
1206   } catch (const sg_exception &e) {
1207     guiErrorMessage("Error saving flight: ", e);
1208     return false;
1209   }
1210   return true;
1211 }
1212
1213
1214 /**
1215  * Restore the current state of the simulator from a stream.
1216  */
1217 bool
1218 fgLoadFlight (istream &input)
1219 {
1220   SGPropertyNode props;
1221   try {
1222     readProperties(input, &props);
1223   } catch (const sg_exception &e) {
1224     guiErrorMessage("Error reading saved flight: ", e);
1225     return false;
1226   }
1227   copyProperties(&props, globals->get_props());
1228   // When loading a flight, make it the
1229   // new initial state.
1230   globals->saveInitialState();
1231   return true;
1232 }
1233
1234
1235 \f
1236 ////////////////////////////////////////////////////////////////////////
1237 // Implementation of FGCondition.
1238 ////////////////////////////////////////////////////////////////////////
1239
1240 FGCondition::FGCondition ()
1241 {
1242 }
1243
1244 FGCondition::~FGCondition ()
1245 {
1246 }
1247
1248
1249 \f
1250 ////////////////////////////////////////////////////////////////////////
1251 // Implementation of FGPropertyCondition.
1252 ////////////////////////////////////////////////////////////////////////
1253
1254 FGPropertyCondition::FGPropertyCondition (const string &propname)
1255   : _node(fgGetNode(propname, true))
1256 {
1257 }
1258
1259 FGPropertyCondition::~FGPropertyCondition ()
1260 {
1261 }
1262
1263
1264 \f
1265 ////////////////////////////////////////////////////////////////////////
1266 // Implementation of FGNotCondition.
1267 ////////////////////////////////////////////////////////////////////////
1268
1269 FGNotCondition::FGNotCondition (FGCondition * condition)
1270   : _condition(condition)
1271 {
1272 }
1273
1274 FGNotCondition::~FGNotCondition ()
1275 {
1276   delete _condition;
1277 }
1278
1279 bool
1280 FGNotCondition::test () const
1281 {
1282   return !(_condition->test());
1283 }
1284
1285
1286 \f
1287 ////////////////////////////////////////////////////////////////////////
1288 // Implementation of FGAndCondition.
1289 ////////////////////////////////////////////////////////////////////////
1290
1291 FGAndCondition::FGAndCondition ()
1292 {
1293 }
1294
1295 FGAndCondition::~FGAndCondition ()
1296 {
1297   for (unsigned int i = 0; i < _conditions.size(); i++)
1298     delete _conditions[i];
1299 }
1300
1301 bool
1302 FGAndCondition::test () const
1303 {
1304   int nConditions = _conditions.size();
1305   for (int i = 0; i < nConditions; i++) {
1306     if (!_conditions[i]->test())
1307       return false;
1308   }
1309   return true;
1310 }
1311
1312 void
1313 FGAndCondition::addCondition (FGCondition * condition)
1314 {
1315   _conditions.push_back(condition);
1316 }
1317
1318
1319 \f
1320 ////////////////////////////////////////////////////////////////////////
1321 // Implementation of FGOrCondition.
1322 ////////////////////////////////////////////////////////////////////////
1323
1324 FGOrCondition::FGOrCondition ()
1325 {
1326 }
1327
1328 FGOrCondition::~FGOrCondition ()
1329 {
1330   for (unsigned int i = 0; i < _conditions.size(); i++)
1331     delete _conditions[i];
1332 }
1333
1334 bool
1335 FGOrCondition::test () const
1336 {
1337   int nConditions = _conditions.size();
1338   for (int i = 0; i < nConditions; i++) {
1339     if (_conditions[i]->test())
1340       return true;
1341   }
1342   return false;
1343 }
1344
1345 void
1346 FGOrCondition::addCondition (FGCondition * condition)
1347 {
1348   _conditions.push_back(condition);
1349 }
1350
1351
1352 \f
1353 ////////////////////////////////////////////////////////////////////////
1354 // Implementation of FGComparisonCondition.
1355 ////////////////////////////////////////////////////////////////////////
1356
1357 static int
1358 doComparison (const SGPropertyNode * left, const SGPropertyNode *right)
1359 {
1360   switch (left->getType()) {
1361   case SGPropertyNode::BOOL: {
1362     bool v1 = left->getBoolValue();
1363     bool v2 = right->getBoolValue();
1364     if (v1 < v2)
1365       return FGComparisonCondition::LESS_THAN;
1366     else if (v1 > v2)
1367       return FGComparisonCondition::GREATER_THAN;
1368     else
1369       return FGComparisonCondition::EQUALS;
1370     break;
1371   }
1372   case SGPropertyNode::INT: {
1373     int v1 = left->getIntValue();
1374     int v2 = right->getIntValue();
1375     if (v1 < v2)
1376       return FGComparisonCondition::LESS_THAN;
1377     else if (v1 > v2)
1378       return FGComparisonCondition::GREATER_THAN;
1379     else
1380       return FGComparisonCondition::EQUALS;
1381     break;
1382   }
1383   case SGPropertyNode::LONG: {
1384     long v1 = left->getLongValue();
1385     long v2 = right->getLongValue();
1386     if (v1 < v2)
1387       return FGComparisonCondition::LESS_THAN;
1388     else if (v1 > v2)
1389       return FGComparisonCondition::GREATER_THAN;
1390     else
1391       return FGComparisonCondition::EQUALS;
1392     break;
1393   }
1394   case SGPropertyNode::FLOAT: {
1395     float v1 = left->getFloatValue();
1396     float v2 = right->getFloatValue();
1397     if (v1 < v2)
1398       return FGComparisonCondition::LESS_THAN;
1399     else if (v1 > v2)
1400       return FGComparisonCondition::GREATER_THAN;
1401     else
1402       return FGComparisonCondition::EQUALS;
1403     break;
1404   }
1405   case SGPropertyNode::DOUBLE: {
1406     double v1 = left->getDoubleValue();
1407     double v2 = right->getDoubleValue();
1408     if (v1 < v2)
1409       return FGComparisonCondition::LESS_THAN;
1410     else if (v1 > v2)
1411       return FGComparisonCondition::GREATER_THAN;
1412     else
1413       return FGComparisonCondition::EQUALS;
1414     break;
1415   }
1416   case SGPropertyNode::STRING: 
1417   case SGPropertyNode::NONE:
1418   case SGPropertyNode::UNSPECIFIED: {
1419     string v1 = left->getStringValue();
1420     string v2 = right->getStringValue();
1421     if (v1 < v2)
1422       return FGComparisonCondition::LESS_THAN;
1423     else if (v1 > v2)
1424       return FGComparisonCondition::GREATER_THAN;
1425     else
1426       return FGComparisonCondition::EQUALS;
1427     break;
1428   }
1429   }
1430   throw sg_exception("Unrecognized node type");
1431   return 0;
1432 }
1433
1434
1435 FGComparisonCondition::FGComparisonCondition (Type type, bool reverse)
1436   : _type(type),
1437     _reverse(reverse),
1438     _left_property(0),
1439     _right_property(0),
1440     _right_value(0)
1441 {
1442 }
1443
1444 FGComparisonCondition::~FGComparisonCondition ()
1445 {
1446   delete _right_value;
1447 }
1448
1449 bool
1450 FGComparisonCondition::test () const
1451 {
1452                                 // Always fail if incompletely specified
1453   if (_left_property == 0 ||
1454       (_right_property == 0 && _right_value == 0))
1455     return false;
1456
1457                                 // Get LESS_THAN, EQUALS, or GREATER_THAN
1458   int cmp =
1459     doComparison(_left_property,
1460                  (_right_property != 0 ? _right_property : _right_value));
1461   if (!_reverse)
1462     return (cmp == _type);
1463   else
1464     return (cmp != _type);
1465 }
1466
1467 void
1468 FGComparisonCondition::setLeftProperty (const string &propname)
1469 {
1470   _left_property = fgGetNode(propname, true);
1471 }
1472
1473 void
1474 FGComparisonCondition::setRightProperty (const string &propname)
1475 {
1476   delete _right_value;
1477   _right_value = 0;
1478   _right_property = fgGetNode(propname, true);
1479 }
1480
1481 void
1482 FGComparisonCondition::setRightValue (const SGPropertyNode *node)
1483 {
1484   _right_property = 0;
1485   delete _right_value;
1486   _right_value = new SGPropertyNode(*node);
1487 }
1488
1489
1490 \f
1491 ////////////////////////////////////////////////////////////////////////
1492 // Read a condition and use it if necessary.
1493 ////////////////////////////////////////////////////////////////////////
1494
1495                                 // Forward declaration
1496 static FGCondition * readCondition (const SGPropertyNode * node);
1497
1498 static FGCondition *
1499 readPropertyCondition (const SGPropertyNode * node)
1500 {
1501   return new FGPropertyCondition(node->getStringValue());
1502 }
1503
1504 static FGCondition *
1505 readNotCondition (const SGPropertyNode * node)
1506 {
1507   int nChildren = node->nChildren();
1508   for (int i = 0; i < nChildren; i++) {
1509     const SGPropertyNode * child = node->getChild(i);
1510     FGCondition * condition = readCondition(child);
1511     if (condition != 0)
1512       return new FGNotCondition(condition);
1513   }
1514   SG_LOG(SG_COCKPIT, SG_ALERT, "Panel: empty 'not' condition");
1515   return 0;
1516 }
1517
1518 static FGCondition *
1519 readAndConditions (const SGPropertyNode * node)
1520 {
1521   FGAndCondition * andCondition = new FGAndCondition;
1522   int nChildren = node->nChildren();
1523   for (int i = 0; i < nChildren; i++) {
1524     const SGPropertyNode * child = node->getChild(i);
1525     FGCondition * condition = readCondition(child);
1526     if (condition != 0)
1527       andCondition->addCondition(condition);
1528   }
1529   return andCondition;
1530 }
1531
1532 static FGCondition *
1533 readOrConditions (const SGPropertyNode * node)
1534 {
1535   FGOrCondition * orCondition = new FGOrCondition;
1536   int nChildren = node->nChildren();
1537   for (int i = 0; i < nChildren; i++) {
1538     const SGPropertyNode * child = node->getChild(i);
1539     FGCondition * condition = readCondition(child);
1540     if (condition != 0)
1541       orCondition->addCondition(condition);
1542   }
1543   return orCondition;
1544 }
1545
1546 static FGCondition *
1547 readComparison (const SGPropertyNode * node,
1548                 FGComparisonCondition::Type type,
1549                 bool reverse)
1550 {
1551   FGComparisonCondition * condition = new FGComparisonCondition(type, reverse);
1552   condition->setLeftProperty(node->getStringValue("property[0]"));
1553   if (node->hasValue("property[1]"))
1554     condition->setRightProperty(node->getStringValue("property[1]"));
1555   else
1556     condition->setRightValue(node->getChild("value", 0));
1557
1558   return condition;
1559 }
1560
1561 static FGCondition *
1562 readCondition (const SGPropertyNode * node)
1563 {
1564   const string &name = node->getName();
1565   if (name == "property")
1566     return readPropertyCondition(node);
1567   else if (name == "not")
1568     return readNotCondition(node);
1569   else if (name == "and")
1570     return readAndConditions(node);
1571   else if (name == "or")
1572     return readOrConditions(node);
1573   else if (name == "less-than")
1574     return readComparison(node, FGComparisonCondition::LESS_THAN, false);
1575   else if (name == "less-than-equals")
1576     return readComparison(node, FGComparisonCondition::GREATER_THAN, true);
1577   else if (name == "greater-than")
1578     return readComparison(node, FGComparisonCondition::GREATER_THAN, false);
1579   else if (name == "greater-than-equals")
1580     return readComparison(node, FGComparisonCondition::LESS_THAN, true);
1581   else if (name == "equals")
1582     return readComparison(node, FGComparisonCondition::EQUALS, false);
1583   else if (name == "not-equals")
1584     return readComparison(node, FGComparisonCondition::EQUALS, true);
1585   else
1586     return 0;
1587 }
1588
1589
1590 \f
1591 ////////////////////////////////////////////////////////////////////////
1592 // Implementation of FGConditional.
1593 ////////////////////////////////////////////////////////////////////////
1594
1595 FGConditional::FGConditional ()
1596   : _condition (0)
1597 {
1598 }
1599
1600 FGConditional::~FGConditional ()
1601 {
1602   delete _condition;
1603 }
1604
1605 void
1606 FGConditional::setCondition (FGCondition * condition)
1607 {
1608   delete _condition;
1609   _condition = condition;
1610 }
1611
1612 bool
1613 FGConditional::test () const
1614 {
1615   return ((_condition == 0) || _condition->test());
1616 }
1617
1618
1619 \f
1620 // The top-level is always an implicit 'and' group
1621 FGCondition *
1622 fgReadCondition (const SGPropertyNode * node)
1623 {
1624   return readAndConditions(node);
1625 }
1626
1627
1628 // end of fg_props.cxx