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