]> git.mxchange.org Git - flightgear.git/blob - src/Main/fg_props.cxx
4d42d8c08a42e4fd7335fa11e0eae807ba12d941
[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 #include <simgear/magvar/magvar.hxx>
29 #include <simgear/timing/sg_time.hxx>
30
31 #include STL_IOSTREAM
32
33 #include <ATC/ATCdisplay.hxx>
34 #include <Aircraft/aircraft.hxx>
35 #include <Time/tmp.hxx>
36 #include <FDM/UIUCModel/uiuc_aircraftdir.h>
37 #ifndef FG_NEW_ENVIRONMENT
38 #  include <WeatherCM/FGLocalWeatherDatabase.h>
39 #else
40 #  include <Environment/environment.hxx>
41 #endif // FG_NEW_ENVIRONMENT
42 #include <Objects/matlib.hxx>
43
44 #include <GUI/gui.h>
45
46 #include "globals.hxx"
47 #include "fgfs.hxx"
48 #include "fg_props.hxx"
49
50 #if !defined(SG_HAVE_NATIVE_SGI_COMPILERS)
51 SG_USING_STD(istream);
52 SG_USING_STD(ostream);
53 #endif
54
55 #if !defined(FG_NEW_ENVIRONMENT)
56 static double getWindNorth ();
57 static double getWindEast ();
58 static double getWindDown ();
59 #endif // FG_NEW_ENVIRONMENT
60
61 static bool winding_ccw = true; // FIXME: temporary
62
63 static bool fdm_data_logging = false; // FIXME: temporary
64
65
66 \f
67 ////////////////////////////////////////////////////////////////////////
68 // Default property bindings (not yet handled by any module).
69 ////////////////////////////////////////////////////////////////////////
70
71 struct LogClassMapping {
72   sgDebugClass c;
73   string name;
74   LogClassMapping(sgDebugClass cc, string nname) { c = cc; name = nname; }
75 };
76
77 LogClassMapping log_class_mappings [] = {
78   LogClassMapping(SG_NONE, "none"),
79   LogClassMapping(SG_TERRAIN, "terrain"),
80   LogClassMapping(SG_ASTRO, "astro"),
81   LogClassMapping(SG_FLIGHT, "flight"),
82   LogClassMapping(SG_INPUT, "input"),
83   LogClassMapping(SG_GL, "gl"),
84   LogClassMapping(SG_VIEW, "view"),
85   LogClassMapping(SG_COCKPIT, "cockpit"),
86   LogClassMapping(SG_GENERAL, "general"),
87   LogClassMapping(SG_MATH, "math"),
88   LogClassMapping(SG_EVENT, "event"),
89   LogClassMapping(SG_AIRCRAFT, "aircraft"),
90   LogClassMapping(SG_AUTOPILOT, "autopilot"),
91   LogClassMapping(SG_IO, "io"),
92   LogClassMapping(SG_CLIPPER, "clipper"),
93   LogClassMapping(SG_NETWORK, "network"),
94   LogClassMapping(SG_UNDEFD, "")
95 };
96
97
98 /**
99  * Get the logging classes.
100  */
101 static string
102 getLoggingClasses ()
103 {
104   sgDebugClass classes = logbuf::get_log_classes();
105   string result = "";
106   for (int i = 0; log_class_mappings[i].c != SG_UNDEFD; i++) {
107     if ((classes&log_class_mappings[i].c) > 0) {
108       if (result != (string)"")
109         result += '|';
110       result += log_class_mappings[i].name;
111     }
112   }
113   return result;
114 }
115
116
117 static void addLoggingClass (const string &name)
118 {
119   sgDebugClass classes = logbuf::get_log_classes();
120   for (int i = 0; log_class_mappings[i].c != SG_UNDEFD; i++) {
121     if (name == log_class_mappings[i].name) {
122       logbuf::set_log_classes(sgDebugClass(classes|log_class_mappings[i].c));
123       return;
124     }
125   }
126   SG_LOG(SG_GENERAL, SG_ALERT, "Unknown logging class: " << name);
127 }
128
129
130 /**
131  * Set the logging classes.
132  */
133 static void
134 setLoggingClasses (string classes)
135 {
136   logbuf::set_log_classes(SG_NONE);
137
138   if (classes == "none") {
139     SG_LOG(SG_GENERAL, SG_INFO, "Disabled all logging classes");
140     return;
141   }
142
143   if (classes == "" || classes == "all") { // default
144     logbuf::set_log_classes(SG_ALL);
145     SG_LOG(SG_GENERAL, SG_INFO, "Enabled all logging classes: "
146            << getLoggingClasses());
147     return;
148   }
149
150   string rest = classes;
151   string name = "";
152   int sep = rest.find('|');
153   while (sep > 0) {
154     name = rest.substr(0, sep);
155     rest = rest.substr(sep+1);
156     addLoggingClass(name);
157     sep = rest.find('|');
158   }
159   addLoggingClass(rest);
160   SG_LOG(SG_GENERAL, SG_INFO, "Set logging classes to "
161          << getLoggingClasses());
162 }
163
164
165 /**
166  * Get the logging priority.
167  */
168 static string
169 getLoggingPriority ()
170 {
171   switch (logbuf::get_log_priority()) {
172   case SG_BULK:
173     return "bulk";
174   case SG_DEBUG:
175     return "debug";
176   case SG_INFO:
177     return "info";
178   case SG_WARN:
179     return "warn";
180   case SG_ALERT:
181     return "alert";
182   default:
183     SG_LOG(SG_GENERAL, SG_WARN, "Internal: Unknown logging priority number: "
184            << logbuf::get_log_priority());
185     return "unknown";
186   }
187 }
188
189
190 /**
191  * Set the logging priority.
192  */
193 static void
194 setLoggingPriority (string priority)
195 {
196   if (priority == "bulk") {
197     logbuf::set_log_priority(SG_BULK);
198   } else if (priority == "debug") {
199     logbuf::set_log_priority(SG_DEBUG);
200   } else if (priority == "" || priority == "info") { // default
201     logbuf::set_log_priority(SG_INFO);
202   } else if (priority == "warn") {
203     logbuf::set_log_priority(SG_WARN);
204   } else if (priority == "alert") {
205     logbuf::set_log_priority(SG_ALERT);
206   } else {
207     SG_LOG(SG_GENERAL, SG_WARN, "Unknown logging priority " << priority);
208   }
209   SG_LOG(SG_GENERAL, SG_INFO, "Logging priority is " << getLoggingPriority());
210 }
211
212
213 #if 0
214 /**
215  * Get the pause state of the sim.
216  */
217 static bool
218 getFreeze ()
219 {
220   return globals->get_freeze();
221 }
222
223
224 /**
225  * Set the pause state of the sim.
226  */
227 static void
228 setFreeze (bool freeze)
229 {
230     globals->set_freeze(freeze);
231     if ( freeze ) {
232         // BusyCursor( 0 );
233         current_atcdisplay->CancelRepeatingMessage();
234         current_atcdisplay->RegisterRepeatingMessage("****    SIM IS FROZEN    ****    SIM IS FROZEN    ****");
235     } else {
236         // BusyCursor( 1 );
237         current_atcdisplay->CancelRepeatingMessage();
238     }
239 }
240 #endif
241
242 /**
243  * Return the current aircraft directory (UIUC) as a string.
244  */
245 static string 
246 getAircraftDir ()
247 {
248   return aircraft_dir;
249 }
250
251
252 /**
253  * Set the current aircraft directory (UIUC).
254  */
255 static void
256 setAircraftDir (string dir)
257 {
258   if (getAircraftDir() != dir) {
259     aircraft_dir = dir;
260 //     needReinit(); FIXME!!
261   }
262 }
263
264
265 /**
266  * Return the number of milliseconds elapsed since simulation started.
267  */
268 static long
269 getElapsedTime_ms ()
270 {
271   return globals->get_elapsed_time_ms();
272 }
273
274
275 /**
276  * Return the current Zulu time.
277  */
278 static string 
279 getDateString ()
280 {
281   string out;
282   char buf[64];
283   struct tm * t = globals->get_time_params()->getGmt();
284   sprintf(buf, "%.4d-%.2d-%.2dT%.2d:%.2d:%.2d",
285           t->tm_year + 1900, t->tm_mon + 1, t->tm_mday,
286           t->tm_hour, t->tm_min, t->tm_sec);
287   out = buf;
288   return out;
289 }
290
291
292 /**
293  * Set the current Zulu time.
294  */
295 static void
296 setDateString (string date_string)
297 {
298   static const SGPropertyNode *cur_time_override
299         = fgGetNode("/sim/time/cur-time-override", true);
300
301   SGTime * st = globals->get_time_params();
302   struct tm * current_time = st->getGmt();
303   struct tm new_time;
304
305                                 // Scan for basic ISO format
306                                 // YYYY-MM-DDTHH:MM:SS
307   int ret = sscanf(date_string.c_str(), "%d-%d-%dT%d:%d:%d",
308                    &(new_time.tm_year), &(new_time.tm_mon),
309                    &(new_time.tm_mday), &(new_time.tm_hour),
310                    &(new_time.tm_min), &(new_time.tm_sec));
311
312                                 // Be pretty picky about this, so
313                                 // that strange things don't happen
314                                 // if the save file has been edited
315                                 // by hand.
316   if (ret != 6) {
317     SG_LOG(SG_INPUT, SG_ALERT, "Date/time string " << date_string
318            << " not in YYYY-MM-DDTHH:MM:SS format; skipped");
319     return;
320   }
321
322                                 // OK, it looks like we got six
323                                 // values, one way or another.
324   new_time.tm_year -= 1900;
325   new_time.tm_mon -= 1;
326
327                                 // Now, tell flight gear to use
328                                 // the new time.  This was far
329                                 // too difficult, by the way.
330   long int warp =
331     mktime(&new_time) - mktime(current_time) + globals->get_warp();
332   double lon = current_aircraft.fdm_state->get_Longitude();
333   double lat = current_aircraft.fdm_state->get_Latitude();
334   globals->set_warp(warp);
335   st->update(lon, lat, cur_time_override->getLongValue(), warp);
336   fgUpdateSkyAndLightingParams();
337 }
338
339 /**
340  * Return the GMT as a string.
341  */
342 static string 
343 getGMTString ()
344 {
345   string out;
346   char buf[16];
347   struct tm *t = globals->get_time_params()->getGmt();
348   sprintf(buf, " %.2d:%.2d:%.2d",
349           t->tm_hour, t->tm_min, t->tm_sec);
350   // cout << t << " " << buf << endl;
351   out = buf;
352   return out;
353 }
354
355
356 /**
357  * Get the texture rendering state.
358  */
359 static bool
360 getTextures ()
361 {
362   return (material_lib.get_step() == 0);
363 }
364
365
366 /**
367  * Set the texture rendering state.
368  */
369 static void
370 setTextures (bool textures)
371 {
372   if (textures)
373     material_lib.set_step(0);
374   else
375     material_lib.set_step(1);
376 }
377
378
379 /**
380  * Return the magnetic variation
381  */
382 static double
383 getMagVar ()
384 {
385   return globals->get_mag()->get_magvar() * SGD_RADIANS_TO_DEGREES;
386 }
387
388
389 /**
390  * Return the magnetic dip
391  */
392 static double
393 getMagDip ()
394 {
395   return globals->get_mag()->get_magdip() * SGD_RADIANS_TO_DEGREES;
396 }
397
398
399 /**
400  * Return the current heading in degrees.
401  */
402 static double
403 getHeadingMag ()
404 {
405   return current_aircraft.fdm_state->get_Psi() * SGD_RADIANS_TO_DEGREES - getMagVar();
406 }
407
408
409 #if !defined(FG_NEW_ENVIRONMENT)
410
411 /**
412  * Get the current visibility (meters).
413  */
414 static double
415 getVisibility ()
416 {
417   return WeatherDatabase->getWeatherVisibility();
418 }
419
420
421 /**
422  * Set the current visibility (meters).
423  */
424 static void
425 setVisibility (double visibility)
426 {
427   WeatherDatabase->setWeatherVisibility(visibility);
428 }
429
430 /**
431  * Get the current wind north velocity (feet/second).
432  */
433 static double
434 getWindNorth ()
435 {
436   return current_aircraft.fdm_state->get_V_north_airmass();
437 }
438
439
440 /**
441  * Set the current wind north velocity (feet/second).
442  */
443 static void
444 setWindNorth (double speed)
445 {
446   current_aircraft.fdm_state
447     ->set_Velocities_Local_Airmass(speed, getWindEast(), getWindDown());
448 }
449
450
451 /**
452  * Get the current wind east velocity (feet/second).
453  */
454 static double
455 getWindEast ()
456 {
457   return current_aircraft.fdm_state->get_V_east_airmass();
458 }
459
460
461 /**
462  * Set the current wind east velocity (feet/second).
463  */
464 static void
465 setWindEast (double speed)
466 {
467   cout << "Set wind-east to " << speed << endl;
468   current_aircraft.fdm_state->set_Velocities_Local_Airmass(getWindNorth(),
469                                                            speed,
470                                                            getWindDown());
471 }
472
473
474 /**
475  * Get the current wind down velocity (feet/second).
476  */
477 static double
478 getWindDown ()
479 {
480   return current_aircraft.fdm_state->get_V_down_airmass();
481 }
482
483
484 /**
485  * Set the current wind down velocity (feet/second).
486  */
487 static void
488 setWindDown (double speed)
489 {
490   current_aircraft.fdm_state->set_Velocities_Local_Airmass(getWindNorth(),
491                                                            getWindEast(),
492                                                            speed);
493 }
494
495 #endif // FG_NEW_ENVIRONMENT
496
497 static long
498 getWarp ()
499 {
500   return globals->get_warp();
501 }
502
503 static void
504 setWarp (long warp)
505 {
506   globals->set_warp(warp);
507 }
508
509 static long
510 getWarpDelta ()
511 {
512   return globals->get_warp_delta();
513 }
514
515 static void
516 setWarpDelta (long delta)
517 {
518   globals->set_warp_delta(delta);
519 }
520
521 static bool
522 getWindingCCW ()
523 {
524   return winding_ccw;
525 }
526
527 static void
528 setWindingCCW (bool state)
529 {
530   winding_ccw = state;
531   if ( winding_ccw )
532     glFrontFace ( GL_CCW );
533   else
534     glFrontFace ( GL_CW );
535 }
536
537 static bool
538 getFullScreen ()
539 {
540 #if defined(FX) && !defined(WIN32)
541   return globals->get_fullscreen();
542 #else
543   return false;
544 #endif
545 }
546
547 static void
548 setFullScreen (bool state)
549 {
550 #if defined(FX) && !defined(WIN32)
551   globals->set_fullscreen(state);
552 #  if defined(XMESA_FX_FULLSCREEN) && defined(XMESA_FX_WINDOW)
553   XMesaSetFXmode( state ? XMESA_FX_FULLSCREEN : XMESA_FX_WINDOW );
554 #  endif
555 #endif
556 }
557
558 static bool
559 getFDMDataLogging ()
560 {
561   return fdm_data_logging;
562 }
563
564 static void
565 setFDMDataLogging (bool state)
566 {
567                                 // kludge; no getter or setter available
568   if (state != fdm_data_logging) {
569     fgToggleFDMdataLogging();
570     fdm_data_logging = state;
571   }
572 }
573
574 \f
575 ////////////////////////////////////////////////////////////////////////
576 // Tie the properties.
577 ////////////////////////////////////////////////////////////////////////
578
579 void
580 fgInitProps ()
581 {
582                                 // Simulation
583   fgTie("/sim/logging/priority", getLoggingPriority, setLoggingPriority);
584   fgTie("/sim/logging/classes", getLoggingClasses, setLoggingClasses);
585   // fgTie("/sim/freeze", getFreeze, setFreeze);
586   fgTie("/sim/aircraft-dir", getAircraftDir, setAircraftDir);
587
588   fgTie("/sim/time/elapsed-ms", getElapsedTime_ms);
589   fgTie("/sim/time/gmt", getDateString, setDateString);
590   fgSetArchivable("/sim/time/gmt");
591   fgTie("/sim/time/gmt-string", getGMTString);
592   fgTie("/sim/rendering/textures", getTextures, setTextures);
593
594                                 // Orientation
595   fgTie("/orientation/heading-magnetic-deg", getHeadingMag);
596
597                                 // Environment
598 #if !defined(FG_NEW_ENVIRONMENT)
599   fgTie("/environment/visibility-m", getVisibility, setVisibility);
600   fgSetArchivable("/environment/visibility-m");
601   fgTie("/environment/wind-from-north-fps", getWindNorth, setWindNorth);
602   fgSetArchivable("/environment/wind-from-north-fps");
603   fgTie("/environment/wind-from-east-fps", getWindEast, setWindEast);
604   fgSetArchivable("/environment/wind-from-east-fps");
605   fgTie("/environment/wind-from-down-fps", getWindDown, setWindDown);
606   fgSetArchivable("/environment/wind-from-down-fps");
607 #endif
608
609   fgTie("/environment/magnetic-variation-deg", getMagVar);
610   fgTie("/environment/magnetic-dip-deg", getMagDip);
611
612   fgTie("/sim/time/warp", getWarp, setWarp, false);
613   fgTie("/sim/time/warp-delta", getWarpDelta, setWarpDelta);
614
615                                 // Misc. Temporary junk.
616   fgTie("/sim/temp/winding-ccw", getWindingCCW, setWindingCCW, false);
617   fgTie("/sim/temp/full-screen", getFullScreen, setFullScreen);
618   fgTie("/sim/temp/fdm-data-logging", getFDMDataLogging, setFDMDataLogging);
619         
620 }
621
622
623 void
624 fgUpdateProps ()
625 {
626 }
627
628
629 \f
630 ////////////////////////////////////////////////////////////////////////
631 // Save and restore.
632 ////////////////////////////////////////////////////////////////////////
633
634
635 /**
636  * Save the current state of the simulator to a stream.
637  */
638 bool
639 fgSaveFlight (ostream &output, bool write_all)
640 {
641
642   fgSetBool("/sim/startup/onground", false);
643   fgSetArchivable("/sim/startup/onground");
644   fgSetBool("/sim/startup/trim", false);
645   fgSetArchivable("/sim/startup/trim");
646   fgSetString("/sim/startup/speed-set", "UVW");
647   fgSetArchivable("/sim/startup/speed-set");
648
649   try {
650     writeProperties(output, globals->get_props(), write_all);
651   } catch (const sg_exception &e) {
652     guiErrorMessage("Error saving flight: ", e);
653     return false;
654   }
655   return true;
656 }
657
658
659 /**
660  * Restore the current state of the simulator from a stream.
661  */
662 bool
663 fgLoadFlight (istream &input)
664 {
665   SGPropertyNode props;
666   try {
667     readProperties(input, &props);
668   } catch (const sg_exception &e) {
669     guiErrorMessage("Error reading saved flight: ", e);
670     return false;
671   }
672
673   fgSetBool("/sim/startup/onground", false);
674   fgSetBool("/sim/startup/trim", false);
675   fgSetString("/sim/startup/speed-set", "UVW");
676
677   copyProperties(&props, globals->get_props());
678   // When loading a flight, make it the
679   // new initial state.
680   globals->saveInitialState();
681   return true;
682 }
683
684
685 \f
686 ////////////////////////////////////////////////////////////////////////
687 // Implementation of FGCondition.
688 ////////////////////////////////////////////////////////////////////////
689
690 FGCondition::FGCondition ()
691 {
692 }
693
694 FGCondition::~FGCondition ()
695 {
696 }
697
698
699 \f
700 ////////////////////////////////////////////////////////////////////////
701 // Implementation of FGPropertyCondition.
702 ////////////////////////////////////////////////////////////////////////
703
704 FGPropertyCondition::FGPropertyCondition (const string &propname)
705   : _node(fgGetNode(propname, true))
706 {
707 }
708
709 FGPropertyCondition::~FGPropertyCondition ()
710 {
711 }
712
713
714 \f
715 ////////////////////////////////////////////////////////////////////////
716 // Implementation of FGNotCondition.
717 ////////////////////////////////////////////////////////////////////////
718
719 FGNotCondition::FGNotCondition (FGCondition * condition)
720   : _condition(condition)
721 {
722 }
723
724 FGNotCondition::~FGNotCondition ()
725 {
726   delete _condition;
727 }
728
729 bool
730 FGNotCondition::test () const
731 {
732   return !(_condition->test());
733 }
734
735
736 \f
737 ////////////////////////////////////////////////////////////////////////
738 // Implementation of FGAndCondition.
739 ////////////////////////////////////////////////////////////////////////
740
741 FGAndCondition::FGAndCondition ()
742 {
743 }
744
745 FGAndCondition::~FGAndCondition ()
746 {
747   for (unsigned int i = 0; i < _conditions.size(); i++)
748     delete _conditions[i];
749 }
750
751 bool
752 FGAndCondition::test () const
753 {
754   int nConditions = _conditions.size();
755   for (int i = 0; i < nConditions; i++) {
756     if (!_conditions[i]->test())
757       return false;
758   }
759   return true;
760 }
761
762 void
763 FGAndCondition::addCondition (FGCondition * condition)
764 {
765   _conditions.push_back(condition);
766 }
767
768
769 \f
770 ////////////////////////////////////////////////////////////////////////
771 // Implementation of FGOrCondition.
772 ////////////////////////////////////////////////////////////////////////
773
774 FGOrCondition::FGOrCondition ()
775 {
776 }
777
778 FGOrCondition::~FGOrCondition ()
779 {
780   for (unsigned int i = 0; i < _conditions.size(); i++)
781     delete _conditions[i];
782 }
783
784 bool
785 FGOrCondition::test () const
786 {
787   int nConditions = _conditions.size();
788   for (int i = 0; i < nConditions; i++) {
789     if (_conditions[i]->test())
790       return true;
791   }
792   return false;
793 }
794
795 void
796 FGOrCondition::addCondition (FGCondition * condition)
797 {
798   _conditions.push_back(condition);
799 }
800
801
802 \f
803 ////////////////////////////////////////////////////////////////////////
804 // Implementation of FGComparisonCondition.
805 ////////////////////////////////////////////////////////////////////////
806
807 static int
808 doComparison (const SGPropertyNode * left, const SGPropertyNode *right)
809 {
810   switch (left->getType()) {
811   case SGPropertyNode::BOOL: {
812     bool v1 = left->getBoolValue();
813     bool v2 = right->getBoolValue();
814     if (v1 < v2)
815       return FGComparisonCondition::LESS_THAN;
816     else if (v1 > v2)
817       return FGComparisonCondition::GREATER_THAN;
818     else
819       return FGComparisonCondition::EQUALS;
820     break;
821   }
822   case SGPropertyNode::INT: {
823     int v1 = left->getIntValue();
824     int v2 = right->getIntValue();
825     if (v1 < v2)
826       return FGComparisonCondition::LESS_THAN;
827     else if (v1 > v2)
828       return FGComparisonCondition::GREATER_THAN;
829     else
830       return FGComparisonCondition::EQUALS;
831     break;
832   }
833   case SGPropertyNode::LONG: {
834     long v1 = left->getLongValue();
835     long v2 = right->getLongValue();
836     if (v1 < v2)
837       return FGComparisonCondition::LESS_THAN;
838     else if (v1 > v2)
839       return FGComparisonCondition::GREATER_THAN;
840     else
841       return FGComparisonCondition::EQUALS;
842     break;
843   }
844   case SGPropertyNode::FLOAT: {
845     float v1 = left->getFloatValue();
846     float v2 = right->getFloatValue();
847     if (v1 < v2)
848       return FGComparisonCondition::LESS_THAN;
849     else if (v1 > v2)
850       return FGComparisonCondition::GREATER_THAN;
851     else
852       return FGComparisonCondition::EQUALS;
853     break;
854   }
855   case SGPropertyNode::DOUBLE: {
856     double v1 = left->getDoubleValue();
857     double v2 = right->getDoubleValue();
858     if (v1 < v2)
859       return FGComparisonCondition::LESS_THAN;
860     else if (v1 > v2)
861       return FGComparisonCondition::GREATER_THAN;
862     else
863       return FGComparisonCondition::EQUALS;
864     break;
865   }
866   case SGPropertyNode::STRING: 
867   case SGPropertyNode::NONE:
868   case SGPropertyNode::UNSPECIFIED: {
869     string v1 = left->getStringValue();
870     string v2 = right->getStringValue();
871     if (v1 < v2)
872       return FGComparisonCondition::LESS_THAN;
873     else if (v1 > v2)
874       return FGComparisonCondition::GREATER_THAN;
875     else
876       return FGComparisonCondition::EQUALS;
877     break;
878   }
879   }
880   throw sg_exception("Unrecognized node type");
881   return 0;
882 }
883
884
885 FGComparisonCondition::FGComparisonCondition (Type type, bool reverse)
886   : _type(type),
887     _reverse(reverse),
888     _left_property(0),
889     _right_property(0),
890     _right_value(0)
891 {
892 }
893
894 FGComparisonCondition::~FGComparisonCondition ()
895 {
896   delete _right_value;
897 }
898
899 bool
900 FGComparisonCondition::test () const
901 {
902                                 // Always fail if incompletely specified
903   if (_left_property == 0 ||
904       (_right_property == 0 && _right_value == 0))
905     return false;
906
907                                 // Get LESS_THAN, EQUALS, or GREATER_THAN
908   int cmp =
909     doComparison(_left_property,
910                  (_right_property != 0 ? _right_property : _right_value));
911   if (!_reverse)
912     return (cmp == _type);
913   else
914     return (cmp != _type);
915 }
916
917 void
918 FGComparisonCondition::setLeftProperty (const string &propname)
919 {
920   _left_property = fgGetNode(propname, true);
921 }
922
923 void
924 FGComparisonCondition::setRightProperty (const string &propname)
925 {
926   delete _right_value;
927   _right_value = 0;
928   _right_property = fgGetNode(propname, true);
929 }
930
931 void
932 FGComparisonCondition::setRightValue (const SGPropertyNode *node)
933 {
934   _right_property = 0;
935   delete _right_value;
936   _right_value = new SGPropertyNode(*node);
937 }
938
939
940 \f
941 ////////////////////////////////////////////////////////////////////////
942 // Read a condition and use it if necessary.
943 ////////////////////////////////////////////////////////////////////////
944
945                                 // Forward declaration
946 static FGCondition * readCondition (const SGPropertyNode * node);
947
948 static FGCondition *
949 readPropertyCondition (const SGPropertyNode * node)
950 {
951   return new FGPropertyCondition(node->getStringValue());
952 }
953
954 static FGCondition *
955 readNotCondition (const SGPropertyNode * node)
956 {
957   int nChildren = node->nChildren();
958   for (int i = 0; i < nChildren; i++) {
959     const SGPropertyNode * child = node->getChild(i);
960     FGCondition * condition = readCondition(child);
961     if (condition != 0)
962       return new FGNotCondition(condition);
963   }
964   SG_LOG(SG_COCKPIT, SG_ALERT, "Panel: empty 'not' condition");
965   return 0;
966 }
967
968 static FGCondition *
969 readAndConditions (const SGPropertyNode * node)
970 {
971   FGAndCondition * andCondition = new FGAndCondition;
972   int nChildren = node->nChildren();
973   for (int i = 0; i < nChildren; i++) {
974     const SGPropertyNode * child = node->getChild(i);
975     FGCondition * condition = readCondition(child);
976     if (condition != 0)
977       andCondition->addCondition(condition);
978   }
979   return andCondition;
980 }
981
982 static FGCondition *
983 readOrConditions (const SGPropertyNode * node)
984 {
985   FGOrCondition * orCondition = new FGOrCondition;
986   int nChildren = node->nChildren();
987   for (int i = 0; i < nChildren; i++) {
988     const SGPropertyNode * child = node->getChild(i);
989     FGCondition * condition = readCondition(child);
990     if (condition != 0)
991       orCondition->addCondition(condition);
992   }
993   return orCondition;
994 }
995
996 static FGCondition *
997 readComparison (const SGPropertyNode * node,
998                 FGComparisonCondition::Type type,
999                 bool reverse)
1000 {
1001   FGComparisonCondition * condition = new FGComparisonCondition(type, reverse);
1002   condition->setLeftProperty(node->getStringValue("property[0]"));
1003   if (node->hasValue("property[1]"))
1004     condition->setRightProperty(node->getStringValue("property[1]"));
1005   else
1006     condition->setRightValue(node->getChild("value", 0));
1007
1008   return condition;
1009 }
1010
1011 static FGCondition *
1012 readCondition (const SGPropertyNode * node)
1013 {
1014   const string &name = node->getName();
1015   if (name == "property")
1016     return readPropertyCondition(node);
1017   else if (name == "not")
1018     return readNotCondition(node);
1019   else if (name == "and")
1020     return readAndConditions(node);
1021   else if (name == "or")
1022     return readOrConditions(node);
1023   else if (name == "less-than")
1024     return readComparison(node, FGComparisonCondition::LESS_THAN, false);
1025   else if (name == "less-than-equals")
1026     return readComparison(node, FGComparisonCondition::GREATER_THAN, true);
1027   else if (name == "greater-than")
1028     return readComparison(node, FGComparisonCondition::GREATER_THAN, false);
1029   else if (name == "greater-than-equals")
1030     return readComparison(node, FGComparisonCondition::LESS_THAN, true);
1031   else if (name == "equals")
1032     return readComparison(node, FGComparisonCondition::EQUALS, false);
1033   else if (name == "not-equals")
1034     return readComparison(node, FGComparisonCondition::EQUALS, true);
1035   else
1036     return 0;
1037 }
1038
1039
1040 \f
1041 ////////////////////////////////////////////////////////////////////////
1042 // Implementation of FGConditional.
1043 ////////////////////////////////////////////////////////////////////////
1044
1045 FGConditional::FGConditional ()
1046   : _condition (0)
1047 {
1048 }
1049
1050 FGConditional::~FGConditional ()
1051 {
1052   delete _condition;
1053 }
1054
1055 void
1056 FGConditional::setCondition (FGCondition * condition)
1057 {
1058   delete _condition;
1059   _condition = condition;
1060 }
1061
1062 bool
1063 FGConditional::test () const
1064 {
1065   return ((_condition == 0) || _condition->test());
1066 }
1067
1068
1069 \f
1070 // The top-level is always an implicit 'and' group
1071 FGCondition *
1072 fgReadCondition (const SGPropertyNode * node)
1073 {
1074   return readAndConditions(node);
1075 }
1076
1077
1078 // end of fg_props.cxx