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