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