#include <simgear/compiler.h>
+#include <sstream>
+#include STL_IOMANIP
+#include <iostream>
#include STL_STRING
SG_USING_STD(string);
+using std::cout;
+
typedef string stdString; // puObject has a "string" member
#include <Main/fg_os.hxx> // fgGetKeyModifiers()
#include "property_list.hxx"
-static int nodeNameCompare(const void *ppNode1, const void *ppNode2)
-{
- const SGPropertyNode_ptr pNode1 = *(const SGPropertyNode_ptr *)ppNode1;
- const SGPropertyNode_ptr pNode2 = *(const SGPropertyNode_ptr *)ppNode2;
-
- int diff = strcmp(pNode1->getName(), pNode2->getName());
- return diff ? diff : pNode1->getIndex() - pNode2->getIndex();
-}
-
-
static string getValueTypeString(const SGPropertyNode *node)
{
string result;
- if (!node)
- return "unknown";
-
SGPropertyNode::Type type = node->getType();
if (type == SGPropertyNode::UNSPECIFIED)
result = "unspecified";
}
+static void dumpProperties(const SGPropertyNode *node)
+{
+ cout << node->getPath() << '/' << endl;
+ for (int i = 0; i < node->nChildren(); i++) {
+ const SGPropertyNode *c = node->getChild(i);
+ SGPropertyNode::Type type = c->getType();
+ if (type == SGPropertyNode::ALIAS || c->nChildren())
+ continue;
+
+ int index = c->getIndex();
+ cout << std::setw(11) << getValueTypeString(c) << " " << c->getName();
+ if (index > 0)
+ cout << '[' << index << ']';
+ cout << " = ";
+
+ switch (c->getType()) {
+ case SGPropertyNode::DOUBLE:
+ case SGPropertyNode::FLOAT:
+ cout << std::setprecision(15) << c->getDoubleValue();
+ break;
+ case SGPropertyNode::LONG:
+ case SGPropertyNode::INT:
+ cout << c->getLongValue();
+ break;
+ case SGPropertyNode::BOOL:
+ cout << (c->getBoolValue() ? "true" : "false");
+ break;
+ case SGPropertyNode::STRING:
+ cout << '"' << c->getStringValue() << '"';
+ break;
+ case SGPropertyNode::NONE:
+ break;
+ default:
+ cout << '\'' << c->getStringValue() << '\'';
+ }
+ cout << endl;
+ }
+ cout << endl;
+}
+
+
+static void sanitize(stdString& s)
+{
+ stdString r = s;
+ s = "";
+ for (unsigned i = 0; i < r.size(); i++) {
+ if (r[i] == '\a')
+ s += "\\a";
+ else if (r[i] == '\b')
+ s += "\\b";
+ else if (r[i] == '\t')
+ s += "\\t";
+ else if (r[i] == '\n')
+ s += "\\n";
+ else if (r[i] == '\v')
+ s += "\\v";
+ else if (r[i] == '\f')
+ s += "\\f";
+ else if (r[i] == '\r')
+ s += "\\r";
+ else if (r[i] == '\'')
+ s += "\'";
+ else if (r[i] == '\\')
+ s += "\\";
+ else if (isprint(r[i]))
+ s += r[i];
+ else {
+ const char *hex = "0123456789abcdef";
+ int c = r[i] & 255;
+ s += stdString("\\x") + hex[c / 16] + hex[c % 16];
+ }
+ }
+}
+
+
PropertyList::PropertyList(int minx, int miny, int maxx, int maxy, SGPropertyNode *start) :
- puList(minx, miny, maxx, maxy, short(0), 20),
+ puaList(minx, miny, maxx, maxy, short(0), 20),
GUI_ID(FGCLASS_PROPERTYLIST),
_curr(start),
- _flags(fgGetNode("/sim/gui/dialogs/property-browser/show-flags", true)),
_return(0),
_entries(0),
- _num_entries(0)
+ _num_entries(0),
+ _verbose(false)
{
_list_box->setUserData(this);
PropertyList::~PropertyList()
{
- // FIXME this seems to cause a crash, which is probably why
- // commented out in prop_picker.cxx since many years
- //delete_arrays();
+ delete_arrays();
}
for (int i = 0; i < _num_entries; i++)
delete[] _entries[i];
- for (int j = 0; j < _num_children; j++)
- if (!_children[j]->nChildren())
- _children[j]->removeChangeListener(this);
-
delete[] _entries;
delete[] _children;
_entries = 0;
{
PropertyList *prop_list = (PropertyList *)list_box->getUserData();
int selected = list_box->getIntegerValue();
- bool mod_ctrl = fgGetKeyModifiers() & KEYMOD_CTRL;
+ int mod_ctrl = fgGetKeyModifiers() & KEYMOD_CTRL;
+ int mod_shift = fgGetKeyModifiers() & KEYMOD_SHIFT;
if (selected >= 0 && selected < prop_list->_num_entries) {
const char *src = prop_list->_entries[selected];
- if (prop_list->dotFiles && (selected < 2)) {
+ if (prop_list->_dot_files && (selected < 2)) {
if (!strcmp(src, ".")) {
if (mod_ctrl)
- prop_list->toggleFlags();
+ prop_list->toggleVerbosity();
+ else if (mod_shift)
+ dumpProperties(prop_list->_curr);
prop_list->update();
return;
// we know we're dealing with a regular entry, so convert
// it to an index into children[]
- if (prop_list->dotFiles)
+ if (prop_list->_dot_files)
selected -= 2;
- SGPropertyNode_ptr child = prop_list->_children[selected];
- assert(child);
+ SGPropertyNode_ptr child = prop_list->_children[selected].node;
// check if it's a directory
if (child->nChildren()) {
+ prop_list->setTopItem(0);
prop_list->setCurrent(child);
return;
}
if (!_curr->getParent()) {
_entries = new char*[_num_entries + 1];
pi = 0;
- dotFiles = false;
+ _dot_files = false;
} else {
_num_entries += 2; // for . and ..
strcpy(_entries[1], "..");
pi = 2;
- dotFiles = true;
+ _dot_files = true;
}
int i;
_num_children = _curr->nChildren();
- _children = new SGPropertyNode_ptr[_num_children];
+ _children = new NodeData[_num_children];
for (i = 0; i < _num_children; i++)
- _children[i] = _curr->getChild(i);
+ _children[i].node = _curr->getChild(i);
qsort(_children, _num_children, sizeof(_children[0]), nodeNameCompare);
// Make lists of the children's names, values, etc.
for (i = 0; i < _num_children; i++, pi++) {
- SGPropertyNode *child = _children[i];
-
- if (child->nChildren() > 0) {
- stdString name = stdString(child->getDisplayName(true)) + '/';
- _entries[pi] = new char[name.size() + 1];
- strcpy(_entries[pi], name.c_str());
-
- } else {
- _entries[pi] = 0; // ensure it's 0 before setting intial value
- updateTextForEntry(i);
- child->addChangeListener(this);
- }
+ _children[i].text = &_entries[pi];
+ _entries[pi] = 0; // make it deletable
+ updateTextForEntry(_children[i]);
+ _children[i].setListener(this);
}
_entries[_num_entries] = 0;
}
-void PropertyList::updateTextForEntry(int index)
+void PropertyList::updateTextForEntry(NodeData& data)
{
- assert((index >= 0) && (index < _num_children));
- SGPropertyNode_ptr node = _children[index];
-
+ SGPropertyNode *node = data.node;
stdString name = node->getDisplayName(true);
stdString type = getValueTypeString(node);
stdString value = node->getStringValue();
- stdString line = name + " = '" + value + "' (" + type;
-
- if (_flags->getBoolValue()) {
- stdString ext;
- if (!node->getAttribute(SGPropertyNode::READ))
- ext += 'r';
- if (!node->getAttribute(SGPropertyNode::WRITE))
- ext += 'w';
- if (node->getAttribute(SGPropertyNode::TRACE_READ))
- ext += 'R';
- if (node->getAttribute(SGPropertyNode::TRACE_WRITE))
- ext += 'W';
- if (node->getAttribute(SGPropertyNode::ARCHIVE))
- ext += 'A';
- if (node->getAttribute(SGPropertyNode::USERARCHIVE))
- ext += 'U';
- if (node->isTied())
- ext += 'T';
- if (ext.size())
- line += ", " + ext;
+ std::ostringstream line;
+ line << name;
+
+ int children = node->nChildren();
+ if (children)
+ line << '/';
+
+ if (!children || (_verbose && node->hasValue())) {
+ if (node->getType() == SGPropertyNode::STRING
+ || node->getType() == SGPropertyNode::UNSPECIFIED)
+ sanitize(value);
+
+ line << " = '" << value << "' (" << type;
+
+ if (_verbose) {
+ stdString ext;
+ if (!node->getAttribute(SGPropertyNode::READ))
+ ext += 'r';
+ if (!node->getAttribute(SGPropertyNode::WRITE))
+ ext += 'w';
+ if (node->getAttribute(SGPropertyNode::TRACE_READ))
+ ext += 'R';
+ if (node->getAttribute(SGPropertyNode::TRACE_WRITE))
+ ext += 'W';
+ if (node->getAttribute(SGPropertyNode::ARCHIVE))
+ ext += 'A';
+ if (node->getAttribute(SGPropertyNode::USERARCHIVE))
+ ext += 'U';
+ if (node->isTied())
+ ext += 'T';
+
+ if (!ext.empty())
+ line << ", " << ext;
+
+ int num = node->nListeners();
+ if (data.listener)
+ num--;
+ if (num)
+ line << ", L" << num;
+ }
+ line << ')';
}
- line += ')';
-
- if (line.size() >= PUSTRING_MAX)
- line.resize(PUSTRING_MAX - 1);
+ stdString out = line.str();
+ if (out.size() >= PUSTRING_MAX)
+ out.resize(PUSTRING_MAX - 1);
- if (dotFiles)
- index += 2;
-
- delete[] _entries[index];
- _entries[index] = new char[line.size() + 1];
- strcpy(_entries[index], line.c_str());
+ delete[] *data.text;
+ *data.text = new char[out.size() + 1];
+ strcpy(*data.text, out.c_str());
}
void PropertyList::valueChanged(SGPropertyNode *nd)
{
for (int i = 0; i < _num_children; i++)
- if (_children[i] == nd) {
- updateTextForEntry(i);
+ if (_children[i].node == nd) {
+ updateTextForEntry(_children[i]);
return;
}
}
+int PropertyList::nodeNameCompare(const void *p1, const void *p2)
+{
+ const SGPropertyNode *n1 = (*(const NodeData *)p1).node;
+ const SGPropertyNode *n2 = (*(const NodeData *)p2).node;
+
+ int diff = strcmp(n1->getName(), n2->getName());
+ return diff ? diff : n1->getIndex() - n2->getIndex();
+}
+
+
void PropertyList::setValue(const char *s)
-try {
- SGPropertyNode *p = fgGetNode(s, false);
- if (p)
- setCurrent(p);
- else
- throw stdString("node doesn't exist");
-} catch (const stdString& m) {
- SG_LOG(SG_GENERAL, SG_DEBUG, "property-list node `" << s << "': " << m);
+{
+ try {
+ SGPropertyNode *p = fgGetNode(s, false);
+ if (p)
+ setCurrent(p);
+ else
+ throw stdString("node doesn't exist");
+ } catch (const stdString& m) {
+ SG_LOG(SG_GENERAL, SG_DEBUG, "property-list node '" << s << "': " << m);
+ }
}