]> git.mxchange.org Git - simgear.git/blob - simgear/canvas/elements/CanvasGroup.cxx
Canvas: allow also C++ callable entities as event callbacks.
[simgear.git] / simgear / canvas / elements / CanvasGroup.cxx
1 // A group of 2D Canvas elements
2 //
3 // Copyright (C) 2012  Thomas Geymayer <tomgey@gmail.com>
4 //
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.
9 //
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.
14 //
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
18
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>
26
27 #include <boost/bind.hpp>
28 #include <boost/foreach.hpp>
29 #include <boost/lambda/core.hpp>
30
31 namespace simgear
32 {
33 namespace canvas
34 {
35   /**
36    * Add canvas Element type to factory map
37    */
38   template<typename ElementType>
39   void add(ElementFactories& factories)
40   {
41     ElementType::staticInit();
42     factories[ElementType::TYPE_NAME] = &Element::create<ElementType>;
43   }
44
45   //----------------------------------------------------------------------------
46   ElementFactories Group::_child_factories;
47   const std::string Group::TYPE_NAME = "group";
48
49   void warnTransformExpired(const char* member_name)
50   {
51     SG_LOG( SG_GENERAL,
52             SG_WARN,
53             "canvas::Group::" << member_name << ": Group has expired." );
54   }
55
56   //----------------------------------------------------------------------------
57   void Group::staticInit()
58   {
59     if( isInit<Group>() )
60       return;
61
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);
67   }
68
69   //----------------------------------------------------------------------------
70   Group::Group( const CanvasWeakPtr& canvas,
71                 const SGPropertyNode_ptr& node,
72                 const Style& parent_style,
73                 Element* parent ):
74     Element(canvas, node, parent_style, parent)
75   {
76     staticInit();
77   }
78
79   //----------------------------------------------------------------------------
80   Group::~Group()
81   {
82
83   }
84
85   //----------------------------------------------------------------------------
86   ElementPtr Group::createChild( const std::string& type,
87                                  const std::string& id )
88   {
89     SGPropertyNode* node = _node->addChild(type, 0, false);
90     if( !id.empty() )
91       node->setStringValue("id", id);
92
93     return getChild(node);
94   }
95
96   //----------------------------------------------------------------------------
97   ElementPtr Group::getChild(const SGPropertyNode* node)
98   {
99     return findChild(node, "");
100   }
101
102   //----------------------------------------------------------------------------
103   ElementPtr Group::getChild(const std::string& id)
104   {
105     return findChild(0, id);
106   }
107
108   //----------------------------------------------------------------------------
109   ElementPtr Group::getOrCreateChild( const std::string& type,
110                                       const std::string& id )
111   {
112     ElementPtr child = getChild(id);
113     if( child )
114     {
115       if( child->getProps()->getNameString() == type )
116         return child;
117
118       SG_LOG
119       (
120         SG_GENERAL,
121         SG_WARN,
122         "Group::getOrCreateChild: type missmatch! "
123         "('" << type << "' != '" << child->getProps()->getName() << "', "
124         "id = '" << id << "')"
125       );
126
127       return ElementPtr();
128     }
129
130     return createChild(type, id);
131   }
132
133   //----------------------------------------------------------------------------
134   ElementPtr Group::getElementById(const std::string& id)
135   {
136     if( !_transform.valid() )
137     {
138       warnTransformExpired("getElementById");
139       return ElementPtr();
140     }
141
142     std::vector<GroupPtr> groups;
143     for(size_t i = 0; i < _transform->getNumChildren(); ++i)
144     {
145       const ElementPtr& el = getChildByIndex(i);
146       if( el->get<std::string>("id") == id )
147         return el;
148
149       GroupPtr group = boost::dynamic_pointer_cast<Group>(el);
150       if( group )
151         groups.push_back(group);
152     }
153
154     BOOST_FOREACH( GroupPtr group, groups )
155     {
156       ElementPtr el = group->getElementById(id);
157       if( el )
158         return el;
159     }
160
161     return ElementPtr();
162   }
163
164   //----------------------------------------------------------------------------
165   void Group::clearEventListener()
166   {
167     if( !_transform.valid() )
168       return warnTransformExpired("clearEventListener");
169
170     for(size_t i = 0; i < _transform->getNumChildren(); ++i)
171       getChildByIndex(i)->clearEventListener();
172
173     Element::clearEventListener();
174   }
175
176   //----------------------------------------------------------------------------
177   void Group::update(double dt)
178   {
179     for(size_t i = 0; i < _transform->getNumChildren(); ++i)
180       getChildByIndex(i)->update(dt);
181
182     Element::update(dt);
183   }
184
185   //----------------------------------------------------------------------------
186   bool Group::traverse(EventVisitor& visitor)
187   {
188     // Iterate in reverse order as last child is displayed on top
189     for(size_t i = _transform->getNumChildren(); i --> 0;)
190     {
191       if( getChildByIndex(i)->accept(visitor) )
192         return true;
193     }
194     return false;
195   }
196
197   //----------------------------------------------------------------------------
198   bool Group::setStyle( const SGPropertyNode* style,
199                         const StyleInfo* style_info )
200   {
201     if( !canApplyStyle(style) )
202       return false;
203
204     bool handled = setStyleImpl(style, style_info);
205     if( style_info->inheritable )
206     {
207       if( !_transform.valid() )
208       {
209         warnTransformExpired("setStyle");
210         return false;
211       }
212
213       for(size_t i = 0; i < _transform->getNumChildren(); ++i)
214         handled |= getChildByIndex(i)->setStyle(style, style_info);
215     }
216
217     return handled;
218   }
219
220   //----------------------------------------------------------------------------
221   osg::BoundingBox Group::getTransformedBounds(const osg::Matrix& m) const
222   {
223     osg::BoundingBox bb;
224     if( !_transform.valid() )
225     {
226       warnTransformExpired("getTransformedBounds");
227       return bb;
228     }
229
230     for(size_t i = 0; i < _transform->getNumChildren(); ++i)
231     {
232       const ElementPtr& child = getChildByIndex(i);
233       if( !child->getMatrixTransform()->getNodeMask() )
234         continue;
235
236       bb.expandBy
237       (
238         child->getTransformedBounds
239         (
240           child->getMatrixTransform()->getMatrix() * m
241         )
242       );
243     }
244
245     return bb;
246   }
247
248   //----------------------------------------------------------------------------
249   ElementFactory Group::getChildFactory(const std::string& type) const
250   {
251     ElementFactories::iterator child_factory = _child_factories.find(type);
252     if( child_factory != _child_factories.end() )
253       return child_factory->second;
254
255     return ElementFactory();
256   }
257
258   //----------------------------------------------------------------------------
259   void Group::childAdded(SGPropertyNode* child)
260   {
261     if( child->getParent() != _node )
262       return;
263
264     ElementFactory child_factory = getChildFactory( child->getNameString() );
265     if( child_factory )
266     {
267       if( !_transform.valid() )
268         return warnTransformExpired("childAdded");
269
270       ElementPtr element = child_factory(_canvas, child, _style, this);
271
272       // Add to osg scene graph...
273       _transform->addChild( element->getMatrixTransform() );
274
275       // ...and ensure correct ordering
276       handleZIndexChanged(element);
277
278       return;
279     }
280
281     StyleInfo const* style = getStyleInfo(child->getNameString());
282     if( style && style->inheritable )
283       _style[ child->getNameString() ] = child;
284   }
285
286   //----------------------------------------------------------------------------
287   void Group::childRemoved(SGPropertyNode* node)
288   {
289     if( node->getParent() != _node )
290       return;
291
292     if( getChildFactory(node->getNameString()) )
293     {
294       if( !_transform.valid() )
295         // If transform is destroyed also all children are destroyed, so we can
296         // not do anything here.
297         return;
298
299       ElementPtr child = getChild(node);
300       if( !child )
301         SG_LOG
302         (
303           SG_GL,
304           SG_WARN,
305           "can't removed unknown child " << node->getDisplayName()
306         );
307       else
308       {
309         // Remove child from the scenegraph (this automatically invalidates the
310         // reference on the element hold by the MatrixTransform)
311         child->onDestroy();
312       }
313     }
314     else
315     {
316       _style.erase( node->getNameString() );
317     }
318   }
319
320   //----------------------------------------------------------------------------
321   void Group::childChanged(SGPropertyNode* node)
322   {
323     if(    node->getParent()->getParent() == _node
324         && node->getNameString() == "z-index" )
325       return handleZIndexChanged( getChild(node->getParent()),
326                                   node->getIntValue() );
327   }
328
329   //----------------------------------------------------------------------------
330   void Group::handleZIndexChanged(ElementPtr child, int z_index)
331   {
332     if( !child || !_transform.valid() )
333       return;
334
335     osg::ref_ptr<osg::MatrixTransform> tf = child->getMatrixTransform();
336     size_t index = _transform->getChildIndex(tf),
337            index_new = index;
338
339     for(;; ++index_new)
340     {
341       if( index_new + 1 == _transform->getNumChildren() )
342         break;
343
344       // Move to end of block with same index (= move upwards until the next
345       // element has a higher index)
346       if( getChildByIndex(index_new + 1)->get<int>("z-index", 0) > z_index )
347         break;
348     }
349
350     if( index_new == index )
351     {
352       // We were not able to move upwards so now try downwards
353       for(;; --index_new)
354       {
355         if( index_new == 0 )
356           break;
357
358         // Move to end of block with same index (= move downwards until the
359         // previous element has the same or a lower index)
360         if( getChildByIndex(index_new - 1)->get<int>("z-index", 0) <= z_index )
361           break;
362       }
363
364       if( index == index_new )
365         return;
366     }
367
368     _transform->removeChild(index);
369     _transform->insertChild(index_new, tf);
370
371     SG_LOG
372     (
373       SG_GENERAL,
374       SG_INFO,
375       "canvas::Group: Moved element " << index << " to position " << index_new
376     );
377   }
378
379   //----------------------------------------------------------------------------
380   ElementPtr Group::getChildByIndex(size_t index) const
381   {
382     assert(_transform.valid());
383     OSGUserData* ud =
384       static_cast<OSGUserData*>(_transform->getChild(index)->getUserData());
385     assert(ud);
386     return ud->element;
387   }
388
389   //----------------------------------------------------------------------------
390   ElementPtr Group::findChild( const SGPropertyNode* node,
391                                const std::string& id ) const
392   {
393     if( !_transform.valid() )
394     {
395       warnTransformExpired("findChild");
396       return ElementPtr();
397     }
398
399     for(size_t i = 0; i < _transform->getNumChildren(); ++i)
400     {
401       ElementPtr el = getChildByIndex(i);
402
403       if( node )
404       {
405         if( el->getProps() == node )
406           return el;
407       }
408       else
409       {
410         if( el->get<std::string>("id") == id )
411           return el;
412       }
413     }
414
415     return ElementPtr();
416   }
417
418 } // namespace canvas
419 } // namespace simgear