]> git.mxchange.org Git - flightgear.git/blob - src/Main/fg_props.cxx
VS2015 fixes
[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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
20 //
21 // $Id$
22
23 #ifdef HAVE_CONFIG_H
24 #  include "config.h"
25 #endif
26
27 #include <simgear/compiler.h>
28 #include <simgear/structure/exception.hxx>
29 #include <simgear/props/props_io.hxx>
30
31 #include <simgear/timing/sg_time.hxx>
32 #include <simgear/misc/sg_path.hxx>
33 #include <simgear/scene/model/particles.hxx>
34 #include <simgear/sound/soundmgr_openal.hxx>
35
36 #include <GUI/gui.h>
37
38 #include "globals.hxx"
39 #include "fg_props.hxx"
40
41 static bool frozen = false;     // FIXME: temporary
42
43 using std::string;
44 ////////////////////////////////////////////////////////////////////////
45 // Default property bindings (not yet handled by any module).
46 ////////////////////////////////////////////////////////////////////////
47
48 struct LogClassMapping {
49   sgDebugClass c;
50   string name;
51   LogClassMapping(sgDebugClass cc, string nname) { c = cc; name = nname; }
52 };
53
54 LogClassMapping log_class_mappings [] = {
55   LogClassMapping(SG_NONE, "none"),
56   LogClassMapping(SG_TERRAIN, "terrain"),
57   LogClassMapping(SG_ASTRO, "astro"),
58   LogClassMapping(SG_FLIGHT, "flight"),
59   LogClassMapping(SG_INPUT, "input"),
60   LogClassMapping(SG_GL, "gl"),
61   LogClassMapping(SG_VIEW, "view"),
62   LogClassMapping(SG_COCKPIT, "cockpit"),
63   LogClassMapping(SG_GENERAL, "general"),
64   LogClassMapping(SG_MATH, "math"),
65   LogClassMapping(SG_EVENT, "event"),
66   LogClassMapping(SG_AIRCRAFT, "aircraft"),
67   LogClassMapping(SG_AUTOPILOT, "autopilot"),
68   LogClassMapping(SG_IO, "io"),
69   LogClassMapping(SG_CLIPPER, "clipper"),
70   LogClassMapping(SG_NETWORK, "network"),
71   LogClassMapping(SG_INSTR, "instrumentation"),
72   LogClassMapping(SG_ATC, "atc"),
73   LogClassMapping(SG_NASAL, "nasal"),
74   LogClassMapping(SG_SYSTEMS, "systems"),
75   LogClassMapping(SG_AI, "ai"),
76   LogClassMapping(SG_ENVIRONMENT, "environment"),
77   LogClassMapping(SG_SOUND, "sound"),
78   LogClassMapping(SG_NAVAID, "navaid"),
79   LogClassMapping(SG_GUI, "gui"),
80   LogClassMapping(SG_TERRASYNC, "terrasync"),
81   LogClassMapping(SG_UNDEFD, "")
82 };
83
84
85 /**
86  * Get the logging classes.
87  */
88 // XXX Making the result buffer be global is a band-aid that hopefully
89 // delays its destruction 'til after its last use.
90 namespace
91 {
92 string loggingResult;
93 }
94
95 static const char *
96 getLoggingClasses ()
97 {
98   sgDebugClass classes = sglog().get_log_classes();
99   loggingResult.clear();
100   for (int i = 0; log_class_mappings[i].c != SG_UNDEFD; i++) {
101     if ((classes&log_class_mappings[i].c) > 0) {
102       if (!loggingResult.empty())
103         loggingResult += '|';
104       loggingResult += log_class_mappings[i].name;
105     }
106   }
107   return loggingResult.c_str();
108 }
109
110
111 static void
112 addLoggingClass (const string &name)
113 {
114   sgDebugClass classes = sglog().get_log_classes();
115   for (int i = 0; log_class_mappings[i].c != SG_UNDEFD; i++) {
116     if (name == log_class_mappings[i].name) {
117       sglog().set_log_classes(sgDebugClass(classes|log_class_mappings[i].c));
118       return;
119     }
120   }
121   SG_LOG(SG_GENERAL, SG_WARN, "Unknown logging class: " << name);
122 }
123
124
125 /**
126  * Set the logging classes.
127  */
128 void
129 setLoggingClasses (const char * c)
130 {
131   string classes = c;
132   sglog().set_log_classes(SG_NONE);
133
134   if (classes == "none") {
135     SG_LOG(SG_GENERAL, SG_INFO, "Disabled all logging classes");
136     return;
137   }
138
139   if (classes.empty() || classes == "all") { // default
140     sglog().set_log_classes(SG_ALL);
141     SG_LOG(SG_GENERAL, SG_INFO, "Enabled all logging classes: "
142            << getLoggingClasses());
143     return;
144   }
145
146   string rest = classes;
147   string name = "";
148   string::size_type sep = rest.find('|');
149   if (sep == string::npos)
150     sep = rest.find(',');
151   while (sep != string::npos) {
152     name = rest.substr(0, sep);
153     rest = rest.substr(sep+1);
154     addLoggingClass(name);
155     sep = rest.find('|');
156     if (sep == string::npos)
157       sep = rest.find(',');
158   }
159   addLoggingClass(rest);
160   SG_LOG(SG_GENERAL, SG_INFO, "Set logging classes to "
161          << getLoggingClasses());
162 }
163
164
165 /**
166  * Get the logging priority.
167  */
168 static const char *
169 getLoggingPriority ()
170 {
171   switch (sglog().get_log_priority()) {
172   case SG_BULK:
173     return "bulk";
174   case SG_DEBUG:
175     return "debug";
176   case SG_INFO:
177     return "info";
178   case SG_WARN:
179     return "warn";
180   case SG_ALERT:
181     return "alert";
182   default:
183     SG_LOG(SG_GENERAL, SG_WARN, "Internal: Unknown logging priority number: "
184            << sglog().get_log_priority());
185     return "unknown";
186   }
187 }
188
189
190 /**
191  * Set the logging priority.
192  */
193 void
194 setLoggingPriority (const char * p)
195 {
196   if (p == 0)
197       return;
198   string priority = p;
199   if (priority == "bulk") {
200     sglog().set_log_priority(SG_BULK);
201   } else if (priority == "debug") {
202     sglog().set_log_priority(SG_DEBUG);
203   } else if (priority.empty() || priority == "info") { // default
204     sglog().set_log_priority(SG_INFO);
205   } else if (priority == "warn") {
206     sglog().set_log_priority(SG_WARN);
207   } else if (priority == "alert") {
208     sglog().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_DEBUG, "Logging priority is " << getLoggingPriority());
213 }
214
215
216 /**
217  * Return the current frozen state.
218  */
219 static bool
220 getFreeze ()
221 {
222   return frozen;
223 }
224
225
226 /**
227  * Set the current frozen state.
228  */
229 static void
230 setFreeze (bool f)
231 {
232     frozen = f;
233
234     // Stop sound on a pause
235     SGSoundMgr *smgr = globals->get_subsystem<SGSoundMgr>();
236     if ( smgr != NULL ) {
237         if ( f ) {
238             smgr->suspend();
239         } else if (fgGetBool("/sim/sound/working")) {
240             smgr->resume();
241         }
242     }
243
244     // Pause the particle system
245     simgear::Particles::setFrozen(f);
246 }
247
248
249 /**
250  * Return the number of milliseconds elapsed since simulation started.
251  */
252 static double
253 getElapsedTime_sec ()
254 {
255   return globals->get_sim_time_sec();
256 }
257
258
259 /**
260  * Return the current Zulu time.
261  */
262 static const char *
263 getDateString ()
264 {
265   static char buf[64];          // FIXME
266   
267   SGTime * st = globals->get_time_params();
268   if (!st) {
269     buf[0] = 0;
270     return buf;
271   }
272   
273   struct tm * t = st->getGmt();
274   sprintf(buf, "%.4d-%.2d-%.2dT%.2d:%.2d:%.2d",
275           t->tm_year + 1900, t->tm_mon + 1, t->tm_mday,
276           t->tm_hour, t->tm_min, t->tm_sec);
277   return buf;
278 }
279
280
281 /**
282  * Set the current Zulu time.
283  */
284 static void
285 setDateString (const char * date_string)
286 {
287   SGTime * st = globals->get_time_params();
288   struct tm * current_time = st->getGmt();
289   struct tm new_time;
290
291                                 // Scan for basic ISO format
292                                 // YYYY-MM-DDTHH:MM:SS
293   int ret = sscanf(date_string, "%d-%d-%dT%d:%d:%d",
294                    &(new_time.tm_year), &(new_time.tm_mon),
295                    &(new_time.tm_mday), &(new_time.tm_hour),
296                    &(new_time.tm_min), &(new_time.tm_sec));
297
298                                 // Be pretty picky about this, so
299                                 // that strange things don't happen
300                                 // if the save file has been edited
301                                 // by hand.
302   if (ret != 6) {
303     SG_LOG(SG_INPUT, SG_WARN, "Date/time string " << date_string
304            << " not in YYYY-MM-DDTHH:MM:SS format; skipped");
305     return;
306   }
307
308                                 // OK, it looks like we got six
309                                 // values, one way or another.
310   new_time.tm_year -= 1900;
311   new_time.tm_mon -= 1;
312                                 // Now, tell flight gear to use
313                                 // the new time.  This was far
314                                 // too difficult, by the way.
315   long int warp =
316     mktime(&new_time) - mktime(current_time) + globals->get_warp();
317     
318   fgSetInt("/sim/time/warp", warp);
319 }
320
321 /**
322  * Return the GMT as a string.
323  */
324 static const char *
325 getGMTString ()
326 {
327   static char buf[16];
328   SGTime * st = globals->get_time_params();
329   if (!st) {
330     buf[0] = 0;
331     return buf;
332   }
333   
334   struct tm *t = st->getGmt();
335   snprintf(buf, 16, "%.2d:%.2d:%.2d",
336       t->tm_hour, t->tm_min, t->tm_sec);
337   return buf;
338 }
339
340 /**
341  * Return the current heading in degrees.
342  */
343 static double
344 getHeadingMag ()
345 {
346   double magheading = fgGetDouble("/orientation/heading-deg") -
347     fgGetDouble("/environment/magnetic-variation-deg");
348   return SGMiscd::normalizePeriodic(0, 360, magheading );
349 }
350
351 /**
352  * Return the current track in degrees.
353  */
354 static double
355 getTrackMag ()
356 {
357   double magtrack = fgGetDouble("/orientation/track-deg") -
358     fgGetDouble("/environment/magnetic-variation-deg");
359   return SGMiscd::normalizePeriodic(0, 360, magtrack );
360 }
361
362 ////////////////////////////////////////////////////////////////////////
363 // Tie the properties.
364 ////////////////////////////////////////////////////////////////////////
365 SGConstPropertyNode_ptr FGProperties::_longDeg;
366 SGConstPropertyNode_ptr FGProperties::_latDeg;
367 SGConstPropertyNode_ptr FGProperties::_lonLatformat;
368
369 /*
370  * Format the latitude and longitude floats into a character array using a variety of coordinate formats.
371  */
372 static void
373 formatLatLongString (double deg, int format, char *buf, char c) {
374   double min, sec;
375   int sign = deg < 0.0 ? -1 : 1;
376   deg = fabs(deg);
377
378   if (format == 0) {
379     // d.dddddd' (DDD format).
380     snprintf(buf, 32, "%3.6f%c", deg, c);
381
382   } else if (format == 1) {
383     // d mm.mmm' (DMM format) -- uses a round-off factor tailored to the
384     // required precision of the minutes field (three decimal places),
385     // preventing minute values of 60.
386     min = (deg - int(deg)) * 60.0;
387     if (min >= 59.9995) {
388         min -= 60.0;
389         deg += 1.0;
390     }
391     snprintf(buf, 32, "%d*%06.3f'%c", int(deg), fabs(min), c);
392
393   } else if (format == 2) {
394     // d mm'ss.s" (DMS format) -- uses a round-off factor tailored to the
395     // required precision of the seconds field (one decimal place),
396     // preventing second values of 60.
397     min = (deg - int(deg)) * 60.0;
398     sec = (min - int(min)) * 60.0;
399     if (sec >= 59.95) {
400         sec -= 60.0;
401         min += 1.0;
402         if (min >= 60.0) {
403             min -= 60.0;
404             deg += 1.0;
405         }
406     }
407     snprintf(buf, 32, "%d*%02d'%04.1f\"%c", int(deg), int(min), fabs(sec), c);
408
409   } else if (format == 3) {
410     // d.dddddd' (signed DDD format).
411     snprintf(buf, 32, "%3.6f", sign*deg);
412
413   } else if (format == 4) {
414     // d mm.mmm' (signed DMM format).
415     min = (deg - int(deg)) * 60.0;
416     if (min >= 59.9995) {
417         min -= 60.0;
418         deg += 1.0;
419     }
420     if (sign == 1) {
421         snprintf(buf, 32, "%d*%06.3f'", int(deg), fabs(min));
422     } else {
423         snprintf(buf, 32, "-%d*%06.3f'", int(deg), fabs(min));
424     }
425
426   } else if (format == 5) {
427     // d mm'ss.s" (signed DMS format).
428     min = (deg - int(deg)) * 60.0;
429     sec = (min - int(min)) * 60.0;
430     if (sec >= 59.95) {
431         sec -= 60.0;
432         min += 1.0;
433         if (min >= 60.0) {
434             min -= 60.0;
435             deg += 1.0;
436         }
437     }
438     if (sign == 1) {
439         snprintf(buf, 32, "%d*%02d'%04.1f\"", int(deg), int(min), fabs(sec));
440     } else {
441         snprintf(buf, 32, "-%d*%02d'%04.1f\"", int(deg), int(min), fabs(sec));
442     }
443
444   } else if (format == 6) {
445     // dd.dddddd X, ddd.dddddd X (zero padded DDD format).
446     if (c == 'N' || c == 'S') {
447       snprintf(buf, 32, "%09.6f%c", deg, c);
448     } else {
449       snprintf(buf, 32, "%010.6f%c", deg, c);
450     }
451
452   } else if (format == 7) {
453     // dd mm.mmm' X, ddd mm.mmm' X (zero padded DMM format).
454     min = (deg - int(deg)) * 60.0;
455     if (min >= 59.9995) {
456         min -= 60.0;
457         deg += 1.0;
458     }
459     if (c == 'N' || c == 'S') {
460       snprintf(buf, 32, "%02d*%06.3f'%c", int(deg), fabs(min), c);
461     } else {
462       snprintf(buf, 32, "%03d*%06.3f'%c", int(deg), fabs(min), c);
463     }
464
465   } else if (format == 8) {
466     // dd mm'ss.s" X, dd mm'ss.s" X (zero padded DMS format).
467     min = (deg - int(deg)) * 60.0;
468     sec = (min - int(min)) * 60.0;
469     if (sec >= 59.95) {
470         sec -= 60.0;
471         min += 1.0;
472         if (min >= 60.0) {
473             min -= 60.0;
474             deg += 1.0;
475         }
476     }
477     if (c == 'N' || c == 'S') {
478       snprintf(buf, 32, "%02d*%02d'%04.1f\"%c", int(deg), int(min), fabs(sec), c);
479     } else {
480       snprintf(buf, 32, "%03d*%02d'%04.1f\"%c", int(deg), int(min), fabs(sec), c);
481     }
482
483   } else if (format == 9) {
484     // dd* mm'.mmm X, ddd* mm'.mmm X (Trinity House Navigation standard).
485     min = (deg - int(deg)) * 60.0;
486     if (min >= 59.9995) {
487         min -= 60.0;
488         deg += 1.0;
489     }
490     if (c == 'N' || c == 'S') {
491         snprintf(buf, 32, "%02d* %02d'.%03d%c", int(deg), int(min), int(SGMisc<double>::round((min-int(min))*1000)), c);
492     } else {
493         snprintf(buf, 32, "%03d* %02d'.%03d%c", int(deg), int(min), int(SGMisc<double>::round((min-int(min))*1000)), c);
494     }
495
496   } else {
497     // Empty the buffer for all invalid formats.
498     buf[0] = '\0';
499   }
500 }
501
502 const char *
503 FGProperties::getLongitudeString ()
504 {
505   static char buf[32];
506   double d = _longDeg->getDoubleValue();
507   int format = _lonLatformat->getIntValue();
508
509   char c = d < 0.0 ? 'W' : 'E';
510   formatLatLongString(d, format, buf, c);
511   return buf;
512 }
513
514 const char *
515 FGProperties::getLatitudeString ()
516 {
517   static char buf[32];
518   double d = _latDeg->getDoubleValue();
519   int format = _lonLatformat->getIntValue();
520
521   char c = d < 0.0 ? 'S' : 'N';
522   formatLatLongString(d, format, buf, c);
523   return buf;
524 }
525
526
527
528 \f
529
530 FGProperties::FGProperties ()
531 {
532 }
533
534 FGProperties::~FGProperties ()
535 {
536 }
537
538 void
539 FGProperties::init ()
540 {
541 }
542
543 void
544 FGProperties::bind ()
545 {
546   _longDeg      = fgGetNode("/position/longitude-deg", true);
547   _latDeg       = fgGetNode("/position/latitude-deg", true);
548   _lonLatformat = fgGetNode("/sim/lon-lat-format", true);
549
550   _offset = fgGetNode("/sim/time/local-offset", true);
551
552   // utc date/time
553   _uyear = fgGetNode("/sim/time/utc/year", true);
554   _umonth = fgGetNode("/sim/time/utc/month", true);
555   _uday = fgGetNode("/sim/time/utc/day", true);
556   _uhour = fgGetNode("/sim/time/utc/hour", true);
557   _umin = fgGetNode("/sim/time/utc/minute", true);
558   _usec = fgGetNode("/sim/time/utc/second", true);
559   _uwday = fgGetNode("/sim/time/utc/weekday", true);
560   _udsec = fgGetNode("/sim/time/utc/day-seconds", true);
561
562   // real local date/time
563   _ryear = fgGetNode("/sim/time/real/year", true);
564   _rmonth = fgGetNode("/sim/time/real/month", true);
565   _rday = fgGetNode("/sim/time/real/day", true);
566   _rhour = fgGetNode("/sim/time/real/hour", true);
567   _rmin = fgGetNode("/sim/time/real/minute", true);
568   _rsec = fgGetNode("/sim/time/real/second", true);
569   _rwday = fgGetNode("/sim/time/real/weekday", true);
570
571   _tiedProperties.setRoot(globals->get_props());
572
573   // Simulation
574   _tiedProperties.Tie("/sim/logging/priority", getLoggingPriority, setLoggingPriority);
575   _tiedProperties.Tie("/sim/logging/classes", getLoggingClasses, setLoggingClasses);
576   _tiedProperties.Tie("/sim/freeze/master", getFreeze, setFreeze);
577
578   _tiedProperties.Tie<double>("/sim/time/elapsed-sec", getElapsedTime_sec);
579   _tiedProperties.Tie("/sim/time/gmt", getDateString, setDateString);
580   fgSetArchivable("/sim/time/gmt");
581   _tiedProperties.Tie<const char*>("/sim/time/gmt-string", getGMTString);
582
583   // Position
584   _tiedProperties.Tie<const char*>("/position/latitude-string", getLatitudeString);
585   _tiedProperties.Tie<const char*>("/position/longitude-string", getLongitudeString);
586
587   // Orientation
588   _tiedProperties.Tie<double>("/orientation/heading-magnetic-deg", getHeadingMag);
589   _tiedProperties.Tie<double>("/orientation/track-magnetic-deg", getTrackMag);
590 }
591
592 void
593 FGProperties::unbind ()
594 {
595     _tiedProperties.Untie();
596
597     // drop static references to properties
598     _longDeg = 0;
599     _latDeg = 0;
600     _lonLatformat = 0;
601 }
602
603 void
604 FGProperties::update (double dt)
605 {
606     _offset->setIntValue(globals->get_time_params()->get_local_offset());
607
608     // utc date/time
609     struct tm *u = globals->get_time_params()->getGmt();
610     _uyear->setIntValue(u->tm_year + 1900);
611     _umonth->setIntValue(u->tm_mon + 1);
612     _uday->setIntValue(u->tm_mday);
613     _uhour->setIntValue(u->tm_hour);
614     _umin->setIntValue(u->tm_min);
615     _usec->setIntValue(u->tm_sec);
616     _uwday->setIntValue(u->tm_wday);
617     _udsec->setIntValue(u->tm_hour * 3600 + u->tm_min * 60 + u->tm_sec);
618
619     // real local date/time
620     time_t real = time(0);
621     struct tm *r = localtime(&real);
622     _ryear->setIntValue(r->tm_year + 1900);
623     _rmonth->setIntValue(r->tm_mon + 1);
624     _rday->setIntValue(r->tm_mday);
625     _rhour->setIntValue(r->tm_hour);
626     _rmin->setIntValue(r->tm_min);
627     _rsec->setIntValue(r->tm_sec);
628     _rwday->setIntValue(r->tm_wday);
629 }
630
631
632 \f
633 ////////////////////////////////////////////////////////////////////////
634 // Save and restore.
635 ////////////////////////////////////////////////////////////////////////
636
637
638 /**
639  * Save the current state of the simulator to a stream.
640  */
641 bool
642 fgSaveFlight (std::ostream &output, bool write_all)
643 {
644
645   fgSetBool("/sim/presets/onground", false);
646   fgSetArchivable("/sim/presets/onground");
647   fgSetBool("/sim/presets/trim", false);
648   fgSetArchivable("/sim/presets/trim");
649   fgSetString("/sim/presets/speed-set", "UVW");
650   fgSetArchivable("/sim/presets/speed-set");
651
652   try {
653     writeProperties(output, globals->get_props(), write_all);
654   } catch (const sg_exception &e) {
655     guiErrorMessage("Error saving flight: ", e);
656     return false;
657   }
658   return true;
659 }
660
661
662 /**
663  * Restore the current state of the simulator from a stream.
664  */
665 bool
666 fgLoadFlight (std::istream &input)
667 {
668   SGPropertyNode props;
669   try {
670     readProperties(input, &props);
671   } catch (const sg_exception &e) {
672     guiErrorMessage("Error reading saved flight: ", e);
673     return false;
674   }
675
676   fgSetBool("/sim/presets/onground", false);
677   fgSetBool("/sim/presets/trim", false);
678   fgSetString("/sim/presets/speed-set", "UVW");
679
680   copyProperties(&props, globals->get_props());
681
682   return true;
683 }
684
685
686 bool
687 fgLoadProps (const char * path, SGPropertyNode * props, bool in_fg_root, int default_mode)
688 {
689     string fullpath;
690     if (in_fg_root) {
691         SGPath loadpath(globals->get_fg_root());
692         loadpath.append(path);
693         fullpath = loadpath.str();
694     } else {
695         fullpath = path;
696     }
697
698     try {
699         readProperties(fullpath, props, default_mode);
700     } catch (const sg_exception &e) {
701         guiErrorMessage("Error reading properties: ", e);
702         return false;
703     }
704     return true;
705 }
706
707
708 \f
709 ////////////////////////////////////////////////////////////////////////
710 // Property convenience functions.
711 ////////////////////////////////////////////////////////////////////////
712
713 SGPropertyNode *
714 fgGetNode (const char * path, bool create)
715 {
716   return globals->get_props()->getNode(path, create);
717 }
718
719 SGPropertyNode * 
720 fgGetNode (const char * path, int index, bool create)
721 {
722   return globals->get_props()->getNode(path, index, create);
723 }
724
725 bool
726 fgHasNode (const char * path)
727 {
728   return (fgGetNode(path, false) != 0);
729 }
730
731 void
732 fgAddChangeListener (SGPropertyChangeListener * listener, const char * path)
733 {
734   fgGetNode(path, true)->addChangeListener(listener);
735 }
736
737 void
738 fgAddChangeListener (SGPropertyChangeListener * listener,
739                      const char * path, int index)
740 {
741   fgGetNode(path, index, true)->addChangeListener(listener);
742 }
743
744 bool
745 fgGetBool (const char * name, bool defaultValue)
746 {
747   return globals->get_props()->getBoolValue(name, defaultValue);
748 }
749
750 int
751 fgGetInt (const char * name, int defaultValue)
752 {
753   return globals->get_props()->getIntValue(name, defaultValue);
754 }
755
756 long
757 fgGetLong (const char * name, long defaultValue)
758 {
759   return globals->get_props()->getLongValue(name, defaultValue);
760 }
761
762 float
763 fgGetFloat (const char * name, float defaultValue)
764 {
765   return globals->get_props()->getFloatValue(name, defaultValue);
766 }
767
768 double
769 fgGetDouble (const char * name, double defaultValue)
770 {
771   return globals->get_props()->getDoubleValue(name, defaultValue);
772 }
773
774 const char *
775 fgGetString (const char * name, const char * defaultValue)
776 {
777   return globals->get_props()->getStringValue(name, defaultValue);
778 }
779
780 bool
781 fgSetBool (const char * name, bool val)
782 {
783   return globals->get_props()->setBoolValue(name, val);
784 }
785
786 bool
787 fgSetInt (const char * name, int val)
788 {
789   return globals->get_props()->setIntValue(name, val);
790 }
791
792 bool
793 fgSetLong (const char * name, long val)
794 {
795   return globals->get_props()->setLongValue(name, val);
796 }
797
798 bool
799 fgSetFloat (const char * name, float val)
800 {
801   return globals->get_props()->setFloatValue(name, val);
802 }
803
804 bool
805 fgSetDouble (const char * name, double val)
806 {
807   return globals->get_props()->setDoubleValue(name, val);
808 }
809
810 bool
811 fgSetString (const char * name, const char * val)
812 {
813   return globals->get_props()->setStringValue(name, val);
814 }
815
816 void
817 fgSetArchivable (const char * name, bool state)
818 {
819   SGPropertyNode * node = globals->get_props()->getNode(name);
820   if (node == 0)
821     SG_LOG(SG_GENERAL, SG_DEBUG,
822            "Attempt to set archive flag for non-existant property "
823            << name);
824   else
825     node->setAttribute(SGPropertyNode::ARCHIVE, state);
826 }
827
828 void
829 fgSetReadable (const char * name, bool state)
830 {
831   SGPropertyNode * node = globals->get_props()->getNode(name);
832   if (node == 0)
833     SG_LOG(SG_GENERAL, SG_DEBUG,
834            "Attempt to set read flag for non-existant property "
835            << name);
836   else
837     node->setAttribute(SGPropertyNode::READ, state);
838 }
839
840 void
841 fgSetWritable (const char * name, bool state)
842 {
843   SGPropertyNode * node = globals->get_props()->getNode(name);
844   if (node == 0)
845     SG_LOG(SG_GENERAL, SG_DEBUG,
846            "Attempt to set write flag for non-existant property "
847            << name);
848   else
849     node->setAttribute(SGPropertyNode::WRITE, state);
850 }
851
852 void
853 fgUntie(const char * name)
854 {
855   SGPropertyNode* node = globals->get_props()->getNode(name);
856   if (!node) {
857     SG_LOG(SG_GENERAL, SG_WARN, "fgUntie: unknown property " << name);
858     return;
859   }
860   
861   if (!node->isTied()) {
862     return;
863   }
864   
865   if (!node->untie()) {
866     SG_LOG(SG_GENERAL, SG_WARN, "Failed to untie property " << name);
867   }
868 }
869
870
871 // end of fg_props.cxx