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