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