]> git.mxchange.org Git - simgear.git/blob - simgear/canvas/elements/CanvasGroup.cxx
Canvas: fix transformation of clip rect.
[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/events/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       Group* group = dynamic_cast<Group*>(el.get());
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     SGPropertyNode* parent = node->getParent();
324     SGPropertyNode* grand_parent = parent ? parent->getParent() : NULL;
325
326     if(    grand_parent == _node
327         && node->getNameString() == "z-index" )
328       return handleZIndexChanged(getChild(parent), node->getIntValue());
329   }
330
331   //----------------------------------------------------------------------------
332   void Group::handleZIndexChanged(ElementPtr child, int z_index)
333   {
334     if( !child || !_transform.valid() )
335       return;
336
337     osg::ref_ptr<osg::MatrixTransform> tf = child->getMatrixTransform();
338     size_t index = _transform->getChildIndex(tf),
339            index_new = index;
340
341     for(;; ++index_new)
342     {
343       if( index_new + 1 == _transform->getNumChildren() )
344         break;
345
346       // Move to end of block with same index (= move upwards until the next
347       // element has a higher index)
348       if( getChildByIndex(index_new + 1)->get<int>("z-index", 0) > z_index )
349         break;
350     }
351
352     if( index_new == index )
353     {
354       // We were not able to move upwards so now try downwards
355       for(;; --index_new)
356       {
357         if( index_new == 0 )
358           break;
359
360         // Move to end of block with same index (= move downwards until the
361         // previous element has the same or a lower index)
362         if( getChildByIndex(index_new - 1)->get<int>("z-index", 0) <= z_index )
363           break;
364       }
365
366       if( index == index_new )
367         return;
368     }
369
370     _transform->removeChild(index);
371     _transform->insertChild(index_new, tf);
372
373     SG_LOG
374     (
375       SG_GENERAL,
376       SG_INFO,
377       "canvas::Group: Moved element " << index << " to position " << index_new
378     );
379   }
380
381   //----------------------------------------------------------------------------
382   ElementPtr Group::getChildByIndex(size_t index) const
383   {
384     assert(_transform.valid());
385     OSGUserData* ud =
386       static_cast<OSGUserData*>(_transform->getChild(index)->getUserData());
387     assert(ud);
388     return ud->element;
389   }
390
391   //----------------------------------------------------------------------------
392   ElementPtr Group::findChild( const SGPropertyNode* node,
393                                const std::string& id ) const
394   {
395     if( !_transform.valid() )
396     {
397       warnTransformExpired("findChild");
398       return ElementPtr();
399     }
400
401     for(size_t i = 0; i < _transform->getNumChildren(); ++i)
402     {
403       ElementPtr el = getChildByIndex(i);
404
405       if( node )
406       {
407         if( el->getProps() == node )
408           return el;
409       }
410       else
411       {
412         if( el->get<std::string>("id") == id )
413           return el;
414       }
415     }
416
417     return ElementPtr();
418   }
419
420 } // namespace canvas
421 } // namespace simgear