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