2006-05-22 14:34:20 +00:00
|
|
|
// Implementation of the <property-list> widget.
|
|
|
|
//
|
|
|
|
// Copyright (C) 2001 Steve BAKER
|
|
|
|
// Copyright (C) 2001 Jim WILSON
|
|
|
|
// Copyright (C) 2006 Melchior FRANZ
|
|
|
|
//
|
|
|
|
// 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
|
|
//
|
|
|
|
// $Id$
|
|
|
|
|
|
|
|
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
|
|
# include <config.h>
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#include <simgear/compiler.h>
|
|
|
|
|
|
|
|
#include STL_STRING
|
|
|
|
SG_USING_STD(string);
|
|
|
|
typedef string stdString; // puObject has a "string" member
|
|
|
|
|
|
|
|
#include <Main/fg_os.hxx> // fgGetKeyModifiers()
|
|
|
|
#include <Main/fg_props.hxx>
|
|
|
|
|
|
|
|
#include "property_list.hxx"
|
|
|
|
|
|
|
|
|
|
|
|
static string getValueTypeString(const SGPropertyNode *node)
|
|
|
|
{
|
|
|
|
string result;
|
2006-05-24 21:02:52 +00:00
|
|
|
assert(node);
|
2006-05-22 14:34:20 +00:00
|
|
|
|
|
|
|
SGPropertyNode::Type type = node->getType();
|
|
|
|
if (type == SGPropertyNode::UNSPECIFIED)
|
|
|
|
result = "unspecified";
|
|
|
|
else if (type == SGPropertyNode::NONE)
|
|
|
|
result = "none";
|
|
|
|
else if (type == SGPropertyNode::BOOL)
|
|
|
|
result = "bool";
|
|
|
|
else if (type == SGPropertyNode::INT)
|
|
|
|
result = "int";
|
|
|
|
else if (type == SGPropertyNode::LONG)
|
|
|
|
result = "long";
|
|
|
|
else if (type == SGPropertyNode::FLOAT)
|
|
|
|
result = "float";
|
|
|
|
else if (type == SGPropertyNode::DOUBLE)
|
|
|
|
result = "double";
|
|
|
|
else if (type == SGPropertyNode::STRING)
|
|
|
|
result = "string";
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2006-05-24 14:00:56 +00:00
|
|
|
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 += "\\";
|
2006-05-24 21:02:52 +00:00
|
|
|
else if (isprint(r[i]))
|
2006-05-24 14:00:56 +00:00
|
|
|
s += r[i];
|
|
|
|
else {
|
|
|
|
const char *hex = "0123456789abcdef";
|
|
|
|
int c = r[i] & 255;
|
|
|
|
s += stdString("\\x") + hex[c / 16] + hex[c % 16];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2006-05-22 14:34:20 +00:00
|
|
|
|
|
|
|
|
|
|
|
PropertyList::PropertyList(int minx, int miny, int maxx, int maxy, SGPropertyNode *start) :
|
2006-05-24 21:05:48 +00:00
|
|
|
puList(minx, miny, maxx, maxy, short(0), 20),
|
2006-05-22 16:14:50 +00:00
|
|
|
GUI_ID(FGCLASS_PROPERTYLIST),
|
2006-05-22 14:34:20 +00:00
|
|
|
_curr(start),
|
|
|
|
_flags(fgGetNode("/sim/gui/dialogs/property-browser/show-flags", true)),
|
|
|
|
_return(0),
|
|
|
|
_entries(0),
|
|
|
|
_num_entries(0)
|
|
|
|
|
|
|
|
{
|
|
|
|
_list_box->setUserData(this);
|
|
|
|
_list_box->setCallback(handle_select);
|
|
|
|
_list_box->setValue(0);
|
|
|
|
update();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
PropertyList::~PropertyList()
|
|
|
|
{
|
2006-05-24 09:42:10 +00:00
|
|
|
delete_arrays();
|
2006-05-22 14:34:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void PropertyList::delete_arrays()
|
|
|
|
{
|
|
|
|
if (!_entries)
|
|
|
|
return;
|
|
|
|
|
|
|
|
for (int i = 0; i < _num_entries; i++)
|
|
|
|
delete[] _entries[i];
|
|
|
|
|
|
|
|
delete[] _entries;
|
|
|
|
delete[] _children;
|
|
|
|
_entries = 0;
|
|
|
|
_children = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void PropertyList::handle_select(puObject *list_box)
|
|
|
|
{
|
|
|
|
PropertyList *prop_list = (PropertyList *)list_box->getUserData();
|
|
|
|
int selected = list_box->getIntegerValue();
|
2006-05-24 14:00:56 +00:00
|
|
|
int mod_ctrl = fgGetKeyModifiers() & KEYMOD_CTRL;
|
2006-05-22 14:34:20 +00:00
|
|
|
|
|
|
|
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();
|
|
|
|
|
|
|
|
prop_list->update();
|
|
|
|
return;
|
|
|
|
|
|
|
|
} else if (!strcmp(src, "..")) {
|
|
|
|
SGPropertyNode *parent = prop_list->getCurrent()->getParent();
|
|
|
|
if (parent) {
|
|
|
|
if (mod_ctrl)
|
|
|
|
for (; parent->getParent(); parent = parent->getParent())
|
|
|
|
;
|
|
|
|
prop_list->setCurrent(parent);
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// we know we're dealing with a regular entry, so convert
|
|
|
|
// it to an index into children[]
|
|
|
|
if (prop_list->dotFiles)
|
|
|
|
selected -= 2;
|
|
|
|
|
2006-05-24 09:42:10 +00:00
|
|
|
SGPropertyNode_ptr child = prop_list->_children[selected].node;
|
2006-05-22 14:34:20 +00:00
|
|
|
assert(child);
|
|
|
|
|
|
|
|
// check if it's a directory
|
|
|
|
if (child->nChildren()) {
|
2006-05-24 21:02:52 +00:00
|
|
|
prop_list->setTopItem(0);
|
2006-05-22 14:34:20 +00:00
|
|
|
prop_list->setCurrent(child);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// it is a regular property
|
|
|
|
if (child->getType() == SGPropertyNode::BOOL && mod_ctrl) {
|
|
|
|
child->setBoolValue(!child->getBoolValue());
|
2006-05-23 21:35:38 +00:00
|
|
|
prop_list->update(true);
|
2006-05-22 14:34:20 +00:00
|
|
|
} else
|
|
|
|
prop_list->publish(child);
|
|
|
|
|
|
|
|
} else {
|
|
|
|
// the user clicked on blank screen
|
2006-05-23 21:35:38 +00:00
|
|
|
prop_list->update(true);
|
2006-05-22 14:34:20 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void PropertyList::update(bool restore_pos)
|
|
|
|
{
|
|
|
|
int pi;
|
|
|
|
|
|
|
|
delete_arrays();
|
|
|
|
_num_entries = (int)_curr->nChildren();
|
|
|
|
|
|
|
|
// instantiate string objects and add [.] and [..] for subdirs
|
|
|
|
if (!_curr->getParent()) {
|
|
|
|
_entries = new char*[_num_entries + 1];
|
|
|
|
pi = 0;
|
|
|
|
dotFiles = false;
|
|
|
|
|
|
|
|
} else {
|
|
|
|
_num_entries += 2; // for . and ..
|
|
|
|
_entries = new char*[_num_entries + 1];
|
|
|
|
|
|
|
|
_entries[0] = new char[2];
|
|
|
|
strcpy(_entries[0], ".");
|
|
|
|
|
|
|
|
_entries[1] = new char[3];
|
|
|
|
strcpy(_entries[1], "..");
|
|
|
|
|
|
|
|
pi = 2;
|
|
|
|
dotFiles = true;
|
|
|
|
}
|
|
|
|
|
2006-05-23 21:35:38 +00:00
|
|
|
int i;
|
2006-05-22 14:34:20 +00:00
|
|
|
_num_children = _curr->nChildren();
|
2006-05-24 09:42:10 +00:00
|
|
|
_children = new NodeData[_num_children];
|
2006-05-22 14:34:20 +00:00
|
|
|
for (i = 0; i < _num_children; i++)
|
2006-05-24 09:42:10 +00:00
|
|
|
_children[i].node = _curr->getChild(i);
|
2006-05-22 14:34:20 +00:00
|
|
|
|
|
|
|
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++) {
|
2006-05-24 09:42:10 +00:00
|
|
|
SGPropertyNode *child = _children[i].node;
|
2006-05-22 14:34:20 +00:00
|
|
|
|
|
|
|
if (child->nChildren() > 0) {
|
|
|
|
stdString name = stdString(child->getDisplayName(true)) + '/';
|
|
|
|
_entries[pi] = new char[name.size() + 1];
|
|
|
|
strcpy(_entries[pi], name.c_str());
|
|
|
|
|
|
|
|
} else {
|
2006-05-24 21:02:52 +00:00
|
|
|
_entries[pi] = 0; // make it delete-able
|
2006-05-22 14:34:20 +00:00
|
|
|
updateTextForEntry(i);
|
2006-05-24 09:42:10 +00:00
|
|
|
_children[i].setListener(this);
|
2006-05-22 14:34:20 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
_entries[_num_entries] = 0;
|
|
|
|
|
|
|
|
int top = getTopItem();
|
|
|
|
newList(_entries);
|
|
|
|
if (restore_pos)
|
|
|
|
setTopItem(top);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void PropertyList::updateTextForEntry(int index)
|
|
|
|
{
|
|
|
|
assert((index >= 0) && (index < _num_children));
|
2006-05-24 09:42:10 +00:00
|
|
|
SGPropertyNode_ptr node = _children[index].node;
|
2006-05-22 14:34:20 +00:00
|
|
|
|
|
|
|
stdString name = node->getDisplayName(true);
|
|
|
|
stdString type = getValueTypeString(node);
|
|
|
|
stdString value = node->getStringValue();
|
|
|
|
|
2006-05-24 14:00:56 +00:00
|
|
|
if (node->getType() == SGPropertyNode::STRING
|
|
|
|
|| node->getType() == SGPropertyNode::UNSPECIFIED)
|
|
|
|
sanitize(value);
|
|
|
|
|
2006-05-22 14:34:20 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
line += ')';
|
|
|
|
|
|
|
|
if (line.size() >= PUSTRING_MAX)
|
|
|
|
line.resize(PUSTRING_MAX - 1);
|
|
|
|
|
|
|
|
if (dotFiles)
|
|
|
|
index += 2;
|
|
|
|
|
|
|
|
delete[] _entries[index];
|
|
|
|
_entries[index] = new char[line.size() + 1];
|
|
|
|
strcpy(_entries[index], line.c_str());
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void PropertyList::valueChanged(SGPropertyNode *nd)
|
|
|
|
{
|
|
|
|
for (int i = 0; i < _num_children; i++)
|
2006-05-24 09:42:10 +00:00
|
|
|
if (_children[i].node == nd) {
|
2006-05-22 14:34:20 +00:00
|
|
|
updateTextForEntry(i);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2006-05-24 21:02:52 +00:00
|
|
|
int PropertyList::nodeNameCompare(const void *p1, const void *p2)
|
2006-05-24 09:42:10 +00:00
|
|
|
{
|
2006-05-24 21:02:52 +00:00
|
|
|
const SGPropertyNode *n1 = (*(const NodeData *)p1).node;
|
|
|
|
const SGPropertyNode *n2 = (*(const NodeData *)p2).node;
|
2006-05-24 09:42:10 +00:00
|
|
|
|
2006-05-24 21:02:52 +00:00
|
|
|
int diff = strcmp(n1->getName(), n2->getName());
|
|
|
|
return diff ? diff : n1->getIndex() - n2->getIndex();
|
2006-05-24 09:42:10 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2006-05-22 14:34:20 +00:00
|
|
|
void PropertyList::setValue(const char *s)
|
2006-05-24 10:51:35 +00:00
|
|
|
{
|
|
|
|
try {
|
|
|
|
SGPropertyNode *p = fgGetNode(s, false);
|
|
|
|
if (p)
|
|
|
|
setCurrent(p);
|
|
|
|
else
|
|
|
|
throw stdString("node doesn't exist");
|
|
|
|
} catch (const stdString& m) {
|
2006-05-24 14:00:56 +00:00
|
|
|
SG_LOG(SG_GENERAL, SG_DEBUG, "property-list node '" << s << "': " << m);
|
2006-05-24 10:51:35 +00:00
|
|
|
}
|
2006-05-22 14:34:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2006-05-23 20:24:56 +00:00
|
|
|
void PropertyList::setCurrent(SGPropertyNode *p)
|
|
|
|
{
|
|
|
|
bool same = (_curr == p);
|
|
|
|
_return = _curr = p;
|
|
|
|
update(same);
|
|
|
|
if (!same)
|
|
|
|
publish(p);
|
|
|
|
}
|
|
|
|
|