1 // A group of 2D Canvas elements
3 // Copyright (C) 2012 Thomas Geymayer <tomgey@gmail.com>
5 // This library is free software; you can redistribute it and/or
6 // modify it under the terms of the GNU Library General Public
7 // License as published by the Free Software Foundation; either
8 // version 2 of the License, or (at your option) any later version.
10 // This library is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 // Library General Public License for more details.
15 // You should have received a copy of the GNU Library General Public
16 // License along with this library; if not, write to the Free Software
17 // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19 #include "CanvasGroup.hxx"
20 #include "CanvasImage.hxx"
21 #include "CanvasMap.hxx"
22 #include "CanvasPath.hxx"
23 #include "CanvasText.hxx"
24 #include <simgear/canvas/CanvasEventVisitor.hxx>
25 #include <simgear/canvas/MouseEvent.hxx>
27 #include <boost/bind.hpp>
28 #include <boost/foreach.hpp>
29 #include <boost/lambda/core.hpp>
36 * Add canvas Element type to factory map
38 template<typename ElementType>
39 void add(ElementFactories& factories)
41 ElementType::staticInit();
42 factories[ElementType::TYPE_NAME] = &Element::create<ElementType>;
45 //----------------------------------------------------------------------------
46 ElementFactories Group::_child_factories;
47 const std::string Group::TYPE_NAME = "group";
49 void warnTransformExpired(const char* member_name)
53 "canvas::Group::" << member_name << ": Group has expired." );
56 //----------------------------------------------------------------------------
57 void Group::staticInit()
62 add<Group>(_child_factories);
63 add<Image>(_child_factories);
64 add<Map >(_child_factories);
65 add<Path >(_child_factories);
66 add<Text >(_child_factories);
69 //----------------------------------------------------------------------------
70 Group::Group( const CanvasWeakPtr& canvas,
71 const SGPropertyNode_ptr& node,
72 const Style& parent_style,
74 Element(canvas, node, parent_style, parent)
79 //----------------------------------------------------------------------------
85 //----------------------------------------------------------------------------
86 ElementPtr Group::createChild( const std::string& type,
87 const std::string& id )
89 SGPropertyNode* node = _node->addChild(type, 0, false);
91 node->setStringValue("id", id);
93 return getChild(node);
96 //----------------------------------------------------------------------------
97 ElementPtr Group::getChild(const SGPropertyNode* node)
99 return findChild(node, "");
102 //----------------------------------------------------------------------------
103 ElementPtr Group::getChild(const std::string& id)
105 return findChild(0, id);
108 //----------------------------------------------------------------------------
109 ElementPtr Group::getOrCreateChild( const std::string& type,
110 const std::string& id )
112 ElementPtr child = getChild(id);
115 if( child->getProps()->getNameString() == type )
122 "Group::getOrCreateChild: type missmatch! "
123 "('" << type << "' != '" << child->getProps()->getName() << "', "
124 "id = '" << id << "')"
130 return createChild(type, id);
133 //----------------------------------------------------------------------------
134 ElementPtr Group::getElementById(const std::string& id)
136 if( !_transform.valid() )
138 warnTransformExpired("getElementById");
142 std::vector<GroupPtr> groups;
143 for(size_t i = 0; i < _transform->getNumChildren(); ++i)
145 const ElementPtr& el = getChildByIndex(i);
146 if( el->get<std::string>("id") == id )
149 GroupPtr group = boost::dynamic_pointer_cast<Group>(el);
151 groups.push_back(group);
154 BOOST_FOREACH( GroupPtr group, groups )
156 ElementPtr el = group->getElementById(id);
164 //----------------------------------------------------------------------------
165 void Group::clearEventListener()
167 if( !_transform.valid() )
168 return warnTransformExpired("clearEventListener");
170 for(size_t i = 0; i < _transform->getNumChildren(); ++i)
171 getChildByIndex(i)->clearEventListener();
173 Element::clearEventListener();
176 //----------------------------------------------------------------------------
177 void Group::update(double dt)
179 for(size_t i = 0; i < _transform->getNumChildren(); ++i)
180 getChildByIndex(i)->update(dt);
185 //----------------------------------------------------------------------------
186 bool Group::traverse(EventVisitor& visitor)
188 // Iterate in reverse order as last child is displayed on top
189 for(size_t i = _transform->getNumChildren(); i --> 0;)
191 if( getChildByIndex(i)->accept(visitor) )
197 //----------------------------------------------------------------------------
198 bool Group::setStyle( const SGPropertyNode* style,
199 const StyleInfo* style_info )
201 if( !canApplyStyle(style) )
204 bool handled = setStyleImpl(style, style_info);
205 if( style_info->inheritable )
207 if( !_transform.valid() )
209 warnTransformExpired("setStyle");
213 for(size_t i = 0; i < _transform->getNumChildren(); ++i)
214 handled |= getChildByIndex(i)->setStyle(style, style_info);
220 //----------------------------------------------------------------------------
221 osg::BoundingBox Group::getTransformedBounds(const osg::Matrix& m) const
223 // Default to a bounding box with size 0x0 and the origin defined by the
225 osg::BoundingBox bb(m.getTrans(), m.getTrans());
227 if( !_transform.valid() )
229 warnTransformExpired("getTransformedBounds");
233 for(size_t i = 0; i < _transform->getNumChildren(); ++i)
235 const ElementPtr& child = getChildByIndex(i);
236 if( !child->getMatrixTransform()->getNodeMask() )
241 child->getTransformedBounds
243 child->getMatrixTransform()->getMatrix() * m
251 //----------------------------------------------------------------------------
252 ElementFactory Group::getChildFactory(const std::string& type) const
254 ElementFactories::iterator child_factory = _child_factories.find(type);
255 if( child_factory != _child_factories.end() )
256 return child_factory->second;
258 return ElementFactory();
261 //----------------------------------------------------------------------------
262 void Group::childAdded(SGPropertyNode* child)
264 if( child->getParent() != _node )
267 ElementFactory child_factory = getChildFactory( child->getNameString() );
270 if( !_transform.valid() )
271 return warnTransformExpired("childAdded");
273 ElementPtr element = child_factory(_canvas, child, _style, this);
275 // Add to osg scene graph...
276 _transform->addChild( element->getMatrixTransform() );
278 // ...and ensure correct ordering
279 handleZIndexChanged(element);
284 StyleInfo const* style = getStyleInfo(child->getNameString());
285 if( style && style->inheritable )
286 _style[ child->getNameString() ] = child;
289 //----------------------------------------------------------------------------
290 void Group::childRemoved(SGPropertyNode* node)
292 if( node->getParent() != _node )
295 if( getChildFactory(node->getNameString()) )
297 if( !_transform.valid() )
298 // If transform is destroyed also all children are destroyed, so we can
299 // not do anything here.
302 ElementPtr child = getChild(node);
308 "can't removed unknown child " << node->getDisplayName()
312 // Remove child from the scenegraph (this automatically invalidates the
313 // reference on the element hold by the MatrixTransform)
319 _style.erase( node->getNameString() );
323 //----------------------------------------------------------------------------
324 void Group::childChanged(SGPropertyNode* node)
326 if( node->getParent()->getParent() == _node
327 && node->getNameString() == "z-index" )
328 return handleZIndexChanged( getChild(node->getParent()),
329 node->getIntValue() );
332 //----------------------------------------------------------------------------
333 void Group::handleZIndexChanged(ElementPtr child, int z_index)
335 if( !child || !_transform.valid() )
338 osg::ref_ptr<osg::MatrixTransform> tf = child->getMatrixTransform();
339 size_t index = _transform->getChildIndex(tf),
344 if( index_new + 1 == _transform->getNumChildren() )
347 // Move to end of block with same index (= move upwards until the next
348 // element has a higher index)
349 if( getChildByIndex(index_new + 1)->get<int>("z-index", 0) > z_index )
353 if( index_new == index )
355 // We were not able to move upwards so now try downwards
361 // Move to end of block with same index (= move downwards until the
362 // previous element has the same or a lower index)
363 if( getChildByIndex(index_new - 1)->get<int>("z-index", 0) <= z_index )
367 if( index == index_new )
371 _transform->removeChild(index);
372 _transform->insertChild(index_new, tf);
378 "canvas::Group: Moved element " << index << " to position " << index_new
382 //----------------------------------------------------------------------------
383 ElementPtr Group::getChildByIndex(size_t index) const
385 assert(_transform.valid());
387 static_cast<OSGUserData*>(_transform->getChild(index)->getUserData());
392 //----------------------------------------------------------------------------
393 ElementPtr Group::findChild( const SGPropertyNode* node,
394 const std::string& id ) const
396 if( !_transform.valid() )
398 warnTransformExpired("findChild");
402 for(size_t i = 0; i < _transform->getNumChildren(); ++i)
404 ElementPtr el = getChildByIndex(i);
408 if( el->getProps() == node )
413 if( el->get<std::string>("id") == id )
421 } // namespace canvas
422 } // namespace simgear