Allow check if property exists and removing properties.
return m;
}
- //----------------------------------------------------------------------------
- static const std::string DATA_PREFIX("data-");
-
- //----------------------------------------------------------------------------
- std::string Element::dataPropToAttrName(const std::string& name)
- {
- // http://www.w3.org/TR/html5/dom.html#attr-data-*
- //
- // 3. Insert the string data- at the front of name.
-
- std::string attr_name;
- for( std::string::const_iterator cur = name.begin();
- cur != name.end();
- ++cur )
- {
- // If name contains a "-" (U+002D) character followed by a lowercase ASCII
- // letter, throw a SyntaxError exception and abort these steps.
- if( *cur == '-' )
- {
- std::string::const_iterator next = cur + 1;
- if( next != name.end() && islower(*next) )
- return std::string();
- }
-
- // For each uppercase ASCII letter in name, insert a "-" (U+002D)
- // character before the character and replace the character with the same
- // character converted to ASCII lowercase.
- if( isupper(*cur) )
- {
- attr_name.push_back('-');
- attr_name.push_back( tolower(*cur) );
- }
- else
- attr_name.push_back( *cur );
- }
- return DATA_PREFIX + attr_name;
- }
-
- //----------------------------------------------------------------------------
- std::string Element::attrToDataPropName(const std::string& name)
- {
- // http://www.w3.org/TR/html5/dom.html#attr-data-*
- //
- // For each "-" (U+002D) character in the name that is followed by a
- // lowercase ASCII letter, remove the "-" (U+002D) character and replace the
- // character that followed it by the same character converted to ASCII
- // uppercase.
-
- if( !boost::starts_with(name, DATA_PREFIX) )
- return std::string();
-
- std::string data_name;
- for( std::string::const_iterator cur = name.begin() + DATA_PREFIX.length();
- cur != name.end();
- ++cur )
- {
- std::string::const_iterator next = cur + 1;
- if( *cur == '-' && next != name.end() && islower(*next) )
- {
- data_name.push_back( toupper(*next) );
- cur = next;
- }
- else
- data_name.push_back(*cur);
- }
- return data_name;
- }
-
//----------------------------------------------------------------------------
Element::StyleSetters Element::_style_setters;
bool addEventListener(const std::string& type, const EventListener& cb);
virtual void clearEventListener();
- template<class T>
- void setDataProp( const std::string& name,
- const T& val )
- {
- const std::string& attr = dataPropToAttrName(name);
- if( !attr.empty() )
- set<T>(attr, val);
- else
- SG_LOG(SG_GENERAL, SG_WARN, "Invalid data-prop name: " << name);
- }
-
- template<class T>
- T getDataProp( const std::string& name,
- const T& def = T() )
- {
- const std::string& attr = dataPropToAttrName(name);
- if( !attr.empty() )
- return get<T>(attr, def);
- else
- SG_LOG(SG_GENERAL, SG_WARN, "Invalid data-prop name: " << name);
-
- return def;
- }
-
virtual bool accept(EventVisitor& visitor);
virtual bool ascend(EventVisitor& visitor);
virtual bool traverse(EventVisitor& visitor);
return ElementPtr( new Derived(canvas, node, style, parent) );
}
- static std::string dataPropToAttrName(const std::string& name);
- static std::string attrToDataPropName(const std::string& name);
-
protected:
enum Attributes
el->setDataProp("myData", 3);
BOOST_CHECK_EQUAL( el->getDataProp<int>("myData"), 3 );
BOOST_CHECK_EQUAL( node->getIntValue("data-my-data"), 3 );
+
+ SGPropertyNode* prop = el->getDataProp<SGPropertyNode*>("notExistingProp");
+ BOOST_CHECK( !prop );
+ prop = el->getDataProp<SGPropertyNode*>("myData");
+ BOOST_CHECK( prop );
+ BOOST_CHECK_EQUAL( prop->getParent(), node );
+ BOOST_CHECK_EQUAL( prop->getIntValue(), 3 );
+
+ BOOST_CHECK( el->hasDataProp("myData") );
+ el->removeDataProp("myData");
+ BOOST_CHECK( !el->hasDataProp("myData") );
+ BOOST_CHECK_EQUAL( el->getDataProp("myData", 5), 5 );
}
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
#include "PropertyBasedElement.hxx"
+#include <boost/algorithm/string/predicate.hpp>
namespace simgear
{
return _node;
}
+ //----------------------------------------------------------------------------
+ bool PropertyBasedElement::hasDataProp(const std::string& name) const
+ {
+ return getDataProp<SGPropertyNode*>(name) != NULL;
+ }
+
+ //----------------------------------------------------------------------------
+ void PropertyBasedElement::removeDataProp(const std::string& name)
+ {
+ SGPropertyNode* node = getDataProp<SGPropertyNode*>(name);
+ if( node )
+ _node->removeChild(node);
+ }
+
+ //----------------------------------------------------------------------------
+ static const std::string DATA_PREFIX("data-");
+
+ //----------------------------------------------------------------------------
+ std::string PropertyBasedElement::dataPropToAttrName(const std::string& name)
+ {
+ // http://www.w3.org/TR/html5/dom.html#attr-data-*
+ //
+ // 3. Insert the string data- at the front of name.
+
+ std::string attr_name;
+ for( std::string::const_iterator cur = name.begin();
+ cur != name.end();
+ ++cur )
+ {
+ // If name contains a "-" (U+002D) character followed by a lowercase ASCII
+ // letter, throw a SyntaxError exception and abort these steps.
+ if( *cur == '-' )
+ {
+ std::string::const_iterator next = cur + 1;
+ if( next != name.end() && islower(*next) )
+ return std::string();
+ }
+
+ // For each uppercase ASCII letter in name, insert a "-" (U+002D)
+ // character before the character and replace the character with the same
+ // character converted to ASCII lowercase.
+ if( isupper(*cur) )
+ {
+ attr_name.push_back('-');
+ attr_name.push_back( tolower(*cur) );
+ }
+ else
+ attr_name.push_back( *cur );
+ }
+ return DATA_PREFIX + attr_name;
+ }
+
+ //----------------------------------------------------------------------------
+ std::string PropertyBasedElement::attrToDataPropName(const std::string& name)
+ {
+ // http://www.w3.org/TR/html5/dom.html#attr-data-*
+ //
+ // For each "-" (U+002D) character in the name that is followed by a
+ // lowercase ASCII letter, remove the "-" (U+002D) character and replace the
+ // character that followed it by the same character converted to ASCII
+ // uppercase.
+
+ if( !boost::starts_with(name, DATA_PREFIX) )
+ return std::string();
+
+ std::string data_name;
+ for( std::string::const_iterator cur = name.begin() + DATA_PREFIX.length();
+ cur != name.end();
+ ++cur )
+ {
+ std::string::const_iterator next = cur + 1;
+ if( *cur == '-' && next != name.end() && islower(*next) )
+ {
+ data_name.push_back( toupper(*next) );
+ cur = next;
+ }
+ else
+ data_name.push_back(*cur);
+ }
+ return data_name;
+ }
+
} // namespace simgear
// Unshadow what we have just hidden...
using SGWeakReferenced::get;
+
+ /** @brief Set a HTML5 like data property on this element.
+ *
+ * Set data-* properties on this element. A camel-case @a name will be
+ * converted to a hyphenated name with 'data-' prefixed. Setting a value
+ * with this method does not trigger an update of the canvas and is meant
+ * to store data related to this element (used eg. inside scripts).
+ *
+ * @code{cpp}
+ * // Set value
+ * my_element->setDataProp("mySpecialInt", 3);
+ *
+ * // Get value (with default value)
+ * int val = my_element->getDataProp<int>("mySpecialInt"); // val == 3
+ * val = my_element->getDataProp<int>("notExisting", 5); // val == 5
+ *
+ * // Check if value exists
+ * SGPropertyNode* node =
+ * my_element->getDataProp<SGPropertyNode*>("mySpecialInt");
+ * if( node )
+ * val = node->getIntValue(); // node != NULL, val == 3
+ *
+ * node = my_element->getDataProp<SGPropertyNode*>("notExisting");
+ * // node == NULL
+ * @endcode
+ */
+ template<class T>
+ void setDataProp( const std::string& name,
+ const T& val )
+ {
+ const std::string& attr = dataPropToAttrName(name);
+ if( !attr.empty() )
+ set<T>(attr, val);
+ else
+ SG_LOG(SG_GENERAL, SG_WARN, "Invalid data-prop name: " << name);
+ }
+
+ /** @brief Get a HTML5 like data property on this element.
+ *
+ * Get value or default value.
+ *
+ * @see setDataProp
+ */
+ template<class T>
+ typename boost::disable_if<
+ boost::is_same<T, SGPropertyNode*>,
+ T
+ >::type getDataProp( const std::string& name,
+ const T& def = T() ) const
+ {
+ SGPropertyNode* node = getDataProp<SGPropertyNode*>(name);
+ if( node )
+ return getValue<T>(node);
+
+ return def;
+ }
+
+ /** @brief Get a HTML5 like data property on this element.
+ *
+ * Use this variant to check if a property exists.
+ *
+ * @see setDataProp
+ */
+ template<class T>
+ typename boost::enable_if<
+ boost::is_same<T, SGPropertyNode*>,
+ T
+ >::type getDataProp( const std::string& name,
+ SGPropertyNode* = NULL ) const
+ {
+ const std::string& attr = dataPropToAttrName(name);
+ if( attr.empty() )
+ {
+ SG_LOG(SG_GENERAL, SG_WARN, "Invalid data-prop name: " << name);
+ return NULL;
+ }
+
+ return _node->getNode(attr);
+ }
+
+ /** @brief Check whether a HTML5 like data property exists on this
+ * element.
+ *
+ */
+ bool hasDataProp(const std::string& name) const;
+
+ /** @brief Remove a HTML5 like data property (if it exists).
+ *
+ */
+ void removeDataProp(const std::string& name);
+
virtual void onDestroy() {};
+ static std::string dataPropToAttrName(const std::string& name);
+ static std::string attrToDataPropName(const std::string& name);
+
protected:
SGPropertyNode_ptr _node;
return children;
}
+//------------------------------------------------------------------------------
+bool SGPropertyNode::removeChild(SGPropertyNode* node)
+{
+ if( node->_parent != this )
+ return false;
-/**
- * Remove child by position.
- */
-SGPropertyNode_ptr
-SGPropertyNode::removeChild(int pos)
+ PropertyList::iterator it =
+ std::find(_children.begin(), _children.end(), node);
+ if( it == _children.end() )
+ return false;
+
+ eraseChild(it);
+ return true;
+}
+
+//------------------------------------------------------------------------------
+SGPropertyNode_ptr SGPropertyNode::removeChild(int pos)
{
- SGPropertyNode_ptr node;
if (pos < 0 || pos >= (int)_children.size())
- return node;
-
- PropertyList::iterator it = _children.begin();
- it += pos;
- node = _children[pos];
- _children.erase(it);
+ return SGPropertyNode_ptr();
- node->setAttribute(REMOVED, true);
- node->clearValue();
- fireChildRemoved(node);
- return node;
+ return eraseChild(_children.begin() + pos);
}
_parent->fireChildRemoved(parent, child);
}
+//------------------------------------------------------------------------------
+SGPropertyNode_ptr
+SGPropertyNode::eraseChild(simgear::PropertyList::iterator child)
+{
+ SGPropertyNode_ptr node = *child;
+ node->setAttribute(REMOVED, true);
+ node->clearValue();
+ fireChildRemoved(node);
+
+ _children.erase(child);
+ return node;
+}
+
////////////////////////////////////////////////////////////////////////
// Implementation of SGPropertyChangeListener.
////////////////////////////////////////////////////////////////////////
simgear::PropertyList getChildren (const std::string& name) const
{ return getChildren(name.c_str()); }
+ /**
+ * Remove child by pointer (if it is a child of this node).
+ *
+ * @return true, if the node was deleted.
+ */
+ bool removeChild(SGPropertyNode* node);
+
// TODO do we need the removeXXX methods to return the deleted nodes?
/**
* Remove child by position.
void fireChildAdded (SGPropertyNode * parent, SGPropertyNode * child);
void fireChildRemoved (SGPropertyNode * parent, SGPropertyNode * child);
+ SGPropertyNode_ptr eraseChild(simgear::PropertyList::iterator child);
+
/**
* Protected constructor for making new nodes on demand.
*/