#include "FGDeviceConfigurationMap.hxx"
+#include <boost/foreach.hpp>
+
#include <simgear/misc/sg_dir.hxx>
#include <simgear/props/props_io.hxx>
+#include <simgear/structure/exception.hxx>
+
#include <Main/globals.hxx>
+#include <Navaids/NavDataCache.hxx>
using simgear::PropertyList;
+using std::string;
-FGDeviceConfigurationMap::FGDeviceConfigurationMap( const char * relative_path, SGPropertyNode_ptr aBase, const char * aChildname ) :
- base(aBase),
- childname(aChildname)
+FGDeviceConfigurationMap::FGDeviceConfigurationMap( const string& relative_path)
{
- int index = 1000;
- scan_dir( SGPath(globals->get_fg_home(), relative_path), &index);
- scan_dir( SGPath(globals->get_fg_root(), relative_path), &index);
+ scan_dir( SGPath(globals->get_fg_home(), relative_path));
+ scan_dir( SGPath(globals->get_fg_root(), relative_path));
+}
- PropertyList childNodes = base->getChildren(childname);
- for (int k = (int)childNodes.size() - 1; k >= 0; k--) {
- SGPropertyNode *n = childNodes[k];
- PropertyList names = n->getChildren("name");
- if (names.size() ) // && (n->getChildren("axis").size() || n->getChildren("button").size()))
- for (unsigned int j = 0; j < names.size(); j++)
- (*this)[names[j]->getStringValue()] = n;
+FGDeviceConfigurationMap::~FGDeviceConfigurationMap()
+{
+}
+
+SGPropertyNode_ptr
+FGDeviceConfigurationMap::configurationForDeviceName(const std::string& name)
+{
+ NamePathMap::iterator it = namePathMap.find(name);
+ if (it == namePathMap.end()) {
+ return SGPropertyNode_ptr();
+ }
+
+ SGPropertyNode_ptr result(new SGPropertyNode);
+ try {
+ readProperties(it->second.str(), result);
+ result->setStringValue("source", it->second.c_str());
+ } catch (sg_exception& e) {
+ SG_LOG(SG_INPUT, SG_WARN, "parse failure reading:" << it->second);
+ return NULL;
}
+ return result;
}
-FGDeviceConfigurationMap::~FGDeviceConfigurationMap()
+bool FGDeviceConfigurationMap::hasConfiguration(const std::string& name) const
{
- // Ensure that the children don't hang around when deleted, as if
- // re-created, we need to ensure that the set of names doesn't contain
- // any unexpected history.
- base->removeChildren( childname, false );
+ return namePathMap.find(name) != namePathMap.end();
}
-void FGDeviceConfigurationMap::scan_dir(const SGPath & path, int *index)
+void FGDeviceConfigurationMap::scan_dir(const SGPath & path)
{
+ if (!path.exists())
+ return;
+
simgear::Dir dir(path);
simgear::PathList children = dir.children(simgear::Dir::TYPE_FILE |
simgear::Dir::TYPE_DIR | simgear::Dir::NO_DOT_OR_DOTDOT);
-
- for (unsigned int c=0; c<children.size(); ++c) {
- SGPath path(children[c]);
+ flightgear::NavDataCache* cache = flightgear::NavDataCache::instance();
+
+ BOOST_FOREACH(SGPath path, children) {
if (path.isDir()) {
- scan_dir(path, index);
+ scan_dir(path);
} else if (path.extension() == "xml") {
- SG_LOG(SG_INPUT, SG_DEBUG, "Reading joystick file " << path.str());
- SGPropertyNode_ptr n = base->getChild(childname, (*index)++, true);
- readProperties(path.str(), n);
- n->setStringValue("source", path.c_str());
- }
- }
+ if (cache->isCachedFileModified(path)) {
+ refreshCacheForFile(path);
+ } else {
+ readCachedData(path);
+ } // of cached file stamp is valid
+ } // of child is a file with '.xml' extension
+ } // of directory children iteration
}
+void FGDeviceConfigurationMap::readCachedData(const SGPath& path)
+{
+ flightgear::NavDataCache* cache = flightgear::NavDataCache::instance();
+ NamePathMap::iterator it;
+ BOOST_FOREACH(string s, cache->readStringListProperty(path.str())) {
+ // important - only insert if not already present. This ensures
+ // user configs can override those in the base package, since they are
+ // searched first.
+ it = namePathMap.find(s);
+ if (it == namePathMap.end()) {
+ namePathMap.insert(std::make_pair(s, path));
+ }
+ } // of cached names iteration
+}
+void FGDeviceConfigurationMap::refreshCacheForFile(const SGPath& path)
+{
+ SG_LOG(SG_INPUT, SG_DEBUG, "Reading joystick file " << path.str());
+ SGPropertyNode_ptr n(new SGPropertyNode);
+ try {
+ readProperties(path.str(), n);
+ } catch (sg_exception& e) {
+ SG_LOG(SG_INPUT, SG_WARN, "parse failure reading:" << path);
+ return;
+ }
+
+ NamePathMap::iterator it;
+ string_list names;
+ BOOST_FOREACH(SGPropertyNode* nameProp, n->getChildren("name")) {
+ names.push_back(nameProp->getStringValue());
+ // same comment as readCachedData: only insert if not already present
+ it = namePathMap.find(names.back());
+ if (it == namePathMap.end()) {
+ namePathMap.insert(std::make_pair(names.back(), path));
+ }
+ }
+
+ flightgear::NavDataCache* cache = flightgear::NavDataCache::instance();
+ cache->stampCacheFile(path);
+ cache->writeStringListProperty(path.str(), names);
+}
#endif
#include <simgear/props/props.hxx>
+#include <simgear/misc/sg_path.hxx>
#include <map>
-class SGPath;
-
-class FGDeviceConfigurationMap : public std::map<std::string,SGPropertyNode_ptr> {
+class FGDeviceConfigurationMap
+{
public:
- FGDeviceConfigurationMap ( const char * relative_path, SGPropertyNode_ptr base, const char * childname );
+ FGDeviceConfigurationMap ( const std::string& relative_path);
virtual ~FGDeviceConfigurationMap();
+
+ SGPropertyNode_ptr configurationForDeviceName(const std::string& name);
+
+ bool hasConfiguration(const std::string& name) const;
private:
- void scan_dir(const SGPath & path, int *index);
- SGPropertyNode_ptr base;
- const char * childname;
+ void scan_dir(const SGPath & path);
+
+ void readCachedData(const SGPath& path);
+ void refreshCacheForFile(const SGPath& path);
+
+ typedef std::map<std::string, SGPath> NamePathMap;
+// mapping from joystick name to XML configuration file path
+ NamePathMap namePathMap;
};
#endif
for (int i = 0; i < MAX_JOYSTICKS; i++)
{
// do not remove predefined joysticks info on reinit
- if ((all)||(!bindings[i].predefined))
+ if (all || (!bindings[i].predefined))
js_nodes->removeChild("js", i, false);
if (bindings[i].js)
delete bindings[i].js;
SGPropertyNode_ptr js_nodes = fgGetNode("/input/joysticks", true);
status_node = fgGetNode("/devices/status/joysticks", true);
- FGDeviceConfigurationMap configMap("Input/Joysticks", js_nodes, "js-named");
+ FGDeviceConfigurationMap configMap("Input/Joysticks");
for (int i = 0; i < MAX_JOYSTICKS; i++) {
jsJoystick * js = new jsJoystick(i);
SG_LOG(SG_INPUT, SG_INFO, "Looking for bindings for joystick \"" << name << '"');
SGPropertyNode_ptr named;
- if ((named = configMap[name])) {
+ if (configMap.hasConfiguration(name)) {
+ named = configMap.configurationForDeviceName(name);
string source = named->getStringValue("source", "user defined");
SG_LOG(SG_INPUT, SG_INFO, "... found joystick: " << source);
- } else if ((named = configMap["default"])) {
+ } else if ((named = configMap.configurationForDeviceName("default"))) {
string source = named->getStringValue("source", "user defined");
SG_LOG(SG_INPUT, SG_INFO, "No config found for joystick \"" << name
<< "\"\nUsing default: \"" << source << '"');
void prepareQueries()
{
clearProperty = prepare("DELETE FROM properties WHERE key=?1");
- writePropertyMulti = prepare("INSERT INTO properties (key, value) VALUES(?,?)");
+ writePropertyMulti = prepare("INSERT INTO properties (key, value) VALUES(?1,?2)");
#define POSITIONED_COLS "rowid, type, ident, name, airport, lon, lat, elev_m, octree_node"
#define AND_TYPED "AND type>=?2 AND type <=?3"
sqlite_bind_stdstring(d->readPropertyQuery, 1, key);
string_list result;
while (d->stepSelect(d->readPropertyQuery)) {
- result.push_back((char*) sqlite3_column_text(d->readPropertyQuery, 1));
+ result.push_back((char*) sqlite3_column_text(d->readPropertyQuery, 0));
}
return result;
sqlite_bind_stdstring(d->clearProperty, 1, key);
d->execUpdate(d->clearProperty);
- sqlite_bind_stdstring(d->writePropertyMulti, 1, key);
BOOST_FOREACH(string value, values) {
d->reset(d->writePropertyMulti);
+ sqlite_bind_stdstring(d->writePropertyMulti, 1, key);
sqlite_bind_stdstring(d->writePropertyMulti, 2, value);
d->execInsert(d->writePropertyMulti);
}