]> git.mxchange.org Git - simgear.git/commitdiff
- Moved some property specific code into simgear/props/
authorcurt <curt>
Tue, 6 May 2003 23:40:43 +0000 (23:40 +0000)
committercurt <curt>
Tue, 6 May 2003 23:40:43 +0000 (23:40 +0000)
- Split out the condition code from fgfs/src/Main/fg_props and put it
  in it's own source file in simgear/props/
- Created a scene subdirectory for scenery, model, and material property
  related code.
- Moved location.[ch]xx into simgear/scene/model/
- The location and condition code had dependencies on flightgear's global
  state (all the globals-> stuff, the flightgear property tree, etc.)  SimGear
  code can't depend on it so that data has to be passed as parameters to the
  functions/methods/constructors.

22 files changed:
configure.ac
simgear/Makefile.am
simgear/misc/Makefile.am
simgear/misc/commands.cxx
simgear/misc/commands.hxx
simgear/misc/props.cxx [deleted file]
simgear/misc/props.hxx [deleted file]
simgear/misc/props_io.cxx [deleted file]
simgear/misc/props_io.hxx [deleted file]
simgear/misc/props_test.cxx [deleted file]
simgear/props/Makefile.am [new file with mode: 0644]
simgear/props/condition.cxx [new file with mode: 0644]
simgear/props/condition.hxx [new file with mode: 0644]
simgear/props/props.cxx [new file with mode: 0644]
simgear/props/props.hxx [new file with mode: 0644]
simgear/props/props_io.cxx [new file with mode: 0644]
simgear/props/props_io.hxx [new file with mode: 0644]
simgear/props/props_test.cxx [new file with mode: 0644]
simgear/scene/Makefile.am [new file with mode: 0644]
simgear/scene/model/Makefile.am [new file with mode: 0644]
simgear/scene/model/location.cxx [new file with mode: 0644]
simgear/scene/model/location.hxx [new file with mode: 0644]

index 7d0e53540979a431b005ca78824804b17a43dc0b..eeab41a4c1047f43b7a494929876b2f662ceeb3d 100644 (file)
@@ -415,7 +415,10 @@ AC_CONFIG_FILES([ \
        simgear/math/Makefile \
        simgear/metar/Makefile \
        simgear/misc/Makefile \
+       simgear/props/Makefile \
        simgear/route/Makefile \
+       simgear/scene/Makefile \
+       simgear/scene/model/Makefile \
        simgear/screen/Makefile \
        simgear/serial/Makefile \
        simgear/sky/Makefile \
index 8a2c435c881f390e98be6e0f5432a7980262a656..ccdcf2bf0e2ef86cd65d33004d52d7e2812e706e 100644 (file)
@@ -23,7 +23,9 @@ SUBDIRS = \
        magvar \
        math \
        $(METAR_DIRS) \
+       props \
        route \
+       scene \
        screen \
        serial \
        sky \
index 6f4e38280d54af1c7b2a1fbbe91054d287d34e17..8c6d70dab99097488bbf95502db8c0a479686b21 100644 (file)
@@ -5,8 +5,6 @@ lib_LIBRARIES = libsgmisc.a
 include_HEADERS = \
         commands.hxx \
         exception.hxx \
-       props.hxx \
-       props_io.hxx \
        sg_path.hxx \
        sgstream.hxx \
        stopwatch.hxx \
@@ -18,8 +16,6 @@ include_HEADERS = \
 libsgmisc_a_SOURCES = \
         commands.cxx \
         exception.cxx \
-       props.cxx \
-        props_io.cxx \
        sg_path.cxx \
        sgstream.cxx \
        strutils.cxx \
@@ -27,12 +23,12 @@ libsgmisc_a_SOURCES = \
        texcoord.cxx \
        zfstream.cxx
 
-noinst_PROGRAMS = props_test tabbed_value_test
-
-props_test_SOURCES = props_test.cxx
-props_test_LDADD = libsgmisc.a ../xml/libsgxml.a ../debug/libsgdebug.a
+noinst_PROGRAMS = tabbed_value_test
 
 tabbed_value_test_SOURCES = tabbed_values_test.cxx
-tabbed_value_test_LDADD = libsgmisc.a ../xml/libsgxml.a ../debug/libsgdebug.a
+tabbed_value_test_LDADD = \
+       $(top_builddir)/simgear/misc/libsgmisc.a \
+       $(top_builddir)/simgear/xml/libsgxml.a \
+       $(top_builddir)/simgear/debug/libsgdebug.a
 
 INCLUDES = -I$(top_srcdir)
index 88869848f7e45fea7c84a07784cbc30c7d75a100..2e2c59215494430a72498266e4617960d4f0bab9 100644 (file)
@@ -4,8 +4,9 @@
 //
 // $Id$
 
+#include <simgear/props/props_io.hxx>
+
 #include "commands.hxx"
-#include "props_io.hxx"
 
 
 \f
index 55f193b48c6cbcd4415da46e9838ceb11e65b4ac..4ab28a09960a7c320bdb90f4b31efcc3f9bd7c75 100644 (file)
@@ -17,7 +17,7 @@
 #include <map>
 #include <vector>
 
-#include "props.hxx"
+#include <simgear/props/props.hxx>
 
 SG_USING_STD(string);
 SG_USING_STD(map);
diff --git a/simgear/misc/props.cxx b/simgear/misc/props.cxx
deleted file mode 100644 (file)
index 0af19b4..0000000
+++ /dev/null
@@ -1,2374 +0,0 @@
-// props.cxx - implementation of a property list.
-// Started Fall 2000 by David Megginson, david@megginson.com
-// This code is released into the Public Domain.
-//
-// See props.html for documentation [replace with URL when available].
-//
-// $Id$
-
-#include "props.hxx"
-
-#include <algorithm>
-#include <stdio.h>
-#include <string.h>
-
-#if PROPS_STANDALONE
-
-#include <iostream>
-using std::cerr;
-using std::endl;
-using std::find;
-using std::sort;
-using std::vector;
-
-#else
-
-#include <simgear/compiler.h>
-#include <simgear/debug/logstream.hxx>
-
-SG_USING_STD(sort);
-SG_USING_STD(find);
-SG_USING_STD(vector);
-
-#ifdef _MSC_VER
-// MSVC is buggy, and needs something strange here
-SG_USING_STD(vector<SGPropertyNode_ptr>);
-SG_USING_STD(vector<SGPropertyChangeListener *>);
-SG_USING_STD(vector<SGPropertyNode *>);
-#endif
-
-#endif
-
-
-\f
-////////////////////////////////////////////////////////////////////////
-// Local classes.
-////////////////////////////////////////////////////////////////////////
-
-/**
- * Comparator class for sorting by index.
- */
-class CompareIndices
-{
-public:
-  int operator() (const SGPropertyNode_ptr n1, const SGPropertyNode_ptr n2) const {
-    return (n1->getIndex() < n2->getIndex());
-  }
-};
-
-
-\f
-////////////////////////////////////////////////////////////////////////
-// Convenience macros for value access.
-////////////////////////////////////////////////////////////////////////
-
-#define TEST_READ(dflt) if (!getAttribute(READ)) return dflt
-#define TEST_WRITE if (!getAttribute(WRITE)) return false
-
-
-\f
-////////////////////////////////////////////////////////////////////////
-// Default values for every type.
-////////////////////////////////////////////////////////////////////////
-
-const bool SGRawValue<bool>::DefaultValue = false;
-const int SGRawValue<int>::DefaultValue = 0;
-const long SGRawValue<long>::DefaultValue = 0L;
-const float SGRawValue<float>::DefaultValue = 0.0;
-const double SGRawValue<double>::DefaultValue = 0.0L;
-const char * const SGRawValue<const char *>::DefaultValue = "";
-
-
-\f
-////////////////////////////////////////////////////////////////////////
-// Local path normalization code.
-////////////////////////////////////////////////////////////////////////
-
-/**
- * A component in a path.
- */
-struct PathComponent
-{
-  string name;
-  int index;
-};
-
-/**
- * Parse the name for a path component.
- *
- * Name: [_a-zA-Z][-._a-zA-Z0-9]*
- */
-static inline const string
-parse_name (const string &path, int &i)
-{
-  string name = "";
-  int max = path.size();
-
-  if (path[i] == '.') {
-    i++;
-    if (i < max && path[i] == '.') {
-      i++;
-      name = "..";
-    } else {
-      name = ".";
-    }
-    if (i < max && path[i] != '/')
-      throw string(string("Illegal character after ") + name);
-  }
-
-  else if (isalpha(path[i]) || path[i] == '_') {
-    name += path[i];
-    i++;
-
-                               // The rules inside a name are a little
-                               // less restrictive.
-    while (i < max) {
-      if (isalpha(path[i]) || isdigit(path[i]) || path[i] == '_' ||
-         path[i] == '-' || path[i] == '.') {
-       name += path[i];
-      } else if (path[i] == '[' || path[i] == '/') {
-       break;
-      } else {
-       throw string("name may contain only ._- and alphanumeric characters");
-      }
-      i++;
-    }
-  }
-
-  else {
-    if (name.size() == 0)
-      throw string("name must begin with alpha or '_'");
-  }
-
-  return name;
-}
-
-
-/**
- * Parse the optional integer index for a path component.
- *
- * Index: "[" [0-9]+ "]"
- */
-static inline int
-parse_index (const string &path, int &i)
-{
-  int index = 0;
-
-  if (path[i] != '[')
-    return 0;
-  else
-    i++;
-
-  for (int max = path.size(); i < max; i++) {
-    if (isdigit(path[i])) {
-      index = (index * 10) + (path[i] - '0');
-    } else if (path[i] == ']') {
-      i++;
-      return index;
-    } else {
-      break;
-    }
-  }
-
-  throw string("unterminated index (looking for ']')");
-}
-
-
-/**
- * Parse a single path component.
- *
- * Component: Name Index?
- */
-static inline PathComponent
-parse_component (const string &path, int &i)
-{
-  PathComponent component;
-  component.name = parse_name(path, i);
-  if (component.name[0] != '.')
-    component.index = parse_index(path, i);
-  else
-    component.index = -1;
-  return component;
-}
-
-
-/**
- * Parse a path into its components.
- */
-static void
-parse_path (const string &path, vector<PathComponent> &components)
-{
-  int pos = 0;
-  int max = path.size();
-
-                               // Check for initial '/'
-  if (path[pos] == '/') {
-    PathComponent root;
-    root.name = "";
-    root.index = -1;
-    components.push_back(root);
-    pos++;
-    while (pos < max && path[pos] == '/')
-      pos++;
-  }
-
-  while (pos < max) {
-    components.push_back(parse_component(path, pos));
-    while (pos < max && path[pos] == '/')
-      pos++;
-  }
-}
-
-
-\f
-////////////////////////////////////////////////////////////////////////
-// Other static utility functions.
-////////////////////////////////////////////////////////////////////////
-
-
-static char *
-copy_string (const char * s)
-{
-                               // FIXME: potential buffer overflow.
-                               // For some reason, strnlen and
-                               // strncpy cause all kinds of crashes.
-  char * copy = new char[strlen(s) + 1];
-  strcpy(copy, s);
-  return copy;
-}
-
-static bool
-compare_strings (const char * s1, const char * s2)
-{
-  return !strncmp(s1, s2, SGPropertyNode::MAX_STRING_LEN);
-}
-
-/**
- * Locate a child node by name and index.
- */
-static int
-find_child (const char * name, int index, vector<SGPropertyNode_ptr> nodes)
-{
-  int nNodes = nodes.size();
-  for (int i = 0; i < nNodes; i++) {
-    SGPropertyNode * node = nodes[i];
-    if (compare_strings(node->getName(), name) && node->getIndex() == index)
-      return i;
-  }
-  return -1;
-}
-
-
-/**
- * Locate another node, given a relative path.
- */
-static SGPropertyNode *
-find_node (SGPropertyNode * current,
-          const vector<PathComponent> &components,
-          int position,
-          bool create)
-{
-                               // Run off the end of the list
-  if (current == 0) {
-    return 0;
-  }
-
-                               // Success! This is the one we want.
-  else if (position >= (int)components.size()) {
-    return current;
-  }
-
-                               // Empty component means root.
-  else if (components[position].name == "") {
-    return find_node(current->getRootNode(), components, position + 1, create);
-  }
-
-                               // . means current directory
-  else if (components[position].name == ".") {
-    return find_node(current, components, position + 1, create);
-  }
-
-                               // .. means parent directory
-  else if (components[position].name == "..") {
-    SGPropertyNode * parent = current->getParent();
-    if (parent == 0)
-      throw string("Attempt to move past root with '..'");
-    else
-      return find_node(parent, components, position + 1, create);
-  }
-
-                               // Otherwise, a child name
-  else {
-    SGPropertyNode * child =
-      current->getChild(components[position].name.c_str(),
-                       components[position].index,
-                       create);
-    return find_node(child, components, position + 1, create);
-  }
-}
-
-
-\f
-////////////////////////////////////////////////////////////////////////
-// Private methods from SGPropertyNode (may be inlined for speed).
-////////////////////////////////////////////////////////////////////////
-
-inline bool
-SGPropertyNode::get_bool () const
-{
-  if (_tied)
-    return _value.bool_val->getValue();
-  else
-    return _local_val.bool_val;
-}
-
-inline int
-SGPropertyNode::get_int () const
-{
-  if (_tied)
-    return _value.int_val->getValue();
-  else
-    return _local_val.int_val;
-}
-
-inline long
-SGPropertyNode::get_long () const
-{
-  if (_tied)
-    return _value.long_val->getValue();
-  else
-    return _local_val.long_val;
-}
-
-inline float
-SGPropertyNode::get_float () const
-{
-  if (_tied)
-    return _value.float_val->getValue();
-  else
-    return _local_val.float_val;
-}
-
-inline double
-SGPropertyNode::get_double () const
-{
-  if (_tied)
-    return _value.double_val->getValue();
-  else
-    return _local_val.double_val;
-}
-
-inline const char *
-SGPropertyNode::get_string () const
-{
-  if (_tied)
-    return _value.string_val->getValue();
-  else
-    return _local_val.string_val;
-}
-
-inline bool
-SGPropertyNode::set_bool (bool val)
-{
-  if (_tied) {
-    if (_value.bool_val->setValue(val)) {
-      fireValueChanged();
-      return true;
-    } else {
-      return false;
-    }
-  } else {
-    _local_val.bool_val = val;
-    fireValueChanged();
-    return true;
-  }
-}
-
-inline bool
-SGPropertyNode::set_int (int val)
-{
-  if (_tied) {
-    if (_value.int_val->setValue(val)) {
-      fireValueChanged();
-      return true;
-    } else {
-      return false;
-    }
-  } else {
-    _local_val.int_val = val;
-    fireValueChanged();
-    return true;
-  }
-}
-
-inline bool
-SGPropertyNode::set_long (long val)
-{
-  if (_tied) {
-    if (_value.long_val->setValue(val)) {
-      fireValueChanged();
-      return true;
-    } else {
-      return false;
-    }
-  } else {
-    _local_val.long_val = val;
-    fireValueChanged();
-    return true;
-  }
-}
-
-inline bool
-SGPropertyNode::set_float (float val)
-{
-  if (_tied) {
-    if (_value.float_val->setValue(val)) {
-      fireValueChanged();
-      return true;
-    } else {
-      return false;
-    }
-  } else {
-    _local_val.float_val = val;
-    fireValueChanged();
-    return true;
-  }
-}
-
-inline bool
-SGPropertyNode::set_double (double val)
-{
-  if (_tied) {
-    if (_value.double_val->setValue(val)) {
-      fireValueChanged();
-      return true;
-    } else {
-      return false;
-    }
-  } else {
-    _local_val.double_val = val;
-    fireValueChanged();
-    return true;
-  }
-}
-
-inline bool
-SGPropertyNode::set_string (const char * val)
-{
-  if (_tied) {
-    if (_value.string_val->setValue(val)) {
-      fireValueChanged();
-      return true;
-    } else {
-      return false;
-    }
-  } else {
-    delete [] _local_val.string_val;
-    _local_val.string_val = copy_string(val);
-    fireValueChanged();
-    return true;
-  }
-}
-
-void
-SGPropertyNode::clear_value ()
-{
-  switch (_type) {
-  case NONE:
-    break;
-  case ALIAS:
-    _value.alias = 0;
-    break;
-  case BOOL:
-    if (_tied) {
-      delete _value.bool_val;
-      _value.bool_val = 0;
-    }
-    _local_val.bool_val = SGRawValue<bool>::DefaultValue;
-    break;
-  case INT:
-    if (_tied) {
-      delete _value.int_val;
-      _value.int_val = 0;
-    }
-    _local_val.int_val = SGRawValue<int>::DefaultValue;
-    break;
-  case LONG:
-    if (_tied) {
-      delete _value.long_val;
-      _value.long_val = 0L;
-    }
-    _local_val.long_val = SGRawValue<long>::DefaultValue;
-    break;
-  case FLOAT:
-    if (_tied) {
-      delete _value.float_val;
-      _value.float_val = 0;
-    }
-    _local_val.float_val = SGRawValue<float>::DefaultValue;
-    break;
-  case DOUBLE:
-    if (_tied) {
-      delete _value.double_val;
-      _value.double_val = 0;
-    }
-    _local_val.double_val = SGRawValue<double>::DefaultValue;
-    break;
-  case STRING:
-  case UNSPECIFIED:
-    if (_tied) {
-      delete _value.string_val;
-      _value.string_val = 0;
-    } else {
-      delete [] _local_val.string_val;
-    }
-    _local_val.string_val = 0;
-    break;
-  }
-  _tied = false;
-  _type = NONE;
-}
-
-
-/**
- * Get the value as a string.
- */
-const char *
-SGPropertyNode::make_string () const
-{
-  if (!getAttribute(READ))
-    return "";
-
-  switch (_type) {
-  case ALIAS:
-    return _value.alias->getStringValue();
-  case BOOL:
-    if (get_bool())
-      return "true";
-    else
-      return "false";
-  case INT:
-    sprintf(_buffer, "%d", get_int());
-    return _buffer;
-  case LONG:
-    sprintf(_buffer, "%ld", get_long());
-    return _buffer;
-  case FLOAT:
-    sprintf(_buffer, "%f", get_float());
-    return _buffer;
-  case DOUBLE:
-    sprintf(_buffer, "%f", get_double());
-    return _buffer;
-  case STRING:
-  case UNSPECIFIED:
-    return get_string();
-  case NONE:
-  default:
-    return "";
-  }
-}
-
-/**
- * Trace a write access for a property.
- */
-void
-SGPropertyNode::trace_write () const
-{
-#if PROPS_STANDALONE
-  cerr << "TRACE: Write node " << getPath () << ", value\""
-       << make_string() << '"' << endl;
-#else
-  SG_LOG(SG_GENERAL, SG_INFO, "TRACE: Write node " << getPath()
-        << ", value\"" << make_string() << '"');
-#endif
-}
-
-/**
- * Trace a read access for a property.
- */
-void
-SGPropertyNode::trace_read () const
-{
-#if PROPS_STANDALONE
-  cerr << "TRACE: Write node " << getPath () << ", value \""
-       << make_string() << '"' << endl;
-#else
-  SG_LOG(SG_GENERAL, SG_INFO, "TRACE: Read node " << getPath()
-        << ", value \"" << make_string() << '"');
-#endif
-}
-
-/**
- * Increment reference counter
- */
-void
-SGPropertyNode::incrementRef()
-{
-  ++_count;
-}
-
-/**
- * Decrement reference counter
- */
-int
-SGPropertyNode::decrementRef()
-{
-  return --_count;
-}
-
-
-\f
-////////////////////////////////////////////////////////////////////////
-// Public methods from SGPropertyNode.
-////////////////////////////////////////////////////////////////////////
-
-/**
- * Last used attribute
- * Update as needed when enum Attribute is changed
- */
-const int SGPropertyNode::LAST_USED_ATTRIBUTE = TRACE_WRITE;
-
-/**
- * Default constructor: always creates a root node.
- */
-SGPropertyNode::SGPropertyNode ()
-  : _name(copy_string("")),
-    _display_name(0),
-    _index(0),
-    _parent(0),
-    _path(0),
-    _path_cache(0),
-    _type(NONE),
-    _tied(false),
-    _attr(READ|WRITE),
-    _count(0),
-    _listeners(0)
-{
-  _local_val.string_val = 0;
-}
-
-
-/**
- * Copy constructor.
- */
-SGPropertyNode::SGPropertyNode (const SGPropertyNode &node)
-  : _display_name(0),
-    _index(node._index),
-    _parent(0),                        // don't copy the parent
-    _path(0),
-    _path_cache(0),
-    _type(node._type),
-    _tied(node._tied),
-    _attr(node._attr),
-    _count(0),
-    _listeners(0)              // CHECK!!
-{
-  _name = copy_string(node._name);
-  _local_val.string_val = 0;
-  switch (_type) {
-  case NONE:
-    break;
-  case ALIAS:
-    _value.alias = node._value.alias;
-    _tied = false;
-    break;
-  case BOOL:
-    if (_tied) {
-      _tied = true;
-      _value.bool_val = node._value.bool_val->clone();
-    } else {
-      _tied = false;
-      set_bool(node.get_bool());
-    }
-    break;
-  case INT:
-    if (_tied) {
-      _tied = true;
-      _value.int_val = node._value.int_val->clone();
-    } else {
-      _tied = false;
-      set_int(node.get_int());
-    }
-    break;
-  case LONG:
-    if (_tied) {
-      _tied = true;
-      _value.long_val = node._value.long_val->clone();
-    } else {
-      _tied = false;
-      set_long(node.get_long());
-    }
-    break;
-  case FLOAT:
-    if (_tied) {
-      _tied = true;
-      _value.float_val = node._value.float_val->clone();
-    } else {
-      _tied = false;
-      set_float(node.get_float());
-    }
-    break;
-  case DOUBLE:
-    if (_tied) {
-      _tied = true;
-      _value.double_val = node._value.double_val->clone();
-    } else {
-      _tied = false;
-      set_double(node.get_double());
-    }
-    break;
-  case STRING:
-  case UNSPECIFIED:
-    if (_tied) {
-      _tied = true;
-      _value.string_val = node._value.string_val->clone();
-    } else {
-      _tied = false;
-      set_string(node.get_string());
-    }
-    break;
-  }
-}
-
-
-/**
- * Convenience constructor.
- */
-SGPropertyNode::SGPropertyNode (const char * name,
-                               int index,
-                               SGPropertyNode * parent)
-  : _display_name(0),
-    _index(index),
-    _parent(parent),
-    _path(0),
-    _path_cache(0),
-    _type(NONE),
-    _tied(false),
-    _attr(READ|WRITE),
-    _count(0),
-    _listeners(0)
-{
-  _name = copy_string(name);
-  _local_val.string_val = 0;
-}
-
-
-/**
- * Destructor.
- */
-SGPropertyNode::~SGPropertyNode ()
-{
-  delete [] _name;
-  delete [] _display_name;
-  delete [] _path;
-  delete _path_cache;
-  clear_value();
-  delete _listeners;
-}
-
-
-/**
- * Alias to another node.
- */
-bool
-SGPropertyNode::alias (SGPropertyNode * target)
-{
-  if (target == 0 || _type == ALIAS || _tied)
-    return false;
-  clear_value();
-  _value.alias = target;
-  _type = ALIAS;
-  return true;
-}
-
-
-/**
- * Alias to another node by path.
- */
-bool
-SGPropertyNode::alias (const char * path)
-{
-  return alias(getNode(path, true));
-}
-
-
-/**
- * Remove an alias.
- */
-bool
-SGPropertyNode::unalias ()
-{
-  if (_type != ALIAS)
-    return false;
-  _type = NONE;
-  _value.alias = 0;
-  return true;
-}
-
-
-/**
- * Get the target of an alias.
- */
-SGPropertyNode *
-SGPropertyNode::getAliasTarget ()
-{
-  return (_type == ALIAS ? _value.alias : 0);
-}
-
-
-const SGPropertyNode *
-SGPropertyNode::getAliasTarget () const
-{
-  return (_type == ALIAS ? _value.alias : 0);
-}
-
-
-/**
- * Get a non-const child by index.
- */
-SGPropertyNode *
-SGPropertyNode::getChild (int position)
-{
-  if (position >= 0 && position < nChildren())
-    return _children[position];
-  else
-    return 0;
-}
-
-
-/**
- * Get a const child by index.
- */
-const SGPropertyNode *
-SGPropertyNode::getChild (int position) const
-{
-  if (position >= 0 && position < nChildren())
-    return _children[position];
-  else
-    return 0;
-}
-
-
-/**
- * Get a non-const child by name and index, creating if necessary.
- */
-SGPropertyNode *
-SGPropertyNode::getChild (const char * name, int index, bool create)
-{
-  int pos = find_child(name, index, _children);
-  if (pos >= 0) {
-    return _children[pos];
-  } else if (create) {
-    SGPropertyNode_ptr node;
-    pos = find_child(name, index, _removedChildren);
-    if (pos >= 0) {
-      vector<SGPropertyNode_ptr>::iterator it = _removedChildren.begin();
-      it += pos;
-      node = _removedChildren[pos];
-      _removedChildren.erase(it);
-      node->setAttribute(REMOVED, false);
-    } else {
-      node = new SGPropertyNode(name, index, this);
-    }
-    _children.push_back(node);
-    fireChildAdded(node);
-    return node;
-  } else {
-    return 0;
-  }
-}
-
-
-/**
- * Get a const child by name and index.
- */
-const SGPropertyNode *
-SGPropertyNode::getChild (const char * name, int index) const
-{
-  int pos = find_child(name, index, _children);
-  if (pos >= 0)
-    return _children[pos];
-  else
-    return 0;
-}
-
-
-/**
- * Get all children with the same name (but different indices).
- */
-vector<SGPropertyNode_ptr>
-SGPropertyNode::getChildren (const char * name) const
-{
-  vector<SGPropertyNode_ptr> children;
-  int max = _children.size();
-
-  for (int i = 0; i < max; i++)
-    if (compare_strings(_children[i]->getName(), name))
-      children.push_back(_children[i]);
-
-  sort(children.begin(), children.end(), CompareIndices());
-  return children;
-}
-
-
-/**
- * Remove a child node
- */
-SGPropertyNode_ptr 
-SGPropertyNode::removeChild (const char * name, int index, bool keep)
-{
-  SGPropertyNode_ptr ret;
-  int pos = find_child(name, index, _children);
-  if (pos >= 0) {
-    vector<SGPropertyNode_ptr>::iterator it = _children.begin();
-    it += pos;
-    SGPropertyNode_ptr node = _children[pos];
-    _children.erase(it);
-    if (keep) {
-      _removedChildren.push_back(node);
-    }
-    node->setAttribute(REMOVED, true);
-    ret = node;
-    fireChildRemoved(node);
-  }
-  return ret;
-}
-
-
-const char *
-SGPropertyNode::getDisplayName (bool simplify) const
-{
-  string display = _name;
-  if (_index != 0 || !simplify) {
-    char buffer[64];
-    sprintf(buffer, "[%d]", _index);
-    display += buffer;
-  }
-  _display_name = copy_string(display.c_str());
-  return _display_name;
-}
-
-
-const char *
-SGPropertyNode::getPath (bool simplify) const
-{
-                               // Calculate the complete path only once.
-  if (_path == 0) {
-    string path;
-    if (_parent == 0) {
-      path = "";
-    } else {
-      path = _parent->getPath(simplify);
-      path += '/';
-      path += getDisplayName(simplify);
-    }
-    _path = copy_string(path.c_str());
-  }
-
-  return _path;
-}
-
-SGPropertyNode::Type
-SGPropertyNode::getType () const
-{
-  if (_type == ALIAS)
-    return _value.alias->getType();
-  else
-    return _type;
-}
-
-
-bool 
-SGPropertyNode::getBoolValue () const
-{
-                               // Shortcut for common case
-  if (_attr == (READ|WRITE) && _type == BOOL)
-    return get_bool();
-
-  if (getAttribute(TRACE_READ))
-    trace_read();
-  if (!getAttribute(READ))
-    return SGRawValue<bool>::DefaultValue;
-  switch (_type) {
-  case ALIAS:
-    return _value.alias->getBoolValue();
-  case BOOL:
-    return get_bool();
-  case INT:
-    return get_int() == 0 ? false : true;
-  case LONG:
-    return get_long() == 0L ? false : true;
-  case FLOAT:
-    return get_float() == 0.0 ? false : true;
-  case DOUBLE:
-    return get_double() == 0.0L ? false : true;
-  case STRING:
-  case UNSPECIFIED:
-    return (compare_strings(get_string(), "true") || getDoubleValue() != 0.0L);
-  case NONE:
-  default:
-    return SGRawValue<bool>::DefaultValue;
-  }
-}
-
-int 
-SGPropertyNode::getIntValue () const
-{
-                               // Shortcut for common case
-  if (_attr == (READ|WRITE) && _type == INT)
-    return get_int();
-
-  if (getAttribute(TRACE_READ))
-    trace_read();
-  if (!getAttribute(READ))
-    return SGRawValue<int>::DefaultValue;
-  switch (_type) {
-  case ALIAS:
-    return _value.alias->getIntValue();
-  case BOOL:
-    return int(get_bool());
-  case INT:
-    return get_int();
-  case LONG:
-    return int(get_long());
-  case FLOAT:
-    return int(get_float());
-  case DOUBLE:
-    return int(get_double());
-  case STRING:
-  case UNSPECIFIED:
-    return atoi(get_string());
-  case NONE:
-  default:
-    return SGRawValue<int>::DefaultValue;
-  }
-}
-
-long 
-SGPropertyNode::getLongValue () const
-{
-                               // Shortcut for common case
-  if (_attr == (READ|WRITE) && _type == LONG)
-    return get_long();
-
-  if (getAttribute(TRACE_READ))
-    trace_read();
-  if (!getAttribute(READ))
-    return SGRawValue<long>::DefaultValue;
-  switch (_type) {
-  case ALIAS:
-    return _value.alias->getLongValue();
-  case BOOL:
-    return long(get_bool());
-  case INT:
-    return long(get_int());
-  case LONG:
-    return get_long();
-  case FLOAT:
-    return long(get_float());
-  case DOUBLE:
-    return long(get_double());
-  case STRING:
-  case UNSPECIFIED:
-    return strtol(get_string(), 0, 0);
-  case NONE:
-  default:
-    return SGRawValue<long>::DefaultValue;
-  }
-}
-
-float 
-SGPropertyNode::getFloatValue () const
-{
-                               // Shortcut for common case
-  if (_attr == (READ|WRITE) && _type == FLOAT)
-    return get_float();
-
-  if (getAttribute(TRACE_READ))
-    trace_read();
-  if (!getAttribute(READ))
-    return SGRawValue<float>::DefaultValue;
-  switch (_type) {
-  case ALIAS:
-    return _value.alias->getFloatValue();
-  case BOOL:
-    return float(get_bool());
-  case INT:
-    return float(get_int());
-  case LONG:
-    return float(get_long());
-  case FLOAT:
-    return get_float();
-  case DOUBLE:
-    return float(get_double());
-  case STRING:
-  case UNSPECIFIED:
-    return atof(get_string());
-  case NONE:
-  default:
-    return SGRawValue<float>::DefaultValue;
-  }
-}
-
-double 
-SGPropertyNode::getDoubleValue () const
-{
-                               // Shortcut for common case
-  if (_attr == (READ|WRITE) && _type == DOUBLE)
-    return get_double();
-
-  if (getAttribute(TRACE_READ))
-    trace_read();
-  if (!getAttribute(READ))
-    return SGRawValue<double>::DefaultValue;
-
-  switch (_type) {
-  case ALIAS:
-    return _value.alias->getDoubleValue();
-  case BOOL:
-    return double(get_bool());
-  case INT:
-    return double(get_int());
-  case LONG:
-    return double(get_long());
-  case FLOAT:
-    return double(get_float());
-  case DOUBLE:
-    return get_double();
-  case STRING:
-  case UNSPECIFIED:
-    return strtod(get_string(), 0);
-  case NONE:
-  default:
-    return SGRawValue<double>::DefaultValue;
-  }
-}
-
-const char *
-SGPropertyNode::getStringValue () const
-{
-                               // Shortcut for common case
-  if (_attr == (READ|WRITE) && _type == STRING)
-    return get_string();
-
-  if (getAttribute(TRACE_READ))
-    trace_read();
-  if (!getAttribute(READ))
-    return SGRawValue<const char *>::DefaultValue;
-  return make_string();
-}
-
-bool
-SGPropertyNode::setBoolValue (bool value)
-{
-                               // Shortcut for common case
-  if (_attr == (READ|WRITE) && _type == BOOL)
-    return set_bool(value);
-
-  bool result = false;
-  TEST_WRITE;
-  if (_type == NONE || _type == UNSPECIFIED) {
-    clear_value();
-    _tied = false;
-    _type = BOOL;
-  }
-
-  switch (_type) {
-  case ALIAS:
-    result = _value.alias->setBoolValue(value);
-    break;
-  case BOOL:
-    result = set_bool(value);
-    break;
-  case INT:
-    result = set_int(int(value));
-    break;
-  case LONG:
-    result = set_long(long(value));
-    break;
-  case FLOAT:
-    result = set_float(float(value));
-    break;
-  case DOUBLE:
-    result = set_double(double(value));
-    break;
-  case STRING:
-  case UNSPECIFIED:
-    result = set_string(value ? "true" : "false");
-    break;
-  case NONE:
-  default:
-    break;
-  }
-
-  if (getAttribute(TRACE_WRITE))
-    trace_write();
-  return result;
-}
-
-bool
-SGPropertyNode::setIntValue (int value)
-{
-                               // Shortcut for common case
-  if (_attr == (READ|WRITE) && _type == INT)
-    return set_int(value);
-
-  bool result = false;
-  TEST_WRITE;
-  if (_type == NONE || _type == UNSPECIFIED) {
-    clear_value();
-    _type = INT;
-    _local_val.int_val = 0;
-  }
-
-  switch (_type) {
-  case ALIAS:
-    result = _value.alias->setIntValue(value);
-    break;
-  case BOOL:
-    result = set_bool(value == 0 ? false : true);
-    break;
-  case INT:
-    result = set_int(value);
-    break;
-  case LONG:
-    result = set_long(long(value));
-    break;
-  case FLOAT:
-    result = set_float(float(value));
-    break;
-  case DOUBLE:
-    result = set_double(double(value));
-    break;
-  case STRING:
-  case UNSPECIFIED: {
-    char buf[128];
-    sprintf(buf, "%d", value);
-    result = set_string(buf);
-    break;
-  }
-  case NONE:
-  default:
-    break;
-  }
-
-  if (getAttribute(TRACE_WRITE))
-    trace_write();
-  return result;
-}
-
-bool
-SGPropertyNode::setLongValue (long value)
-{
-                               // Shortcut for common case
-  if (_attr == (READ|WRITE) && _type == LONG)
-    return set_long(value);
-
-  bool result = false;
-  TEST_WRITE;
-  if (_type == NONE || _type == UNSPECIFIED) {
-    clear_value();
-    _type = LONG;
-    _local_val.long_val = 0L;
-  }
-
-  switch (_type) {
-  case ALIAS:
-    result = _value.alias->setLongValue(value);
-    break;
-  case BOOL:
-    result = set_bool(value == 0L ? false : true);
-    break;
-  case INT:
-    result = set_int(int(value));
-    break;
-  case LONG:
-    result = set_long(value);
-    break;
-  case FLOAT:
-    result = set_float(float(value));
-    break;
-  case DOUBLE:
-    result = set_double(double(value));
-    break;
-  case STRING:
-  case UNSPECIFIED: {
-    char buf[128];
-    sprintf(buf, "%ld", value);
-    result = set_string(buf);
-    break;
-  }
-  case NONE:
-  default:
-    break;
-  }
-
-  if (getAttribute(TRACE_WRITE))
-    trace_write();
-  return result;
-}
-
-bool
-SGPropertyNode::setFloatValue (float value)
-{
-                               // Shortcut for common case
-  if (_attr == (READ|WRITE) && _type == FLOAT)
-    return set_float(value);
-
-  bool result = false;
-  TEST_WRITE;
-  if (_type == NONE || _type == UNSPECIFIED) {
-    clear_value();
-    _type = FLOAT;
-    _local_val.float_val = 0;
-  }
-
-  switch (_type) {
-  case ALIAS:
-    result = _value.alias->setFloatValue(value);
-    break;
-  case BOOL:
-    result = set_bool(value == 0.0 ? false : true);
-    break;
-  case INT:
-    result = set_int(int(value));
-    break;
-  case LONG:
-    result = set_long(long(value));
-    break;
-  case FLOAT:
-    result = set_float(value);
-    break;
-  case DOUBLE:
-    result = set_double(double(value));
-    break;
-  case STRING:
-  case UNSPECIFIED: {
-    char buf[128];
-    sprintf(buf, "%f", value);
-    result = set_string(buf);
-    break;
-  }
-  case NONE:
-  default:
-    break;
-  }
-
-  if (getAttribute(TRACE_WRITE))
-    trace_write();
-  return result;
-}
-
-bool
-SGPropertyNode::setDoubleValue (double value)
-{
-                               // Shortcut for common case
-  if (_attr == (READ|WRITE) && _type == DOUBLE)
-    return set_double(value);
-
-  bool result = false;
-  TEST_WRITE;
-  if (_type == NONE || _type == UNSPECIFIED) {
-    clear_value();
-    _local_val.double_val = value;
-    _type = DOUBLE;
-  }
-
-  switch (_type) {
-  case ALIAS:
-    result = _value.alias->setDoubleValue(value);
-    break;
-  case BOOL:
-    result = set_bool(value == 0.0L ? false : true);
-    break;
-  case INT:
-    result = set_int(int(value));
-    break;
-  case LONG:
-    result = set_long(long(value));
-    break;
-  case FLOAT:
-    result = set_float(float(value));
-    break;
-  case DOUBLE:
-    result = set_double(value);
-    break;
-  case STRING:
-  case UNSPECIFIED: {
-    char buf[128];
-    sprintf(buf, "%f", value);
-    result = set_string(buf);
-    break;
-  }
-  case NONE:
-  default:
-    break;
-  }
-
-  if (getAttribute(TRACE_WRITE))
-    trace_write();
-  return result;
-}
-
-bool
-SGPropertyNode::setStringValue (const char * value)
-{
-                               // Shortcut for common case
-  if (_attr == (READ|WRITE) && _type == STRING)
-    return set_string(value);
-
-  bool result = false;
-  TEST_WRITE;
-  if (_type == NONE || _type == UNSPECIFIED) {
-    clear_value();
-    _type = STRING;
-  }
-
-  switch (_type) {
-  case ALIAS:
-    result = _value.alias->setStringValue(value);
-    break;
-  case BOOL:
-    result = set_bool((compare_strings(value, "true")
-                      || atoi(value)) ? true : false);
-    break;
-  case INT:
-    result = set_int(atoi(value));
-    break;
-  case LONG:
-    result = set_long(strtol(value, 0, 0));
-    break;
-  case FLOAT:
-    result = set_float(atof(value));
-    break;
-  case DOUBLE:
-    result = set_double(strtod(value, 0));
-    break;
-  case STRING:
-  case UNSPECIFIED:
-    result = set_string(value);
-    break;
-  case NONE:
-  default:
-    break;
-  }
-
-  if (getAttribute(TRACE_WRITE))
-    trace_write();
-  return result;
-}
-
-bool
-SGPropertyNode::setUnspecifiedValue (const char * value)
-{
-  bool result = false;
-  TEST_WRITE;
-  if (_type == NONE) {
-    clear_value();
-    _type = UNSPECIFIED;
-  }
-
-  switch (_type) {
-  case ALIAS:
-    result = _value.alias->setUnspecifiedValue(value);
-    break;
-  case BOOL:
-    result = set_bool((compare_strings(value, "true")
-                      || atoi(value)) ? true : false);
-    break;
-  case INT:
-    result = set_int(atoi(value));
-    break;
-  case LONG:
-    result = set_long(strtol(value, 0, 0));
-    break;
-  case FLOAT:
-    result = set_float(atof(value));
-    break;
-  case DOUBLE:
-    result = set_double(strtod(value, 0));
-    break;
-  case STRING:
-  case UNSPECIFIED:
-    result = set_string(value);
-    break;
-  case NONE:
-  default:
-    break;
-  }
-
-  if (getAttribute(TRACE_WRITE))
-    trace_write();
-  return result;
-}
-
-bool
-SGPropertyNode::tie (const SGRawValue<bool> &rawValue, bool useDefault)
-{
-  if (_type == ALIAS || _tied)
-    return false;
-
-  useDefault = useDefault && hasValue();
-  bool old_val = false;
-  if (useDefault)
-    old_val = getBoolValue();
-
-  clear_value();
-  _type = BOOL;
-  _tied = true;
-  _value.bool_val = rawValue.clone();
-
-  if (useDefault)
-    setBoolValue(old_val);
-
-  return true;
-}
-
-bool
-SGPropertyNode::tie (const SGRawValue<int> &rawValue, bool useDefault)
-{
-  if (_type == ALIAS || _tied)
-    return false;
-
-  useDefault = useDefault && hasValue();
-  int old_val = 0;
-  if (useDefault)
-    old_val = getIntValue();
-
-  clear_value();
-  _type = INT;
-  _tied = true;
-  _value.int_val = rawValue.clone();
-
-  if (useDefault)
-    setIntValue(old_val);
-
-  return true;
-}
-
-bool
-SGPropertyNode::tie (const SGRawValue<long> &rawValue, bool useDefault)
-{
-  if (_type == ALIAS || _tied)
-    return false;
-
-  useDefault = useDefault && hasValue();
-  long old_val = 0;
-  if (useDefault)
-    old_val = getLongValue();
-
-  clear_value();
-  _type = LONG;
-  _tied = true;
-  _value.long_val = rawValue.clone();
-
-  if (useDefault)
-    setLongValue(old_val);
-
-  return true;
-}
-
-bool
-SGPropertyNode::tie (const SGRawValue<float> &rawValue, bool useDefault)
-{
-  if (_type == ALIAS || _tied)
-    return false;
-
-  useDefault = useDefault && hasValue();
-  float old_val = 0.0;
-  if (useDefault)
-    old_val = getFloatValue();
-
-  clear_value();
-  _type = FLOAT;
-  _tied = true;
-  _value.float_val = rawValue.clone();
-
-  if (useDefault)
-    setFloatValue(old_val);
-
-  return true;
-}
-
-bool
-SGPropertyNode::tie (const SGRawValue<double> &rawValue, bool useDefault)
-{
-  if (_type == ALIAS || _tied)
-    return false;
-
-  useDefault = useDefault && hasValue();
-  double old_val = 0.0;
-  if (useDefault)
-    old_val = getDoubleValue();
-
-  clear_value();
-  _type = DOUBLE;
-  _tied = true;
-  _value.double_val = rawValue.clone();
-
-  if (useDefault)
-    setDoubleValue(old_val);
-
-  return true;
-
-}
-
-bool
-SGPropertyNode::tie (const SGRawValue<const char *> &rawValue, bool useDefault)
-{
-  if (_type == ALIAS || _tied)
-    return false;
-
-  useDefault = useDefault && hasValue();
-  string old_val;
-  if (useDefault)
-    old_val = getStringValue();
-
-  clear_value();
-  _type = STRING;
-  _tied = true;
-  _value.string_val = rawValue.clone();
-
-  if (useDefault)
-    setStringValue(old_val.c_str());
-
-  return true;
-}
-
-bool
-SGPropertyNode::untie ()
-{
-  if (!_tied)
-    return false;
-
-  switch (_type) {
-  case BOOL: {
-    bool val = getBoolValue();
-    clear_value();
-    _type = BOOL;
-    _local_val.bool_val = val;
-    break;
-  }
-  case INT: {
-    int val = getIntValue();
-    clear_value();
-    _type = INT;
-    _local_val.int_val = val;
-    break;
-  }
-  case LONG: {
-    long val = getLongValue();
-    clear_value();
-    _type = LONG;
-    _local_val.long_val = val;
-    break;
-  }
-  case FLOAT: {
-    float val = getFloatValue();
-    clear_value();
-    _type = FLOAT;
-    _local_val.float_val = val;
-    break;
-  }
-  case DOUBLE: {
-    double val = getDoubleValue();
-    clear_value();
-    _type = DOUBLE;
-    _local_val.double_val = val;
-    break;
-  }
-  case STRING:
-  case UNSPECIFIED: {
-    string val = getStringValue();
-    clear_value();
-    _type = STRING;
-    _local_val.string_val = copy_string(val.c_str());
-    break;
-  }
-  case NONE:
-  default:
-    break;
-  }
-
-  _tied = false;
-  return true;
-}
-
-SGPropertyNode *
-SGPropertyNode::getRootNode ()
-{
-  if (_parent == 0)
-    return this;
-  else
-    return _parent->getRootNode();
-}
-
-const SGPropertyNode *
-SGPropertyNode::getRootNode () const
-{
-  if (_parent == 0)
-    return this;
-  else
-    return _parent->getRootNode();
-}
-
-SGPropertyNode *
-SGPropertyNode::getNode (const char * relative_path, bool create)
-{
-  if (_path_cache == 0)
-    _path_cache = new hash_table;
-
-  SGPropertyNode * result = _path_cache->get(relative_path);
-  if (result == 0) {
-    vector<PathComponent> components;
-    parse_path(relative_path, components);
-    result = find_node(this, components, 0, create);
-    if (result != 0)
-      _path_cache->put(relative_path, result);
-  }
-  
-  return result;
-}
-
-SGPropertyNode *
-SGPropertyNode::getNode (const char * relative_path, int index, bool create)
-{
-  vector<PathComponent> components;
-  parse_path(relative_path, components);
-  if (components.size() > 0)
-    components[components.size()-1].index = index;
-  return find_node(this, components, 0, create);
-}
-
-const SGPropertyNode *
-SGPropertyNode::getNode (const char * relative_path) const
-{
-  return ((SGPropertyNode *)this)->getNode(relative_path, false);
-}
-
-const SGPropertyNode *
-SGPropertyNode::getNode (const char * relative_path, int index) const
-{
-  return ((SGPropertyNode *)this)->getNode(relative_path, index, false);
-}
-
-\f
-////////////////////////////////////////////////////////////////////////
-// Convenience methods using relative paths.
-////////////////////////////////////////////////////////////////////////
-
-
-/**
- * Test whether another node has a value attached.
- */
-bool
-SGPropertyNode::hasValue (const char * relative_path) const
-{
-  const SGPropertyNode * node = getNode(relative_path);
-  return (node == 0 ? false : node->hasValue());
-}
-
-
-/**
- * Get the value type for another node.
- */
-SGPropertyNode::Type
-SGPropertyNode::getType (const char * relative_path) const
-{
-  const SGPropertyNode * node = getNode(relative_path);
-  return (node == 0 ? UNSPECIFIED : (Type)(node->getType()));
-}
-
-
-/**
- * Get a bool value for another node.
- */
-bool
-SGPropertyNode::getBoolValue (const char * relative_path,
-                             bool defaultValue) const
-{
-  const SGPropertyNode * node = getNode(relative_path);
-  return (node == 0 ? defaultValue : node->getBoolValue());
-}
-
-
-/**
- * Get an int value for another node.
- */
-int
-SGPropertyNode::getIntValue (const char * relative_path,
-                            int defaultValue) const
-{
-  const SGPropertyNode * node = getNode(relative_path);
-  return (node == 0 ? defaultValue : node->getIntValue());
-}
-
-
-/**
- * Get a long value for another node.
- */
-long
-SGPropertyNode::getLongValue (const char * relative_path,
-                             long defaultValue) const
-{
-  const SGPropertyNode * node = getNode(relative_path);
-  return (node == 0 ? defaultValue : node->getLongValue());
-}
-
-
-/**
- * Get a float value for another node.
- */
-float
-SGPropertyNode::getFloatValue (const char * relative_path,
-                              float defaultValue) const
-{
-  const SGPropertyNode * node = getNode(relative_path);
-  return (node == 0 ? defaultValue : node->getFloatValue());
-}
-
-
-/**
- * Get a double value for another node.
- */
-double
-SGPropertyNode::getDoubleValue (const char * relative_path,
-                               double defaultValue) const
-{
-  const SGPropertyNode * node = getNode(relative_path);
-  return (node == 0 ? defaultValue : node->getDoubleValue());
-}
-
-
-/**
- * Get a string value for another node.
- */
-const char *
-SGPropertyNode::getStringValue (const char * relative_path,
-                               const char * defaultValue) const
-{
-  const SGPropertyNode * node = getNode(relative_path);
-  return (node == 0 ? defaultValue : node->getStringValue());
-}
-
-
-/**
- * Set a bool value for another node.
- */
-bool
-SGPropertyNode::setBoolValue (const char * relative_path, bool value)
-{
-  return getNode(relative_path, true)->setBoolValue(value);
-}
-
-
-/**
- * Set an int value for another node.
- */
-bool
-SGPropertyNode::setIntValue (const char * relative_path, int value)
-{
-  return getNode(relative_path, true)->setIntValue(value);
-}
-
-
-/**
- * Set a long value for another node.
- */
-bool
-SGPropertyNode::setLongValue (const char * relative_path, long value)
-{
-  return getNode(relative_path, true)->setLongValue(value);
-}
-
-
-/**
- * Set a float value for another node.
- */
-bool
-SGPropertyNode::setFloatValue (const char * relative_path, float value)
-{
-  return getNode(relative_path, true)->setFloatValue(value);
-}
-
-
-/**
- * Set a double value for another node.
- */
-bool
-SGPropertyNode::setDoubleValue (const char * relative_path, double value)
-{
-  return getNode(relative_path, true)->setDoubleValue(value);
-}
-
-
-/**
- * Set a string value for another node.
- */
-bool
-SGPropertyNode::setStringValue (const char * relative_path, const char * value)
-{
-  return getNode(relative_path, true)->setStringValue(value);
-}
-
-
-/**
- * Set an unknown value for another node.
- */
-bool
-SGPropertyNode::setUnspecifiedValue (const char * relative_path,
-                                    const char * value)
-{
-  return getNode(relative_path, true)->setUnspecifiedValue(value);
-}
-
-
-/**
- * Test whether another node is tied.
- */
-bool
-SGPropertyNode::isTied (const char * relative_path) const
-{
-  const SGPropertyNode * node = getNode(relative_path);
-  return (node == 0 ? false : node->isTied());
-}
-
-
-/**
- * Tie a node reached by a relative path, creating it if necessary.
- */
-bool
-SGPropertyNode::tie (const char * relative_path,
-                    const SGRawValue<bool> &rawValue,
-                    bool useDefault)
-{
-  return getNode(relative_path, true)->tie(rawValue, useDefault);
-}
-
-
-/**
- * Tie a node reached by a relative path, creating it if necessary.
- */
-bool
-SGPropertyNode::tie (const char * relative_path,
-                    const SGRawValue<int> &rawValue,
-                    bool useDefault)
-{
-  return getNode(relative_path, true)->tie(rawValue, useDefault);
-}
-
-
-/**
- * Tie a node reached by a relative path, creating it if necessary.
- */
-bool
-SGPropertyNode::tie (const char * relative_path,
-                    const SGRawValue<long> &rawValue,
-                    bool useDefault)
-{
-  return getNode(relative_path, true)->tie(rawValue, useDefault);
-}
-
-
-/**
- * Tie a node reached by a relative path, creating it if necessary.
- */
-bool
-SGPropertyNode::tie (const char * relative_path,
-                    const SGRawValue<float> &rawValue,
-                    bool useDefault)
-{
-  return getNode(relative_path, true)->tie(rawValue, useDefault);
-}
-
-
-/**
- * Tie a node reached by a relative path, creating it if necessary.
- */
-bool
-SGPropertyNode::tie (const char * relative_path,
-                    const SGRawValue<double> &rawValue,
-                    bool useDefault)
-{
-  return getNode(relative_path, true)->tie(rawValue, useDefault);
-}
-
-
-/**
- * Tie a node reached by a relative path, creating it if necessary.
- */
-bool
-SGPropertyNode::tie (const char * relative_path,
-                    const SGRawValue<const char *> &rawValue,
-                    bool useDefault)
-{
-  return getNode(relative_path, true)->tie(rawValue, useDefault);
-}
-
-
-/**
- * Attempt to untie another node reached by a relative path.
- */
-bool
-SGPropertyNode::untie (const char * relative_path)
-{
-  SGPropertyNode * node = getNode(relative_path);
-  return (node == 0 ? false : node->untie());
-}
-
-void
-SGPropertyNode::addChangeListener (SGPropertyChangeListener * listener)
-{
-  if (_listeners == 0)
-    _listeners = new vector<SGPropertyChangeListener *>;
-  _listeners->push_back(listener);
-  listener->register_property(this);
-}
-
-void
-SGPropertyNode::removeChangeListener (SGPropertyChangeListener * listener)
-{
-  vector<SGPropertyChangeListener *>::iterator it =
-    find(_listeners->begin(), _listeners->end(), listener);
-  if (it != _listeners->end()) {
-    _listeners->erase(it);
-    listener->unregister_property(this);
-    if (_listeners->empty()) {
-      vector<SGPropertyChangeListener *> * tmp = _listeners;
-      _listeners = 0;
-      delete tmp;
-    }
-  }
-}
-
-void
-SGPropertyNode::fireValueChanged ()
-{
-  fireValueChanged(this);
-}
-
-void
-SGPropertyNode::fireChildAdded (SGPropertyNode * child)
-{
-  fireChildAdded(this, child);
-}
-
-void
-SGPropertyNode::fireChildRemoved (SGPropertyNode * child)
-{
-  fireChildRemoved(this, child);
-}
-
-void
-SGPropertyNode::fireValueChanged (SGPropertyNode * node)
-{
-  if (_listeners != 0) {
-    for (unsigned int i = 0; i < _listeners->size(); i++) {
-      (*_listeners)[i]->valueChanged(node);
-    }
-  }
-  if (_parent != 0)
-    _parent->fireValueChanged(node);
-}
-
-void
-SGPropertyNode::fireChildAdded (SGPropertyNode * parent,
-                               SGPropertyNode * child)
-{
-  if (_listeners != 0) {
-    for (unsigned int i = 0; i < _listeners->size(); i++) {
-      (*_listeners)[i]->childAdded(parent, child);
-    }
-  }
-  if (_parent != 0)
-    _parent->fireChildAdded(parent, child);
-}
-
-void
-SGPropertyNode::fireChildRemoved (SGPropertyNode * parent,
-                                 SGPropertyNode * child)
-{
-  if (_listeners != 0) {
-    for (unsigned int i = 0; i < _listeners->size(); i++) {
-      (*_listeners)[i]->childRemoved(parent, child);
-    }
-  }
-  if (_parent != 0)
-    _parent->fireChildRemoved(parent, child);
-}
-
-
-\f
-////////////////////////////////////////////////////////////////////////
-// Simplified hash table for caching paths.
-////////////////////////////////////////////////////////////////////////
-
-#define HASH_TABLE_SIZE 199
-
-SGPropertyNode::hash_table::entry::entry ()
-  : _key(0),
-    _value(0)
-{
-}
-
-SGPropertyNode::hash_table::entry::~entry ()
-{
-                               // Don't delete the value; we don't own
-                               // the pointer.
-  delete [] _key;
-}
-
-void
-SGPropertyNode::hash_table::entry::set_key (const char * key)
-{
-  _key = copy_string(key);
-}
-
-void
-SGPropertyNode::hash_table::entry::set_value (SGPropertyNode * value)
-{
-  _value = value;
-}
-
-SGPropertyNode::hash_table::bucket::bucket ()
-  : _length(0),
-    _entries(0)
-{
-}
-
-SGPropertyNode::hash_table::bucket::~bucket ()
-{
-  for (int i = 0; i < _length; i++)
-    delete _entries[i];
-}
-
-SGPropertyNode::hash_table::entry *
-SGPropertyNode::hash_table::bucket::get_entry (const char * key, bool create)
-{
-  int i;
-  for (i = 0; i < _length; i++) {
-    if (!strcmp(_entries[i]->get_key(), key))
-      return _entries[i];
-  }
-  if (create) {
-    entry ** new_entries = new entry*[_length+1];
-    for (i = 0; i < _length; i++) {
-      new_entries[i] = _entries[i];
-    }
-    delete [] _entries;
-    _entries = new_entries;
-    _entries[_length] = new entry;
-    _entries[_length]->set_key(key);
-    _length++;
-    return _entries[_length - 1];
-  } else {
-    return 0;
-  }
-}
-
-
-SGPropertyNode::hash_table::hash_table ()
-  : _data_length(0),
-    _data(0)
-{
-}
-
-SGPropertyNode::hash_table::~hash_table ()
-{
-  for (unsigned int i = 0; i < _data_length; i++)
-    delete _data[i];
-}
-
-SGPropertyNode *
-SGPropertyNode::hash_table::get (const char * key)
-{
-  if (_data_length == 0)
-    return 0;
-  unsigned int index = hashcode(key) % _data_length;
-  if (_data[index] == 0)
-    return 0;
-  entry * e = _data[index]->get_entry(key);
-  if (e == 0)
-    return 0;
-  else
-    return e->get_value();
-}
-
-void
-SGPropertyNode::hash_table::put (const char * key, SGPropertyNode * value)
-{
-  if (_data_length == 0) {
-    _data = new bucket*[HASH_TABLE_SIZE];
-    _data_length = HASH_TABLE_SIZE;
-    for (unsigned int i = 0; i < HASH_TABLE_SIZE; i++)
-      _data[i] = 0;
-  }
-  unsigned int index = hashcode(key) % _data_length;
-  if (_data[index] == 0) {
-    _data[index] = new bucket;
-  }
-  entry * e = _data[index]->get_entry(key, true);
-  e->set_value(value);
-}
-
-unsigned int
-SGPropertyNode::hash_table::hashcode (const char * key)
-{
-  unsigned int hash = 0;
-  while (*key != 0) {
-    hash = 31 * hash + *key;
-    key++;
-  }
-  return hash;
-}
-
-
-
-/**
- * Default constructor
- */
-SGPropertyNode_ptr::SGPropertyNode_ptr()
-{
-  _ptr = 0;
-}
-
-/**
- * Copy constructor
- */
-SGPropertyNode_ptr::SGPropertyNode_ptr( const SGPropertyNode_ptr &r )
-{
-  _ptr = r._ptr;
-  if (_ptr)
-     _ptr->incrementRef();
-}
-
-/**
- * Constructor from a pointer to a node
- */
-SGPropertyNode_ptr::SGPropertyNode_ptr( SGPropertyNode *p )
-{
-  _ptr = p;
-  if (_ptr)
-     _ptr->incrementRef();
-}
-
-/**
- * Destructor
- */
-SGPropertyNode_ptr::~SGPropertyNode_ptr()
-{
-  if (_ptr && _ptr->decrementRef() == 0)
-    delete _ptr;
-}
-
-/**
- * Assignement operator
- */
-SGPropertyNode_ptr &
-SGPropertyNode_ptr::operator=( const SGPropertyNode_ptr &r )
-{
-  if (_ptr && _ptr->decrementRef() == 0)
-    delete _ptr;
-  _ptr = r._ptr;
-  if (_ptr)
-     _ptr->incrementRef();
-
-  return *this;
-}
-
-/**
- * Pointer access operator
- */
-SGPropertyNode *
-SGPropertyNode_ptr::operator->()
-{
-  return _ptr;
-}
-
-/**
- * Pointer access operator (const)
- */
-const SGPropertyNode *
-SGPropertyNode_ptr::operator->() const
-{
-  return _ptr;
-}
-
-/**
- * Conversion to SGPropertyNode * operator
- */
-SGPropertyNode_ptr::operator SGPropertyNode *()
-{
-  return _ptr;
-}
-
-/**
- * Conversion to const SGPropertyNode * operator
- */
-SGPropertyNode_ptr::operator const SGPropertyNode *() const
-{
-  return _ptr;
-}
-
-/**
- * Validity test
- */
-bool 
-SGPropertyNode_ptr::valid() const
-{
-  return _ptr != 0;
-}
-
-
-\f
-////////////////////////////////////////////////////////////////////////
-// Implementation of SGPropertyChangeListener.
-////////////////////////////////////////////////////////////////////////
-
-SGPropertyChangeListener::~SGPropertyChangeListener ()
-{
-                               // This will come back and remove
-                               // the current item each time.  Is
-                               // that OK?
-  vector<SGPropertyNode *>::iterator it;
-  for (it = _properties.begin(); it != _properties.end(); it++)
-    (*it)->removeChangeListener(this);
-}
-
-void
-SGPropertyChangeListener::valueChanged (SGPropertyNode * node)
-{
-  // NO-OP
-}
-
-void
-SGPropertyChangeListener::childAdded (SGPropertyNode * node,
-                                     SGPropertyNode * child)
-{
-  // NO-OP
-}
-
-void
-SGPropertyChangeListener::childRemoved (SGPropertyNode * parent,
-                                       SGPropertyNode * child)
-{
-  // NO-OP
-}
-
-void
-SGPropertyChangeListener::register_property (SGPropertyNode * node)
-{
-  _properties.push_back(node);
-}
-
-void
-SGPropertyChangeListener::unregister_property (SGPropertyNode * node)
-{
-  vector<SGPropertyNode *>::iterator it =
-    find(_properties.begin(), _properties.end(), node);
-  if (it != _properties.end())
-    _properties.erase(it);
-}
-
-
-// end of props.cxx
diff --git a/simgear/misc/props.hxx b/simgear/misc/props.hxx
deleted file mode 100644 (file)
index a042c61..0000000
+++ /dev/null
@@ -1,1337 +0,0 @@
-/**
- * \file props.hxx
- * Interface definition for a property list.
- * Started Fall 2000 by David Megginson, david@megginson.com
- * This code is released into the Public Domain.
- *
- * See props.html for documentation [replace with URL when available].
- *
- * $Id$
- */
-
-#ifndef __PROPS_HXX
-#define __PROPS_HXX
-
-#ifndef PROPS_STANDALONE
-#define PROPS_STANDALONE 0
-#endif
-
-#include <vector>
-
-#if PROPS_STANDALONE
-
-#include <string>
-#include <iostream>
-
-using std::string;
-using std::vector;
-using std::istream;
-using std::ostream;
-
-#else
-
-#include <simgear/compiler.h>
-#include <simgear/debug/logstream.hxx>
-#include STL_STRING
-#include STL_IOSTREAM
-SG_USING_STD(string);
-SG_USING_STD(vector);
-SG_USING_STD(istream);
-SG_USING_STD(ostream);
-
-#endif
-
-
-#ifdef NONE
-#pragma warn A sloppy coder has defined NONE as a macro!
-#undef NONE
-#endif
-
-#ifdef ALIAS
-#pragma warn A sloppy coder has defined ALIAS as a macro!
-#undef ALIAS
-#endif
-
-#ifdef UNSPECIFIED
-#pragma warn A sloppy coder has defined UNSPECIFIED as a macro!
-#undef UNSPECIFIED
-#endif
-
-#ifdef BOOL
-#pragma warn A sloppy coder has defined BOOL as a macro!
-#undef BOOL
-#endif
-
-#ifdef INT
-#pragma warn A sloppy coder has defined INT as a macro!
-#undef INT
-#endif
-
-#ifdef LONG
-#pragma warn A sloppy coder has defined LONG as a macro!
-#undef LONG
-#endif
-
-#ifdef FLOAT
-#pragma warn A sloppy coder has defined FLOAT as a macro!
-#undef FLOAT
-#endif
-
-#ifdef DOUBLE
-#pragma warn A sloppy coder has defined DOUBLE as a macro!
-#undef DOUBLE
-#endif
-
-#ifdef STRING
-#pragma warn A sloppy coder has defined STRING as a macro!
-#undef STRING
-#endif
-
-
-\f
-////////////////////////////////////////////////////////////////////////
-// A raw value.
-//
-// This is the mechanism that information-providing routines can
-// use to link their own values to the property manager.  Any
-// SGValue can be tied to a raw value and then untied again.
-//
-// Note: we are forced to use inlined methods here to ensure
-// that the templates will be instantiated.  We're probably taking
-// a small performance hit for that.
-////////////////////////////////////////////////////////////////////////
-
-
-/**
- * Abstract base class for a raw value.
- *
- * <p>The property manager is implemented in two layers.  The {@link
- * SGPropertyNode} is the highest and most abstract layer,
- * representing an LValue/RValue pair: it records the position of the
- * property in the property tree and contains facilities for
- * navigation to other nodes.  It is guaranteed to be persistent: the
- * {@link SGPropertyNode} will not change during a session, even if
- * the property is bound and unbound multiple times.</p>
- *
- * <p>When the property value is not managed internally in the
- * SGPropertyNode, the SGPropertyNode will contain a reference to an
- * SGRawValue (this class), which provides an abstract way to get,
- * set, and clone the underlying value.  The SGRawValue may change
- * frequently during a session as a value is retyped or bound and
- * unbound to various data source, but the abstract SGPropertyNode
- * layer insulates the application from those changes.  The raw value
- * contains no facilities for data binding or for type conversion: it
- * is simply the abstraction of a primitive data type (or a compound
- * data type, in the case of a string).</p>
- *
- * <p>The SGPropertyNode class always keeps a *copy* of a raw value,
- * not the original one passed to it; if you override a derived class
- * but do not replace the {@link #clone} method, strange things will
- * happen.</p>
- *
- * <p>All derived SGRawValue classes must implement {@link #getValue},
- * {@link #setValue}, and {@link #clone} for the appropriate type.</p>
- *
- * @see SGPropertyNode
- * @see SGRawValuePointer
- * @see SGRawValueFunctions
- * @see SGRawValueFunctionsIndexed
- * @see SGRawValueMethods
- * @see SGRawValueMethodsIndexed
- */
-template <class T>
-class SGRawValue
-{
-public:
-
-  /**
-   * The default underlying value for this type.
-   *
-   * Every raw value has a default; the default is false for a
-   * boolean, 0 for the various numeric values, and "" for a string.
-   * If additional types of raw values are added in the future, they
-   * may need different kinds of default values (such as epoch for a
-   * date type).  The default value is used when creating new values.
-   */
-  static const T DefaultValue; // Default for this kind of raw value.
-
-
-  /**
-   * Constructor.
-   *
-   * Use the default value for this type.
-   */
-  SGRawValue () {}
-
-
-  /**
-   * Destructor.
-   */
-  virtual ~SGRawValue () {}
-
-
-  /**
-   * Return the underlying value.
-   *
-   * @return The actual value for the property.
-   * @see #setValue
-   */
-  virtual T getValue () const = 0;
-
-
-  /**
-   * Assign a new underlying value.
-   *
-   * If the new value cannot be set (because this is a read-only
-   * raw value, or because the new value is not acceptable for
-   * some reason) this method returns false and leaves the original
-   * value unchanged.
-   *
-   * @param value The actual value for the property.
-   * @return true if the value was set successfully, false otherwise.
-   * @see #getValue
-   */
-  virtual bool setValue (T value) = 0;
-
-
-  /**
-   * Create a new deep copy of this raw value.
-   *
-   * The copy will contain its own version of the underlying value
-   * as well.
-   *
-   * @return A deep copy of the current object.
-   */
-  virtual SGRawValue * clone () const = 0;
-};
-
-
-/**
- * A raw value bound to a pointer.
- *
- * This is the most efficient way to tie an external value, but also
- * the most dangerous, because there is no way for the supplier to
- * perform bounds checking and derived calculations except by polling
- * the variable to see if it has changed.  There is no default
- * constructor, because this class would be meaningless without a
- * pointer.
- */
-template <class T>
-class SGRawValuePointer : public SGRawValue<T>
-{
-public:
-
-  /**
-   * Explicit pointer constructor.
-   *
-   * Create a new raw value bound to the value of the variable
-   * referenced by the pointer.
-   *
-   * @param ptr The pointer to the variable to which this raw value
-   * will be bound.
-   */
-  SGRawValuePointer (T * ptr) : _ptr(ptr) {}
-
-  /**
-   * Destructor.
-   */
-  virtual ~SGRawValuePointer () {}
-
-  /**
-   * Get the underlying value.
-   *
-   * This method will dereference the pointer and return the
-   * variable's value.
-   */
-  virtual T getValue () const { return *_ptr; }
-
-  /**
-   * Set the underlying value.
-   *
-   * This method will dereference the pointer and change the
-   * variable's value.
-   */
-  virtual bool setValue (T value) { *_ptr = value; return true; }
-
-  /**
-   * Create a copy of this raw value.
-   *
-   * The copy will use the same external pointer as the original.
-   */
-  virtual SGRawValue<T> * clone () const {
-    return new SGRawValuePointer<T>(_ptr);
-  }
-
-private:
-  T * _ptr;
-};
-
-
-/**
- * A value managed through static functions.
- *
- * A read-only value will not have a setter; a write-only value will
- * not have a getter.
- */
-template <class T>
-class SGRawValueFunctions : public SGRawValue<T>
-{
-public:
-
-  /**
-   * The template type of a static getter function.
-   */
-  typedef T (*getter_t)();
-
-  /**
-   * The template type of a static setter function.
-   */
-  typedef void (*setter_t)(T);
-
-  /**
-   * Explicit constructor.
-   *
-   * Create a new raw value bound to the getter and setter supplied.
-   *
-   * @param getter A static function for getting a value, or 0
-   * to read-disable the value.
-   * @param setter A static function for setting a value, or 0
-   * to write-disable the value.
-   */
-  SGRawValueFunctions (getter_t getter = 0, setter_t setter = 0)
-    : _getter(getter), _setter(setter) {}
-
-  /**
-   * Destructor.
-   */
-  virtual ~SGRawValueFunctions () {}
-
-  /**
-   * Get the underlying value.
-   *
-   * This method will invoke the getter function to get a value.
-   * If no getter function was supplied, this method will always
-   * return the default value for the type.
-   */
-  virtual T getValue () const {
-    if (_getter) return (*_getter)();
-    else return SGRawValue<T>::DefaultValue;
-  }
-
-  /**
-   * Set the underlying value.
-   *
-   * This method will invoke the setter function to change the
-   * underlying value.  If no setter function was supplied, this
-   * method will return false.
-   */
-  virtual bool setValue (T value) {
-    if (_setter) { (*_setter)(value); return true; }
-    else return false;
-  }
-
-  /**
-   * Create a copy of this raw value, bound to the same functions.
-   */
-  virtual SGRawValue<T> * clone () const {
-    return new SGRawValueFunctions<T>(_getter,_setter);
-  }
-
-private:
-  getter_t _getter;
-  setter_t _setter;
-};
-
-
-/**
- * An indexed value bound to static functions.
- *
- * A read-only value will not have a setter; a write-only value will
- * not have a getter.  An indexed value is useful for binding one
- * of a list of possible values (such as multiple engines for a
- * plane).  The index is hard-coded at creation time.
- *
- * @see SGRawValue
- */
-template <class T>
-class SGRawValueFunctionsIndexed : public SGRawValue<T>
-{
-public:
-  typedef T (*getter_t)(int);
-  typedef void (*setter_t)(int,T);
-  SGRawValueFunctionsIndexed (int index, getter_t getter = 0, setter_t setter = 0)
-    : _index(index), _getter(getter), _setter(setter) {}
-  virtual ~SGRawValueFunctionsIndexed () {}
-  virtual T getValue () const {
-    if (_getter) return (*_getter)(_index);
-    else return SGRawValue<T>::DefaultValue;
-  }
-  virtual bool setValue (T value) {
-    if (_setter) { (*_setter)(_index, value); return true; }
-    else return false;
-  }
-  virtual SGRawValue<T> * clone () const {
-    return new SGRawValueFunctionsIndexed<T>(_index, _getter, _setter);
-  }
-private:
-  int _index;
-  getter_t _getter;
-  setter_t _setter;
-};
-
-
-/**
- * A value managed through an object and access methods.
- *
- * A read-only value will not have a setter; a write-only value will
- * not have a getter.
- */
-template <class C, class T>
-class SGRawValueMethods : public SGRawValue<T>
-{
-public:
-  typedef T (C::*getter_t)() const;
-  typedef void (C::*setter_t)(T);
-  SGRawValueMethods (C &obj, getter_t getter = 0, setter_t setter = 0)
-    : _obj(obj), _getter(getter), _setter(setter) {}
-  virtual ~SGRawValueMethods () {}
-  virtual T getValue () const {
-    if (_getter) { return (_obj.*_getter)(); }
-    else { return SGRawValue<T>::DefaultValue; }
-  }
-  virtual bool setValue (T value) {
-    if (_setter) { (_obj.*_setter)(value); return true; }
-    else return false;
-  }
-  virtual SGRawValue<T> * clone () const {
-    return new SGRawValueMethods<C,T>(_obj, _getter, _setter);
-  }
-private:
-  C &_obj;
-  getter_t _getter;
-  setter_t _setter;
-};
-
-
-/**
- * An indexed value managed through an object and access methods.
- *
- * A read-only value will not have a setter; a write-only value will
- * not have a getter.
- */
-template <class C, class T>
-class SGRawValueMethodsIndexed : public SGRawValue<T>
-{
-public:
-  typedef T (C::*getter_t)(int) const;
-  typedef void (C::*setter_t)(int, T);
-  SGRawValueMethodsIndexed (C &obj, int index,
-                    getter_t getter = 0, setter_t setter = 0)
-    : _obj(obj), _index(index), _getter(getter), _setter(setter) {}
-  virtual ~SGRawValueMethodsIndexed () {}
-  virtual T getValue () const {
-    if (_getter) { return (_obj.*_getter)(_index); }
-    else { return SGRawValue<T>::DefaultValue; }
-  }
-  virtual bool setValue (T value) {
-    if (_setter) { (_obj.*_setter)(_index, value); return true; }
-    else return false;
-  }
-  virtual SGRawValue<T> * clone () const {
-    return new SGRawValueMethodsIndexed<C,T>(_obj, _index, _getter, _setter);
-  }
-private:
-  C &_obj;
-  int _index;
-  getter_t _getter;
-  setter_t _setter;
-};
-
-\f
-/**
- * The smart pointer that manage reference counting
- */
-class SGPropertyNode;
-class SGPropertyNode_ptr
-{
-public:
-
-  /**
-   * Default constructor
-   */
-  SGPropertyNode_ptr();
-
-  /**
-   * Copy constructor
-   */
-  SGPropertyNode_ptr( const SGPropertyNode_ptr &r );
-
-  /**
-   * Constructor from a pointer to a node
-   */
-  SGPropertyNode_ptr( SGPropertyNode *p );
-
-  /**
-   * Destructor
-   */
-  ~SGPropertyNode_ptr();
-
-  /**
-   * Assignement operator
-   */
-  SGPropertyNode_ptr &operator=( const SGPropertyNode_ptr &r );
-
-  /**
-   * Pointer access operator
-   */
-  SGPropertyNode *operator->();
-
-  /**
-   * Pointer access operator (const)
-   */
-  const SGPropertyNode *operator->() const;
-
-  /**
-   * Conversion to SGPropertyNode * operator
-   */
-  operator SGPropertyNode *();
-
-  /**
-   * Conversion to const SGPropertyNode * operator
-   */
-  operator const SGPropertyNode *() const;
-
-  /**
-   * Return the pointer.
-   */
-  SGPropertyNode * ptr () { return _ptr; }
-
-  /**
-   * Validity test
-   */
-  bool valid() const;
-
-private:
-
-  SGPropertyNode *_ptr;
-};
-
-
-\f
-/**
- * The property change listener interface.
- *
- * <p>Any class that needs to listen for property changes must implement
- * this interface.</p>
- */
-class SGPropertyChangeListener
-{
-public:
-  virtual ~SGPropertyChangeListener ();
-  virtual void valueChanged (SGPropertyNode * node);
-  virtual void childAdded (SGPropertyNode * parent, SGPropertyNode * child);
-  virtual void childRemoved (SGPropertyNode * parent, SGPropertyNode * child);
-
-protected:
-  friend class SGPropertyNode;
-  virtual void register_property (SGPropertyNode * node);
-  virtual void unregister_property (SGPropertyNode * node);
-
-private:
-  vector<SGPropertyNode *> _properties;
-};
-
-
-\f
-/**
- * A node in a property tree.
- */
-class SGPropertyNode
-{
-public:
-
-  /**
-   * Public constants.
-   */
-  enum {
-    MAX_STRING_LEN = 1024
-  };
-
-  /**
-   * Property value types.
-   */
-  enum Type {
-    NONE,
-    ALIAS,
-    BOOL,
-    INT,
-    LONG,
-    FLOAT,
-    DOUBLE,
-    STRING,
-    UNSPECIFIED
-  };
-
-
-  /**
-   * Access mode attributes.
-   *
-   * <p>The ARCHIVE attribute is strictly advisory, and controls
-   * whether the property should normally be saved and restored.</p>
-   */
-  enum Attribute {
-    READ = 1,
-    WRITE = 2,
-    ARCHIVE = 4,
-    REMOVED = 8,
-    TRACE_READ = 16,
-    TRACE_WRITE = 32
-  };
-
-
-  /**
-   * Last used attribute
-   * Update as needed when enum Attribute is changed
-   */
-  static const int LAST_USED_ATTRIBUTE;
-
-
-  /**
-   * Default constructor.
-   */
-  SGPropertyNode ();
-
-
-  /**
-   * Copy constructor.
-   */
-  SGPropertyNode (const SGPropertyNode &node);
-
-
-  /**
-   * Destructor.
-   */
-  virtual ~SGPropertyNode ();
-
-
-
-  //
-  // Basic properties.
-  //
-
-  /**
-   * Test whether this node contains a primitive leaf value.
-   */
-  bool hasValue () const { return (_type != NONE); }
-
-
-  /**
-   * Get the node's simple (XML) name.
-   */
-  const char * getName () const { return _name; }
-
-
-  /**
-   * Get the node's pretty display name, with subscript when needed.
-   */
-  const char * getDisplayName (bool simplify = false) const;
-
-
-  /**
-   * Get the node's integer index.
-   */
-  int getIndex () const { return _index; }
-
-
-  /**
-   * Get a non-const pointer to the node's parent.
-   */
-  SGPropertyNode * getParent () { return _parent; }
-
-
-  /**
-   * Get a const pointer to the node's parent.
-   */
-  const SGPropertyNode * getParent () const { return _parent; }
-
-
-  //
-  // Children.
-  //
-
-
-  /**
-   * Get the number of child nodes.
-   */
-  int nChildren () const { return _children.size(); }
-
-
-  /**
-   * Get a child node by position (*NOT* index).
-   */
-  SGPropertyNode * getChild (int position);
-
-
-  /**
-   * Get a const child node by position (*NOT* index).
-   */
-  const SGPropertyNode * getChild (int position) const;
-
-
-  /**
-   * Test whether a named child exists.
-   */
-  bool hasChild (const char * name, int index = 0) const
-  {
-    return (getChild(name, index) != 0);
-  }
-
-
-  /**
-   * Get a child node by name and index.
-   */
-  SGPropertyNode * getChild (const char * name, int index = 0,
-                            bool create = false);
-
-
-  /**
-   * Get a const child node by name and index.
-   */
-  const SGPropertyNode * getChild (const char * name, int index = 0) const;
-
-
-  /**
-   * Get a vector of all children with the specified name.
-   */
-  vector<SGPropertyNode_ptr> getChildren (const char * name) const;
-
-
-  /**
-   * Remove a child node
-   */
-  SGPropertyNode_ptr removeChild (const char * name, int index = 0,
-                                  bool keep = true);
-
-
-  //
-  // Alias support.
-  //
-
-
-  /**
-   * Alias this node's leaf value to another's.
-   */
-  bool alias (SGPropertyNode * target);
-
-
-  /**
-   * Alias this node's leaf value to another's by relative path.
-   */
-  bool alias (const char * path);
-
-
-  /**
-   * Remove any alias for this node.
-   */
-  bool unalias ();
-
-
-  /**
-   * Test whether the node's leaf value is aliased to another's.
-   */
-  bool isAlias () const { return (_type == ALIAS); }
-
-
-  /**
-   * Get a non-const pointer to the current alias target, if any.
-   */
-  SGPropertyNode * getAliasTarget ();
-
-
-  /**
-   * Get a const pointer to the current alias target, if any.
-   */
-  const SGPropertyNode * getAliasTarget () const;
-
-
-  //
-  // Path information.
-  //
-
-
-  /**
-   * Get the path to this node from the root.
-   */
-  const char * getPath (bool simplify = false) const;
-
-
-  /**
-   * Get a pointer to the root node.
-   */
-  SGPropertyNode * getRootNode ();
-
-
-  /**
-   * Get a const pointer to the root node.
-   */
-  const SGPropertyNode * getRootNode () const;
-
-
-  /**
-   * Get a pointer to another node by relative path.
-   */
-  SGPropertyNode * getNode (const char * relative_path, bool create = false);
-
-
-  /**
-   * Get a pointer to another node by relative path.
-   *
-   * This method leaves the index off the last member of the path,
-   * so that the user can specify it separately (and save some
-   * string building).  For example, getNode("/bar[1]/foo", 3) is
-   * exactly equivalent to getNode("bar[1]/foo[3]").  The index
-   * provided overrides any given in the path itself for the last
-   * component.
-   */
-  SGPropertyNode * getNode (const char * relative_path, int index,
-                           bool create = false);
-
-
-  /**
-   * Get a const pointer to another node by relative path.
-   */
-  const SGPropertyNode * getNode (const char * relative_path) const;
-
-
-  /**
-   * Get a const pointer to another node by relative path.
-   *
-   * This method leaves the index off the last member of the path,
-   * so that the user can specify it separate.
-   */
-  const SGPropertyNode * getNode (const char * relative_path,
-                                 int index) const;
-
-
-  //
-  // Access Mode.
-  //
-
-  /**
-   * Check a single mode attribute for the property node.
-   */
-  bool getAttribute (Attribute attr) const { return ((_attr & attr) != 0); }
-
-
-  /**
-   * Set a single mode attribute for the property node.
-   */
-  void setAttribute (Attribute attr, bool state) {
-    (state ? _attr |= attr : _attr &= ~attr);
-  }
-
-
-  /**
-   * Get all of the mode attributes for the property node.
-   */
-  int getAttributes () const { return _attr; }
-
-
-  /**
-   * Set all of the mode attributes for the property node.
-   */
-  void setAttributes (int attr) { _attr = attr; }
-  
-
-  //
-  // Leaf Value (primitive).
-  //
-
-
-  /**
-   * Get the type of leaf value, if any, for this node.
-   */
-  Type getType () const;
-
-
-  /**
-   * Get a bool value for this node.
-   */
-  bool getBoolValue () const;
-
-
-  /**
-   * Get an int value for this node.
-   */
-  int getIntValue () const;
-
-
-  /**
-   * Get a long int value for this node.
-   */
-  long getLongValue () const;
-
-
-  /**
-   * Get a float value for this node.
-   */
-  float getFloatValue () const;
-
-
-  /**
-   * Get a double value for this node.
-   */
-  double getDoubleValue () const;
-
-
-  /**
-   * Get a string value for this node.
-   */
-  const char * getStringValue () const;
-
-
-
-  /**
-   * Set a bool value for this node.
-   */
-  bool setBoolValue (bool value);
-
-
-  /**
-   * Set an int value for this node.
-   */
-  bool setIntValue (int value);
-
-
-  /**
-   * Set a long int value for this node.
-   */
-  bool setLongValue (long value);
-
-
-  /**
-   * Set a float value for this node.
-   */
-  bool setFloatValue (float value);
-
-
-  /**
-   * Set a double value for this node.
-   */
-  bool setDoubleValue (double value);
-
-
-  /**
-   * Set a string value for this node.
-   */
-  bool setStringValue (const char * value);
-
-
-  /**
-   * Set a value of unspecified type for this node.
-   */
-  bool setUnspecifiedValue (const char * value);
-
-
-  //
-  // Data binding.
-  //
-
-
-  /**
-   * Test whether this node is bound to an external data source.
-   */
-  bool isTied () const { return _tied; }
-
-
-  /**
-   * Bind this node to an external bool source.
-   */
-  bool tie (const SGRawValue<bool> &rawValue, bool useDefault = true);
-
-
-  /**
-   * Bind this node to an external int source.
-   */
-  bool tie (const SGRawValue<int> &rawValue, bool useDefault = true);
-
-
-  /**
-   * Bind this node to an external long int source.
-   */
-  bool tie (const SGRawValue<long> &rawValue, bool useDefault = true);
-
-
-  /**
-   * Bind this node to an external float source.
-   */
-  bool tie (const SGRawValue<float> &rawValue, bool useDefault = true);
-
-
-  /**
-   * Bind this node to an external double source.
-   */
-  bool tie (const SGRawValue<double> &rawValue, bool useDefault = true);
-
-
-  /**
-   * Bind this node to an external string source.
-   */
-  bool tie (const SGRawValue<const char *> &rawValue, bool useDefault = true);
-
-
-  /**
-   * Unbind this node from any external data source.
-   */
-  bool untie ();
-
-
-  //
-  // Convenience methods using paths.
-  // TODO: add attribute methods
-  //
-
-
-  /**
-   * Get another node's type.
-   */
-  Type getType (const char * relative_path) const;
-
-
-  /**
-   * Test whether another node has a leaf value.
-   */
-  bool hasValue (const char * relative_path) const;
-
-
-  /**
-   * Get another node's value as a bool.
-   */
-  bool getBoolValue (const char * relative_path,
-                    bool defaultValue = false) const;
-
-
-  /**
-   * Get another node's value as an int.
-   */
-  int getIntValue (const char * relative_path,
-                  int defaultValue = 0) const;
-
-
-  /**
-   * Get another node's value as a long int.
-   */
-  long getLongValue (const char * relative_path,
-                    long defaultValue = 0L) const;
-
-
-  /**
-   * Get another node's value as a float.
-   */
-  float getFloatValue (const char * relative_path,
-                      float defaultValue = 0.0) const;
-
-
-  /**
-   * Get another node's value as a double.
-   */
-  double getDoubleValue (const char * relative_path,
-                        double defaultValue = 0.0L) const;
-
-
-  /**
-   * Get another node's value as a string.
-   */
-  const char * getStringValue (const char * relative_path,
-                              const char * defaultValue = "") const;
-
-
-  /**
-   * Set another node's value as a bool.
-   */
-  bool setBoolValue (const char * relative_path, bool value);
-
-
-  /**
-   * Set another node's value as an int.
-   */
-  bool setIntValue (const char * relative_path, int value);
-
-
-  /**
-   * Set another node's value as a long int.
-   */
-  bool setLongValue (const char * relative_path, long value);
-
-
-  /**
-   * Set another node's value as a float.
-   */
-  bool setFloatValue (const char * relative_path, float value);
-
-
-  /**
-   * Set another node's value as a double.
-   */
-  bool setDoubleValue (const char * relative_path, double value);
-
-
-  /**
-   * Set another node's value as a string.
-   */
-  bool setStringValue (const char * relative_path, const char * value);
-
-
-  /**
-   * Set another node's value with no specified type.
-   */
-  bool setUnspecifiedValue (const char * relative_path, const char * value);
-
-
-  /**
-   * Test whether another node is bound to an external data source.
-   */
-  bool isTied (const char * relative_path) const;
-
-
-  /**
-   * Bind another node to an external bool source.
-   */
-  bool tie (const char * relative_path, const SGRawValue<bool> &rawValue,
-           bool useDefault = true);
-
-
-  /**
-   * Bind another node to an external int source.
-   */
-  bool tie (const char * relative_path, const SGRawValue<int> &rawValue,
-           bool useDefault = true);
-
-
-  /**
-   * Bind another node to an external long int source.
-   */
-  bool tie (const char * relative_path, const SGRawValue<long> &rawValue,
-           bool useDefault = true);
-
-
-  /**
-   * Bind another node to an external float source.
-   */
-  bool tie (const char * relative_path, const SGRawValue<float> &rawValue,
-           bool useDefault = true);
-
-
-  /**
-   * Bind another node to an external double source.
-   */
-  bool tie (const char * relative_path, const SGRawValue<double> &rawValue,
-           bool useDefault = true);
-
-
-  /**
-   * Bind another node to an external string source.
-   */
-  bool tie (const char * relative_path, const SGRawValue<const char *> &rawValue,
-           bool useDefault = true);
-
-
-  /**
-   * Unbind another node from any external data source.
-   */
-  bool untie (const char * relative_path);
-
-
-  /**
-   * Add a change listener to the property.
-   */
-  void addChangeListener (SGPropertyChangeListener * listener);
-
-
-  /**
-   * Remove a change listener from the property.
-   */
-  void removeChangeListener (SGPropertyChangeListener * listener);
-
-
-  /**
-   * Fire a value change event to all listeners.
-   */
-  void fireValueChanged ();
-
-
-  /**
-   * Fire a child-added event to all listeners.
-   */
-  void fireChildAdded (SGPropertyNode * child);
-
-
-  /**
-   * Fire a child-removed event to all listeners.
-   */
-  void fireChildRemoved (SGPropertyNode * child);
-
-
-protected:
-
-  void fireValueChanged (SGPropertyNode * node);
-  void fireChildAdded (SGPropertyNode * parent, SGPropertyNode * child);
-  void fireChildRemoved (SGPropertyNode * parent, SGPropertyNode * child);
-
-  /**
-   * Protected constructor for making new nodes on demand.
-   */
-  SGPropertyNode (const char * name, int index, SGPropertyNode * parent);
-
-
-private:
-
-                               // Get the raw value
-  bool get_bool () const;
-  int get_int () const;
-  long get_long () const;
-  float get_float () const;
-  double get_double () const;
-  const char * get_string () const;
-
-                               // Set the raw value
-  bool set_bool (bool value);
-  bool set_int (int value);
-  bool set_long (long value);
-  bool set_float (float value);
-  bool set_double (double value);
-  bool set_string (const char * value);
-
-
-  /**
-   * Clear any existing value and set the type to NONE.
-   */
-  void clear_value ();
-
-
-  /**
-   * Get the value as a string.
-   */
-  const char * make_string () const;
-
-
-  /**
-   * Trace a read access.
-   */
-  void trace_read () const;
-
-
-  /**
-   * Trace a write access.
-   */
-  void trace_write () const;
-
-
-  /**
-   * Increment reference counter
-   */
-  void incrementRef();
-
-  /**
-   * Decrement reference counter
-   */
-  int decrementRef();
-
-  friend class SGPropertyNode_ptr;
-
-
-  mutable char _buffer[MAX_STRING_LEN+1];
-
-  class hash_table;
-
-  char * _name;
-  mutable char * _display_name;
-  int _index;
-  SGPropertyNode * _parent;
-  vector<SGPropertyNode_ptr> _children;
-  vector<SGPropertyNode_ptr> _removedChildren;
-  mutable char * _path;
-  hash_table * _path_cache;
-  Type _type;
-  bool _tied;
-  int _attr;
-  int _count;
-
-                               // The right kind of pointer...
-  union {
-    SGPropertyNode * alias;
-    SGRawValue<bool> * bool_val;
-    SGRawValue<int> * int_val;
-    SGRawValue<long> * long_val;
-    SGRawValue<float> * float_val;
-    SGRawValue<double> * double_val;
-    SGRawValue<const char *> * string_val;
-  } _value;
-
-  union {
-    bool bool_val;
-    int int_val;
-    long long_val;
-    float float_val;
-    double double_val;
-    char * string_val;
-  } _local_val;
-
-  vector <SGPropertyChangeListener *> * _listeners;
-
-
-\f
-  /**
-   * A very simple hash table with no remove functionality.
-   */
-  class hash_table {
-  public:
-
-    /**
-     * An entry in a bucket in a hash table.
-     */
-    class entry {
-    public:
-      entry ();
-      virtual ~entry ();
-      virtual const char * get_key () { return _key; }
-      virtual void set_key (const char * key);
-      virtual SGPropertyNode * get_value () { return _value; }
-      virtual void set_value (SGPropertyNode * value);
-    private:
-      char * _key;
-      SGPropertyNode * _value;
-    };
-
-
-    /**
-     * A bucket in a hash table.
-     */
-    class bucket {
-    public:
-      bucket ();
-      virtual ~bucket ();
-      virtual entry * get_entry (const char * key, bool create = false);
-    private:
-      int _length;
-      entry ** _entries;
-    };
-
-    friend class bucket;
-
-    hash_table ();
-    virtual ~hash_table ();
-    virtual SGPropertyNode * get (const char * key);
-    virtual void put (const char * key, SGPropertyNode * value);
-
-  private:
-    unsigned int hashcode (const char * key);
-    unsigned int _data_length;
-    bucket ** _data;
-  };
-
-};
-
-#endif // __PROPS_HXX
-
-// end of props.hxx
diff --git a/simgear/misc/props_io.cxx b/simgear/misc/props_io.cxx
deleted file mode 100644 (file)
index 36ea2b7..0000000
+++ /dev/null
@@ -1,586 +0,0 @@
-
-#include <simgear/compiler.h>
-
-#include <stdlib.h>            // atof() atoi()
-
-#include <simgear/sg_inlines.h>
-#include <simgear/debug/logstream.hxx>
-#include <simgear/xml/easyxml.hxx>
-
-#include "sg_path.hxx"
-#include "props.hxx"
-#include "props_io.hxx"
-
-#include STL_IOSTREAM
-#include STL_FSTREAM
-#include STL_STRING
-#include <vector>
-#include <map>
-
-SG_USING_STD(istream);
-SG_USING_STD(ifstream);
-SG_USING_STD(ostream);
-SG_USING_STD(ofstream);
-SG_USING_STD(string);
-SG_USING_STD(vector);
-SG_USING_STD(map);
-
-#define DEFAULT_MODE (SGPropertyNode::READ|SGPropertyNode::WRITE)
-
-
-\f
-////////////////////////////////////////////////////////////////////////
-// Property list visitor, for XML parsing.
-////////////////////////////////////////////////////////////////////////
-
-class PropsVisitor : public XMLVisitor
-{
-public:
-
-  PropsVisitor (SGPropertyNode * root, const string &base)
-    : _root(root), _level(0), _base(base), _hasException(false) {}
-
-  virtual ~PropsVisitor () {}
-
-  void startXML ();
-  void endXML ();
-  void startElement (const char * name, const XMLAttributes &atts);
-  void endElement (const char * name);
-  void data (const char * s, int length);
-  void warning (const char * message, int line, int column);
-
-  bool hasException () const { return _hasException; }
-  sg_io_exception &getException () { return _exception; }
-  void setException (const sg_io_exception &exception) {
-    _exception = exception;
-    _hasException = true;
-  }
-
-private:
-
-  struct State
-  {
-    State () : node(0), type(""), mode(DEFAULT_MODE) {}
-    State (SGPropertyNode * _node, const char * _type, int _mode)
-      : node(_node), type(_type), mode(_mode) {}
-    SGPropertyNode * node;
-    string type;
-    int mode;
-    map<string,int> counters;
-  };
-
-  State &state () { return _state_stack[_state_stack.size() - 1]; }
-
-  void push_state (SGPropertyNode * node, const char * type, int mode) {
-    if (type == 0)
-      _state_stack.push_back(State(node, "unspecified", mode));
-    else
-      _state_stack.push_back(State(node, type, mode));
-    _level++;
-    _data = "";
-  }
-
-  void pop_state () {
-    _state_stack.pop_back();
-    _level--;
-  }
-
-  string _data;
-  SGPropertyNode * _root;
-  int _level;
-  vector<State> _state_stack;
-  string _base;
-  sg_io_exception _exception;
-  bool _hasException;
-};
-
-void
-PropsVisitor::startXML ()
-{
-  _level = 0;
-  _state_stack.resize(0);
-}
-
-void
-PropsVisitor::endXML ()
-{
-  _level = 0;
-  _state_stack.resize(0);
-}
-
-
-/**
- * Check a yes/no flag, with default.
- */
-static bool
-checkFlag (const char * flag, bool defaultState = true)
-{
-  if (flag == 0)
-    return defaultState;
-  else if (string(flag) == "y")
-    return true;
-  else if (string(flag) == "n")
-    return false;
-  else {
-    string message = "Unrecognized flag value '";
-    message += flag;
-    message += '\'';
-                               // FIXME: add location info
-    throw sg_io_exception(message, "SimGear Property Reader");
-  }
-}
-
-void
-PropsVisitor::startElement (const char * name, const XMLAttributes &atts)
-{
-  State &st = state();
-  const char * attval;
-
-  if (_level == 0) {
-    if (string(name) != (string)"PropertyList") {
-      string message = "Root element name is ";
-      message += name;
-      message += "; expected PropertyList";
-      throw sg_io_exception(message, "SimGear Property Reader");
-    }
-
-                               // Check for an include.
-    attval = atts.getValue("include");
-    if (attval != 0) {
-      SGPath path(SGPath(_base).dir());
-      path.append(attval);
-      try {
-       readProperties(path.str(), _root);
-      } catch (sg_io_exception &e) {
-       setException(e);
-      }
-    }
-
-    push_state(_root, "", DEFAULT_MODE);
-  }
-
-  else {
-                               // Get the index.
-    attval = atts.getValue("n");
-    int index = 0;
-    if (attval != 0) {
-      index = atoi(attval);
-      st.counters[name] = SG_MAX2(st.counters[name], index+1);
-    } else {
-      index = st.counters[name];
-      st.counters[name]++;
-    }
-
-                               // Got the index, so grab the node.
-    SGPropertyNode * node = st.node->getChild(name, index, true);
-
-                               // Get the access-mode attributes,
-                               // but don't set yet (in case they
-                               // prevent us from recording the value).
-    int mode = 0;
-
-    attval = atts.getValue("read");
-    if (checkFlag(attval, true))
-      mode |= SGPropertyNode::READ;
-    attval = atts.getValue("write");
-    if (checkFlag(attval, true))
-      mode |= SGPropertyNode::WRITE;
-    attval = atts.getValue("archive");
-    if (checkFlag(attval, false))
-      mode |= SGPropertyNode::ARCHIVE;
-    attval = atts.getValue("trace-read");
-    if (checkFlag(attval, false))
-      mode |= SGPropertyNode::TRACE_READ;
-    attval = atts.getValue("trace-write");
-    if (checkFlag(attval, false))
-      mode |= SGPropertyNode::TRACE_WRITE;
-
-                               // Check for an alias.
-    attval = atts.getValue("alias");
-    if (attval != 0) {
-      if (!node->alias(attval))
-       SG_LOG(SG_INPUT, SG_ALERT, "Failed to set alias to " << attval);
-    }
-
-                               // Check for an include.
-    attval = atts.getValue("include");
-    if (attval != 0) {
-      SGPath path(SGPath(_base).dir());
-      path.append(attval);
-      try {
-       readProperties(path.str(), node);
-      } catch (sg_io_exception &e) {
-       setException(e);
-      }
-    }
-
-    push_state(node, atts.getValue("type"), mode);
-  }
-}
-
-void
-PropsVisitor::endElement (const char * name)
-{
-  State &st = state();
-  bool ret;
-
-                               // If there are no children and it's
-                               // not an alias, then it's a leaf value.
-  if (st.node->nChildren() == 0 && !st.node->isAlias()) {
-    if (st.type == "bool") {
-      if (_data == "true" || atoi(_data.c_str()) != 0)
-       ret = st.node->setBoolValue(true);
-      else
-       ret = st.node->setBoolValue(false);
-    } else if (st.type == "int") {
-      ret = st.node->setIntValue(atoi(_data.c_str()));
-    } else if (st.type == "long") {
-      ret = st.node->setLongValue(strtol(_data.c_str(), 0, 0));
-    } else if (st.type == "float") {
-      ret = st.node->setFloatValue(atof(_data.c_str()));
-    } else if (st.type == "double") {
-      ret = st.node->setDoubleValue(strtod(_data.c_str(), 0));
-    } else if (st.type == "string") {
-      ret = st.node->setStringValue(_data.c_str());
-    } else if (st.type == "unspecified") {
-      ret = st.node->setUnspecifiedValue(_data.c_str());
-    } else {
-      string message = "Unrecognized data type '";
-      message += st.type;
-      message += '\'';
-                               // FIXME: add location information
-      throw sg_io_exception(message, "SimGear Property Reader");
-    }
-    if (!ret)
-      SG_LOG(SG_INPUT, SG_ALERT, "readProperties: Failed to set "
-            << st.node->getPath() << " to value \""
-            << _data << "\" with type " << st.type);
-  }
-
-                               // Set the access-mode attributes now,
-                               // once the value has already been 
-                               // assigned.
-  st.node->setAttributes(st.mode);
-
-  pop_state();
-}
-
-void
-PropsVisitor::data (const char * s, int length)
-{
-  if (state().node->nChildren() == 0)
-    _data.append(string(s, length));
-}
-
-void
-PropsVisitor::warning (const char * message, int line, int column)
-{
-  SG_LOG(SG_INPUT, SG_ALERT, "readProperties: warning: "
-        << message << " at line " << line << ", column " << column);
-}
-
-
-\f
-////////////////////////////////////////////////////////////////////////
-// Property list reader.
-////////////////////////////////////////////////////////////////////////
-
-
-/**
- * Read properties from an input stream.
- *
- * @param input The input stream containing an XML property file.
- * @param start_node The root node for reading properties.
- * @param base A base path for resolving external include references.
- * @return true if the read succeeded, false otherwise.
- */
-void
-readProperties (istream &input, SGPropertyNode * start_node,
-               const string &base)
-{
-  PropsVisitor visitor(start_node, base);
-  readXML(input, visitor, base);
-  if (visitor.hasException())
-    throw visitor.getException();
-}
-
-
-/**
- * Read properties from a file.
- *
- * @param file A string containing the file path.
- * @param start_node The root node for reading properties.
- * @return true if the read succeeded, false otherwise.
- */
-void
-readProperties (const string &file, SGPropertyNode * start_node)
-{
-  PropsVisitor visitor(start_node, file);
-  readXML(file, visitor);
-  if (visitor.hasException())
-    throw visitor.getException();
-}
-
-
-\f
-////////////////////////////////////////////////////////////////////////
-// Property list writer.
-////////////////////////////////////////////////////////////////////////
-
-#define INDENT_STEP 2
-
-/**
- * Return the type name.
- */
-static const char *
-getTypeName (SGPropertyNode::Type type)
-{
-  switch (type) {
-  case SGPropertyNode::UNSPECIFIED:
-    return "unspecified";
-  case SGPropertyNode::BOOL:
-    return "bool";
-  case SGPropertyNode::INT:
-    return "int";
-  case SGPropertyNode::LONG:
-    return "long";
-  case SGPropertyNode::FLOAT:
-    return "float";
-  case SGPropertyNode::DOUBLE:
-    return "double";
-  case SGPropertyNode::STRING:
-    return "string";
-  case SGPropertyNode::ALIAS:
-  case SGPropertyNode::NONE:
-    return "unspecified";
-  }
-
-  // keep the compiler from squawking
-  return "unspecified";
-}
-
-
-/**
- * Escape characters for output.
- */
-static void
-writeData (ostream &output, const string &data)
-{
-  for (int i = 0; i < (int)data.size(); i++) {
-    switch (data[i]) {
-    case '&':
-      output << "&amp;";
-      break;
-    case '<':
-      output << "&lt;";
-      break;
-    case '>':
-      output << "&gt;";
-      break;
-    default:
-      output << data[i];
-      break;
-    }
-  }
-}
-
-static void
-doIndent (ostream &output, int indent)
-{
-  while (indent-- > 0) {
-    output << ' ';
-  }
-}
-
-
-static void
-writeAtts (ostream &output, const SGPropertyNode * node)
-{
-  int index = node->getIndex();
-
-  if (index != 0)
-    output << " n=\"" << index << '"';
-
-#if 0
-  if (!node->getAttribute(SGPropertyNode::READ))
-    output << " read=\"n\"";
-
-  if (!node->getAttribute(SGPropertyNode::WRITE))
-    output << " write=\"n\"";
-
-  if (node->getAttribute(SGPropertyNode::ARCHIVE))
-    output << " archive=\"y\"";
-#endif
-
-}
-
-
-/**
- * Test whether a node is archivable or has archivable descendants.
- */
-static bool
-isArchivable (const SGPropertyNode * node)
-{
-  // FIXME: it's inefficient to do this all the time
-  if (node->getAttribute(SGPropertyNode::ARCHIVE))
-    return true;
-  else {
-    int nChildren = node->nChildren();
-    for (int i = 0; i < nChildren; i++)
-      if (isArchivable(node->getChild(i)))
-       return true;
-  }
-  return false;
-}
-
-
-static bool
-writeNode (ostream &output, const SGPropertyNode * node,
-          bool write_all, int indent)
-{
-                               // Don't write the node or any of
-                               // its descendants unless it is
-                               // allowed to be archived.
-  if (!write_all && !isArchivable(node))
-    return true;               // Everything's OK, but we won't write.
-
-  const string name = node->getName();
-  int nChildren = node->nChildren();
-
-                               // If there is a literal value,
-                               // write it first.
-  if (node->hasValue() && (write_all || node->getAttribute(SGPropertyNode::ARCHIVE))) {
-    doIndent(output, indent);
-    output << '<' << name;
-    writeAtts(output, node);
-    if (node->isAlias() && node->getAliasTarget() != 0) {
-      output << " alias=\"" << node->getAliasTarget()->getPath()
-            << "\"/>" << endl;
-    } else {
-      if (node->getType() != SGPropertyNode::UNSPECIFIED)
-       output << " type=\"" << getTypeName(node->getType()) << '"';
-      output << '>';
-      writeData(output, node->getStringValue());
-      output << "</" << name << '>' << endl;
-    }
-  }
-
-                               // If there are children, write them next.
-  if (nChildren > 0 || node->isAlias()) {
-    doIndent(output, indent);
-    output << '<' << name;
-    writeAtts(output, node);
-    output << '>' << endl;
-    for (int i = 0; i < nChildren; i++)
-      writeNode(output, node->getChild(i), write_all, indent + INDENT_STEP);
-    doIndent(output, indent);
-    output << "</" << name << '>' << endl;
-  }
-
-  return true;
-}
-
-
-void
-writeProperties (ostream &output, const SGPropertyNode * start_node,
-                bool write_all)
-{
-  int nChildren = start_node->nChildren();
-
-  output << "<?xml version=\"1.0\"?>" << endl << endl;
-  output << "<PropertyList>" << endl;
-
-  for (int i = 0; i < nChildren; i++) {
-    writeNode(output, start_node->getChild(i), write_all, INDENT_STEP);
-  }
-
-  output << "</PropertyList>" << endl;
-}
-
-
-void
-writeProperties (const string &file, const SGPropertyNode * start_node,
-                bool write_all)
-{
-  ofstream output(file.c_str());
-  if (output.good()) {
-    writeProperties(output, start_node, write_all);
-  } else {
-    throw sg_io_exception("Cannot open file", sg_location(file));
-  }
-}
-
-
-\f
-////////////////////////////////////////////////////////////////////////
-// Copy properties from one tree to another.
-////////////////////////////////////////////////////////////////////////
-
-
-/**
- * Copy one property tree to another.
- * 
- * @param in The source property tree.
- * @param out The destination property tree.
- * @return true if all properties were copied, false if some failed
- *  (for example, if the property's value is tied read-only).
- */
-bool
-copyProperties (const SGPropertyNode *in, SGPropertyNode *out)
-{
-  bool retval = true;
-
-                               // First, copy the actual value,
-                               // if any.
-  if (in->hasValue()) {
-    switch (in->getType()) {
-    case SGPropertyNode::BOOL:
-      if (!out->setBoolValue(in->getBoolValue()))
-       retval = false;
-      break;
-    case SGPropertyNode::INT:
-      if (!out->setIntValue(in->getIntValue()))
-       retval = false;
-      break;
-    case SGPropertyNode::LONG:
-      if (!out->setLongValue(in->getLongValue()))
-       retval = false;
-      break;
-    case SGPropertyNode::FLOAT:
-      if (!out->setFloatValue(in->getFloatValue()))
-       retval = false;
-      break;
-    case SGPropertyNode::DOUBLE:
-      if (!out->setDoubleValue(in->getDoubleValue()))
-       retval = false;
-      break;
-    case SGPropertyNode::STRING:
-      if (!out->setStringValue(in->getStringValue()))
-       retval = false;
-      break;
-    case SGPropertyNode::UNSPECIFIED:
-      if (!out->setUnspecifiedValue(in->getStringValue()))
-       retval = false;
-      break;
-    default:
-      string message = "Unknown internal SGPropertyNode type";
-      message += in->getType();
-      throw sg_error(message, "SimGear Property Reader");
-    }
-  }
-
-                               // Next, copy the children.
-  int nChildren = in->nChildren();
-  for (int i = 0; i < nChildren; i++) {
-    const SGPropertyNode * in_child = in->getChild(i);
-    SGPropertyNode * out_child = out->getChild(in_child->getName(),
-                                              in_child->getIndex(),
-                                              true);
-    if (!copyProperties(in_child, out_child))
-      retval = false;
-  }
-
-  return retval;
-}
-
-// end of props_io.cxx
diff --git a/simgear/misc/props_io.hxx b/simgear/misc/props_io.hxx
deleted file mode 100644 (file)
index 83e84c5..0000000
+++ /dev/null
@@ -1,66 +0,0 @@
-/**
- * \file props_io.hxx
- * Interface definition for property list io.
- * Started Fall 2000 by David Megginson, david@megginson.com
- * This code is released into the Public Domain.
- *
- * See props.html for documentation [replace with URL when available].
- *
- * $Id$
- */
-
-#ifndef __PROPS_IO_HXX
-#define __PROPS_IO_HXX
-
-#include <simgear/compiler.h>
-#include <simgear/misc/props.hxx>
-
-#include <stdio.h>
-
-#include STL_STRING
-#include <vector>
-#include <map>
-#include STL_IOSTREAM
-
-SG_USING_STD(string);
-SG_USING_STD(vector);
-SG_USING_STD(map);
-SG_USING_STD(istream);
-SG_USING_STD(ostream);
-
-/**
- * Read properties from an XML input stream.
- */
-void readProperties (istream &input, SGPropertyNode * start_node,
-                    const string &base = "");
-
-
-/**
- * Read properties from an XML file.
- */
-void readProperties (const string &file, SGPropertyNode * start_node);
-
-
-/**
- * Write properties to an XML output stream.
- */
-void writeProperties (ostream &output, const SGPropertyNode * start_node,
-                     bool write_all = false);
-
-
-/**
- * Write properties to an XML file.
- */
-void writeProperties (const string &file, const SGPropertyNode * start_node,
-                     bool write_all = false);
-
-
-/**
- * Copy properties from one node to another.
- */
-bool copyProperties (const SGPropertyNode *in, SGPropertyNode *out);
-
-
-#endif // __PROPS_IO_HXX
-
-// end of props_io.hxx
diff --git a/simgear/misc/props_test.cxx b/simgear/misc/props_test.cxx
deleted file mode 100644 (file)
index ab29dee..0000000
+++ /dev/null
@@ -1,346 +0,0 @@
-
-////////////////////////////////////////////////////////////////////////
-// Test harness.
-////////////////////////////////////////////////////////////////////////
-
-#include <simgear/compiler.h>
-
-#include STL_IOSTREAM
-#include "props.hxx"
-#include "props_io.hxx"
-
-SG_USING_STD(cout);
-SG_USING_STD(cerr);
-SG_USING_STD(endl);
-
-
-\f
-////////////////////////////////////////////////////////////////////////
-// Sample object.
-////////////////////////////////////////////////////////////////////////
-
-class Stuff {
-public:
-  Stuff () : _stuff(199.0) {}
-  virtual float getStuff () const { return _stuff; }
-  virtual void setStuff (float stuff) { _stuff = stuff; }
-  virtual float getStuff (int index) const { return _stuff * index; }
-  virtual void setStuff (int index, float val) {
-    _stuff = val / index;
-  }
-private:
-  float _stuff;
-};
-
-
-\f
-////////////////////////////////////////////////////////////////////////
-// Sample function.
-////////////////////////////////////////////////////////////////////////
-
-static int get100 () { return 100; }
-
-static double getNum (int index) { return 1.0 / index; }
-
-
-\f
-////////////////////////////////////////////////////////////////////////
-// Show a value.
-////////////////////////////////////////////////////////////////////////
-
-static void
-show_values (const SGPropertyNode * node)
-{
-  cout << "Bool: " << (node->getBoolValue() ? "true" : "false") << endl;
-  cout << "Int: " << node->getIntValue() << endl;
-  cout << "Float: " << node->getFloatValue() << endl;
-  cout << "Double: " << node->getDoubleValue() << endl;
-  cout << "String: " << node->getStringValue() << endl;
-}
-
-
-\f
-////////////////////////////////////////////////////////////////////////
-// Test individual values.
-////////////////////////////////////////////////////////////////////////
-
-static void
-test_value ()
-{
-  SGPropertyNode * node;
-
-  cout << endl << "Value" << endl << endl;
-
-  //
-  // Test coercion for getters.
-  //
-
-  cout << "Testing coercion from bool (expect true)" << endl;
-  node = new SGPropertyNode;
-  node->setBoolValue(true);
-  show_values(node);
-  delete node;
-  cout << endl;
-
-  cout << "Testing coercion from int (expect 128)" << endl;
-  node = new SGPropertyNode;
-  node->setIntValue(128);
-  show_values(node);
-  delete node;
-  cout << endl;
-
-  cout << "Testing coercion from float (expect 1.0/3.0)" << endl;
-  node = new SGPropertyNode;
-  node->setFloatValue(1.0/3.0);
-  show_values(node);
-  delete node;
-  cout << endl;
-
-  cout << "Testing coercion from double (expect 1.0/3.0)" << endl;
-  node = new SGPropertyNode;
-  node->setDoubleValue(1.0/3.0);
-  show_values(node);
-  delete node;
-  cout << endl;
-
-  cout << "Testing coercion from string (expect 10e4)" << endl;
-  node = new SGPropertyNode;
-  node->setStringValue("10e4");
-  show_values(node);
-  delete node;
-  cout << endl;
-
-  cout << "Testing coercion from unspecified (expect -10e-4)" << endl;
-  node = new SGPropertyNode;
-  node->setUnspecifiedValue("-10e-4");
-  show_values(node);
-  delete node;
-  cout << endl;
-
-  //
-  // Test coercion for setters.
-  //
-
-  node = new SGPropertyNode;
-
-  cout << "Testing coercion to bool from bool (expect false)" << endl;
-  node->setBoolValue(false);
-  show_values(node);
-  cout << endl;
-
-  cout << "Testing coercion to bool from int (expect 1)" << endl;
-  node->setIntValue(1);
-  show_values(node);
-  cout << endl;
-
-  cout << "Testing coercion to bool from float (expect 1.1)" << endl;
-  node->setFloatValue(1.1);
-  show_values(node);
-  cout << endl;
-
-  cout << "Testing coercion to bool from double (expect 1.1)" << endl;
-  node->setDoubleValue(1.1);
-  show_values(node);
-  cout << endl;
-
-  cout << "Testing coercion to bool from string (expect 1e10)" << endl;
-  node->setStringValue("1e10");
-  show_values(node);
-  cout << endl;
-
-  cout << "Testing coercion to bool from unspecified (expect 1e10)" << endl;
-  node->setUnspecifiedValue("1e10");
-  show_values(node);
-  cout << endl;
-
-  // Test tying to a pointer.
-
-  static int myValue = 10;
-
-  cout << "Testing tying to a pointer (expect 10)" << endl;
-  if (!node->tie(SGRawValuePointer<int>(&myValue), false))
-    cout << "*** FAILED TO TIE VALUE!!!" << endl;
-  show_values(node);
-  cout << endl;
-
-  cout << "Changing base variable (expect -5)" << endl;
-  myValue = -5;
-  show_values(node);
-  if (!node->untie())
-    cout << "*** FAILED TO UNTIE VALUE!!!" << endl;
-  cout << endl;
-
-
-  // Test tying to static functions.
-
-  cout << "Create a new int value (expect 10)" << endl;
-  node->setIntValue(10);
-  show_values(node);
-  cout << endl;
-
-  cout << "Testing tying to static getter (expect 100)" << endl;
-  if (!node->tie(SGRawValueFunctions<int>(get100)))
-    cout << "*** FAILED TO TIE VALUE!!!" << endl;
-  show_values(node);
-  cout << endl;
-
-  cout << "Try changing value with no setter (expect 100)" << endl;
-  if (node->setIntValue(200))
-    cout << "*** setIntValue did not return false!!!" << endl;
-  show_values(node);
-  cout << endl;
-
-  cout << "Untie value (expect 100)" << endl;
-  if (!node->untie())
-    cout << "*** FAILED TO UNTIE VALUE!!!" << endl;
-  show_values(node);
-  cout << endl;
-
-  cout << "Try changing value (expect 200)" << endl;
-  if (!node->setIntValue(200))
-    cout << "*** setIntValue RETURNED FALSE!!!" << endl;
-  show_values(node);
-  cout << endl;
-
-  // Test tying to indexed static functions.
-
-  cout << "Create a new int value (expect 10)" << endl;
-  node->setIntValue(10);
-  show_values(node);
-  cout << endl;
-
-  cout << "Testing tying to indexed static getter (0.3333...)" << endl;
-  if (!node->tie(SGRawValueFunctionsIndexed<double>(3, getNum)))
-    cout << "*** FAILED TO TIE VALUE!!!" << endl;
-  show_values(node);
-  cout << endl;
-
-  cout << "Untie value (expect 0.3333...)" << endl;
-  if (!node->untie())
-    cout << "*** FAILED TO UNTIE VALUE!!!" << endl;
-  show_values(node);
-  cout << endl;
-
-
-  // Test methods.
-
-  cout << "Try tying to an object without defaults (expect 199)" << endl;
-  Stuff stuff;
-  SGRawValueMethods<class Stuff,float> tiedstuff(stuff,
-                                                &Stuff::getStuff,
-                                                &Stuff::setStuff);
-  if (!node->tie(tiedstuff, false))
-    cout << "*** FAILED TO TIE VALUE!!!" << endl;
-  show_values(node);
-  cout << endl;
-
-  cout << "Try untying from object (expect 199)" << endl;
-  if (!node->untie())
-    cout << "*** FAILED TO UNTIE VALUE!!!" << endl;
-  show_values(node);
-  cout << endl;
-
-  cout << "Try tying to an indexed method (expect 199)" << endl;
-  if (!node->tie(SGRawValueMethodsIndexed<class Stuff, float>
-                 (stuff, 2, &Stuff::getStuff, &Stuff::setStuff)))
-    cout << "*** FAILED TO TIE VALUE!!!" << endl;
-  show_values(node);
-  cout << endl;
-
-  node->untie();
-
-  cout << "Change value (expect 50)" << endl;
-  if (!node->setIntValue(50))
-    cout << "*** FAILED TO SET VALUE!!!" << endl;
-  show_values(node);
-  cout << endl;
-
-  cout << "Try tying to an object with defaults (expect 50)" << endl;
-  if (!node->tie(tiedstuff, true))
-    cout << "*** FAILED TO TIE VALUE!!!" << endl;
-  show_values(node);
-  cout << endl;
-
-  delete node;
-}
-
-
-\f
-////////////////////////////////////////////////////////////////////////
-// Check property nodes.
-////////////////////////////////////////////////////////////////////////
-
-static void
-dump_node (const SGPropertyNode * node)
-{
-  writeProperties(cout, node, true);
-}
-
-static void
-test_property_nodes ()
-{
-  SGPropertyNode root;
-  cout << "Created root node " << root.getPath() << endl;
-
-  SGPropertyNode * child = root.getChild("foo", 0, true);
-
-  SGPropertyNode *grandchild = child->getChild("bar", 0, true);
-  grandchild->setDoubleValue(100);
-  grandchild = child->getChild("bar", 1, true);
-  grandchild->setDoubleValue(200);
-  grandchild = child->getChild("bar", 2, true);
-  grandchild->setDoubleValue(300);
-  grandchild = child->getChild("bar", 3, true);
-  grandchild->setDoubleValue(400);
-
-  child = root.getChild("hack", 0, true);
-
-  grandchild = child->getChild("bar", 0, true);
-  grandchild->setDoubleValue(100);
-  grandchild = child->getChild("bar", 3, true);
-  grandchild->setDoubleValue(200);
-  grandchild = child->getChild("bar", 1, true);
-  grandchild->setDoubleValue(300);
-  grandchild = child->getChild("bar", 2, true);
-  grandchild->setDoubleValue(400);
-  dump_node(&root);
-
-  cout << "Trying path (expect /foo[0]/bar[0])" << endl;
-  grandchild = root.getNode("/hack/../foo/./bar[0]");
-  cout << "Path is " << grandchild->getPath() << endl;
-  cout << endl;
-
-  cout << "Looking for all /hack[0]/bar children" << endl;
-  vector<SGPropertyNode_ptr> bar = child->getChildren("bar");
-  cout << "There are " << bar.size() << " matches" << endl;
-  for (int i = 0; i < (int)bar.size(); i++)
-    cout << bar[i]->getName() << '[' << bar[i]->getIndex() << ']' << endl;
-  cout << endl;
-
-  cout << "Testing addition of a totally empty node" << endl;
-  if (root.getNode("/a/b/c", true) == 0)
-    cerr << "** failed to create /a/b/c" << endl;
-  dump_node(&root);
-  cout << endl;
-}
-
-
-int main (int ac, char ** av)
-{
-  test_value();
-  test_property_nodes();
-
-  for (int i = 1; i < ac; i++) {
-    try {
-      cout << "Reading " << av[i] << endl;
-      SGPropertyNode root;
-      readProperties(av[i], &root);
-      writeProperties(cout, &root, true);
-      cout << endl;
-    } catch (string &message) {
-      cout << "Aborted with " << message << endl;
-    }
-  }
-
-  return 0;
-}
diff --git a/simgear/props/Makefile.am b/simgear/props/Makefile.am
new file mode 100644 (file)
index 0000000..3195e69
--- /dev/null
@@ -0,0 +1,24 @@
+includedir = @includedir@/props
+
+lib_LIBRARIES = libsgprops.a
+
+include_HEADERS = \
+       condition.hxx \
+       props.hxx \
+       props_io.hxx
+
+libsgprops_a_SOURCES = \
+       condition.cxx \
+       props.cxx \
+        props_io.cxx
+
+noinst_PROGRAMS = props_test
+
+props_test_SOURCES = props_test.cxx
+props_test_LDADD = \
+       $(top_builddir)/simgear/props/libsgprops.a \
+       $(top_builddir)/simgear/xml/libsgxml.a \
+       $(top_builddir)/simgear/misc/libsgmisc.a \
+       $(top_builddir)/simgear/debug/libsgdebug.a
+
+INCLUDES = -I$(top_srcdir)
diff --git a/simgear/props/condition.cxx b/simgear/props/condition.cxx
new file mode 100644 (file)
index 0000000..d50f461
--- /dev/null
@@ -0,0 +1,435 @@
+// condition.hxx - Declarations and inline methods for property conditions.
+// Written by David Megginson, started 2000.
+// CLO May 2003 - Split out condition specific code.
+//
+// This file is in the Public Domain, and comes with no warranty.
+//
+// $Id$
+
+#ifdef HAVE_CONFIG_H
+#  include <simgear/compiler.h>
+#endif
+
+// #include STL_IOSTREAM
+
+#include <simgear/misc/exception.hxx>
+
+#include "props.hxx"
+
+#include "condition.hxx"
+
+SG_USING_STD(istream);
+SG_USING_STD(ostream);
+
+
+
+\f
+////////////////////////////////////////////////////////////////////////
+// Implementation of FGCondition.
+////////////////////////////////////////////////////////////////////////
+
+FGCondition::FGCondition ()
+{
+}
+
+FGCondition::~FGCondition ()
+{
+}
+
+
+\f
+////////////////////////////////////////////////////////////////////////
+// Implementation of FGPropertyCondition.
+////////////////////////////////////////////////////////////////////////
+
+FGPropertyCondition::FGPropertyCondition ( SGPropertyNode *prop_root,
+                                           const char *propname )
+    : _node( prop_root->getNode(propname, true) )
+{
+    cout << "FGPropertyCondition::FGPropertyCondition()" << endl;
+    cout << "  prop_root = " << prop_root << endl;
+    cout << "  propname = " << propname << endl;
+    _node = prop_root->getNode(propname, true);
+    cout << "  _node = " << _node << endl;
+}
+
+FGPropertyCondition::~FGPropertyCondition ()
+{
+}
+
+
+\f
+////////////////////////////////////////////////////////////////////////
+// Implementation of FGNotCondition.
+////////////////////////////////////////////////////////////////////////
+
+FGNotCondition::FGNotCondition (FGCondition * condition)
+  : _condition(condition)
+{
+}
+
+FGNotCondition::~FGNotCondition ()
+{
+  delete _condition;
+}
+
+bool
+FGNotCondition::test () const
+{
+  return !(_condition->test());
+}
+
+
+\f
+////////////////////////////////////////////////////////////////////////
+// Implementation of FGAndCondition.
+////////////////////////////////////////////////////////////////////////
+
+FGAndCondition::FGAndCondition ()
+{
+}
+
+FGAndCondition::~FGAndCondition ()
+{
+  for (unsigned int i = 0; i < _conditions.size(); i++)
+    delete _conditions[i];
+}
+
+bool
+FGAndCondition::test () const
+{
+  int nConditions = _conditions.size();
+  for (int i = 0; i < nConditions; i++) {
+    if (!_conditions[i]->test())
+      return false;
+  }
+  return true;
+}
+
+void
+FGAndCondition::addCondition (FGCondition * condition)
+{
+  _conditions.push_back(condition);
+}
+
+
+\f
+////////////////////////////////////////////////////////////////////////
+// Implementation of FGOrCondition.
+////////////////////////////////////////////////////////////////////////
+
+FGOrCondition::FGOrCondition ()
+{
+}
+
+FGOrCondition::~FGOrCondition ()
+{
+  for (unsigned int i = 0; i < _conditions.size(); i++)
+    delete _conditions[i];
+}
+
+bool
+FGOrCondition::test () const
+{
+  int nConditions = _conditions.size();
+  for (int i = 0; i < nConditions; i++) {
+    if (_conditions[i]->test())
+      return true;
+  }
+  return false;
+}
+
+void
+FGOrCondition::addCondition (FGCondition * condition)
+{
+  _conditions.push_back(condition);
+}
+
+
+\f
+////////////////////////////////////////////////////////////////////////
+// Implementation of FGComparisonCondition.
+////////////////////////////////////////////////////////////////////////
+
+static int
+doComparison (const SGPropertyNode * left, const SGPropertyNode *right)
+{
+  switch (left->getType()) {
+  case SGPropertyNode::BOOL: {
+    bool v1 = left->getBoolValue();
+    bool v2 = right->getBoolValue();
+    if (v1 < v2)
+      return FGComparisonCondition::LESS_THAN;
+    else if (v1 > v2)
+      return FGComparisonCondition::GREATER_THAN;
+    else
+      return FGComparisonCondition::EQUALS;
+    break;
+  }
+  case SGPropertyNode::INT: {
+    int v1 = left->getIntValue();
+    int v2 = right->getIntValue();
+    if (v1 < v2)
+      return FGComparisonCondition::LESS_THAN;
+    else if (v1 > v2)
+      return FGComparisonCondition::GREATER_THAN;
+    else
+      return FGComparisonCondition::EQUALS;
+    break;
+  }
+  case SGPropertyNode::LONG: {
+    long v1 = left->getLongValue();
+    long v2 = right->getLongValue();
+    if (v1 < v2)
+      return FGComparisonCondition::LESS_THAN;
+    else if (v1 > v2)
+      return FGComparisonCondition::GREATER_THAN;
+    else
+      return FGComparisonCondition::EQUALS;
+    break;
+  }
+  case SGPropertyNode::FLOAT: {
+    float v1 = left->getFloatValue();
+    float v2 = right->getFloatValue();
+    if (v1 < v2)
+      return FGComparisonCondition::LESS_THAN;
+    else if (v1 > v2)
+      return FGComparisonCondition::GREATER_THAN;
+    else
+      return FGComparisonCondition::EQUALS;
+    break;
+  }
+  case SGPropertyNode::DOUBLE: {
+    double v1 = left->getDoubleValue();
+    double v2 = right->getDoubleValue();
+    if (v1 < v2)
+      return FGComparisonCondition::LESS_THAN;
+    else if (v1 > v2)
+      return FGComparisonCondition::GREATER_THAN;
+    else
+      return FGComparisonCondition::EQUALS;
+    break;
+  }
+  case SGPropertyNode::STRING: 
+  case SGPropertyNode::NONE:
+  case SGPropertyNode::UNSPECIFIED: {
+    string v1 = left->getStringValue();
+    string v2 = right->getStringValue();
+    if (v1 < v2)
+      return FGComparisonCondition::LESS_THAN;
+    else if (v1 > v2)
+      return FGComparisonCondition::GREATER_THAN;
+    else
+      return FGComparisonCondition::EQUALS;
+    break;
+  }
+  }
+  throw sg_exception("Unrecognized node type");
+  return 0;
+}
+
+
+FGComparisonCondition::FGComparisonCondition (Type type, bool reverse)
+  : _type(type),
+    _reverse(reverse),
+    _left_property(0),
+    _right_property(0),
+    _right_value(0)
+{
+}
+
+FGComparisonCondition::~FGComparisonCondition ()
+{
+  delete _right_value;
+}
+
+bool
+FGComparisonCondition::test () const
+{
+                               // Always fail if incompletely specified
+  if (_left_property == 0 ||
+      (_right_property == 0 && _right_value == 0))
+    return false;
+
+                               // Get LESS_THAN, EQUALS, or GREATER_THAN
+  int cmp =
+    doComparison(_left_property,
+                (_right_property != 0 ? _right_property : _right_value));
+  if (!_reverse)
+    return (cmp == _type);
+  else
+    return (cmp != _type);
+}
+
+void
+FGComparisonCondition::setLeftProperty( SGPropertyNode *prop_root,
+                                        const char * propname )
+{
+  _left_property = prop_root->getNode(propname, true);
+}
+
+void
+FGComparisonCondition::setRightProperty( SGPropertyNode *prop_root,
+                                         const char * propname )
+{
+  delete _right_value;
+  _right_value = 0;
+  _right_property = prop_root->getNode(propname, true);
+}
+
+void
+FGComparisonCondition::setRightValue (const SGPropertyNode *node)
+{
+  _right_property = 0;
+  delete _right_value;
+  _right_value = new SGPropertyNode(*node);
+}
+
+
+\f
+////////////////////////////////////////////////////////////////////////
+// Read a condition and use it if necessary.
+////////////////////////////////////////////////////////////////////////
+
+                                // Forward declaration
+static FGCondition * readCondition( SGPropertyNode *prop_root,
+                                    const SGPropertyNode *node );
+
+static FGCondition *
+readPropertyCondition( SGPropertyNode *prop_root,
+                       const SGPropertyNode *node )
+{
+  return new FGPropertyCondition( prop_root, node->getStringValue() );
+}
+
+static FGCondition *
+readNotCondition( SGPropertyNode *prop_root, const SGPropertyNode *node )
+{
+  int nChildren = node->nChildren();
+  for (int i = 0; i < nChildren; i++) {
+    const SGPropertyNode * child = node->getChild(i);
+    FGCondition * condition = readCondition(prop_root, child);
+    if (condition != 0)
+      return new FGNotCondition(condition);
+  }
+  SG_LOG(SG_COCKPIT, SG_ALERT, "Panel: empty 'not' condition");
+  return 0;
+}
+
+static FGCondition *
+readAndConditions( SGPropertyNode *prop_root, const SGPropertyNode *node )
+{
+  FGAndCondition * andCondition = new FGAndCondition;
+  int nChildren = node->nChildren();
+  for (int i = 0; i < nChildren; i++) {
+    const SGPropertyNode * child = node->getChild(i);
+    FGCondition * condition = readCondition(prop_root, child);
+    if (condition != 0)
+      andCondition->addCondition(condition);
+  }
+  return andCondition;
+}
+
+static FGCondition *
+readOrConditions( SGPropertyNode *prop_root, const SGPropertyNode *node )
+{
+  FGOrCondition * orCondition = new FGOrCondition;
+  int nChildren = node->nChildren();
+  for (int i = 0; i < nChildren; i++) {
+    const SGPropertyNode * child = node->getChild(i);
+    FGCondition * condition = readCondition(prop_root, child);
+    if (condition != 0)
+      orCondition->addCondition(condition);
+  }
+  return orCondition;
+}
+
+static FGCondition *
+readComparison( SGPropertyNode *prop_root,
+                const SGPropertyNode *node,
+                FGComparisonCondition::Type type,
+               bool reverse)
+{
+  FGComparisonCondition * condition = new FGComparisonCondition(type, reverse);
+  condition->setLeftProperty(prop_root, node->getStringValue("property[0]"));
+  if (node->hasValue("property[1]"))
+    condition->setRightProperty(prop_root, node->getStringValue("property[1]"));
+  else
+    condition->setRightValue(node->getChild("value", 0));
+
+  return condition;
+}
+
+static FGCondition *
+readCondition( SGPropertyNode *prop_root, const SGPropertyNode *node )
+{
+  const string &name = node->getName();
+  if (name == "property")
+    return readPropertyCondition(prop_root, node);
+  else if (name == "not")
+    return readNotCondition(prop_root, node);
+  else if (name == "and")
+    return readAndConditions(prop_root, node);
+  else if (name == "or")
+    return readOrConditions(prop_root, node);
+  else if (name == "less-than")
+    return readComparison(prop_root, node, FGComparisonCondition::LESS_THAN,
+                          false);
+  else if (name == "less-than-equals")
+    return readComparison(prop_root, node, FGComparisonCondition::GREATER_THAN,
+                          true);
+  else if (name == "greater-than")
+    return readComparison(prop_root, node, FGComparisonCondition::GREATER_THAN,
+                          false);
+  else if (name == "greater-than-equals")
+    return readComparison(prop_root, node, FGComparisonCondition::LESS_THAN,
+                          true);
+  else if (name == "equals")
+    return readComparison(prop_root, node, FGComparisonCondition::EQUALS,
+                          false);
+  else if (name == "not-equals")
+    return readComparison(prop_root, node, FGComparisonCondition::EQUALS, true);
+  else
+    return 0;
+}
+
+
+\f
+////////////////////////////////////////////////////////////////////////
+// Implementation of FGConditional.
+////////////////////////////////////////////////////////////////////////
+
+FGConditional::FGConditional ()
+  : _condition (0)
+{
+}
+
+FGConditional::~FGConditional ()
+{
+  delete _condition;
+}
+
+void
+FGConditional::setCondition (FGCondition * condition)
+{
+  delete _condition;
+  _condition = condition;
+}
+
+bool
+FGConditional::test () const
+{
+  return ((_condition == 0) || _condition->test());
+}
+
+
+\f
+// The top-level is always an implicit 'and' group
+FGCondition *
+fgReadCondition( SGPropertyNode *prop_root, const SGPropertyNode *node )
+{
+  return readAndConditions(prop_root, node);
+}
+
+
+// end of fg_props.cxx
diff --git a/simgear/props/condition.hxx b/simgear/props/condition.hxx
new file mode 100644 (file)
index 0000000..3da6371
--- /dev/null
@@ -0,0 +1,176 @@
+// condition.hxx - Declarations and inline methods for property conditions.
+// Written by David Megginson, started 2000.
+// CLO May 2003 - Split out condition specific code.
+//
+// This file is in the Public Domain, and comes with no warranty.
+
+#ifndef __SG_CONDITION_HXX
+#define __SG_CONDITION_HXX
+
+#include <simgear/debug/logstream.hxx>
+#include <simgear/props/props.hxx>
+#include <simgear/props/props_io.hxx>
+
+\f
+////////////////////////////////////////////////////////////////////////
+// Conditions.
+////////////////////////////////////////////////////////////////////////
+
+
+/**
+ * An encoded condition.
+ *
+ * This class encodes a single condition of some sort, possibly
+ * connected with properties.
+ *
+ * This class should migrate to somewhere more general.
+ */
+class FGCondition
+{
+public:
+  FGCondition ();
+  virtual ~FGCondition ();
+  virtual bool test () const = 0;
+};
+
+
+/**
+ * Condition for a single property.
+ *
+ * This condition is true only if the property returns a boolean
+ * true value.
+ */
+class FGPropertyCondition : public FGCondition
+{
+public:
+  FGPropertyCondition ( SGPropertyNode *prop_root,
+                        const char * propname  );
+  virtual ~FGPropertyCondition ();
+  virtual bool test () const { return _node->getBoolValue(); }
+private:
+  const SGPropertyNode * _node;
+};
+
+
+/**
+ * Condition for a 'not' operator.
+ *
+ * This condition is true only if the child condition is false.
+ */
+class FGNotCondition : public FGCondition
+{
+public:
+                               // transfer pointer ownership
+  FGNotCondition (FGCondition * condition);
+  virtual ~FGNotCondition ();
+  virtual bool test () const;
+private:
+  FGCondition * _condition;
+};
+
+
+/**
+ * Condition for an 'and' group.
+ *
+ * This condition is true only if all of the conditions
+ * in the group are true.
+ */
+class FGAndCondition : public FGCondition
+{
+public:
+  FGAndCondition ();
+  virtual ~FGAndCondition ();
+  virtual bool test () const;
+                               // transfer pointer ownership
+  virtual void addCondition (FGCondition * condition);
+private:
+  vector<FGCondition *> _conditions;
+};
+
+
+/**
+ * Condition for an 'or' group.
+ *
+ * This condition is true if at least one of the conditions in the
+ * group is true.
+ */
+class FGOrCondition : public FGCondition
+{
+public:
+  FGOrCondition ();
+  virtual ~FGOrCondition ();
+  virtual bool test () const;
+                               // transfer pointer ownership
+  virtual void addCondition (FGCondition * condition);
+private:
+  vector<FGCondition *> _conditions;
+};
+
+
+/**
+ * Abstract base class for property comparison conditions.
+ */
+class FGComparisonCondition : public FGCondition
+{
+public:
+  enum Type {
+    LESS_THAN,
+    GREATER_THAN,
+    EQUALS
+  };
+  FGComparisonCondition (Type type, bool reverse = false);
+  virtual ~FGComparisonCondition ();
+  virtual bool test () const;
+  virtual void setLeftProperty( SGPropertyNode *prop_root,
+                                const char * propname );
+  virtual void setRightProperty( SGPropertyNode *prop_root,
+                                 const char * propname );
+                               // will make a local copy
+  virtual void setRightValue (const SGPropertyNode * value);
+private:
+  Type _type;
+  bool _reverse;
+  const SGPropertyNode * _left_property;
+  const SGPropertyNode * _right_property;
+  const SGPropertyNode * _right_value;
+};
+
+
+/**
+ * Base class for a conditional components.
+ *
+ * This class manages the conditions and tests; the component should
+ * invoke the test() method whenever it needs to decide whether to
+ * active itself, draw itself, and so on.
+ */
+class FGConditional
+{
+public:
+  FGConditional ();
+  virtual ~FGConditional ();
+                               // transfer pointer ownership
+  virtual void setCondition (FGCondition * condition);
+  virtual const FGCondition * getCondition () const { return _condition; }
+  virtual bool test () const;
+private:
+  FGCondition * _condition;
+};
+
+
+/**
+ * Global function to make a condition out of properties.
+ *
+ * The top-level is always an implicit 'and' group, whatever the
+ * node's name (it should usually be "condition").
+ *
+ * @param node The top-level condition node (usually named "condition").
+ * @return A pointer to a newly-allocated condition; it is the
+ *         responsibility of the caller to delete the condition when
+ *         it is no longer needed.
+ */
+FGCondition * fgReadCondition( SGPropertyNode *prop_root,
+                               const SGPropertyNode *node );
+
+
+#endif // __SG_CONDITION_HXX
+
diff --git a/simgear/props/props.cxx b/simgear/props/props.cxx
new file mode 100644 (file)
index 0000000..790aa05
--- /dev/null
@@ -0,0 +1,2374 @@
+// props.cxx - implementation of a property list.
+// Started Fall 2000 by David Megginson, david@megginson.com
+// This code is released into the Public Domain.
+//
+// See props.html for documentation [replace with URL when available].
+//
+// $Id$
+
+#include "props.hxx"
+
+#include <algorithm>
+#include <stdio.h>
+#include <string.h>
+
+#if PROPS_STANDALONE
+
+#include <iostream>
+using std::cerr;
+using std::endl;
+using std::find;
+using std::sort;
+using std::vector;
+
+#else
+
+#include <simgear/compiler.h>
+#include <simgear/debug/logstream.hxx>
+
+SG_USING_STD(sort);
+SG_USING_STD(find);
+SG_USING_STD(vector);
+
+#ifdef _MSC_VER
+// MSVC is buggy, and needs something strange here
+SG_USING_STD(vector<SGPropertyNode_ptr>);
+SG_USING_STD(vector<SGPropertyChangeListener *>);
+SG_USING_STD(vector<SGPropertyNode *>);
+#endif
+
+#endif
+
+
+\f
+////////////////////////////////////////////////////////////////////////
+// Local classes.
+////////////////////////////////////////////////////////////////////////
+
+/**
+ * Comparator class for sorting by index.
+ */
+class CompareIndices
+{
+public:
+  int operator() (const SGPropertyNode_ptr n1, const SGPropertyNode_ptr n2) const {
+    return (n1->getIndex() < n2->getIndex());
+  }
+};
+
+
+\f
+////////////////////////////////////////////////////////////////////////
+// Convenience macros for value access.
+////////////////////////////////////////////////////////////////////////
+
+#define TEST_READ(dflt) if (!getAttribute(READ)) return dflt
+#define TEST_WRITE if (!getAttribute(WRITE)) return false
+
+
+\f
+////////////////////////////////////////////////////////////////////////
+// Default values for every type.
+////////////////////////////////////////////////////////////////////////
+
+const bool SGRawValue<bool>::DefaultValue = false;
+const int SGRawValue<int>::DefaultValue = 0;
+const long SGRawValue<long>::DefaultValue = 0L;
+const float SGRawValue<float>::DefaultValue = 0.0;
+const double SGRawValue<double>::DefaultValue = 0.0L;
+const char * const SGRawValue<const char *>::DefaultValue = "";
+
+
+\f
+////////////////////////////////////////////////////////////////////////
+// Local path normalization code.
+////////////////////////////////////////////////////////////////////////
+
+/**
+ * A component in a path.
+ */
+struct PathComponent
+{
+  string name;
+  int index;
+};
+
+/**
+ * Parse the name for a path component.
+ *
+ * Name: [_a-zA-Z][-._a-zA-Z0-9]*
+ */
+static inline const string
+parse_name (const string &path, int &i)
+{
+  string name = "";
+  int max = path.size();
+
+  if (path[i] == '.') {
+    i++;
+    if (i < max && path[i] == '.') {
+      i++;
+      name = "..";
+    } else {
+      name = ".";
+    }
+    if (i < max && path[i] != '/')
+      throw string(string("Illegal character after ") + name);
+  }
+
+  else if (isalpha(path[i]) || path[i] == '_') {
+    name += path[i];
+    i++;
+
+                               // The rules inside a name are a little
+                               // less restrictive.
+    while (i < max) {
+      if (isalpha(path[i]) || isdigit(path[i]) || path[i] == '_' ||
+         path[i] == '-' || path[i] == '.') {
+       name += path[i];
+      } else if (path[i] == '[' || path[i] == '/') {
+       break;
+      } else {
+       throw string("name may contain only ._- and alphanumeric characters");
+      }
+      i++;
+    }
+  }
+
+  else {
+    if (name.size() == 0)
+      throw string("name must begin with alpha or '_'");
+  }
+
+  return name;
+}
+
+
+/**
+ * Parse the optional integer index for a path component.
+ *
+ * Index: "[" [0-9]+ "]"
+ */
+static inline int
+parse_index (const string &path, int &i)
+{
+  int index = 0;
+
+  if (path[i] != '[')
+    return 0;
+  else
+    i++;
+
+  for (int max = path.size(); i < max; i++) {
+    if (isdigit(path[i])) {
+      index = (index * 10) + (path[i] - '0');
+    } else if (path[i] == ']') {
+      i++;
+      return index;
+    } else {
+      break;
+    }
+  }
+
+  throw string("unterminated index (looking for ']')");
+}
+
+
+/**
+ * Parse a single path component.
+ *
+ * Component: Name Index?
+ */
+static inline PathComponent
+parse_component (const string &path, int &i)
+{
+  PathComponent component;
+  component.name = parse_name(path, i);
+  if (component.name[0] != '.')
+    component.index = parse_index(path, i);
+  else
+    component.index = -1;
+  return component;
+}
+
+
+/**
+ * Parse a path into its components.
+ */
+static void
+parse_path (const string &path, vector<PathComponent> &components)
+{
+  int pos = 0;
+  int max = path.size();
+
+                               // Check for initial '/'
+  if (path[pos] == '/') {
+    PathComponent root;
+    root.name = "";
+    root.index = -1;
+    components.push_back(root);
+    pos++;
+    while (pos < max && path[pos] == '/')
+      pos++;
+  }
+
+  while (pos < max) {
+    components.push_back(parse_component(path, pos));
+    while (pos < max && path[pos] == '/')
+      pos++;
+  }
+}
+
+
+\f
+////////////////////////////////////////////////////////////////////////
+// Other static utility functions.
+////////////////////////////////////////////////////////////////////////
+
+
+static char *
+copy_string (const char * s)
+{
+                               // FIXME: potential buffer overflow.
+                               // For some reason, strnlen and
+                               // strncpy cause all kinds of crashes.
+  char * copy = new char[strlen(s) + 1];
+  strcpy(copy, s);
+  return copy;
+}
+
+static bool
+compare_strings (const char * s1, const char * s2)
+{
+  return !strncmp(s1, s2, SGPropertyNode::MAX_STRING_LEN);
+}
+
+/**
+ * Locate a child node by name and index.
+ */
+static int
+find_child (const char * name, int index, vector<SGPropertyNode_ptr> nodes)
+{
+  int nNodes = nodes.size();
+  for (int i = 0; i < nNodes; i++) {
+    SGPropertyNode * node = nodes[i];
+    if (compare_strings(node->getName(), name) && node->getIndex() == index)
+      return i;
+  }
+  return -1;
+}
+
+
+/**
+ * Locate another node, given a relative path.
+ */
+static SGPropertyNode *
+find_node (SGPropertyNode * current,
+          const vector<PathComponent> &components,
+          int position,
+          bool create)
+{
+                               // Run off the end of the list
+  if (current == 0) {
+    return 0;
+  }
+
+                               // Success! This is the one we want.
+  else if (position >= (int)components.size()) {
+    return current;
+  }
+
+                               // Empty component means root.
+  else if (components[position].name == "") {
+    return find_node(current->getRootNode(), components, position + 1, create);
+  }
+
+                               // . means current directory
+  else if (components[position].name == ".") {
+    return find_node(current, components, position + 1, create);
+  }
+
+                               // .. means parent directory
+  else if (components[position].name == "..") {
+    SGPropertyNode * parent = current->getParent();
+    if (parent == 0)
+      throw string("Attempt to move past root with '..'");
+    else
+      return find_node(parent, components, position + 1, create);
+  }
+
+                               // Otherwise, a child name
+  else {
+    SGPropertyNode * child =
+      current->getChild(components[position].name.c_str(),
+                       components[position].index,
+                       create);
+    return find_node(child, components, position + 1, create);
+  }
+}
+
+
+\f
+////////////////////////////////////////////////////////////////////////
+// Private methods from SGPropertyNode (may be inlined for speed).
+////////////////////////////////////////////////////////////////////////
+
+inline bool
+SGPropertyNode::get_bool () const
+{
+  if (_tied)
+    return _value.bool_val->getValue();
+  else
+    return _local_val.bool_val;
+}
+
+inline int
+SGPropertyNode::get_int () const
+{
+  if (_tied)
+    return _value.int_val->getValue();
+  else
+    return _local_val.int_val;
+}
+
+inline long
+SGPropertyNode::get_long () const
+{
+  if (_tied)
+    return _value.long_val->getValue();
+  else
+    return _local_val.long_val;
+}
+
+inline float
+SGPropertyNode::get_float () const
+{
+  if (_tied)
+    return _value.float_val->getValue();
+  else
+    return _local_val.float_val;
+}
+
+inline double
+SGPropertyNode::get_double () const
+{
+  if (_tied)
+    return _value.double_val->getValue();
+  else
+    return _local_val.double_val;
+}
+
+inline const char *
+SGPropertyNode::get_string () const
+{
+  if (_tied)
+    return _value.string_val->getValue();
+  else
+    return _local_val.string_val;
+}
+
+inline bool
+SGPropertyNode::set_bool (bool val)
+{
+  if (_tied) {
+    if (_value.bool_val->setValue(val)) {
+      fireValueChanged();
+      return true;
+    } else {
+      return false;
+    }
+  } else {
+    _local_val.bool_val = val;
+    fireValueChanged();
+    return true;
+  }
+}
+
+inline bool
+SGPropertyNode::set_int (int val)
+{
+  if (_tied) {
+    if (_value.int_val->setValue(val)) {
+      fireValueChanged();
+      return true;
+    } else {
+      return false;
+    }
+  } else {
+    _local_val.int_val = val;
+    fireValueChanged();
+    return true;
+  }
+}
+
+inline bool
+SGPropertyNode::set_long (long val)
+{
+  if (_tied) {
+    if (_value.long_val->setValue(val)) {
+      fireValueChanged();
+      return true;
+    } else {
+      return false;
+    }
+  } else {
+    _local_val.long_val = val;
+    fireValueChanged();
+    return true;
+  }
+}
+
+inline bool
+SGPropertyNode::set_float (float val)
+{
+  if (_tied) {
+    if (_value.float_val->setValue(val)) {
+      fireValueChanged();
+      return true;
+    } else {
+      return false;
+    }
+  } else {
+    _local_val.float_val = val;
+    fireValueChanged();
+    return true;
+  }
+}
+
+inline bool
+SGPropertyNode::set_double (double val)
+{
+  if (_tied) {
+    if (_value.double_val->setValue(val)) {
+      fireValueChanged();
+      return true;
+    } else {
+      return false;
+    }
+  } else {
+    _local_val.double_val = val;
+    fireValueChanged();
+    return true;
+  }
+}
+
+inline bool
+SGPropertyNode::set_string (const char * val)
+{
+  if (_tied) {
+    if (_value.string_val->setValue(val)) {
+      fireValueChanged();
+      return true;
+    } else {
+      return false;
+    }
+  } else {
+    delete [] _local_val.string_val;
+    _local_val.string_val = copy_string(val);
+    fireValueChanged();
+    return true;
+  }
+}
+
+void
+SGPropertyNode::clear_value ()
+{
+  switch (_type) {
+  case NONE:
+    break;
+  case ALIAS:
+    _value.alias = 0;
+    break;
+  case BOOL:
+    if (_tied) {
+      delete _value.bool_val;
+      _value.bool_val = 0;
+    }
+    _local_val.bool_val = SGRawValue<bool>::DefaultValue;
+    break;
+  case INT:
+    if (_tied) {
+      delete _value.int_val;
+      _value.int_val = 0;
+    }
+    _local_val.int_val = SGRawValue<int>::DefaultValue;
+    break;
+  case LONG:
+    if (_tied) {
+      delete _value.long_val;
+      _value.long_val = 0L;
+    }
+    _local_val.long_val = SGRawValue<long>::DefaultValue;
+    break;
+  case FLOAT:
+    if (_tied) {
+      delete _value.float_val;
+      _value.float_val = 0;
+    }
+    _local_val.float_val = SGRawValue<float>::DefaultValue;
+    break;
+  case DOUBLE:
+    if (_tied) {
+      delete _value.double_val;
+      _value.double_val = 0;
+    }
+    _local_val.double_val = SGRawValue<double>::DefaultValue;
+    break;
+  case STRING:
+  case UNSPECIFIED:
+    if (_tied) {
+      delete _value.string_val;
+      _value.string_val = 0;
+    } else {
+      delete [] _local_val.string_val;
+    }
+    _local_val.string_val = 0;
+    break;
+  }
+  _tied = false;
+  _type = NONE;
+}
+
+
+/**
+ * Get the value as a string.
+ */
+const char *
+SGPropertyNode::make_string () const
+{
+  if (!getAttribute(READ))
+    return "";
+
+  switch (_type) {
+  case ALIAS:
+    return _value.alias->getStringValue();
+  case BOOL:
+    if (get_bool())
+      return "true";
+    else
+      return "false";
+  case INT:
+    sprintf(_buffer, "%d", get_int());
+    return _buffer;
+  case LONG:
+    sprintf(_buffer, "%ld", get_long());
+    return _buffer;
+  case FLOAT:
+    sprintf(_buffer, "%f", get_float());
+    return _buffer;
+  case DOUBLE:
+    sprintf(_buffer, "%f", get_double());
+    return _buffer;
+  case STRING:
+  case UNSPECIFIED:
+    return get_string();
+  case NONE:
+  default:
+    return "";
+  }
+}
+
+/**
+ * Trace a write access for a property.
+ */
+void
+SGPropertyNode::trace_write () const
+{
+#if PROPS_STANDALONE
+  cerr << "TRACE: Write node " << getPath () << ", value\""
+       << make_string() << '"' << endl;
+#else
+  SG_LOG(SG_GENERAL, SG_INFO, "TRACE: Write node " << getPath()
+        << ", value\"" << make_string() << '"');
+#endif
+}
+
+/**
+ * Trace a read access for a property.
+ */
+void
+SGPropertyNode::trace_read () const
+{
+#if PROPS_STANDALONE
+  cerr << "TRACE: Write node " << getPath () << ", value \""
+       << make_string() << '"' << endl;
+#else
+  SG_LOG(SG_GENERAL, SG_INFO, "TRACE: Read node " << getPath()
+        << ", value \"" << make_string() << '"');
+#endif
+}
+
+/**
+ * Increment reference counter
+ */
+void
+SGPropertyNode::incrementRef()
+{
+  ++_count;
+}
+
+/**
+ * Decrement reference counter
+ */
+int
+SGPropertyNode::decrementRef()
+{
+  return --_count;
+}
+
+
+\f
+////////////////////////////////////////////////////////////////////////
+// Public methods from SGPropertyNode.
+////////////////////////////////////////////////////////////////////////
+
+/**
+ * Last used attribute
+ * Update as needed when enum Attribute is changed
+ */
+const int SGPropertyNode::LAST_USED_ATTRIBUTE = TRACE_WRITE;
+
+/**
+ * Default constructor: always creates a root node.
+ */
+SGPropertyNode::SGPropertyNode ()
+  : _name(copy_string("")),
+    _display_name(0),
+    _index(0),
+    _parent(0),
+    _path(0),
+    _path_cache(0),
+    _type(NONE),
+    _tied(false),
+    _attr(READ|WRITE),
+    _count(0),
+    _listeners(0)
+{
+  _local_val.string_val = 0;
+}
+
+
+/**
+ * Copy constructor.
+ */
+SGPropertyNode::SGPropertyNode (const SGPropertyNode &node)
+  : _display_name(0),
+    _index(node._index),
+    _parent(0),                        // don't copy the parent
+    _path(0),
+    _path_cache(0),
+    _type(node._type),
+    _tied(node._tied),
+    _attr(node._attr),
+    _count(0),
+    _listeners(0)              // CHECK!!
+{
+  _name = copy_string(node._name);
+  _local_val.string_val = 0;
+  switch (_type) {
+  case NONE:
+    break;
+  case ALIAS:
+    _value.alias = node._value.alias;
+    _tied = false;
+    break;
+  case BOOL:
+    if (_tied) {
+      _tied = true;
+      _value.bool_val = node._value.bool_val->clone();
+    } else {
+      _tied = false;
+      set_bool(node.get_bool());
+    }
+    break;
+  case INT:
+    if (_tied) {
+      _tied = true;
+      _value.int_val = node._value.int_val->clone();
+    } else {
+      _tied = false;
+      set_int(node.get_int());
+    }
+    break;
+  case LONG:
+    if (_tied) {
+      _tied = true;
+      _value.long_val = node._value.long_val->clone();
+    } else {
+      _tied = false;
+      set_long(node.get_long());
+    }
+    break;
+  case FLOAT:
+    if (_tied) {
+      _tied = true;
+      _value.float_val = node._value.float_val->clone();
+    } else {
+      _tied = false;
+      set_float(node.get_float());
+    }
+    break;
+  case DOUBLE:
+    if (_tied) {
+      _tied = true;
+      _value.double_val = node._value.double_val->clone();
+    } else {
+      _tied = false;
+      set_double(node.get_double());
+    }
+    break;
+  case STRING:
+  case UNSPECIFIED:
+    if (_tied) {
+      _tied = true;
+      _value.string_val = node._value.string_val->clone();
+    } else {
+      _tied = false;
+      set_string(node.get_string());
+    }
+    break;
+  }
+}
+
+
+/**
+ * Convenience constructor.
+ */
+SGPropertyNode::SGPropertyNode (const char * name,
+                               int index,
+                               SGPropertyNode * parent)
+  : _display_name(0),
+    _index(index),
+    _parent(parent),
+    _path(0),
+    _path_cache(0),
+    _type(NONE),
+    _tied(false),
+    _attr(READ|WRITE),
+    _count(0),
+    _listeners(0)
+{
+  _name = copy_string(name);
+  _local_val.string_val = 0;
+}
+
+
+/**
+ * Destructor.
+ */
+SGPropertyNode::~SGPropertyNode ()
+{
+  delete [] _name;
+  delete [] _display_name;
+  delete [] _path;
+  delete _path_cache;
+  clear_value();
+  delete _listeners;
+}
+
+
+/**
+ * Alias to another node.
+ */
+bool
+SGPropertyNode::alias (SGPropertyNode * target)
+{
+  if (target == 0 || _type == ALIAS || _tied)
+    return false;
+  clear_value();
+  _value.alias = target;
+  _type = ALIAS;
+  return true;
+}
+
+
+/**
+ * Alias to another node by path.
+ */
+bool
+SGPropertyNode::alias (const char * path)
+{
+  return alias(getNode(path, true));
+}
+
+
+/**
+ * Remove an alias.
+ */
+bool
+SGPropertyNode::unalias ()
+{
+  if (_type != ALIAS)
+    return false;
+  _type = NONE;
+  _value.alias = 0;
+  return true;
+}
+
+
+/**
+ * Get the target of an alias.
+ */
+SGPropertyNode *
+SGPropertyNode::getAliasTarget ()
+{
+  return (_type == ALIAS ? _value.alias : 0);
+}
+
+
+const SGPropertyNode *
+SGPropertyNode::getAliasTarget () const
+{
+  return (_type == ALIAS ? _value.alias : 0);
+}
+
+
+/**
+ * Get a non-const child by index.
+ */
+SGPropertyNode *
+SGPropertyNode::getChild (int position)
+{
+  if (position >= 0 && position < nChildren())
+    return _children[position];
+  else
+    return 0;
+}
+
+
+/**
+ * Get a const child by index.
+ */
+const SGPropertyNode *
+SGPropertyNode::getChild (int position) const
+{
+  if (position >= 0 && position < nChildren())
+    return _children[position];
+  else
+    return 0;
+}
+
+
+/**
+ * Get a non-const child by name and index, creating if necessary.
+ */
+SGPropertyNode *
+SGPropertyNode::getChild (const char * name, int index, bool create)
+{
+  int pos = find_child(name, index, _children);
+  if (pos >= 0) {
+    return _children[pos];
+  } else if (create) {
+    SGPropertyNode_ptr node;
+    pos = find_child(name, index, _removedChildren);
+    if (pos >= 0) {
+      vector<SGPropertyNode_ptr>::iterator it = _removedChildren.begin();
+      it += pos;
+      node = _removedChildren[pos];
+      _removedChildren.erase(it);
+      node->setAttribute(REMOVED, false);
+    } else {
+      node = new SGPropertyNode(name, index, this);
+    }
+    _children.push_back(node);
+    fireChildAdded(node);
+    return node;
+  } else {
+    return 0;
+  }
+}
+
+
+/**
+ * Get a const child by name and index.
+ */
+const SGPropertyNode *
+SGPropertyNode::getChild (const char * name, int index) const
+{
+  int pos = find_child(name, index, _children);
+  if (pos >= 0)
+    return _children[pos];
+  else
+    return 0;
+}
+
+
+/**
+ * Get all children with the same name (but different indices).
+ */
+vector<SGPropertyNode_ptr>
+SGPropertyNode::getChildren (const char * name) const
+{
+  vector<SGPropertyNode_ptr> children;
+  int max = _children.size();
+
+  for (int i = 0; i < max; i++)
+    if (compare_strings(_children[i]->getName(), name))
+      children.push_back(_children[i]);
+
+  sort(children.begin(), children.end(), CompareIndices());
+  return children;
+}
+
+
+/**
+ * Remove a child node
+ */
+SGPropertyNode_ptr 
+SGPropertyNode::removeChild (const char * name, int index, bool keep)
+{
+  SGPropertyNode_ptr ret;
+  int pos = find_child(name, index, _children);
+  if (pos >= 0) {
+    vector<SGPropertyNode_ptr>::iterator it = _children.begin();
+    it += pos;
+    SGPropertyNode_ptr node = _children[pos];
+    _children.erase(it);
+    if (keep) {
+      _removedChildren.push_back(node);
+    }
+    node->setAttribute(REMOVED, true);
+    ret = node;
+    fireChildRemoved(node);
+  }
+  return ret;
+}
+
+
+const char *
+SGPropertyNode::getDisplayName (bool simplify) const
+{
+  string display = _name;
+  if (_index != 0 || !simplify) {
+    char buffer[64];
+    sprintf(buffer, "[%d]", _index);
+    display += buffer;
+  }
+  _display_name = copy_string(display.c_str());
+  return _display_name;
+}
+
+
+const char *
+SGPropertyNode::getPath (bool simplify) const
+{
+                               // Calculate the complete path only once.
+  if (_path == 0) {
+    string path;
+    if (_parent == 0) {
+      path = "";
+    } else {
+      path = _parent->getPath(simplify);
+      path += '/';
+      path += getDisplayName(simplify);
+    }
+    _path = copy_string(path.c_str());
+  }
+
+  return _path;
+}
+
+SGPropertyNode::Type
+SGPropertyNode::getType () const
+{
+  if (_type == ALIAS)
+    return _value.alias->getType();
+  else
+    return _type;
+}
+
+
+bool 
+SGPropertyNode::getBoolValue () const
+{
+                               // Shortcut for common case
+  if (_attr == (READ|WRITE) && _type == BOOL)
+    return get_bool();
+
+  if (getAttribute(TRACE_READ))
+    trace_read();
+  if (!getAttribute(READ))
+    return SGRawValue<bool>::DefaultValue;
+  switch (_type) {
+  case ALIAS:
+    return _value.alias->getBoolValue();
+  case BOOL:
+    return get_bool();
+  case INT:
+    return get_int() == 0 ? false : true;
+  case LONG:
+    return get_long() == 0L ? false : true;
+  case FLOAT:
+    return get_float() == 0.0 ? false : true;
+  case DOUBLE:
+    return get_double() == 0.0L ? false : true;
+  case STRING:
+  case UNSPECIFIED:
+    return (compare_strings(get_string(), "true") || getDoubleValue() != 0.0L);
+  case NONE:
+  default:
+    return SGRawValue<bool>::DefaultValue;
+  }
+}
+
+int 
+SGPropertyNode::getIntValue () const
+{
+                               // Shortcut for common case
+  if (_attr == (READ|WRITE) && _type == INT)
+    return get_int();
+
+  if (getAttribute(TRACE_READ))
+    trace_read();
+  if (!getAttribute(READ))
+    return SGRawValue<int>::DefaultValue;
+  switch (_type) {
+  case ALIAS:
+    return _value.alias->getIntValue();
+  case BOOL:
+    return int(get_bool());
+  case INT:
+    return get_int();
+  case LONG:
+    return int(get_long());
+  case FLOAT:
+    return int(get_float());
+  case DOUBLE:
+    return int(get_double());
+  case STRING:
+  case UNSPECIFIED:
+    return atoi(get_string());
+  case NONE:
+  default:
+    return SGRawValue<int>::DefaultValue;
+  }
+}
+
+long 
+SGPropertyNode::getLongValue () const
+{
+                               // Shortcut for common case
+  if (_attr == (READ|WRITE) && _type == LONG)
+    return get_long();
+
+  if (getAttribute(TRACE_READ))
+    trace_read();
+  if (!getAttribute(READ))
+    return SGRawValue<long>::DefaultValue;
+  switch (_type) {
+  case ALIAS:
+    return _value.alias->getLongValue();
+  case BOOL:
+    return long(get_bool());
+  case INT:
+    return long(get_int());
+  case LONG:
+    return get_long();
+  case FLOAT:
+    return long(get_float());
+  case DOUBLE:
+    return long(get_double());
+  case STRING:
+  case UNSPECIFIED:
+    return strtol(get_string(), 0, 0);
+  case NONE:
+  default:
+    return SGRawValue<long>::DefaultValue;
+  }
+}
+
+float 
+SGPropertyNode::getFloatValue () const
+{
+                               // Shortcut for common case
+  if (_attr == (READ|WRITE) && _type == FLOAT)
+    return get_float();
+
+  if (getAttribute(TRACE_READ))
+    trace_read();
+  if (!getAttribute(READ))
+    return SGRawValue<float>::DefaultValue;
+  switch (_type) {
+  case ALIAS:
+    return _value.alias->getFloatValue();
+  case BOOL:
+    return float(get_bool());
+  case INT:
+    return float(get_int());
+  case LONG:
+    return float(get_long());
+  case FLOAT:
+    return get_float();
+  case DOUBLE:
+    return float(get_double());
+  case STRING:
+  case UNSPECIFIED:
+    return atof(get_string());
+  case NONE:
+  default:
+    return SGRawValue<float>::DefaultValue;
+  }
+}
+
+double 
+SGPropertyNode::getDoubleValue () const
+{
+                               // Shortcut for common case
+  if (_attr == (READ|WRITE) && _type == DOUBLE)
+    return get_double();
+
+  if (getAttribute(TRACE_READ))
+    trace_read();
+  if (!getAttribute(READ))
+    return SGRawValue<double>::DefaultValue;
+
+  switch (_type) {
+  case ALIAS:
+    return _value.alias->getDoubleValue();
+  case BOOL:
+    return double(get_bool());
+  case INT:
+    return double(get_int());
+  case LONG:
+    return double(get_long());
+  case FLOAT:
+    return double(get_float());
+  case DOUBLE:
+    return get_double();
+  case STRING:
+  case UNSPECIFIED:
+    return strtod(get_string(), 0);
+  case NONE:
+  default:
+    return SGRawValue<double>::DefaultValue;
+  }
+}
+
+const char *
+SGPropertyNode::getStringValue () const
+{
+                               // Shortcut for common case
+  if (_attr == (READ|WRITE) && _type == STRING)
+    return get_string();
+
+  if (getAttribute(TRACE_READ))
+    trace_read();
+  if (!getAttribute(READ))
+    return SGRawValue<const char *>::DefaultValue;
+  return make_string();
+}
+
+bool
+SGPropertyNode::setBoolValue (bool value)
+{
+                               // Shortcut for common case
+  if (_attr == (READ|WRITE) && _type == BOOL)
+    return set_bool(value);
+
+  bool result = false;
+  TEST_WRITE;
+  if (_type == NONE || _type == UNSPECIFIED) {
+    clear_value();
+    _tied = false;
+    _type = BOOL;
+  }
+
+  switch (_type) {
+  case ALIAS:
+    result = _value.alias->setBoolValue(value);
+    break;
+  case BOOL:
+    result = set_bool(value);
+    break;
+  case INT:
+    result = set_int(int(value));
+    break;
+  case LONG:
+    result = set_long(long(value));
+    break;
+  case FLOAT:
+    result = set_float(float(value));
+    break;
+  case DOUBLE:
+    result = set_double(double(value));
+    break;
+  case STRING:
+  case UNSPECIFIED:
+    result = set_string(value ? "true" : "false");
+    break;
+  case NONE:
+  default:
+    break;
+  }
+
+  if (getAttribute(TRACE_WRITE))
+    trace_write();
+  return result;
+}
+
+bool
+SGPropertyNode::setIntValue (int value)
+{
+                               // Shortcut for common case
+  if (_attr == (READ|WRITE) && _type == INT)
+    return set_int(value);
+
+  bool result = false;
+  TEST_WRITE;
+  if (_type == NONE || _type == UNSPECIFIED) {
+    clear_value();
+    _type = INT;
+    _local_val.int_val = 0;
+  }
+
+  switch (_type) {
+  case ALIAS:
+    result = _value.alias->setIntValue(value);
+    break;
+  case BOOL:
+    result = set_bool(value == 0 ? false : true);
+    break;
+  case INT:
+    result = set_int(value);
+    break;
+  case LONG:
+    result = set_long(long(value));
+    break;
+  case FLOAT:
+    result = set_float(float(value));
+    break;
+  case DOUBLE:
+    result = set_double(double(value));
+    break;
+  case STRING:
+  case UNSPECIFIED: {
+    char buf[128];
+    sprintf(buf, "%d", value);
+    result = set_string(buf);
+    break;
+  }
+  case NONE:
+  default:
+    break;
+  }
+
+  if (getAttribute(TRACE_WRITE))
+    trace_write();
+  return result;
+}
+
+bool
+SGPropertyNode::setLongValue (long value)
+{
+                               // Shortcut for common case
+  if (_attr == (READ|WRITE) && _type == LONG)
+    return set_long(value);
+
+  bool result = false;
+  TEST_WRITE;
+  if (_type == NONE || _type == UNSPECIFIED) {
+    clear_value();
+    _type = LONG;
+    _local_val.long_val = 0L;
+  }
+
+  switch (_type) {
+  case ALIAS:
+    result = _value.alias->setLongValue(value);
+    break;
+  case BOOL:
+    result = set_bool(value == 0L ? false : true);
+    break;
+  case INT:
+    result = set_int(int(value));
+    break;
+  case LONG:
+    result = set_long(value);
+    break;
+  case FLOAT:
+    result = set_float(float(value));
+    break;
+  case DOUBLE:
+    result = set_double(double(value));
+    break;
+  case STRING:
+  case UNSPECIFIED: {
+    char buf[128];
+    sprintf(buf, "%ld", value);
+    result = set_string(buf);
+    break;
+  }
+  case NONE:
+  default:
+    break;
+  }
+
+  if (getAttribute(TRACE_WRITE))
+    trace_write();
+  return result;
+}
+
+bool
+SGPropertyNode::setFloatValue (float value)
+{
+                               // Shortcut for common case
+  if (_attr == (READ|WRITE) && _type == FLOAT)
+    return set_float(value);
+
+  bool result = false;
+  TEST_WRITE;
+  if (_type == NONE || _type == UNSPECIFIED) {
+    clear_value();
+    _type = FLOAT;
+    _local_val.float_val = 0;
+  }
+
+  switch (_type) {
+  case ALIAS:
+    result = _value.alias->setFloatValue(value);
+    break;
+  case BOOL:
+    result = set_bool(value == 0.0 ? false : true);
+    break;
+  case INT:
+    result = set_int(int(value));
+    break;
+  case LONG:
+    result = set_long(long(value));
+    break;
+  case FLOAT:
+    result = set_float(value);
+    break;
+  case DOUBLE:
+    result = set_double(double(value));
+    break;
+  case STRING:
+  case UNSPECIFIED: {
+    char buf[128];
+    sprintf(buf, "%f", value);
+    result = set_string(buf);
+    break;
+  }
+  case NONE:
+  default:
+    break;
+  }
+
+  if (getAttribute(TRACE_WRITE))
+    trace_write();
+  return result;
+}
+
+bool
+SGPropertyNode::setDoubleValue (double value)
+{
+                               // Shortcut for common case
+  if (_attr == (READ|WRITE) && _type == DOUBLE)
+    return set_double(value);
+
+  bool result = false;
+  TEST_WRITE;
+  if (_type == NONE || _type == UNSPECIFIED) {
+    clear_value();
+    _local_val.double_val = value;
+    _type = DOUBLE;
+  }
+
+  switch (_type) {
+  case ALIAS:
+    result = _value.alias->setDoubleValue(value);
+    break;
+  case BOOL:
+    result = set_bool(value == 0.0L ? false : true);
+    break;
+  case INT:
+    result = set_int(int(value));
+    break;
+  case LONG:
+    result = set_long(long(value));
+    break;
+  case FLOAT:
+    result = set_float(float(value));
+    break;
+  case DOUBLE:
+    result = set_double(value);
+    break;
+  case STRING:
+  case UNSPECIFIED: {
+    char buf[128];
+    sprintf(buf, "%f", value);
+    result = set_string(buf);
+    break;
+  }
+  case NONE:
+  default:
+    break;
+  }
+
+  if (getAttribute(TRACE_WRITE))
+    trace_write();
+  return result;
+}
+
+bool
+SGPropertyNode::setStringValue (const char * value)
+{
+                               // Shortcut for common case
+  if (_attr == (READ|WRITE) && _type == STRING)
+    return set_string(value);
+
+  bool result = false;
+  TEST_WRITE;
+  if (_type == NONE || _type == UNSPECIFIED) {
+    clear_value();
+    _type = STRING;
+  }
+
+  switch (_type) {
+  case ALIAS:
+    result = _value.alias->setStringValue(value);
+    break;
+  case BOOL:
+    result = set_bool((compare_strings(value, "true")
+                      || atoi(value)) ? true : false);
+    break;
+  case INT:
+    result = set_int(atoi(value));
+    break;
+  case LONG:
+    result = set_long(strtol(value, 0, 0));
+    break;
+  case FLOAT:
+    result = set_float(atof(value));
+    break;
+  case DOUBLE:
+    result = set_double(strtod(value, 0));
+    break;
+  case STRING:
+  case UNSPECIFIED:
+    result = set_string(value);
+    break;
+  case NONE:
+  default:
+    break;
+  }
+
+  if (getAttribute(TRACE_WRITE))
+    trace_write();
+  return result;
+}
+
+bool
+SGPropertyNode::setUnspecifiedValue (const char * value)
+{
+  bool result = false;
+  TEST_WRITE;
+  if (_type == NONE) {
+    clear_value();
+    _type = UNSPECIFIED;
+  }
+
+  switch (_type) {
+  case ALIAS:
+    result = _value.alias->setUnspecifiedValue(value);
+    break;
+  case BOOL:
+    result = set_bool((compare_strings(value, "true")
+                      || atoi(value)) ? true : false);
+    break;
+  case INT:
+    result = set_int(atoi(value));
+    break;
+  case LONG:
+    result = set_long(strtol(value, 0, 0));
+    break;
+  case FLOAT:
+    result = set_float(atof(value));
+    break;
+  case DOUBLE:
+    result = set_double(strtod(value, 0));
+    break;
+  case STRING:
+  case UNSPECIFIED:
+    result = set_string(value);
+    break;
+  case NONE:
+  default:
+    break;
+  }
+
+  if (getAttribute(TRACE_WRITE))
+    trace_write();
+  return result;
+}
+
+bool
+SGPropertyNode::tie (const SGRawValue<bool> &rawValue, bool useDefault)
+{
+  if (_type == ALIAS || _tied)
+    return false;
+
+  useDefault = useDefault && hasValue();
+  bool old_val = false;
+  if (useDefault)
+    old_val = getBoolValue();
+
+  clear_value();
+  _type = BOOL;
+  _tied = true;
+  _value.bool_val = rawValue.clone();
+
+  if (useDefault)
+    setBoolValue(old_val);
+
+  return true;
+}
+
+bool
+SGPropertyNode::tie (const SGRawValue<int> &rawValue, bool useDefault)
+{
+  if (_type == ALIAS || _tied)
+    return false;
+
+  useDefault = useDefault && hasValue();
+  int old_val = 0;
+  if (useDefault)
+    old_val = getIntValue();
+
+  clear_value();
+  _type = INT;
+  _tied = true;
+  _value.int_val = rawValue.clone();
+
+  if (useDefault)
+    setIntValue(old_val);
+
+  return true;
+}
+
+bool
+SGPropertyNode::tie (const SGRawValue<long> &rawValue, bool useDefault)
+{
+  if (_type == ALIAS || _tied)
+    return false;
+
+  useDefault = useDefault && hasValue();
+  long old_val = 0;
+  if (useDefault)
+    old_val = getLongValue();
+
+  clear_value();
+  _type = LONG;
+  _tied = true;
+  _value.long_val = rawValue.clone();
+
+  if (useDefault)
+    setLongValue(old_val);
+
+  return true;
+}
+
+bool
+SGPropertyNode::tie (const SGRawValue<float> &rawValue, bool useDefault)
+{
+  if (_type == ALIAS || _tied)
+    return false;
+
+  useDefault = useDefault && hasValue();
+  float old_val = 0.0;
+  if (useDefault)
+    old_val = getFloatValue();
+
+  clear_value();
+  _type = FLOAT;
+  _tied = true;
+  _value.float_val = rawValue.clone();
+
+  if (useDefault)
+    setFloatValue(old_val);
+
+  return true;
+}
+
+bool
+SGPropertyNode::tie (const SGRawValue<double> &rawValue, bool useDefault)
+{
+  if (_type == ALIAS || _tied)
+    return false;
+
+  useDefault = useDefault && hasValue();
+  double old_val = 0.0;
+  if (useDefault)
+    old_val = getDoubleValue();
+
+  clear_value();
+  _type = DOUBLE;
+  _tied = true;
+  _value.double_val = rawValue.clone();
+
+  if (useDefault)
+    setDoubleValue(old_val);
+
+  return true;
+
+}
+
+bool
+SGPropertyNode::tie (const SGRawValue<const char *> &rawValue, bool useDefault)
+{
+  if (_type == ALIAS || _tied)
+    return false;
+
+  useDefault = useDefault && hasValue();
+  string old_val;
+  if (useDefault)
+    old_val = getStringValue();
+
+  clear_value();
+  _type = STRING;
+  _tied = true;
+  _value.string_val = rawValue.clone();
+
+  if (useDefault)
+    setStringValue(old_val.c_str());
+
+  return true;
+}
+
+bool
+SGPropertyNode::untie ()
+{
+  if (!_tied)
+    return false;
+
+  switch (_type) {
+  case BOOL: {
+    bool val = getBoolValue();
+    clear_value();
+    _type = BOOL;
+    _local_val.bool_val = val;
+    break;
+  }
+  case INT: {
+    int val = getIntValue();
+    clear_value();
+    _type = INT;
+    _local_val.int_val = val;
+    break;
+  }
+  case LONG: {
+    long val = getLongValue();
+    clear_value();
+    _type = LONG;
+    _local_val.long_val = val;
+    break;
+  }
+  case FLOAT: {
+    float val = getFloatValue();
+    clear_value();
+    _type = FLOAT;
+    _local_val.float_val = val;
+    break;
+  }
+  case DOUBLE: {
+    double val = getDoubleValue();
+    clear_value();
+    _type = DOUBLE;
+    _local_val.double_val = val;
+    break;
+  }
+  case STRING:
+  case UNSPECIFIED: {
+    string val = getStringValue();
+    clear_value();
+    _type = STRING;
+    _local_val.string_val = copy_string(val.c_str());
+    break;
+  }
+  case NONE:
+  default:
+    break;
+  }
+
+  _tied = false;
+  return true;
+}
+
+SGPropertyNode *
+SGPropertyNode::getRootNode ()
+{
+  if (_parent == 0)
+    return this;
+  else
+    return _parent->getRootNode();
+}
+
+const SGPropertyNode *
+SGPropertyNode::getRootNode () const
+{
+  if (_parent == 0)
+    return this;
+  else
+    return _parent->getRootNode();
+}
+
+SGPropertyNode *
+SGPropertyNode::getNode (const char * relative_path, bool create)
+{
+  if (_path_cache == 0)
+    _path_cache = new hash_table;
+
+  SGPropertyNode * result = _path_cache->get(relative_path);
+  if (result == 0) {
+    vector<PathComponent> components;
+    parse_path(relative_path, components);
+    result = find_node(this, components, 0, create);
+    if (result != 0)
+      _path_cache->put(relative_path, result);
+  }
+
+  return result;
+}
+
+SGPropertyNode *
+SGPropertyNode::getNode (const char * relative_path, int index, bool create)
+{
+  vector<PathComponent> components;
+  parse_path(relative_path, components);
+  if (components.size() > 0)
+    components[components.size()-1].index = index;
+  return find_node(this, components, 0, create);
+}
+
+const SGPropertyNode *
+SGPropertyNode::getNode (const char * relative_path) const
+{
+  return ((SGPropertyNode *)this)->getNode(relative_path, false);
+}
+
+const SGPropertyNode *
+SGPropertyNode::getNode (const char * relative_path, int index) const
+{
+  return ((SGPropertyNode *)this)->getNode(relative_path, index, false);
+}
+
+\f
+////////////////////////////////////////////////////////////////////////
+// Convenience methods using relative paths.
+////////////////////////////////////////////////////////////////////////
+
+
+/**
+ * Test whether another node has a value attached.
+ */
+bool
+SGPropertyNode::hasValue (const char * relative_path) const
+{
+  const SGPropertyNode * node = getNode(relative_path);
+  return (node == 0 ? false : node->hasValue());
+}
+
+
+/**
+ * Get the value type for another node.
+ */
+SGPropertyNode::Type
+SGPropertyNode::getType (const char * relative_path) const
+{
+  const SGPropertyNode * node = getNode(relative_path);
+  return (node == 0 ? UNSPECIFIED : (Type)(node->getType()));
+}
+
+
+/**
+ * Get a bool value for another node.
+ */
+bool
+SGPropertyNode::getBoolValue (const char * relative_path,
+                             bool defaultValue) const
+{
+  const SGPropertyNode * node = getNode(relative_path);
+  return (node == 0 ? defaultValue : node->getBoolValue());
+}
+
+
+/**
+ * Get an int value for another node.
+ */
+int
+SGPropertyNode::getIntValue (const char * relative_path,
+                            int defaultValue) const
+{
+  const SGPropertyNode * node = getNode(relative_path);
+  return (node == 0 ? defaultValue : node->getIntValue());
+}
+
+
+/**
+ * Get a long value for another node.
+ */
+long
+SGPropertyNode::getLongValue (const char * relative_path,
+                             long defaultValue) const
+{
+  const SGPropertyNode * node = getNode(relative_path);
+  return (node == 0 ? defaultValue : node->getLongValue());
+}
+
+
+/**
+ * Get a float value for another node.
+ */
+float
+SGPropertyNode::getFloatValue (const char * relative_path,
+                              float defaultValue) const
+{
+  const SGPropertyNode * node = getNode(relative_path);
+  return (node == 0 ? defaultValue : node->getFloatValue());
+}
+
+
+/**
+ * Get a double value for another node.
+ */
+double
+SGPropertyNode::getDoubleValue (const char * relative_path,
+                               double defaultValue) const
+{
+  const SGPropertyNode * node = getNode(relative_path);
+  return (node == 0 ? defaultValue : node->getDoubleValue());
+}
+
+
+/**
+ * Get a string value for another node.
+ */
+const char *
+SGPropertyNode::getStringValue (const char * relative_path,
+                               const char * defaultValue) const
+{
+  const SGPropertyNode * node = getNode(relative_path);
+  return (node == 0 ? defaultValue : node->getStringValue());
+}
+
+
+/**
+ * Set a bool value for another node.
+ */
+bool
+SGPropertyNode::setBoolValue (const char * relative_path, bool value)
+{
+  return getNode(relative_path, true)->setBoolValue(value);
+}
+
+
+/**
+ * Set an int value for another node.
+ */
+bool
+SGPropertyNode::setIntValue (const char * relative_path, int value)
+{
+  return getNode(relative_path, true)->setIntValue(value);
+}
+
+
+/**
+ * Set a long value for another node.
+ */
+bool
+SGPropertyNode::setLongValue (const char * relative_path, long value)
+{
+  return getNode(relative_path, true)->setLongValue(value);
+}
+
+
+/**
+ * Set a float value for another node.
+ */
+bool
+SGPropertyNode::setFloatValue (const char * relative_path, float value)
+{
+  return getNode(relative_path, true)->setFloatValue(value);
+}
+
+
+/**
+ * Set a double value for another node.
+ */
+bool
+SGPropertyNode::setDoubleValue (const char * relative_path, double value)
+{
+  return getNode(relative_path, true)->setDoubleValue(value);
+}
+
+
+/**
+ * Set a string value for another node.
+ */
+bool
+SGPropertyNode::setStringValue (const char * relative_path, const char * value)
+{
+  return getNode(relative_path, true)->setStringValue(value);
+}
+
+
+/**
+ * Set an unknown value for another node.
+ */
+bool
+SGPropertyNode::setUnspecifiedValue (const char * relative_path,
+                                    const char * value)
+{
+  return getNode(relative_path, true)->setUnspecifiedValue(value);
+}
+
+
+/**
+ * Test whether another node is tied.
+ */
+bool
+SGPropertyNode::isTied (const char * relative_path) const
+{
+  const SGPropertyNode * node = getNode(relative_path);
+  return (node == 0 ? false : node->isTied());
+}
+
+
+/**
+ * Tie a node reached by a relative path, creating it if necessary.
+ */
+bool
+SGPropertyNode::tie (const char * relative_path,
+                    const SGRawValue<bool> &rawValue,
+                    bool useDefault)
+{
+  return getNode(relative_path, true)->tie(rawValue, useDefault);
+}
+
+
+/**
+ * Tie a node reached by a relative path, creating it if necessary.
+ */
+bool
+SGPropertyNode::tie (const char * relative_path,
+                    const SGRawValue<int> &rawValue,
+                    bool useDefault)
+{
+  return getNode(relative_path, true)->tie(rawValue, useDefault);
+}
+
+
+/**
+ * Tie a node reached by a relative path, creating it if necessary.
+ */
+bool
+SGPropertyNode::tie (const char * relative_path,
+                    const SGRawValue<long> &rawValue,
+                    bool useDefault)
+{
+  return getNode(relative_path, true)->tie(rawValue, useDefault);
+}
+
+
+/**
+ * Tie a node reached by a relative path, creating it if necessary.
+ */
+bool
+SGPropertyNode::tie (const char * relative_path,
+                    const SGRawValue<float> &rawValue,
+                    bool useDefault)
+{
+  return getNode(relative_path, true)->tie(rawValue, useDefault);
+}
+
+
+/**
+ * Tie a node reached by a relative path, creating it if necessary.
+ */
+bool
+SGPropertyNode::tie (const char * relative_path,
+                    const SGRawValue<double> &rawValue,
+                    bool useDefault)
+{
+  return getNode(relative_path, true)->tie(rawValue, useDefault);
+}
+
+
+/**
+ * Tie a node reached by a relative path, creating it if necessary.
+ */
+bool
+SGPropertyNode::tie (const char * relative_path,
+                    const SGRawValue<const char *> &rawValue,
+                    bool useDefault)
+{
+  return getNode(relative_path, true)->tie(rawValue, useDefault);
+}
+
+
+/**
+ * Attempt to untie another node reached by a relative path.
+ */
+bool
+SGPropertyNode::untie (const char * relative_path)
+{
+  SGPropertyNode * node = getNode(relative_path);
+  return (node == 0 ? false : node->untie());
+}
+
+void
+SGPropertyNode::addChangeListener (SGPropertyChangeListener * listener)
+{
+  if (_listeners == 0)
+    _listeners = new vector<SGPropertyChangeListener *>;
+  _listeners->push_back(listener);
+  listener->register_property(this);
+}
+
+void
+SGPropertyNode::removeChangeListener (SGPropertyChangeListener * listener)
+{
+  vector<SGPropertyChangeListener *>::iterator it =
+    find(_listeners->begin(), _listeners->end(), listener);
+  if (it != _listeners->end()) {
+    _listeners->erase(it);
+    listener->unregister_property(this);
+    if (_listeners->empty()) {
+      vector<SGPropertyChangeListener *> * tmp = _listeners;
+      _listeners = 0;
+      delete tmp;
+    }
+  }
+}
+
+void
+SGPropertyNode::fireValueChanged ()
+{
+  fireValueChanged(this);
+}
+
+void
+SGPropertyNode::fireChildAdded (SGPropertyNode * child)
+{
+  fireChildAdded(this, child);
+}
+
+void
+SGPropertyNode::fireChildRemoved (SGPropertyNode * child)
+{
+  fireChildRemoved(this, child);
+}
+
+void
+SGPropertyNode::fireValueChanged (SGPropertyNode * node)
+{
+  if (_listeners != 0) {
+    for (unsigned int i = 0; i < _listeners->size(); i++) {
+      (*_listeners)[i]->valueChanged(node);
+    }
+  }
+  if (_parent != 0)
+    _parent->fireValueChanged(node);
+}
+
+void
+SGPropertyNode::fireChildAdded (SGPropertyNode * parent,
+                               SGPropertyNode * child)
+{
+  if (_listeners != 0) {
+    for (unsigned int i = 0; i < _listeners->size(); i++) {
+      (*_listeners)[i]->childAdded(parent, child);
+    }
+  }
+  if (_parent != 0)
+    _parent->fireChildAdded(parent, child);
+}
+
+void
+SGPropertyNode::fireChildRemoved (SGPropertyNode * parent,
+                                 SGPropertyNode * child)
+{
+  if (_listeners != 0) {
+    for (unsigned int i = 0; i < _listeners->size(); i++) {
+      (*_listeners)[i]->childRemoved(parent, child);
+    }
+  }
+  if (_parent != 0)
+    _parent->fireChildRemoved(parent, child);
+}
+
+
+\f
+////////////////////////////////////////////////////////////////////////
+// Simplified hash table for caching paths.
+////////////////////////////////////////////////////////////////////////
+
+#define HASH_TABLE_SIZE 199
+
+SGPropertyNode::hash_table::entry::entry ()
+  : _key(0),
+    _value(0)
+{
+}
+
+SGPropertyNode::hash_table::entry::~entry ()
+{
+                               // Don't delete the value; we don't own
+                               // the pointer.
+  delete [] _key;
+}
+
+void
+SGPropertyNode::hash_table::entry::set_key (const char * key)
+{
+  _key = copy_string(key);
+}
+
+void
+SGPropertyNode::hash_table::entry::set_value (SGPropertyNode * value)
+{
+  _value = value;
+}
+
+SGPropertyNode::hash_table::bucket::bucket ()
+  : _length(0),
+    _entries(0)
+{
+}
+
+SGPropertyNode::hash_table::bucket::~bucket ()
+{
+  for (int i = 0; i < _length; i++)
+    delete _entries[i];
+}
+
+SGPropertyNode::hash_table::entry *
+SGPropertyNode::hash_table::bucket::get_entry (const char * key, bool create)
+{
+  int i;
+  for (i = 0; i < _length; i++) {
+    if (!strcmp(_entries[i]->get_key(), key))
+      return _entries[i];
+  }
+  if (create) {
+    entry ** new_entries = new entry*[_length+1];
+    for (i = 0; i < _length; i++) {
+      new_entries[i] = _entries[i];
+    }
+    delete [] _entries;
+    _entries = new_entries;
+    _entries[_length] = new entry;
+    _entries[_length]->set_key(key);
+    _length++;
+    return _entries[_length - 1];
+  } else {
+    return 0;
+  }
+}
+
+
+SGPropertyNode::hash_table::hash_table ()
+  : _data_length(0),
+    _data(0)
+{
+}
+
+SGPropertyNode::hash_table::~hash_table ()
+{
+  for (unsigned int i = 0; i < _data_length; i++)
+    delete _data[i];
+}
+
+SGPropertyNode *
+SGPropertyNode::hash_table::get (const char * key)
+{
+  if (_data_length == 0)
+    return 0;
+  unsigned int index = hashcode(key) % _data_length;
+  if (_data[index] == 0)
+    return 0;
+  entry * e = _data[index]->get_entry(key);
+  if (e == 0)
+    return 0;
+  else
+    return e->get_value();
+}
+
+void
+SGPropertyNode::hash_table::put (const char * key, SGPropertyNode * value)
+{
+  if (_data_length == 0) {
+    _data = new bucket*[HASH_TABLE_SIZE];
+    _data_length = HASH_TABLE_SIZE;
+    for (unsigned int i = 0; i < HASH_TABLE_SIZE; i++)
+      _data[i] = 0;
+  }
+  unsigned int index = hashcode(key) % _data_length;
+  if (_data[index] == 0) {
+    _data[index] = new bucket;
+  }
+  entry * e = _data[index]->get_entry(key, true);
+  e->set_value(value);
+}
+
+unsigned int
+SGPropertyNode::hash_table::hashcode (const char * key)
+{
+  unsigned int hash = 0;
+  while (*key != 0) {
+    hash = 31 * hash + *key;
+    key++;
+  }
+  return hash;
+}
+
+
+
+/**
+ * Default constructor
+ */
+SGPropertyNode_ptr::SGPropertyNode_ptr()
+{
+  _ptr = 0;
+}
+
+/**
+ * Copy constructor
+ */
+SGPropertyNode_ptr::SGPropertyNode_ptr( const SGPropertyNode_ptr &r )
+{
+  _ptr = r._ptr;
+  if (_ptr)
+     _ptr->incrementRef();
+}
+
+/**
+ * Constructor from a pointer to a node
+ */
+SGPropertyNode_ptr::SGPropertyNode_ptr( SGPropertyNode *p )
+{
+  _ptr = p;
+  if (_ptr)
+     _ptr->incrementRef();
+}
+
+/**
+ * Destructor
+ */
+SGPropertyNode_ptr::~SGPropertyNode_ptr()
+{
+  if (_ptr && _ptr->decrementRef() == 0)
+    delete _ptr;
+}
+
+/**
+ * Assignement operator
+ */
+SGPropertyNode_ptr &
+SGPropertyNode_ptr::operator=( const SGPropertyNode_ptr &r )
+{
+  if (_ptr && _ptr->decrementRef() == 0)
+    delete _ptr;
+  _ptr = r._ptr;
+  if (_ptr)
+     _ptr->incrementRef();
+
+  return *this;
+}
+
+/**
+ * Pointer access operator
+ */
+SGPropertyNode *
+SGPropertyNode_ptr::operator->()
+{
+  return _ptr;
+}
+
+/**
+ * Pointer access operator (const)
+ */
+const SGPropertyNode *
+SGPropertyNode_ptr::operator->() const
+{
+  return _ptr;
+}
+
+/**
+ * Conversion to SGPropertyNode * operator
+ */
+SGPropertyNode_ptr::operator SGPropertyNode *()
+{
+  return _ptr;
+}
+
+/**
+ * Conversion to const SGPropertyNode * operator
+ */
+SGPropertyNode_ptr::operator const SGPropertyNode *() const
+{
+  return _ptr;
+}
+
+/**
+ * Validity test
+ */
+bool 
+SGPropertyNode_ptr::valid() const
+{
+  return _ptr != 0;
+}
+
+
+\f
+////////////////////////////////////////////////////////////////////////
+// Implementation of SGPropertyChangeListener.
+////////////////////////////////////////////////////////////////////////
+
+SGPropertyChangeListener::~SGPropertyChangeListener ()
+{
+                               // This will come back and remove
+                               // the current item each time.  Is
+                               // that OK?
+  vector<SGPropertyNode *>::iterator it;
+  for (it = _properties.begin(); it != _properties.end(); it++)
+    (*it)->removeChangeListener(this);
+}
+
+void
+SGPropertyChangeListener::valueChanged (SGPropertyNode * node)
+{
+  // NO-OP
+}
+
+void
+SGPropertyChangeListener::childAdded (SGPropertyNode * node,
+                                     SGPropertyNode * child)
+{
+  // NO-OP
+}
+
+void
+SGPropertyChangeListener::childRemoved (SGPropertyNode * parent,
+                                       SGPropertyNode * child)
+{
+  // NO-OP
+}
+
+void
+SGPropertyChangeListener::register_property (SGPropertyNode * node)
+{
+  _properties.push_back(node);
+}
+
+void
+SGPropertyChangeListener::unregister_property (SGPropertyNode * node)
+{
+  vector<SGPropertyNode *>::iterator it =
+    find(_properties.begin(), _properties.end(), node);
+  if (it != _properties.end())
+    _properties.erase(it);
+}
+
+
+// end of props.cxx
diff --git a/simgear/props/props.hxx b/simgear/props/props.hxx
new file mode 100644 (file)
index 0000000..a042c61
--- /dev/null
@@ -0,0 +1,1337 @@
+/**
+ * \file props.hxx
+ * Interface definition for a property list.
+ * Started Fall 2000 by David Megginson, david@megginson.com
+ * This code is released into the Public Domain.
+ *
+ * See props.html for documentation [replace with URL when available].
+ *
+ * $Id$
+ */
+
+#ifndef __PROPS_HXX
+#define __PROPS_HXX
+
+#ifndef PROPS_STANDALONE
+#define PROPS_STANDALONE 0
+#endif
+
+#include <vector>
+
+#if PROPS_STANDALONE
+
+#include <string>
+#include <iostream>
+
+using std::string;
+using std::vector;
+using std::istream;
+using std::ostream;
+
+#else
+
+#include <simgear/compiler.h>
+#include <simgear/debug/logstream.hxx>
+#include STL_STRING
+#include STL_IOSTREAM
+SG_USING_STD(string);
+SG_USING_STD(vector);
+SG_USING_STD(istream);
+SG_USING_STD(ostream);
+
+#endif
+
+
+#ifdef NONE
+#pragma warn A sloppy coder has defined NONE as a macro!
+#undef NONE
+#endif
+
+#ifdef ALIAS
+#pragma warn A sloppy coder has defined ALIAS as a macro!
+#undef ALIAS
+#endif
+
+#ifdef UNSPECIFIED
+#pragma warn A sloppy coder has defined UNSPECIFIED as a macro!
+#undef UNSPECIFIED
+#endif
+
+#ifdef BOOL
+#pragma warn A sloppy coder has defined BOOL as a macro!
+#undef BOOL
+#endif
+
+#ifdef INT
+#pragma warn A sloppy coder has defined INT as a macro!
+#undef INT
+#endif
+
+#ifdef LONG
+#pragma warn A sloppy coder has defined LONG as a macro!
+#undef LONG
+#endif
+
+#ifdef FLOAT
+#pragma warn A sloppy coder has defined FLOAT as a macro!
+#undef FLOAT
+#endif
+
+#ifdef DOUBLE
+#pragma warn A sloppy coder has defined DOUBLE as a macro!
+#undef DOUBLE
+#endif
+
+#ifdef STRING
+#pragma warn A sloppy coder has defined STRING as a macro!
+#undef STRING
+#endif
+
+
+\f
+////////////////////////////////////////////////////////////////////////
+// A raw value.
+//
+// This is the mechanism that information-providing routines can
+// use to link their own values to the property manager.  Any
+// SGValue can be tied to a raw value and then untied again.
+//
+// Note: we are forced to use inlined methods here to ensure
+// that the templates will be instantiated.  We're probably taking
+// a small performance hit for that.
+////////////////////////////////////////////////////////////////////////
+
+
+/**
+ * Abstract base class for a raw value.
+ *
+ * <p>The property manager is implemented in two layers.  The {@link
+ * SGPropertyNode} is the highest and most abstract layer,
+ * representing an LValue/RValue pair: it records the position of the
+ * property in the property tree and contains facilities for
+ * navigation to other nodes.  It is guaranteed to be persistent: the
+ * {@link SGPropertyNode} will not change during a session, even if
+ * the property is bound and unbound multiple times.</p>
+ *
+ * <p>When the property value is not managed internally in the
+ * SGPropertyNode, the SGPropertyNode will contain a reference to an
+ * SGRawValue (this class), which provides an abstract way to get,
+ * set, and clone the underlying value.  The SGRawValue may change
+ * frequently during a session as a value is retyped or bound and
+ * unbound to various data source, but the abstract SGPropertyNode
+ * layer insulates the application from those changes.  The raw value
+ * contains no facilities for data binding or for type conversion: it
+ * is simply the abstraction of a primitive data type (or a compound
+ * data type, in the case of a string).</p>
+ *
+ * <p>The SGPropertyNode class always keeps a *copy* of a raw value,
+ * not the original one passed to it; if you override a derived class
+ * but do not replace the {@link #clone} method, strange things will
+ * happen.</p>
+ *
+ * <p>All derived SGRawValue classes must implement {@link #getValue},
+ * {@link #setValue}, and {@link #clone} for the appropriate type.</p>
+ *
+ * @see SGPropertyNode
+ * @see SGRawValuePointer
+ * @see SGRawValueFunctions
+ * @see SGRawValueFunctionsIndexed
+ * @see SGRawValueMethods
+ * @see SGRawValueMethodsIndexed
+ */
+template <class T>
+class SGRawValue
+{
+public:
+
+  /**
+   * The default underlying value for this type.
+   *
+   * Every raw value has a default; the default is false for a
+   * boolean, 0 for the various numeric values, and "" for a string.
+   * If additional types of raw values are added in the future, they
+   * may need different kinds of default values (such as epoch for a
+   * date type).  The default value is used when creating new values.
+   */
+  static const T DefaultValue; // Default for this kind of raw value.
+
+
+  /**
+   * Constructor.
+   *
+   * Use the default value for this type.
+   */
+  SGRawValue () {}
+
+
+  /**
+   * Destructor.
+   */
+  virtual ~SGRawValue () {}
+
+
+  /**
+   * Return the underlying value.
+   *
+   * @return The actual value for the property.
+   * @see #setValue
+   */
+  virtual T getValue () const = 0;
+
+
+  /**
+   * Assign a new underlying value.
+   *
+   * If the new value cannot be set (because this is a read-only
+   * raw value, or because the new value is not acceptable for
+   * some reason) this method returns false and leaves the original
+   * value unchanged.
+   *
+   * @param value The actual value for the property.
+   * @return true if the value was set successfully, false otherwise.
+   * @see #getValue
+   */
+  virtual bool setValue (T value) = 0;
+
+
+  /**
+   * Create a new deep copy of this raw value.
+   *
+   * The copy will contain its own version of the underlying value
+   * as well.
+   *
+   * @return A deep copy of the current object.
+   */
+  virtual SGRawValue * clone () const = 0;
+};
+
+
+/**
+ * A raw value bound to a pointer.
+ *
+ * This is the most efficient way to tie an external value, but also
+ * the most dangerous, because there is no way for the supplier to
+ * perform bounds checking and derived calculations except by polling
+ * the variable to see if it has changed.  There is no default
+ * constructor, because this class would be meaningless without a
+ * pointer.
+ */
+template <class T>
+class SGRawValuePointer : public SGRawValue<T>
+{
+public:
+
+  /**
+   * Explicit pointer constructor.
+   *
+   * Create a new raw value bound to the value of the variable
+   * referenced by the pointer.
+   *
+   * @param ptr The pointer to the variable to which this raw value
+   * will be bound.
+   */
+  SGRawValuePointer (T * ptr) : _ptr(ptr) {}
+
+  /**
+   * Destructor.
+   */
+  virtual ~SGRawValuePointer () {}
+
+  /**
+   * Get the underlying value.
+   *
+   * This method will dereference the pointer and return the
+   * variable's value.
+   */
+  virtual T getValue () const { return *_ptr; }
+
+  /**
+   * Set the underlying value.
+   *
+   * This method will dereference the pointer and change the
+   * variable's value.
+   */
+  virtual bool setValue (T value) { *_ptr = value; return true; }
+
+  /**
+   * Create a copy of this raw value.
+   *
+   * The copy will use the same external pointer as the original.
+   */
+  virtual SGRawValue<T> * clone () const {
+    return new SGRawValuePointer<T>(_ptr);
+  }
+
+private:
+  T * _ptr;
+};
+
+
+/**
+ * A value managed through static functions.
+ *
+ * A read-only value will not have a setter; a write-only value will
+ * not have a getter.
+ */
+template <class T>
+class SGRawValueFunctions : public SGRawValue<T>
+{
+public:
+
+  /**
+   * The template type of a static getter function.
+   */
+  typedef T (*getter_t)();
+
+  /**
+   * The template type of a static setter function.
+   */
+  typedef void (*setter_t)(T);
+
+  /**
+   * Explicit constructor.
+   *
+   * Create a new raw value bound to the getter and setter supplied.
+   *
+   * @param getter A static function for getting a value, or 0
+   * to read-disable the value.
+   * @param setter A static function for setting a value, or 0
+   * to write-disable the value.
+   */
+  SGRawValueFunctions (getter_t getter = 0, setter_t setter = 0)
+    : _getter(getter), _setter(setter) {}
+
+  /**
+   * Destructor.
+   */
+  virtual ~SGRawValueFunctions () {}
+
+  /**
+   * Get the underlying value.
+   *
+   * This method will invoke the getter function to get a value.
+   * If no getter function was supplied, this method will always
+   * return the default value for the type.
+   */
+  virtual T getValue () const {
+    if (_getter) return (*_getter)();
+    else return SGRawValue<T>::DefaultValue;
+  }
+
+  /**
+   * Set the underlying value.
+   *
+   * This method will invoke the setter function to change the
+   * underlying value.  If no setter function was supplied, this
+   * method will return false.
+   */
+  virtual bool setValue (T value) {
+    if (_setter) { (*_setter)(value); return true; }
+    else return false;
+  }
+
+  /**
+   * Create a copy of this raw value, bound to the same functions.
+   */
+  virtual SGRawValue<T> * clone () const {
+    return new SGRawValueFunctions<T>(_getter,_setter);
+  }
+
+private:
+  getter_t _getter;
+  setter_t _setter;
+};
+
+
+/**
+ * An indexed value bound to static functions.
+ *
+ * A read-only value will not have a setter; a write-only value will
+ * not have a getter.  An indexed value is useful for binding one
+ * of a list of possible values (such as multiple engines for a
+ * plane).  The index is hard-coded at creation time.
+ *
+ * @see SGRawValue
+ */
+template <class T>
+class SGRawValueFunctionsIndexed : public SGRawValue<T>
+{
+public:
+  typedef T (*getter_t)(int);
+  typedef void (*setter_t)(int,T);
+  SGRawValueFunctionsIndexed (int index, getter_t getter = 0, setter_t setter = 0)
+    : _index(index), _getter(getter), _setter(setter) {}
+  virtual ~SGRawValueFunctionsIndexed () {}
+  virtual T getValue () const {
+    if (_getter) return (*_getter)(_index);
+    else return SGRawValue<T>::DefaultValue;
+  }
+  virtual bool setValue (T value) {
+    if (_setter) { (*_setter)(_index, value); return true; }
+    else return false;
+  }
+  virtual SGRawValue<T> * clone () const {
+    return new SGRawValueFunctionsIndexed<T>(_index, _getter, _setter);
+  }
+private:
+  int _index;
+  getter_t _getter;
+  setter_t _setter;
+};
+
+
+/**
+ * A value managed through an object and access methods.
+ *
+ * A read-only value will not have a setter; a write-only value will
+ * not have a getter.
+ */
+template <class C, class T>
+class SGRawValueMethods : public SGRawValue<T>
+{
+public:
+  typedef T (C::*getter_t)() const;
+  typedef void (C::*setter_t)(T);
+  SGRawValueMethods (C &obj, getter_t getter = 0, setter_t setter = 0)
+    : _obj(obj), _getter(getter), _setter(setter) {}
+  virtual ~SGRawValueMethods () {}
+  virtual T getValue () const {
+    if (_getter) { return (_obj.*_getter)(); }
+    else { return SGRawValue<T>::DefaultValue; }
+  }
+  virtual bool setValue (T value) {
+    if (_setter) { (_obj.*_setter)(value); return true; }
+    else return false;
+  }
+  virtual SGRawValue<T> * clone () const {
+    return new SGRawValueMethods<C,T>(_obj, _getter, _setter);
+  }
+private:
+  C &_obj;
+  getter_t _getter;
+  setter_t _setter;
+};
+
+
+/**
+ * An indexed value managed through an object and access methods.
+ *
+ * A read-only value will not have a setter; a write-only value will
+ * not have a getter.
+ */
+template <class C, class T>
+class SGRawValueMethodsIndexed : public SGRawValue<T>
+{
+public:
+  typedef T (C::*getter_t)(int) const;
+  typedef void (C::*setter_t)(int, T);
+  SGRawValueMethodsIndexed (C &obj, int index,
+                    getter_t getter = 0, setter_t setter = 0)
+    : _obj(obj), _index(index), _getter(getter), _setter(setter) {}
+  virtual ~SGRawValueMethodsIndexed () {}
+  virtual T getValue () const {
+    if (_getter) { return (_obj.*_getter)(_index); }
+    else { return SGRawValue<T>::DefaultValue; }
+  }
+  virtual bool setValue (T value) {
+    if (_setter) { (_obj.*_setter)(_index, value); return true; }
+    else return false;
+  }
+  virtual SGRawValue<T> * clone () const {
+    return new SGRawValueMethodsIndexed<C,T>(_obj, _index, _getter, _setter);
+  }
+private:
+  C &_obj;
+  int _index;
+  getter_t _getter;
+  setter_t _setter;
+};
+
+\f
+/**
+ * The smart pointer that manage reference counting
+ */
+class SGPropertyNode;
+class SGPropertyNode_ptr
+{
+public:
+
+  /**
+   * Default constructor
+   */
+  SGPropertyNode_ptr();
+
+  /**
+   * Copy constructor
+   */
+  SGPropertyNode_ptr( const SGPropertyNode_ptr &r );
+
+  /**
+   * Constructor from a pointer to a node
+   */
+  SGPropertyNode_ptr( SGPropertyNode *p );
+
+  /**
+   * Destructor
+   */
+  ~SGPropertyNode_ptr();
+
+  /**
+   * Assignement operator
+   */
+  SGPropertyNode_ptr &operator=( const SGPropertyNode_ptr &r );
+
+  /**
+   * Pointer access operator
+   */
+  SGPropertyNode *operator->();
+
+  /**
+   * Pointer access operator (const)
+   */
+  const SGPropertyNode *operator->() const;
+
+  /**
+   * Conversion to SGPropertyNode * operator
+   */
+  operator SGPropertyNode *();
+
+  /**
+   * Conversion to const SGPropertyNode * operator
+   */
+  operator const SGPropertyNode *() const;
+
+  /**
+   * Return the pointer.
+   */
+  SGPropertyNode * ptr () { return _ptr; }
+
+  /**
+   * Validity test
+   */
+  bool valid() const;
+
+private:
+
+  SGPropertyNode *_ptr;
+};
+
+
+\f
+/**
+ * The property change listener interface.
+ *
+ * <p>Any class that needs to listen for property changes must implement
+ * this interface.</p>
+ */
+class SGPropertyChangeListener
+{
+public:
+  virtual ~SGPropertyChangeListener ();
+  virtual void valueChanged (SGPropertyNode * node);
+  virtual void childAdded (SGPropertyNode * parent, SGPropertyNode * child);
+  virtual void childRemoved (SGPropertyNode * parent, SGPropertyNode * child);
+
+protected:
+  friend class SGPropertyNode;
+  virtual void register_property (SGPropertyNode * node);
+  virtual void unregister_property (SGPropertyNode * node);
+
+private:
+  vector<SGPropertyNode *> _properties;
+};
+
+
+\f
+/**
+ * A node in a property tree.
+ */
+class SGPropertyNode
+{
+public:
+
+  /**
+   * Public constants.
+   */
+  enum {
+    MAX_STRING_LEN = 1024
+  };
+
+  /**
+   * Property value types.
+   */
+  enum Type {
+    NONE,
+    ALIAS,
+    BOOL,
+    INT,
+    LONG,
+    FLOAT,
+    DOUBLE,
+    STRING,
+    UNSPECIFIED
+  };
+
+
+  /**
+   * Access mode attributes.
+   *
+   * <p>The ARCHIVE attribute is strictly advisory, and controls
+   * whether the property should normally be saved and restored.</p>
+   */
+  enum Attribute {
+    READ = 1,
+    WRITE = 2,
+    ARCHIVE = 4,
+    REMOVED = 8,
+    TRACE_READ = 16,
+    TRACE_WRITE = 32
+  };
+
+
+  /**
+   * Last used attribute
+   * Update as needed when enum Attribute is changed
+   */
+  static const int LAST_USED_ATTRIBUTE;
+
+
+  /**
+   * Default constructor.
+   */
+  SGPropertyNode ();
+
+
+  /**
+   * Copy constructor.
+   */
+  SGPropertyNode (const SGPropertyNode &node);
+
+
+  /**
+   * Destructor.
+   */
+  virtual ~SGPropertyNode ();
+
+
+
+  //
+  // Basic properties.
+  //
+
+  /**
+   * Test whether this node contains a primitive leaf value.
+   */
+  bool hasValue () const { return (_type != NONE); }
+
+
+  /**
+   * Get the node's simple (XML) name.
+   */
+  const char * getName () const { return _name; }
+
+
+  /**
+   * Get the node's pretty display name, with subscript when needed.
+   */
+  const char * getDisplayName (bool simplify = false) const;
+
+
+  /**
+   * Get the node's integer index.
+   */
+  int getIndex () const { return _index; }
+
+
+  /**
+   * Get a non-const pointer to the node's parent.
+   */
+  SGPropertyNode * getParent () { return _parent; }
+
+
+  /**
+   * Get a const pointer to the node's parent.
+   */
+  const SGPropertyNode * getParent () const { return _parent; }
+
+
+  //
+  // Children.
+  //
+
+
+  /**
+   * Get the number of child nodes.
+   */
+  int nChildren () const { return _children.size(); }
+
+
+  /**
+   * Get a child node by position (*NOT* index).
+   */
+  SGPropertyNode * getChild (int position);
+
+
+  /**
+   * Get a const child node by position (*NOT* index).
+   */
+  const SGPropertyNode * getChild (int position) const;
+
+
+  /**
+   * Test whether a named child exists.
+   */
+  bool hasChild (const char * name, int index = 0) const
+  {
+    return (getChild(name, index) != 0);
+  }
+
+
+  /**
+   * Get a child node by name and index.
+   */
+  SGPropertyNode * getChild (const char * name, int index = 0,
+                            bool create = false);
+
+
+  /**
+   * Get a const child node by name and index.
+   */
+  const SGPropertyNode * getChild (const char * name, int index = 0) const;
+
+
+  /**
+   * Get a vector of all children with the specified name.
+   */
+  vector<SGPropertyNode_ptr> getChildren (const char * name) const;
+
+
+  /**
+   * Remove a child node
+   */
+  SGPropertyNode_ptr removeChild (const char * name, int index = 0,
+                                  bool keep = true);
+
+
+  //
+  // Alias support.
+  //
+
+
+  /**
+   * Alias this node's leaf value to another's.
+   */
+  bool alias (SGPropertyNode * target);
+
+
+  /**
+   * Alias this node's leaf value to another's by relative path.
+   */
+  bool alias (const char * path);
+
+
+  /**
+   * Remove any alias for this node.
+   */
+  bool unalias ();
+
+
+  /**
+   * Test whether the node's leaf value is aliased to another's.
+   */
+  bool isAlias () const { return (_type == ALIAS); }
+
+
+  /**
+   * Get a non-const pointer to the current alias target, if any.
+   */
+  SGPropertyNode * getAliasTarget ();
+
+
+  /**
+   * Get a const pointer to the current alias target, if any.
+   */
+  const SGPropertyNode * getAliasTarget () const;
+
+
+  //
+  // Path information.
+  //
+
+
+  /**
+   * Get the path to this node from the root.
+   */
+  const char * getPath (bool simplify = false) const;
+
+
+  /**
+   * Get a pointer to the root node.
+   */
+  SGPropertyNode * getRootNode ();
+
+
+  /**
+   * Get a const pointer to the root node.
+   */
+  const SGPropertyNode * getRootNode () const;
+
+
+  /**
+   * Get a pointer to another node by relative path.
+   */
+  SGPropertyNode * getNode (const char * relative_path, bool create = false);
+
+
+  /**
+   * Get a pointer to another node by relative path.
+   *
+   * This method leaves the index off the last member of the path,
+   * so that the user can specify it separately (and save some
+   * string building).  For example, getNode("/bar[1]/foo", 3) is
+   * exactly equivalent to getNode("bar[1]/foo[3]").  The index
+   * provided overrides any given in the path itself for the last
+   * component.
+   */
+  SGPropertyNode * getNode (const char * relative_path, int index,
+                           bool create = false);
+
+
+  /**
+   * Get a const pointer to another node by relative path.
+   */
+  const SGPropertyNode * getNode (const char * relative_path) const;
+
+
+  /**
+   * Get a const pointer to another node by relative path.
+   *
+   * This method leaves the index off the last member of the path,
+   * so that the user can specify it separate.
+   */
+  const SGPropertyNode * getNode (const char * relative_path,
+                                 int index) const;
+
+
+  //
+  // Access Mode.
+  //
+
+  /**
+   * Check a single mode attribute for the property node.
+   */
+  bool getAttribute (Attribute attr) const { return ((_attr & attr) != 0); }
+
+
+  /**
+   * Set a single mode attribute for the property node.
+   */
+  void setAttribute (Attribute attr, bool state) {
+    (state ? _attr |= attr : _attr &= ~attr);
+  }
+
+
+  /**
+   * Get all of the mode attributes for the property node.
+   */
+  int getAttributes () const { return _attr; }
+
+
+  /**
+   * Set all of the mode attributes for the property node.
+   */
+  void setAttributes (int attr) { _attr = attr; }
+  
+
+  //
+  // Leaf Value (primitive).
+  //
+
+
+  /**
+   * Get the type of leaf value, if any, for this node.
+   */
+  Type getType () const;
+
+
+  /**
+   * Get a bool value for this node.
+   */
+  bool getBoolValue () const;
+
+
+  /**
+   * Get an int value for this node.
+   */
+  int getIntValue () const;
+
+
+  /**
+   * Get a long int value for this node.
+   */
+  long getLongValue () const;
+
+
+  /**
+   * Get a float value for this node.
+   */
+  float getFloatValue () const;
+
+
+  /**
+   * Get a double value for this node.
+   */
+  double getDoubleValue () const;
+
+
+  /**
+   * Get a string value for this node.
+   */
+  const char * getStringValue () const;
+
+
+
+  /**
+   * Set a bool value for this node.
+   */
+  bool setBoolValue (bool value);
+
+
+  /**
+   * Set an int value for this node.
+   */
+  bool setIntValue (int value);
+
+
+  /**
+   * Set a long int value for this node.
+   */
+  bool setLongValue (long value);
+
+
+  /**
+   * Set a float value for this node.
+   */
+  bool setFloatValue (float value);
+
+
+  /**
+   * Set a double value for this node.
+   */
+  bool setDoubleValue (double value);
+
+
+  /**
+   * Set a string value for this node.
+   */
+  bool setStringValue (const char * value);
+
+
+  /**
+   * Set a value of unspecified type for this node.
+   */
+  bool setUnspecifiedValue (const char * value);
+
+
+  //
+  // Data binding.
+  //
+
+
+  /**
+   * Test whether this node is bound to an external data source.
+   */
+  bool isTied () const { return _tied; }
+
+
+  /**
+   * Bind this node to an external bool source.
+   */
+  bool tie (const SGRawValue<bool> &rawValue, bool useDefault = true);
+
+
+  /**
+   * Bind this node to an external int source.
+   */
+  bool tie (const SGRawValue<int> &rawValue, bool useDefault = true);
+
+
+  /**
+   * Bind this node to an external long int source.
+   */
+  bool tie (const SGRawValue<long> &rawValue, bool useDefault = true);
+
+
+  /**
+   * Bind this node to an external float source.
+   */
+  bool tie (const SGRawValue<float> &rawValue, bool useDefault = true);
+
+
+  /**
+   * Bind this node to an external double source.
+   */
+  bool tie (const SGRawValue<double> &rawValue, bool useDefault = true);
+
+
+  /**
+   * Bind this node to an external string source.
+   */
+  bool tie (const SGRawValue<const char *> &rawValue, bool useDefault = true);
+
+
+  /**
+   * Unbind this node from any external data source.
+   */
+  bool untie ();
+
+
+  //
+  // Convenience methods using paths.
+  // TODO: add attribute methods
+  //
+
+
+  /**
+   * Get another node's type.
+   */
+  Type getType (const char * relative_path) const;
+
+
+  /**
+   * Test whether another node has a leaf value.
+   */
+  bool hasValue (const char * relative_path) const;
+
+
+  /**
+   * Get another node's value as a bool.
+   */
+  bool getBoolValue (const char * relative_path,
+                    bool defaultValue = false) const;
+
+
+  /**
+   * Get another node's value as an int.
+   */
+  int getIntValue (const char * relative_path,
+                  int defaultValue = 0) const;
+
+
+  /**
+   * Get another node's value as a long int.
+   */
+  long getLongValue (const char * relative_path,
+                    long defaultValue = 0L) const;
+
+
+  /**
+   * Get another node's value as a float.
+   */
+  float getFloatValue (const char * relative_path,
+                      float defaultValue = 0.0) const;
+
+
+  /**
+   * Get another node's value as a double.
+   */
+  double getDoubleValue (const char * relative_path,
+                        double defaultValue = 0.0L) const;
+
+
+  /**
+   * Get another node's value as a string.
+   */
+  const char * getStringValue (const char * relative_path,
+                              const char * defaultValue = "") const;
+
+
+  /**
+   * Set another node's value as a bool.
+   */
+  bool setBoolValue (const char * relative_path, bool value);
+
+
+  /**
+   * Set another node's value as an int.
+   */
+  bool setIntValue (const char * relative_path, int value);
+
+
+  /**
+   * Set another node's value as a long int.
+   */
+  bool setLongValue (const char * relative_path, long value);
+
+
+  /**
+   * Set another node's value as a float.
+   */
+  bool setFloatValue (const char * relative_path, float value);
+
+
+  /**
+   * Set another node's value as a double.
+   */
+  bool setDoubleValue (const char * relative_path, double value);
+
+
+  /**
+   * Set another node's value as a string.
+   */
+  bool setStringValue (const char * relative_path, const char * value);
+
+
+  /**
+   * Set another node's value with no specified type.
+   */
+  bool setUnspecifiedValue (const char * relative_path, const char * value);
+
+
+  /**
+   * Test whether another node is bound to an external data source.
+   */
+  bool isTied (const char * relative_path) const;
+
+
+  /**
+   * Bind another node to an external bool source.
+   */
+  bool tie (const char * relative_path, const SGRawValue<bool> &rawValue,
+           bool useDefault = true);
+
+
+  /**
+   * Bind another node to an external int source.
+   */
+  bool tie (const char * relative_path, const SGRawValue<int> &rawValue,
+           bool useDefault = true);
+
+
+  /**
+   * Bind another node to an external long int source.
+   */
+  bool tie (const char * relative_path, const SGRawValue<long> &rawValue,
+           bool useDefault = true);
+
+
+  /**
+   * Bind another node to an external float source.
+   */
+  bool tie (const char * relative_path, const SGRawValue<float> &rawValue,
+           bool useDefault = true);
+
+
+  /**
+   * Bind another node to an external double source.
+   */
+  bool tie (const char * relative_path, const SGRawValue<double> &rawValue,
+           bool useDefault = true);
+
+
+  /**
+   * Bind another node to an external string source.
+   */
+  bool tie (const char * relative_path, const SGRawValue<const char *> &rawValue,
+           bool useDefault = true);
+
+
+  /**
+   * Unbind another node from any external data source.
+   */
+  bool untie (const char * relative_path);
+
+
+  /**
+   * Add a change listener to the property.
+   */
+  void addChangeListener (SGPropertyChangeListener * listener);
+
+
+  /**
+   * Remove a change listener from the property.
+   */
+  void removeChangeListener (SGPropertyChangeListener * listener);
+
+
+  /**
+   * Fire a value change event to all listeners.
+   */
+  void fireValueChanged ();
+
+
+  /**
+   * Fire a child-added event to all listeners.
+   */
+  void fireChildAdded (SGPropertyNode * child);
+
+
+  /**
+   * Fire a child-removed event to all listeners.
+   */
+  void fireChildRemoved (SGPropertyNode * child);
+
+
+protected:
+
+  void fireValueChanged (SGPropertyNode * node);
+  void fireChildAdded (SGPropertyNode * parent, SGPropertyNode * child);
+  void fireChildRemoved (SGPropertyNode * parent, SGPropertyNode * child);
+
+  /**
+   * Protected constructor for making new nodes on demand.
+   */
+  SGPropertyNode (const char * name, int index, SGPropertyNode * parent);
+
+
+private:
+
+                               // Get the raw value
+  bool get_bool () const;
+  int get_int () const;
+  long get_long () const;
+  float get_float () const;
+  double get_double () const;
+  const char * get_string () const;
+
+                               // Set the raw value
+  bool set_bool (bool value);
+  bool set_int (int value);
+  bool set_long (long value);
+  bool set_float (float value);
+  bool set_double (double value);
+  bool set_string (const char * value);
+
+
+  /**
+   * Clear any existing value and set the type to NONE.
+   */
+  void clear_value ();
+
+
+  /**
+   * Get the value as a string.
+   */
+  const char * make_string () const;
+
+
+  /**
+   * Trace a read access.
+   */
+  void trace_read () const;
+
+
+  /**
+   * Trace a write access.
+   */
+  void trace_write () const;
+
+
+  /**
+   * Increment reference counter
+   */
+  void incrementRef();
+
+  /**
+   * Decrement reference counter
+   */
+  int decrementRef();
+
+  friend class SGPropertyNode_ptr;
+
+
+  mutable char _buffer[MAX_STRING_LEN+1];
+
+  class hash_table;
+
+  char * _name;
+  mutable char * _display_name;
+  int _index;
+  SGPropertyNode * _parent;
+  vector<SGPropertyNode_ptr> _children;
+  vector<SGPropertyNode_ptr> _removedChildren;
+  mutable char * _path;
+  hash_table * _path_cache;
+  Type _type;
+  bool _tied;
+  int _attr;
+  int _count;
+
+                               // The right kind of pointer...
+  union {
+    SGPropertyNode * alias;
+    SGRawValue<bool> * bool_val;
+    SGRawValue<int> * int_val;
+    SGRawValue<long> * long_val;
+    SGRawValue<float> * float_val;
+    SGRawValue<double> * double_val;
+    SGRawValue<const char *> * string_val;
+  } _value;
+
+  union {
+    bool bool_val;
+    int int_val;
+    long long_val;
+    float float_val;
+    double double_val;
+    char * string_val;
+  } _local_val;
+
+  vector <SGPropertyChangeListener *> * _listeners;
+
+
+\f
+  /**
+   * A very simple hash table with no remove functionality.
+   */
+  class hash_table {
+  public:
+
+    /**
+     * An entry in a bucket in a hash table.
+     */
+    class entry {
+    public:
+      entry ();
+      virtual ~entry ();
+      virtual const char * get_key () { return _key; }
+      virtual void set_key (const char * key);
+      virtual SGPropertyNode * get_value () { return _value; }
+      virtual void set_value (SGPropertyNode * value);
+    private:
+      char * _key;
+      SGPropertyNode * _value;
+    };
+
+
+    /**
+     * A bucket in a hash table.
+     */
+    class bucket {
+    public:
+      bucket ();
+      virtual ~bucket ();
+      virtual entry * get_entry (const char * key, bool create = false);
+    private:
+      int _length;
+      entry ** _entries;
+    };
+
+    friend class bucket;
+
+    hash_table ();
+    virtual ~hash_table ();
+    virtual SGPropertyNode * get (const char * key);
+    virtual void put (const char * key, SGPropertyNode * value);
+
+  private:
+    unsigned int hashcode (const char * key);
+    unsigned int _data_length;
+    bucket ** _data;
+  };
+
+};
+
+#endif // __PROPS_HXX
+
+// end of props.hxx
diff --git a/simgear/props/props_io.cxx b/simgear/props/props_io.cxx
new file mode 100644 (file)
index 0000000..e808e0b
--- /dev/null
@@ -0,0 +1,586 @@
+
+#include <simgear/compiler.h>
+
+#include <stdlib.h>            // atof() atoi()
+
+#include <simgear/sg_inlines.h>
+#include <simgear/debug/logstream.hxx>
+#include <simgear/misc/sg_path.hxx>
+#include <simgear/xml/easyxml.hxx>
+
+#include "props.hxx"
+#include "props_io.hxx"
+
+#include STL_IOSTREAM
+#include STL_FSTREAM
+#include STL_STRING
+#include <vector>
+#include <map>
+
+SG_USING_STD(istream);
+SG_USING_STD(ifstream);
+SG_USING_STD(ostream);
+SG_USING_STD(ofstream);
+SG_USING_STD(string);
+SG_USING_STD(vector);
+SG_USING_STD(map);
+
+#define DEFAULT_MODE (SGPropertyNode::READ|SGPropertyNode::WRITE)
+
+
+\f
+////////////////////////////////////////////////////////////////////////
+// Property list visitor, for XML parsing.
+////////////////////////////////////////////////////////////////////////
+
+class PropsVisitor : public XMLVisitor
+{
+public:
+
+  PropsVisitor (SGPropertyNode * root, const string &base)
+    : _root(root), _level(0), _base(base), _hasException(false) {}
+
+  virtual ~PropsVisitor () {}
+
+  void startXML ();
+  void endXML ();
+  void startElement (const char * name, const XMLAttributes &atts);
+  void endElement (const char * name);
+  void data (const char * s, int length);
+  void warning (const char * message, int line, int column);
+
+  bool hasException () const { return _hasException; }
+  sg_io_exception &getException () { return _exception; }
+  void setException (const sg_io_exception &exception) {
+    _exception = exception;
+    _hasException = true;
+  }
+
+private:
+
+  struct State
+  {
+    State () : node(0), type(""), mode(DEFAULT_MODE) {}
+    State (SGPropertyNode * _node, const char * _type, int _mode)
+      : node(_node), type(_type), mode(_mode) {}
+    SGPropertyNode * node;
+    string type;
+    int mode;
+    map<string,int> counters;
+  };
+
+  State &state () { return _state_stack[_state_stack.size() - 1]; }
+
+  void push_state (SGPropertyNode * node, const char * type, int mode) {
+    if (type == 0)
+      _state_stack.push_back(State(node, "unspecified", mode));
+    else
+      _state_stack.push_back(State(node, type, mode));
+    _level++;
+    _data = "";
+  }
+
+  void pop_state () {
+    _state_stack.pop_back();
+    _level--;
+  }
+
+  string _data;
+  SGPropertyNode * _root;
+  int _level;
+  vector<State> _state_stack;
+  string _base;
+  sg_io_exception _exception;
+  bool _hasException;
+};
+
+void
+PropsVisitor::startXML ()
+{
+  _level = 0;
+  _state_stack.resize(0);
+}
+
+void
+PropsVisitor::endXML ()
+{
+  _level = 0;
+  _state_stack.resize(0);
+}
+
+
+/**
+ * Check a yes/no flag, with default.
+ */
+static bool
+checkFlag (const char * flag, bool defaultState = true)
+{
+  if (flag == 0)
+    return defaultState;
+  else if (string(flag) == "y")
+    return true;
+  else if (string(flag) == "n")
+    return false;
+  else {
+    string message = "Unrecognized flag value '";
+    message += flag;
+    message += '\'';
+                               // FIXME: add location info
+    throw sg_io_exception(message, "SimGear Property Reader");
+  }
+}
+
+void
+PropsVisitor::startElement (const char * name, const XMLAttributes &atts)
+{
+  State &st = state();
+  const char * attval;
+
+  if (_level == 0) {
+    if (string(name) != (string)"PropertyList") {
+      string message = "Root element name is ";
+      message += name;
+      message += "; expected PropertyList";
+      throw sg_io_exception(message, "SimGear Property Reader");
+    }
+
+                               // Check for an include.
+    attval = atts.getValue("include");
+    if (attval != 0) {
+      SGPath path(SGPath(_base).dir());
+      path.append(attval);
+      try {
+       readProperties(path.str(), _root);
+      } catch (sg_io_exception &e) {
+       setException(e);
+      }
+    }
+
+    push_state(_root, "", DEFAULT_MODE);
+  }
+
+  else {
+                               // Get the index.
+    attval = atts.getValue("n");
+    int index = 0;
+    if (attval != 0) {
+      index = atoi(attval);
+      st.counters[name] = SG_MAX2(st.counters[name], index+1);
+    } else {
+      index = st.counters[name];
+      st.counters[name]++;
+    }
+
+                               // Got the index, so grab the node.
+    SGPropertyNode * node = st.node->getChild(name, index, true);
+
+                               // Get the access-mode attributes,
+                               // but don't set yet (in case they
+                               // prevent us from recording the value).
+    int mode = 0;
+
+    attval = atts.getValue("read");
+    if (checkFlag(attval, true))
+      mode |= SGPropertyNode::READ;
+    attval = atts.getValue("write");
+    if (checkFlag(attval, true))
+      mode |= SGPropertyNode::WRITE;
+    attval = atts.getValue("archive");
+    if (checkFlag(attval, false))
+      mode |= SGPropertyNode::ARCHIVE;
+    attval = atts.getValue("trace-read");
+    if (checkFlag(attval, false))
+      mode |= SGPropertyNode::TRACE_READ;
+    attval = atts.getValue("trace-write");
+    if (checkFlag(attval, false))
+      mode |= SGPropertyNode::TRACE_WRITE;
+
+                               // Check for an alias.
+    attval = atts.getValue("alias");
+    if (attval != 0) {
+      if (!node->alias(attval))
+       SG_LOG(SG_INPUT, SG_ALERT, "Failed to set alias to " << attval);
+    }
+
+                               // Check for an include.
+    attval = atts.getValue("include");
+    if (attval != 0) {
+      SGPath path(SGPath(_base).dir());
+      path.append(attval);
+      try {
+       readProperties(path.str(), node);
+      } catch (sg_io_exception &e) {
+       setException(e);
+      }
+    }
+
+    push_state(node, atts.getValue("type"), mode);
+  }
+}
+
+void
+PropsVisitor::endElement (const char * name)
+{
+  State &st = state();
+  bool ret;
+
+                               // If there are no children and it's
+                               // not an alias, then it's a leaf value.
+  if (st.node->nChildren() == 0 && !st.node->isAlias()) {
+    if (st.type == "bool") {
+      if (_data == "true" || atoi(_data.c_str()) != 0)
+       ret = st.node->setBoolValue(true);
+      else
+       ret = st.node->setBoolValue(false);
+    } else if (st.type == "int") {
+      ret = st.node->setIntValue(atoi(_data.c_str()));
+    } else if (st.type == "long") {
+      ret = st.node->setLongValue(strtol(_data.c_str(), 0, 0));
+    } else if (st.type == "float") {
+      ret = st.node->setFloatValue(atof(_data.c_str()));
+    } else if (st.type == "double") {
+      ret = st.node->setDoubleValue(strtod(_data.c_str(), 0));
+    } else if (st.type == "string") {
+      ret = st.node->setStringValue(_data.c_str());
+    } else if (st.type == "unspecified") {
+      ret = st.node->setUnspecifiedValue(_data.c_str());
+    } else {
+      string message = "Unrecognized data type '";
+      message += st.type;
+      message += '\'';
+                               // FIXME: add location information
+      throw sg_io_exception(message, "SimGear Property Reader");
+    }
+    if (!ret)
+      SG_LOG(SG_INPUT, SG_ALERT, "readProperties: Failed to set "
+            << st.node->getPath() << " to value \""
+            << _data << "\" with type " << st.type);
+  }
+
+                               // Set the access-mode attributes now,
+                               // once the value has already been 
+                               // assigned.
+  st.node->setAttributes(st.mode);
+
+  pop_state();
+}
+
+void
+PropsVisitor::data (const char * s, int length)
+{
+  if (state().node->nChildren() == 0)
+    _data.append(string(s, length));
+}
+
+void
+PropsVisitor::warning (const char * message, int line, int column)
+{
+  SG_LOG(SG_INPUT, SG_ALERT, "readProperties: warning: "
+        << message << " at line " << line << ", column " << column);
+}
+
+
+\f
+////////////////////////////////////////////////////////////////////////
+// Property list reader.
+////////////////////////////////////////////////////////////////////////
+
+
+/**
+ * Read properties from an input stream.
+ *
+ * @param input The input stream containing an XML property file.
+ * @param start_node The root node for reading properties.
+ * @param base A base path for resolving external include references.
+ * @return true if the read succeeded, false otherwise.
+ */
+void
+readProperties (istream &input, SGPropertyNode * start_node,
+               const string &base)
+{
+  PropsVisitor visitor(start_node, base);
+  readXML(input, visitor, base);
+  if (visitor.hasException())
+    throw visitor.getException();
+}
+
+
+/**
+ * Read properties from a file.
+ *
+ * @param file A string containing the file path.
+ * @param start_node The root node for reading properties.
+ * @return true if the read succeeded, false otherwise.
+ */
+void
+readProperties (const string &file, SGPropertyNode * start_node)
+{
+  PropsVisitor visitor(start_node, file);
+  readXML(file, visitor);
+  if (visitor.hasException())
+    throw visitor.getException();
+}
+
+
+\f
+////////////////////////////////////////////////////////////////////////
+// Property list writer.
+////////////////////////////////////////////////////////////////////////
+
+#define INDENT_STEP 2
+
+/**
+ * Return the type name.
+ */
+static const char *
+getTypeName (SGPropertyNode::Type type)
+{
+  switch (type) {
+  case SGPropertyNode::UNSPECIFIED:
+    return "unspecified";
+  case SGPropertyNode::BOOL:
+    return "bool";
+  case SGPropertyNode::INT:
+    return "int";
+  case SGPropertyNode::LONG:
+    return "long";
+  case SGPropertyNode::FLOAT:
+    return "float";
+  case SGPropertyNode::DOUBLE:
+    return "double";
+  case SGPropertyNode::STRING:
+    return "string";
+  case SGPropertyNode::ALIAS:
+  case SGPropertyNode::NONE:
+    return "unspecified";
+  }
+
+  // keep the compiler from squawking
+  return "unspecified";
+}
+
+
+/**
+ * Escape characters for output.
+ */
+static void
+writeData (ostream &output, const string &data)
+{
+  for (int i = 0; i < (int)data.size(); i++) {
+    switch (data[i]) {
+    case '&':
+      output << "&amp;";
+      break;
+    case '<':
+      output << "&lt;";
+      break;
+    case '>':
+      output << "&gt;";
+      break;
+    default:
+      output << data[i];
+      break;
+    }
+  }
+}
+
+static void
+doIndent (ostream &output, int indent)
+{
+  while (indent-- > 0) {
+    output << ' ';
+  }
+}
+
+
+static void
+writeAtts (ostream &output, const SGPropertyNode * node)
+{
+  int index = node->getIndex();
+
+  if (index != 0)
+    output << " n=\"" << index << '"';
+
+#if 0
+  if (!node->getAttribute(SGPropertyNode::READ))
+    output << " read=\"n\"";
+
+  if (!node->getAttribute(SGPropertyNode::WRITE))
+    output << " write=\"n\"";
+
+  if (node->getAttribute(SGPropertyNode::ARCHIVE))
+    output << " archive=\"y\"";
+#endif
+
+}
+
+
+/**
+ * Test whether a node is archivable or has archivable descendants.
+ */
+static bool
+isArchivable (const SGPropertyNode * node)
+{
+  // FIXME: it's inefficient to do this all the time
+  if (node->getAttribute(SGPropertyNode::ARCHIVE))
+    return true;
+  else {
+    int nChildren = node->nChildren();
+    for (int i = 0; i < nChildren; i++)
+      if (isArchivable(node->getChild(i)))
+       return true;
+  }
+  return false;
+}
+
+
+static bool
+writeNode (ostream &output, const SGPropertyNode * node,
+          bool write_all, int indent)
+{
+                               // Don't write the node or any of
+                               // its descendants unless it is
+                               // allowed to be archived.
+  if (!write_all && !isArchivable(node))
+    return true;               // Everything's OK, but we won't write.
+
+  const string name = node->getName();
+  int nChildren = node->nChildren();
+
+                               // If there is a literal value,
+                               // write it first.
+  if (node->hasValue() && (write_all || node->getAttribute(SGPropertyNode::ARCHIVE))) {
+    doIndent(output, indent);
+    output << '<' << name;
+    writeAtts(output, node);
+    if (node->isAlias() && node->getAliasTarget() != 0) {
+      output << " alias=\"" << node->getAliasTarget()->getPath()
+            << "\"/>" << endl;
+    } else {
+      if (node->getType() != SGPropertyNode::UNSPECIFIED)
+       output << " type=\"" << getTypeName(node->getType()) << '"';
+      output << '>';
+      writeData(output, node->getStringValue());
+      output << "</" << name << '>' << endl;
+    }
+  }
+
+                               // If there are children, write them next.
+  if (nChildren > 0 || node->isAlias()) {
+    doIndent(output, indent);
+    output << '<' << name;
+    writeAtts(output, node);
+    output << '>' << endl;
+    for (int i = 0; i < nChildren; i++)
+      writeNode(output, node->getChild(i), write_all, indent + INDENT_STEP);
+    doIndent(output, indent);
+    output << "</" << name << '>' << endl;
+  }
+
+  return true;
+}
+
+
+void
+writeProperties (ostream &output, const SGPropertyNode * start_node,
+                bool write_all)
+{
+  int nChildren = start_node->nChildren();
+
+  output << "<?xml version=\"1.0\"?>" << endl << endl;
+  output << "<PropertyList>" << endl;
+
+  for (int i = 0; i < nChildren; i++) {
+    writeNode(output, start_node->getChild(i), write_all, INDENT_STEP);
+  }
+
+  output << "</PropertyList>" << endl;
+}
+
+
+void
+writeProperties (const string &file, const SGPropertyNode * start_node,
+                bool write_all)
+{
+  ofstream output(file.c_str());
+  if (output.good()) {
+    writeProperties(output, start_node, write_all);
+  } else {
+    throw sg_io_exception("Cannot open file", sg_location(file));
+  }
+}
+
+
+\f
+////////////////////////////////////////////////////////////////////////
+// Copy properties from one tree to another.
+////////////////////////////////////////////////////////////////////////
+
+
+/**
+ * Copy one property tree to another.
+ * 
+ * @param in The source property tree.
+ * @param out The destination property tree.
+ * @return true if all properties were copied, false if some failed
+ *  (for example, if the property's value is tied read-only).
+ */
+bool
+copyProperties (const SGPropertyNode *in, SGPropertyNode *out)
+{
+  bool retval = true;
+
+                               // First, copy the actual value,
+                               // if any.
+  if (in->hasValue()) {
+    switch (in->getType()) {
+    case SGPropertyNode::BOOL:
+      if (!out->setBoolValue(in->getBoolValue()))
+       retval = false;
+      break;
+    case SGPropertyNode::INT:
+      if (!out->setIntValue(in->getIntValue()))
+       retval = false;
+      break;
+    case SGPropertyNode::LONG:
+      if (!out->setLongValue(in->getLongValue()))
+       retval = false;
+      break;
+    case SGPropertyNode::FLOAT:
+      if (!out->setFloatValue(in->getFloatValue()))
+       retval = false;
+      break;
+    case SGPropertyNode::DOUBLE:
+      if (!out->setDoubleValue(in->getDoubleValue()))
+       retval = false;
+      break;
+    case SGPropertyNode::STRING:
+      if (!out->setStringValue(in->getStringValue()))
+       retval = false;
+      break;
+    case SGPropertyNode::UNSPECIFIED:
+      if (!out->setUnspecifiedValue(in->getStringValue()))
+       retval = false;
+      break;
+    default:
+      string message = "Unknown internal SGPropertyNode type";
+      message += in->getType();
+      throw sg_error(message, "SimGear Property Reader");
+    }
+  }
+
+                               // Next, copy the children.
+  int nChildren = in->nChildren();
+  for (int i = 0; i < nChildren; i++) {
+    const SGPropertyNode * in_child = in->getChild(i);
+    SGPropertyNode * out_child = out->getChild(in_child->getName(),
+                                              in_child->getIndex(),
+                                              true);
+    if (!copyProperties(in_child, out_child))
+      retval = false;
+  }
+
+  return retval;
+}
+
+// end of props_io.cxx
diff --git a/simgear/props/props_io.hxx b/simgear/props/props_io.hxx
new file mode 100644 (file)
index 0000000..d541e1c
--- /dev/null
@@ -0,0 +1,66 @@
+/**
+ * \file props_io.hxx
+ * Interface definition for property list io.
+ * Started Fall 2000 by David Megginson, david@megginson.com
+ * This code is released into the Public Domain.
+ *
+ * See props.html for documentation [replace with URL when available].
+ *
+ * $Id$
+ */
+
+#ifndef __PROPS_IO_HXX
+#define __PROPS_IO_HXX
+
+#include <simgear/compiler.h>
+#include <simgear/props/props.hxx>
+
+#include <stdio.h>
+
+#include STL_STRING
+#include <vector>
+#include <map>
+#include STL_IOSTREAM
+
+SG_USING_STD(string);
+SG_USING_STD(vector);
+SG_USING_STD(map);
+SG_USING_STD(istream);
+SG_USING_STD(ostream);
+
+/**
+ * Read properties from an XML input stream.
+ */
+void readProperties (istream &input, SGPropertyNode * start_node,
+                    const string &base = "");
+
+
+/**
+ * Read properties from an XML file.
+ */
+void readProperties (const string &file, SGPropertyNode * start_node);
+
+
+/**
+ * Write properties to an XML output stream.
+ */
+void writeProperties (ostream &output, const SGPropertyNode * start_node,
+                     bool write_all = false);
+
+
+/**
+ * Write properties to an XML file.
+ */
+void writeProperties (const string &file, const SGPropertyNode * start_node,
+                     bool write_all = false);
+
+
+/**
+ * Copy properties from one node to another.
+ */
+bool copyProperties (const SGPropertyNode *in, SGPropertyNode *out);
+
+
+#endif // __PROPS_IO_HXX
+
+// end of props_io.hxx
diff --git a/simgear/props/props_test.cxx b/simgear/props/props_test.cxx
new file mode 100644 (file)
index 0000000..ab29dee
--- /dev/null
@@ -0,0 +1,346 @@
+
+////////////////////////////////////////////////////////////////////////
+// Test harness.
+////////////////////////////////////////////////////////////////////////
+
+#include <simgear/compiler.h>
+
+#include STL_IOSTREAM
+#include "props.hxx"
+#include "props_io.hxx"
+
+SG_USING_STD(cout);
+SG_USING_STD(cerr);
+SG_USING_STD(endl);
+
+
+\f
+////////////////////////////////////////////////////////////////////////
+// Sample object.
+////////////////////////////////////////////////////////////////////////
+
+class Stuff {
+public:
+  Stuff () : _stuff(199.0) {}
+  virtual float getStuff () const { return _stuff; }
+  virtual void setStuff (float stuff) { _stuff = stuff; }
+  virtual float getStuff (int index) const { return _stuff * index; }
+  virtual void setStuff (int index, float val) {
+    _stuff = val / index;
+  }
+private:
+  float _stuff;
+};
+
+
+\f
+////////////////////////////////////////////////////////////////////////
+// Sample function.
+////////////////////////////////////////////////////////////////////////
+
+static int get100 () { return 100; }
+
+static double getNum (int index) { return 1.0 / index; }
+
+
+\f
+////////////////////////////////////////////////////////////////////////
+// Show a value.
+////////////////////////////////////////////////////////////////////////
+
+static void
+show_values (const SGPropertyNode * node)
+{
+  cout << "Bool: " << (node->getBoolValue() ? "true" : "false") << endl;
+  cout << "Int: " << node->getIntValue() << endl;
+  cout << "Float: " << node->getFloatValue() << endl;
+  cout << "Double: " << node->getDoubleValue() << endl;
+  cout << "String: " << node->getStringValue() << endl;
+}
+
+
+\f
+////////////////////////////////////////////////////////////////////////
+// Test individual values.
+////////////////////////////////////////////////////////////////////////
+
+static void
+test_value ()
+{
+  SGPropertyNode * node;
+
+  cout << endl << "Value" << endl << endl;
+
+  //
+  // Test coercion for getters.
+  //
+
+  cout << "Testing coercion from bool (expect true)" << endl;
+  node = new SGPropertyNode;
+  node->setBoolValue(true);
+  show_values(node);
+  delete node;
+  cout << endl;
+
+  cout << "Testing coercion from int (expect 128)" << endl;
+  node = new SGPropertyNode;
+  node->setIntValue(128);
+  show_values(node);
+  delete node;
+  cout << endl;
+
+  cout << "Testing coercion from float (expect 1.0/3.0)" << endl;
+  node = new SGPropertyNode;
+  node->setFloatValue(1.0/3.0);
+  show_values(node);
+  delete node;
+  cout << endl;
+
+  cout << "Testing coercion from double (expect 1.0/3.0)" << endl;
+  node = new SGPropertyNode;
+  node->setDoubleValue(1.0/3.0);
+  show_values(node);
+  delete node;
+  cout << endl;
+
+  cout << "Testing coercion from string (expect 10e4)" << endl;
+  node = new SGPropertyNode;
+  node->setStringValue("10e4");
+  show_values(node);
+  delete node;
+  cout << endl;
+
+  cout << "Testing coercion from unspecified (expect -10e-4)" << endl;
+  node = new SGPropertyNode;
+  node->setUnspecifiedValue("-10e-4");
+  show_values(node);
+  delete node;
+  cout << endl;
+
+  //
+  // Test coercion for setters.
+  //
+
+  node = new SGPropertyNode;
+
+  cout << "Testing coercion to bool from bool (expect false)" << endl;
+  node->setBoolValue(false);
+  show_values(node);
+  cout << endl;
+
+  cout << "Testing coercion to bool from int (expect 1)" << endl;
+  node->setIntValue(1);
+  show_values(node);
+  cout << endl;
+
+  cout << "Testing coercion to bool from float (expect 1.1)" << endl;
+  node->setFloatValue(1.1);
+  show_values(node);
+  cout << endl;
+
+  cout << "Testing coercion to bool from double (expect 1.1)" << endl;
+  node->setDoubleValue(1.1);
+  show_values(node);
+  cout << endl;
+
+  cout << "Testing coercion to bool from string (expect 1e10)" << endl;
+  node->setStringValue("1e10");
+  show_values(node);
+  cout << endl;
+
+  cout << "Testing coercion to bool from unspecified (expect 1e10)" << endl;
+  node->setUnspecifiedValue("1e10");
+  show_values(node);
+  cout << endl;
+
+  // Test tying to a pointer.
+
+  static int myValue = 10;
+
+  cout << "Testing tying to a pointer (expect 10)" << endl;
+  if (!node->tie(SGRawValuePointer<int>(&myValue), false))
+    cout << "*** FAILED TO TIE VALUE!!!" << endl;
+  show_values(node);
+  cout << endl;
+
+  cout << "Changing base variable (expect -5)" << endl;
+  myValue = -5;
+  show_values(node);
+  if (!node->untie())
+    cout << "*** FAILED TO UNTIE VALUE!!!" << endl;
+  cout << endl;
+
+
+  // Test tying to static functions.
+
+  cout << "Create a new int value (expect 10)" << endl;
+  node->setIntValue(10);
+  show_values(node);
+  cout << endl;
+
+  cout << "Testing tying to static getter (expect 100)" << endl;
+  if (!node->tie(SGRawValueFunctions<int>(get100)))
+    cout << "*** FAILED TO TIE VALUE!!!" << endl;
+  show_values(node);
+  cout << endl;
+
+  cout << "Try changing value with no setter (expect 100)" << endl;
+  if (node->setIntValue(200))
+    cout << "*** setIntValue did not return false!!!" << endl;
+  show_values(node);
+  cout << endl;
+
+  cout << "Untie value (expect 100)" << endl;
+  if (!node->untie())
+    cout << "*** FAILED TO UNTIE VALUE!!!" << endl;
+  show_values(node);
+  cout << endl;
+
+  cout << "Try changing value (expect 200)" << endl;
+  if (!node->setIntValue(200))
+    cout << "*** setIntValue RETURNED FALSE!!!" << endl;
+  show_values(node);
+  cout << endl;
+
+  // Test tying to indexed static functions.
+
+  cout << "Create a new int value (expect 10)" << endl;
+  node->setIntValue(10);
+  show_values(node);
+  cout << endl;
+
+  cout << "Testing tying to indexed static getter (0.3333...)" << endl;
+  if (!node->tie(SGRawValueFunctionsIndexed<double>(3, getNum)))
+    cout << "*** FAILED TO TIE VALUE!!!" << endl;
+  show_values(node);
+  cout << endl;
+
+  cout << "Untie value (expect 0.3333...)" << endl;
+  if (!node->untie())
+    cout << "*** FAILED TO UNTIE VALUE!!!" << endl;
+  show_values(node);
+  cout << endl;
+
+
+  // Test methods.
+
+  cout << "Try tying to an object without defaults (expect 199)" << endl;
+  Stuff stuff;
+  SGRawValueMethods<class Stuff,float> tiedstuff(stuff,
+                                                &Stuff::getStuff,
+                                                &Stuff::setStuff);
+  if (!node->tie(tiedstuff, false))
+    cout << "*** FAILED TO TIE VALUE!!!" << endl;
+  show_values(node);
+  cout << endl;
+
+  cout << "Try untying from object (expect 199)" << endl;
+  if (!node->untie())
+    cout << "*** FAILED TO UNTIE VALUE!!!" << endl;
+  show_values(node);
+  cout << endl;
+
+  cout << "Try tying to an indexed method (expect 199)" << endl;
+  if (!node->tie(SGRawValueMethodsIndexed<class Stuff, float>
+                 (stuff, 2, &Stuff::getStuff, &Stuff::setStuff)))
+    cout << "*** FAILED TO TIE VALUE!!!" << endl;
+  show_values(node);
+  cout << endl;
+
+  node->untie();
+
+  cout << "Change value (expect 50)" << endl;
+  if (!node->setIntValue(50))
+    cout << "*** FAILED TO SET VALUE!!!" << endl;
+  show_values(node);
+  cout << endl;
+
+  cout << "Try tying to an object with defaults (expect 50)" << endl;
+  if (!node->tie(tiedstuff, true))
+    cout << "*** FAILED TO TIE VALUE!!!" << endl;
+  show_values(node);
+  cout << endl;
+
+  delete node;
+}
+
+
+\f
+////////////////////////////////////////////////////////////////////////
+// Check property nodes.
+////////////////////////////////////////////////////////////////////////
+
+static void
+dump_node (const SGPropertyNode * node)
+{
+  writeProperties(cout, node, true);
+}
+
+static void
+test_property_nodes ()
+{
+  SGPropertyNode root;
+  cout << "Created root node " << root.getPath() << endl;
+
+  SGPropertyNode * child = root.getChild("foo", 0, true);
+
+  SGPropertyNode *grandchild = child->getChild("bar", 0, true);
+  grandchild->setDoubleValue(100);
+  grandchild = child->getChild("bar", 1, true);
+  grandchild->setDoubleValue(200);
+  grandchild = child->getChild("bar", 2, true);
+  grandchild->setDoubleValue(300);
+  grandchild = child->getChild("bar", 3, true);
+  grandchild->setDoubleValue(400);
+
+  child = root.getChild("hack", 0, true);
+
+  grandchild = child->getChild("bar", 0, true);
+  grandchild->setDoubleValue(100);
+  grandchild = child->getChild("bar", 3, true);
+  grandchild->setDoubleValue(200);
+  grandchild = child->getChild("bar", 1, true);
+  grandchild->setDoubleValue(300);
+  grandchild = child->getChild("bar", 2, true);
+  grandchild->setDoubleValue(400);
+  dump_node(&root);
+
+  cout << "Trying path (expect /foo[0]/bar[0])" << endl;
+  grandchild = root.getNode("/hack/../foo/./bar[0]");
+  cout << "Path is " << grandchild->getPath() << endl;
+  cout << endl;
+
+  cout << "Looking for all /hack[0]/bar children" << endl;
+  vector<SGPropertyNode_ptr> bar = child->getChildren("bar");
+  cout << "There are " << bar.size() << " matches" << endl;
+  for (int i = 0; i < (int)bar.size(); i++)
+    cout << bar[i]->getName() << '[' << bar[i]->getIndex() << ']' << endl;
+  cout << endl;
+
+  cout << "Testing addition of a totally empty node" << endl;
+  if (root.getNode("/a/b/c", true) == 0)
+    cerr << "** failed to create /a/b/c" << endl;
+  dump_node(&root);
+  cout << endl;
+}
+
+
+int main (int ac, char ** av)
+{
+  test_value();
+  test_property_nodes();
+
+  for (int i = 1; i < ac; i++) {
+    try {
+      cout << "Reading " << av[i] << endl;
+      SGPropertyNode root;
+      readProperties(av[i], &root);
+      writeProperties(cout, &root, true);
+      cout << endl;
+    } catch (string &message) {
+      cout << "Aborted with " << message << endl;
+    }
+  }
+
+  return 0;
+}
diff --git a/simgear/scene/Makefile.am b/simgear/scene/Makefile.am
new file mode 100644 (file)
index 0000000..494d96c
--- /dev/null
@@ -0,0 +1,13 @@
+includedir = @includedir@/scene
+
+SUBDIRS = model
+
+# lib_LIBRARIES = libsgscene.a
+
+# noinst_HEADERS =
+
+# include_HEADERS =
+
+# libsgscene_a_SOURCES =
+
+INCLUDES = -I$(top_srcdir)
diff --git a/simgear/scene/model/Makefile.am b/simgear/scene/model/Makefile.am
new file mode 100644 (file)
index 0000000..26de102
--- /dev/null
@@ -0,0 +1,11 @@
+includedir = @includedir@/scene/model
+
+lib_LIBRARIES = libsgmodel.a
+
+noinst_HEADERS =
+
+include_HEADERS = location.hxx
+
+libsgmodel_a_SOURCES = location.cxx
+
+INCLUDES = -I$(top_srcdir)
diff --git a/simgear/scene/model/location.cxx b/simgear/scene/model/location.cxx
new file mode 100644 (file)
index 0000000..033cf61
--- /dev/null
@@ -0,0 +1,283 @@
+// location.cxx -- class for determining model location in the flightgear world.
+//
+// Written by Jim Wilson, David Megginson, started April 2002.
+// Based largely on code by Curtis Olson and Norman Vine.
+//
+// Copyright (C) 2002  Curtis L. Olson - curt@flightgear.org
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License as
+// published by the Free Software Foundation; either version 2 of the
+// License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+//
+// $Id$
+
+
+#include <simgear/compiler.h>
+
+#ifdef HAVE_CONFIG_H
+#  include <simgear_config.h>
+#endif
+
+#include <simgear/debug/logstream.hxx>
+#include <simgear/constants.h>
+#include <simgear/math/point3d.hxx>
+#include <simgear/math/polar3d.hxx>
+#include <simgear/math/sg_geodesy.hxx>
+#include <simgear/math/vector.hxx>
+
+// #include <Scenery/scenery.hxx>
+// #include "globals.hxx"
+
+#include "location.hxx"
+
+
+/**
+ * make model transformation Matrix - based on optimizations by NHV
+ */
+static void MakeTRANS( sgMat4 dst, const double Theta,
+                       const double Phi, const double Psi, 
+                        const sgMat4 UP)
+{
+    SGfloat cosTheta = (SGfloat) cos(Theta);
+    SGfloat sinTheta = (SGfloat) sin(Theta);
+    SGfloat cosPhi   = (SGfloat) cos(Phi);
+    SGfloat sinPhi   = (SGfloat) sin(Phi);
+    SGfloat sinPsi   = (SGfloat) sin(Psi) ;
+    SGfloat cosPsi   = (SGfloat) cos(Psi) ;
+
+    sgMat4 tmp;
+       
+    tmp[0][0] = cosPhi * cosTheta;
+    tmp[0][1] =        sinPhi * cosPsi + cosPhi * -sinTheta * -sinPsi;
+    tmp[0][2] =        sinPhi * sinPsi + cosPhi * -sinTheta * cosPsi;
+
+    tmp[1][0] = -sinPhi * cosTheta;
+    tmp[1][1] =        cosPhi * cosPsi + -sinPhi * -sinTheta * -sinPsi;
+    tmp[1][2] =        cosPhi * sinPsi + -sinPhi * -sinTheta * cosPsi;
+       
+    tmp[2][0] = sinTheta;
+    tmp[2][1] =        cosTheta * -sinPsi;
+    tmp[2][2] =        cosTheta * cosPsi;
+       
+    float a = UP[0][0];
+    float b = UP[1][0];
+    float c = UP[2][0];
+    dst[2][0] = a*tmp[0][0] + b*tmp[0][1] + c*tmp[0][2] ;
+    dst[1][0] = a*tmp[1][0] + b*tmp[1][1] + c*tmp[1][2] ;
+    dst[0][0] = -(a*tmp[2][0] + b*tmp[2][1] + c*tmp[2][2]) ;
+    dst[3][0] = SG_ZERO ;
+
+    a = UP[0][1];
+    b = UP[1][1];
+    c = UP[2][1];
+    dst[2][1] = a*tmp[0][0] + b*tmp[0][1] + c*tmp[0][2] ;
+    dst[1][1] = a*tmp[1][0] + b*tmp[1][1] + c*tmp[1][2] ;
+    dst[0][1] = -(a*tmp[2][0] + b*tmp[2][1] + c*tmp[2][2]) ;
+    dst[3][1] = SG_ZERO ;
+
+    a = UP[0][2];
+    c = UP[2][2];
+    dst[2][2] = a*tmp[0][0] + c*tmp[0][2] ;
+    dst[1][2] = a*tmp[1][0] + c*tmp[1][2] ;
+    dst[0][2] = -(a*tmp[2][0] + c*tmp[2][2]) ;
+    dst[3][2] = SG_ZERO ;
+
+    dst[2][3] = SG_ZERO ;
+    dst[1][3] = SG_ZERO ;
+    dst[0][3] = SG_ZERO ;
+    dst[3][3] = SG_ONE ;
+
+}
+
+
+////////////////////////////////////////////////////////////////////////
+// Implementation of FGLocation.
+////////////////////////////////////////////////////////////////////////
+
+// Constructor
+FGLocation::FGLocation( void ):
+    _dirty(true),
+    _lon_deg(0),
+    _lat_deg(0),
+    _alt_ft(0),
+    _roll_deg(0),
+    _pitch_deg(0),
+    _heading_deg(0),
+    _cur_elev_m(0),
+    _tile_center(0)
+{
+    sgdZeroVec3(_absolute_view_pos);
+    sgZeroVec3(_relative_view_pos);
+    sgZeroVec3(_zero_elev_view_pos);
+    sgMakeRotMat4( UP, 0.0, 0.0, 0.0 );
+    sgMakeRotMat4( TRANS, 0.0, 0.0, 0.0 );
+}
+
+
+// Destructor
+FGLocation::~FGLocation( void ) {
+}
+
+void
+FGLocation::init ()
+{
+}
+
+void
+FGLocation::bind ()
+{
+}
+
+void
+FGLocation::unbind ()
+{
+}
+
+void
+FGLocation::setPosition (double lon_deg, double lat_deg, double alt_ft)
+{
+  _dirty = true;
+  _lon_deg = lon_deg;
+  _lat_deg = lat_deg;
+  _alt_ft = alt_ft;
+}
+
+void
+FGLocation::setOrientation (double roll_deg, double pitch_deg, double heading_deg)
+{
+  _dirty = true;
+  _roll_deg = roll_deg;
+  _pitch_deg = pitch_deg;
+  _heading_deg = heading_deg;
+}
+
+double *
+FGLocation::get_absolute_view_pos( const Point3D scenery_center ) 
+{
+    if ( _dirty ) {
+        recalc( scenery_center );
+    }
+    return _absolute_view_pos;
+}
+
+float *
+FGLocation::getRelativeViewPos( const Point3D scenery_center ) 
+{
+    if ( _dirty ) {
+        recalc( scenery_center );
+    }
+    return _relative_view_pos;
+}
+
+float *
+FGLocation::getZeroElevViewPos( const Point3D scenery_center ) 
+{
+    if ( _dirty ) {
+        recalc( scenery_center );
+    }
+    return _zero_elev_view_pos;
+}
+
+
+// recalc() is done every time one of the setters is called (making the 
+// cached data "dirty") on the next "get".  It calculates all the outputs 
+// for viewer.
+void
+FGLocation::recalc( const Point3D scenery_center )
+{
+
+  recalcPosition( _lon_deg, _lat_deg, _alt_ft, scenery_center );
+
+  // Make the world up rotation matrix for eye positioin...
+  sgMakeRotMat4( UP, _lon_deg, 0.0, -_lat_deg );
+
+
+  // get the world up radial vector from planet center for output
+  sgSetVec3( _world_up, UP[0][0], UP[0][1], UP[0][2] );
+
+  // Creat local matrix with current geodetic position.  Converting
+  // the orientation (pitch/roll/heading) to vectors.
+  MakeTRANS( TRANS, _pitch_deg * SG_DEGREES_TO_RADIANS,
+                      _roll_deg * SG_DEGREES_TO_RADIANS,
+                      -_heading_deg * SG_DEGREES_TO_RADIANS,
+                      UP);
+
+  // Given a vector pointing straight down (-Z), map into onto the
+  // local plane representing "horizontal".  This should give us the
+  // local direction for moving "south".
+  sgVec3 minus_z;
+  sgSetVec3( minus_z, 0.0, 0.0, -1.0 );
+
+  sgmap_vec_onto_cur_surface_plane(_world_up, _relative_view_pos, minus_z,
+                                    _surface_south);
+  sgNormalizeVec3(_surface_south);
+
+  // now calculate the surface east vector
+  sgVec3 world_down;
+  sgNegateVec3(world_down, _world_up);
+  sgVectorProductVec3(_surface_east, _surface_south, world_down);
+
+  set_clean();
+}
+
+void
+FGLocation::recalcPosition( double lon_deg, double lat_deg, double alt_ft,
+                            const Point3D scenery_center ) const
+{
+  double sea_level_radius_m;
+  double lat_geoc_rad;
+
+
+                               // Convert from geodetic to geocentric
+                               // coordinates.
+  sgGeodToGeoc(lat_deg * SGD_DEGREES_TO_RADIANS,
+              alt_ft * SG_FEET_TO_METER,
+              &sea_level_radius_m,
+              &lat_geoc_rad);
+
+                               // Calculate the cartesian coordinates
+                               // of point directly below at sea level.
+                                // aka Zero Elevation Position
+  Point3D p = Point3D(lon_deg * SG_DEGREES_TO_RADIANS,
+                     lat_geoc_rad,
+                     sea_level_radius_m);
+  Point3D tmp = sgPolarToCart3d(p) - _tile_center;
+  sgSetVec3(_zero_elev_view_pos, tmp[0], tmp[1], tmp[2]);
+
+                               // Calculate the absolute view position
+                               // in fgfs coordinates.
+                                // aka Absolute View Position
+  p.setz(p.radius() + alt_ft * SG_FEET_TO_METER);
+  tmp = sgPolarToCart3d(p);
+  sgdSetVec3(_absolute_view_pos, tmp[0], tmp[1], tmp[2]);
+
+                               // Calculate the relative view position
+                               // from the scenery center.
+                                // aka Relative View Position
+
+  // FIXME: view position should ONLY be calculated in the viewer...
+  // Anything else should calculate their own positions relative to the 
+  // viewer's tile_center.
+  sgdVec3 center;
+  sgdSetVec3( center,
+              scenery_center.x(), scenery_center.y(), scenery_center.z() );
+  sgdVec3 view_pos;
+  sgdSubVec3(view_pos, _absolute_view_pos, center);
+  sgSetVec3(_relative_view_pos, view_pos);
+
+}
+
+void
+FGLocation::update (int dt)
+{
+}
diff --git a/simgear/scene/model/location.hxx b/simgear/scene/model/location.hxx
new file mode 100644 (file)
index 0000000..0cba637
--- /dev/null
@@ -0,0 +1,187 @@
+// location.hxx -- class for determining model location in the flightgear world.
+//
+// Written by Jim Wilson, David Megginson, started April 2002.
+//
+// Copyright (C) 2002  Jim Wilson, David Megginson
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License as
+// published by the Free Software Foundation; either version 2 of the
+// License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+//
+// $Id$
+
+
+#ifndef _LOCATION_HXX
+#define _LOCATION_HXX
+
+
+#ifndef __cplusplus                                                          
+# error This library requires C++
+#endif                                   
+
+#include <simgear/compiler.h>
+#include <simgear/constants.h>
+#include <simgear/math/point3d.hxx>
+
+#include <plib/sg.h>           // plib include
+
+// #include "fgfs.hxx"
+
+
+// Define a structure containing view information
+class FGLocation
+{
+
+public:
+
+    // Constructor
+    FGLocation( void );
+
+    // Destructor
+    virtual ~FGLocation( void );
+
+    //////////////////////////////////////////////////////////////////////
+    // Part 1: standard FGSubsystem implementation.
+    //////////////////////////////////////////////////////////////////////
+
+    virtual void init ();
+    virtual void bind ();
+    virtual void unbind ();
+    void update (int dt);
+
+
+    //////////////////////////////////////////////////////////////////////
+    // Part 2: user settings.
+    //////////////////////////////////////////////////////////////////////
+
+    // Geodetic position of model...
+    virtual double getLongitude_deg () const { return _lon_deg; }
+    virtual double getLatitude_deg () const { return _lat_deg; }
+    virtual double getAltitudeASL_ft () const { return _alt_ft; }
+    virtual void setPosition (double lon_deg, double lat_deg, double alt_ft);
+
+
+    // Reference orientation rotations...
+    //   These are rotations that represent the plane attitude effect on
+    //   the view (in Pilot view).  IE The view frustrum rotates as the plane
+    //   turns, pitches, and rolls.
+    //   In model view (lookat/chaseview) these end up changing the angle that
+    //   the eye is looking at the ojbect (ie the model).
+    //   FIXME: the FGModel class should have its own version of these so that
+    //   it can generate it's own model rotations.
+    virtual double getRoll_deg () const { return _roll_deg; }
+    virtual double getPitch_deg () const {return _pitch_deg; }
+    virtual double getHeading_deg () const {return _heading_deg; }
+    virtual void setOrientation (double roll_deg, double pitch_deg, double heading_deg);
+
+
+    //////////////////////////////////////////////////////////////////////
+    // Part 3: output vectors and matrices in FlightGear coordinates.
+    //////////////////////////////////////////////////////////////////////
+
+    // Vectors and positions...
+
+    // Get zero view_pos
+    virtual float * get_view_pos() { return _relative_view_pos; }
+    // Get the absolute view position in fgfs coordinates.
+    virtual double * get_absolute_view_pos( const Point3D scenery_center );
+    // Get zero elev
+    virtual float * get_zero_elev() { return _zero_elev_view_pos; }
+    // Get world up vector
+    virtual float *get_world_up() { return _world_up; }
+    // Get the relative (to scenery center) view position in fgfs coordinates.
+    virtual float * getRelativeViewPos( const Point3D scenery_center );
+    // Get the absolute zero-elevation view position in fgfs coordinates.
+    virtual float * getZeroElevViewPos( const Point3D scenery_center );
+    // Get surface east vector
+    virtual float *get_surface_east() {        return _surface_east; }
+    // Get surface south vector
+    virtual float *get_surface_south() { return _surface_south; }
+    // Elevation of ground under location (based on scenery output)...
+    void set_cur_elev_m ( double elev ) { _cur_elev_m = elev; }
+    inline double get_cur_elev_m () { return _cur_elev_m; }
+    // Interface to current buckets for use with tilemgr...
+    void set_tile_center ( Point3D tile_center ) { _tile_center = tile_center; }
+    inline Point3D get_tile_center () { return _tile_center; }
+
+    // Matrices...
+    virtual const sgVec4 *getTransformMatrix( const Point3D scenery_center ) {
+        if ( _dirty ) {
+            recalc( scenery_center );
+        }
+       return TRANS;
+    }
+    virtual const sgVec4 *getCachedTransformMatrix() { return TRANS; }
+    virtual const sgVec4 *getUpMatrix( const Point3D scenery_center )  {
+        if ( _dirty ) {
+            recalc( scenery_center );
+        }
+       return UP;
+    }
+    virtual const sgVec4 *getCachedUpMatrix() { return UP; }
+
+
+private:
+
+    //////////////////////////////////////////////////////////////////
+    // private data                                                 //
+    //////////////////////////////////////////////////////////////////
+
+    // flag forcing a recalc of derived view parameters
+    bool _dirty;
+
+    mutable sgdVec3 _absolute_view_pos;
+    mutable sgVec3 _relative_view_pos;
+    mutable sgVec3 _zero_elev_view_pos;
+
+    double _lon_deg;
+    double _lat_deg;
+    double _alt_ft;
+
+    double _roll_deg;
+    double _pitch_deg;
+    double _heading_deg;
+
+    // elevation of ground under this location...
+    double _cur_elev_m;
+    Point3D _tile_center;
+
+    // surface vector heading south
+    sgVec3 _surface_south;
+
+    // surface vector heading east (used to unambiguously align sky
+    // with sun)
+    sgVec3 _surface_east;
+
+    // world up vector (normal to the plane tangent to the earth's
+    // surface at the spot we are directly above
+    sgVec3 _world_up;
+
+    // sg versions of our friendly matrices
+    sgMat4 TRANS, UP;
+
+    //////////////////////////////////////////////////////////////////
+    // private functions                                            //
+    //////////////////////////////////////////////////////////////////
+
+    void recalc( const Point3D scenery_center );
+    void recalcPosition( double lon_deg, double lat_deg, double alt_ft,
+                         const Point3D scenery_center ) const;
+
+    inline void set_dirty() { _dirty = true; }
+    inline void set_clean() { _dirty = false; }
+
+};
+
+
+#endif // _LOCATION_HXX