]> git.mxchange.org Git - flightgear.git/blobdiff - src/GUI/property_list.cxx
Bugfix - untie property.
[flightgear.git] / src / GUI / property_list.cxx
index 28dc91973d816bf4bb6c64a6546db67e0cbeb36e..5bcb46a3542cbca9a729460102433b3fd0cfbfae 100644 (file)
 
 #include <simgear/compiler.h>
 
-#include STL_STRING
-SG_USING_STD(string);
+#include <sstream>
+#include <iomanip>
+#include <iostream>
+#include <string>
+using std::string;
+using std::cout;
+
 typedef string stdString;      // puObject has a "string" member
 
 #include <Main/fg_os.hxx>      // fgGetKeyModifiers()
@@ -37,55 +42,127 @@ typedef string stdString;      // puObject has a "string" member
 #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)
 {
+    using namespace simgear;
     string result;
 
-    if (!node)
-        return "unknown";
-
-    SGPropertyNode::Type type = node->getType();
-    if (type == SGPropertyNode::UNSPECIFIED)
+    props::Type type = node->getType();
+    if (type == props::UNSPECIFIED)
         result = "unspecified";
-    else if (type == SGPropertyNode::NONE)
+    else if (type == props::NONE)
         result = "none";
-    else if (type == SGPropertyNode::BOOL)
+    else if (type == props::BOOL)
         result = "bool";
-    else if (type == SGPropertyNode::INT)
+    else if (type == props::INT)
         result = "int";
-    else if (type == SGPropertyNode::LONG)
+    else if (type == props::LONG)
         result = "long";
-    else if (type == SGPropertyNode::FLOAT)
+    else if (type == props::FLOAT)
         result = "float";
-    else if (type == SGPropertyNode::DOUBLE)
+    else if (type == props::DOUBLE)
         result = "double";
-    else if (type == SGPropertyNode::STRING)
+    else if (type == props::STRING)
         result = "string";
+    else if (type == props::VEC3D)
+        result = "vec3d";
+    else if (type == props::VEC4D)
+        result = "vec4d";
 
     return result;
 }
 
 
+static void dumpProperties(const SGPropertyNode *node)
+{
+    using namespace simgear;
+    cout << node->getPath() << '/' << endl;
+    for (int i = 0; i < node->nChildren(); i++) {
+        const SGPropertyNode *c = node->getChild(i);
+        props::Type type = c->getType();
+        if (type == props::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 props::DOUBLE:
+        case props::FLOAT:
+        case props::VEC3D:
+        case props::VEC4D:
+        {
+            streamsize precision = cout.precision(15);
+            c->printOn(cout);
+            cout.precision(precision);
+        }
+            break;
+        case props::LONG:
+        case props::INT:
+        case props::BOOL:
+            c->printOn(cout);
+            break;
+        case props::STRING:
+            cout << '"' << c->getStringValue() << '"';
+            break;
+        case props::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);
@@ -97,9 +174,7 @@ PropertyList::PropertyList(int minx, int miny, int maxx, int maxy, SGPropertyNod
 
 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();
 }
 
 
@@ -111,10 +186,6 @@ void PropertyList::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;
@@ -126,15 +197,20 @@ void PropertyList::handle_select(puObject *list_box)
 {
     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 (!strcmp(src, ".")) {
-                if (mod_ctrl)
-                    prop_list->toggleFlags();
+        if (prop_list->_dot_files && (selected < 2)) {
+            if (src[0] == '.' && (src[1] == '\0' || src[1] == ' ')) {
+                if (mod_ctrl && mod_shift)
+                    prop_list->_curr->fireValueChanged();
+                else if (mod_ctrl)
+                    prop_list->toggleVerbosity();
+                else if (mod_shift)
+                    dumpProperties(prop_list->_curr);
 
                 prop_list->update();
                 return;
@@ -153,28 +229,28 @@ void PropertyList::handle_select(puObject *list_box)
 
         // 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;
         }
 
         // it is a regular property
-        if (child->getType() == SGPropertyNode::BOOL && mod_ctrl) {
+        if (child->getType() == simgear::props::BOOL && mod_ctrl) {
             child->setBoolValue(!child->getBoolValue());
-            prop_list->update();
+            prop_list->update(true);
         } else
             prop_list->publish(child);
 
     } else {
         // the user clicked on blank screen
-        prop_list->update();
+        prop_list->update(true);
     }
 }
 
@@ -182,7 +258,6 @@ void PropertyList::handle_select(puObject *list_box)
 void PropertyList::update(bool restore_pos)
 {
     int pi;
-    int i;
 
     delete_arrays();
     _num_entries = (int)_curr->nChildren();
@@ -191,43 +266,36 @@ void PropertyList::update(bool restore_pos)
     if (!_curr->getParent()) {
         _entries = new char*[_num_entries + 1];
         pi = 0;
-        dotFiles = false;
+        _dot_files = false;
 
     } else {
         _num_entries += 2;    // for . and ..
         _entries = new char*[_num_entries + 1];
 
-        _entries[0] = new char[2];
-        strcpy(_entries[0], ".");
+        _entries[0] = new char[16];
+        strcpy(_entries[0], _verbose ? ".     [verbose]" : ".");
 
         _entries[1] = new char[3];
         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;
@@ -239,71 +307,98 @@ void PropertyList::update(bool restore_pos)
 }
 
 
-void PropertyList::updateTextForEntry(int index)
+void PropertyList::updateTextForEntry(NodeData& data)
 {
-    assert((index >= 0) && (index < _num_children));
-    SGPropertyNode_ptr node = _children[index];
-
+    using namespace simgear;
+    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() == props::STRING
+                || node->getType() == props::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);
-
-    if (dotFiles)
-        index += 2;
+    stdString out = line.str();
+    if (out.size() >= PUSTRING_MAX)
+        out.resize(PUSTRING_MAX - 1);
 
-    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)
 {
-    SGPropertyNode *p;
     try {
-        p = fgGetNode(s, false);
+        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: " << m);
-        return;
+        SG_LOG(SG_GENERAL, SG_DEBUG, "property-list node '" << s << "': " << m);
     }
-    setCurrent(p);
 }