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