]> git.mxchange.org Git - flightgear.git/blob - src/Main/fg_props.cxx
Centralized most view-management code in FGViewMgr. It's still a
[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   try {
640     writeProperties(output, globals->get_props(), write_all);
641   } catch (const sg_exception &e) {
642     guiErrorMessage("Error saving flight: ", e);
643     return false;
644   }
645   return true;
646 }
647
648
649 /**
650  * Restore the current state of the simulator from a stream.
651  */
652 bool
653 fgLoadFlight (istream &input)
654 {
655   SGPropertyNode props;
656   try {
657     readProperties(input, &props);
658   } catch (const sg_exception &e) {
659     guiErrorMessage("Error reading saved flight: ", e);
660     return false;
661   }
662   copyProperties(&props, globals->get_props());
663   // When loading a flight, make it the
664   // new initial state.
665   globals->saveInitialState();
666   return true;
667 }
668
669
670 \f
671 ////////////////////////////////////////////////////////////////////////
672 // Implementation of FGCondition.
673 ////////////////////////////////////////////////////////////////////////
674
675 FGCondition::FGCondition ()
676 {
677 }
678
679 FGCondition::~FGCondition ()
680 {
681 }
682
683
684 \f
685 ////////////////////////////////////////////////////////////////////////
686 // Implementation of FGPropertyCondition.
687 ////////////////////////////////////////////////////////////////////////
688
689 FGPropertyCondition::FGPropertyCondition (const string &propname)
690   : _node(fgGetNode(propname, true))
691 {
692 }
693
694 FGPropertyCondition::~FGPropertyCondition ()
695 {
696 }
697
698
699 \f
700 ////////////////////////////////////////////////////////////////////////
701 // Implementation of FGNotCondition.
702 ////////////////////////////////////////////////////////////////////////
703
704 FGNotCondition::FGNotCondition (FGCondition * condition)
705   : _condition(condition)
706 {
707 }
708
709 FGNotCondition::~FGNotCondition ()
710 {
711   delete _condition;
712 }
713
714 bool
715 FGNotCondition::test () const
716 {
717   return !(_condition->test());
718 }
719
720
721 \f
722 ////////////////////////////////////////////////////////////////////////
723 // Implementation of FGAndCondition.
724 ////////////////////////////////////////////////////////////////////////
725
726 FGAndCondition::FGAndCondition ()
727 {
728 }
729
730 FGAndCondition::~FGAndCondition ()
731 {
732   for (unsigned int i = 0; i < _conditions.size(); i++)
733     delete _conditions[i];
734 }
735
736 bool
737 FGAndCondition::test () const
738 {
739   int nConditions = _conditions.size();
740   for (int i = 0; i < nConditions; i++) {
741     if (!_conditions[i]->test())
742       return false;
743   }
744   return true;
745 }
746
747 void
748 FGAndCondition::addCondition (FGCondition * condition)
749 {
750   _conditions.push_back(condition);
751 }
752
753
754 \f
755 ////////////////////////////////////////////////////////////////////////
756 // Implementation of FGOrCondition.
757 ////////////////////////////////////////////////////////////////////////
758
759 FGOrCondition::FGOrCondition ()
760 {
761 }
762
763 FGOrCondition::~FGOrCondition ()
764 {
765   for (unsigned int i = 0; i < _conditions.size(); i++)
766     delete _conditions[i];
767 }
768
769 bool
770 FGOrCondition::test () const
771 {
772   int nConditions = _conditions.size();
773   for (int i = 0; i < nConditions; i++) {
774     if (_conditions[i]->test())
775       return true;
776   }
777   return false;
778 }
779
780 void
781 FGOrCondition::addCondition (FGCondition * condition)
782 {
783   _conditions.push_back(condition);
784 }
785
786
787 \f
788 ////////////////////////////////////////////////////////////////////////
789 // Implementation of FGComparisonCondition.
790 ////////////////////////////////////////////////////////////////////////
791
792 static int
793 doComparison (const SGPropertyNode * left, const SGPropertyNode *right)
794 {
795   switch (left->getType()) {
796   case SGPropertyNode::BOOL: {
797     bool v1 = left->getBoolValue();
798     bool v2 = right->getBoolValue();
799     if (v1 < v2)
800       return FGComparisonCondition::LESS_THAN;
801     else if (v1 > v2)
802       return FGComparisonCondition::GREATER_THAN;
803     else
804       return FGComparisonCondition::EQUALS;
805     break;
806   }
807   case SGPropertyNode::INT: {
808     int v1 = left->getIntValue();
809     int v2 = right->getIntValue();
810     if (v1 < v2)
811       return FGComparisonCondition::LESS_THAN;
812     else if (v1 > v2)
813       return FGComparisonCondition::GREATER_THAN;
814     else
815       return FGComparisonCondition::EQUALS;
816     break;
817   }
818   case SGPropertyNode::LONG: {
819     long v1 = left->getLongValue();
820     long v2 = right->getLongValue();
821     if (v1 < v2)
822       return FGComparisonCondition::LESS_THAN;
823     else if (v1 > v2)
824       return FGComparisonCondition::GREATER_THAN;
825     else
826       return FGComparisonCondition::EQUALS;
827     break;
828   }
829   case SGPropertyNode::FLOAT: {
830     float v1 = left->getFloatValue();
831     float v2 = right->getFloatValue();
832     if (v1 < v2)
833       return FGComparisonCondition::LESS_THAN;
834     else if (v1 > v2)
835       return FGComparisonCondition::GREATER_THAN;
836     else
837       return FGComparisonCondition::EQUALS;
838     break;
839   }
840   case SGPropertyNode::DOUBLE: {
841     double v1 = left->getDoubleValue();
842     double v2 = right->getDoubleValue();
843     if (v1 < v2)
844       return FGComparisonCondition::LESS_THAN;
845     else if (v1 > v2)
846       return FGComparisonCondition::GREATER_THAN;
847     else
848       return FGComparisonCondition::EQUALS;
849     break;
850   }
851   case SGPropertyNode::STRING: 
852   case SGPropertyNode::NONE:
853   case SGPropertyNode::UNSPECIFIED: {
854     string v1 = left->getStringValue();
855     string v2 = right->getStringValue();
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   }
865   throw sg_exception("Unrecognized node type");
866   return 0;
867 }
868
869
870 FGComparisonCondition::FGComparisonCondition (Type type, bool reverse)
871   : _type(type),
872     _reverse(reverse),
873     _left_property(0),
874     _right_property(0),
875     _right_value(0)
876 {
877 }
878
879 FGComparisonCondition::~FGComparisonCondition ()
880 {
881   delete _right_value;
882 }
883
884 bool
885 FGComparisonCondition::test () const
886 {
887                                 // Always fail if incompletely specified
888   if (_left_property == 0 ||
889       (_right_property == 0 && _right_value == 0))
890     return false;
891
892                                 // Get LESS_THAN, EQUALS, or GREATER_THAN
893   int cmp =
894     doComparison(_left_property,
895                  (_right_property != 0 ? _right_property : _right_value));
896   if (!_reverse)
897     return (cmp == _type);
898   else
899     return (cmp != _type);
900 }
901
902 void
903 FGComparisonCondition::setLeftProperty (const string &propname)
904 {
905   _left_property = fgGetNode(propname, true);
906 }
907
908 void
909 FGComparisonCondition::setRightProperty (const string &propname)
910 {
911   delete _right_value;
912   _right_value = 0;
913   _right_property = fgGetNode(propname, true);
914 }
915
916 void
917 FGComparisonCondition::setRightValue (const SGPropertyNode *node)
918 {
919   _right_property = 0;
920   delete _right_value;
921   _right_value = new SGPropertyNode(*node);
922 }
923
924
925 \f
926 ////////////////////////////////////////////////////////////////////////
927 // Read a condition and use it if necessary.
928 ////////////////////////////////////////////////////////////////////////
929
930                                 // Forward declaration
931 static FGCondition * readCondition (const SGPropertyNode * node);
932
933 static FGCondition *
934 readPropertyCondition (const SGPropertyNode * node)
935 {
936   return new FGPropertyCondition(node->getStringValue());
937 }
938
939 static FGCondition *
940 readNotCondition (const SGPropertyNode * node)
941 {
942   int nChildren = node->nChildren();
943   for (int i = 0; i < nChildren; i++) {
944     const SGPropertyNode * child = node->getChild(i);
945     FGCondition * condition = readCondition(child);
946     if (condition != 0)
947       return new FGNotCondition(condition);
948   }
949   SG_LOG(SG_COCKPIT, SG_ALERT, "Panel: empty 'not' condition");
950   return 0;
951 }
952
953 static FGCondition *
954 readAndConditions (const SGPropertyNode * node)
955 {
956   FGAndCondition * andCondition = new FGAndCondition;
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       andCondition->addCondition(condition);
963   }
964   return andCondition;
965 }
966
967 static FGCondition *
968 readOrConditions (const SGPropertyNode * node)
969 {
970   FGOrCondition * orCondition = new FGOrCondition;
971   int nChildren = node->nChildren();
972   for (int i = 0; i < nChildren; i++) {
973     const SGPropertyNode * child = node->getChild(i);
974     FGCondition * condition = readCondition(child);
975     if (condition != 0)
976       orCondition->addCondition(condition);
977   }
978   return orCondition;
979 }
980
981 static FGCondition *
982 readComparison (const SGPropertyNode * node,
983                 FGComparisonCondition::Type type,
984                 bool reverse)
985 {
986   FGComparisonCondition * condition = new FGComparisonCondition(type, reverse);
987   condition->setLeftProperty(node->getStringValue("property[0]"));
988   if (node->hasValue("property[1]"))
989     condition->setRightProperty(node->getStringValue("property[1]"));
990   else
991     condition->setRightValue(node->getChild("value", 0));
992
993   return condition;
994 }
995
996 static FGCondition *
997 readCondition (const SGPropertyNode * node)
998 {
999   const string &name = node->getName();
1000   if (name == "property")
1001     return readPropertyCondition(node);
1002   else if (name == "not")
1003     return readNotCondition(node);
1004   else if (name == "and")
1005     return readAndConditions(node);
1006   else if (name == "or")
1007     return readOrConditions(node);
1008   else if (name == "less-than")
1009     return readComparison(node, FGComparisonCondition::LESS_THAN, false);
1010   else if (name == "less-than-equals")
1011     return readComparison(node, FGComparisonCondition::GREATER_THAN, true);
1012   else if (name == "greater-than")
1013     return readComparison(node, FGComparisonCondition::GREATER_THAN, false);
1014   else if (name == "greater-than-equals")
1015     return readComparison(node, FGComparisonCondition::LESS_THAN, true);
1016   else if (name == "equals")
1017     return readComparison(node, FGComparisonCondition::EQUALS, false);
1018   else if (name == "not-equals")
1019     return readComparison(node, FGComparisonCondition::EQUALS, true);
1020   else
1021     return 0;
1022 }
1023
1024
1025 \f
1026 ////////////////////////////////////////////////////////////////////////
1027 // Implementation of FGConditional.
1028 ////////////////////////////////////////////////////////////////////////
1029
1030 FGConditional::FGConditional ()
1031   : _condition (0)
1032 {
1033 }
1034
1035 FGConditional::~FGConditional ()
1036 {
1037   delete _condition;
1038 }
1039
1040 void
1041 FGConditional::setCondition (FGCondition * condition)
1042 {
1043   delete _condition;
1044   _condition = condition;
1045 }
1046
1047 bool
1048 FGConditional::test () const
1049 {
1050   return ((_condition == 0) || _condition->test());
1051 }
1052
1053
1054 \f
1055 // The top-level is always an implicit 'and' group
1056 FGCondition *
1057 fgReadCondition (const SGPropertyNode * node)
1058 {
1059   return readAndConditions(node);
1060 }
1061
1062
1063 // end of fg_props.cxx