]> git.mxchange.org Git - simgear.git/blob - simgear/canvas/elements/CanvasGroup.cxx
CanvasImage: improve fetching from http and add mime detection.
[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     // Default to a bounding box with size 0x0 and the origin defined by the
224     // matrix.
225     osg::BoundingBox bb(m.getTrans(), m.getTrans());
226
227     if( !_transform.valid() )
228     {
229       warnTransformExpired("getTransformedBounds");
230       return bb;
231     }
232
233     for(size_t i = 0; i < _transform->getNumChildren(); ++i)
234     {
235       const ElementPtr& child = getChildByIndex(i);
236       if( !child->getMatrixTransform()->getNodeMask() )
237         continue;
238
239       bb.expandBy
240       (
241         child->getTransformedBounds
242         (
243           child->getMatrixTransform()->getMatrix() * m
244         )
245       );
246     }
247
248     return bb;
249   }
250
251   //----------------------------------------------------------------------------
252   ElementFactory Group::getChildFactory(const std::string& type) const
253   {
254     ElementFactories::iterator child_factory = _child_factories.find(type);
255     if( child_factory != _child_factories.end() )
256       return child_factory->second;
257
258     return ElementFactory();
259   }
260
261   //----------------------------------------------------------------------------
262   void Group::childAdded(SGPropertyNode* child)
263   {
264     if( child->getParent() != _node )
265       return;
266
267     ElementFactory child_factory = getChildFactory( child->getNameString() );
268     if( child_factory )
269     {
270       if( !_transform.valid() )
271         return warnTransformExpired("childAdded");
272
273       ElementPtr element = child_factory(_canvas, child, _style, this);
274
275       // Add to osg scene graph...
276       _transform->addChild( element->getMatrixTransform() );
277
278       // ...and ensure correct ordering
279       handleZIndexChanged(element);
280
281       return;
282     }
283
284     StyleInfo const* style = getStyleInfo(child->getNameString());
285     if( style && style->inheritable )
286       _style[ child->getNameString() ] = child;
287   }
288
289   //----------------------------------------------------------------------------
290   void Group::childRemoved(SGPropertyNode* node)
291   {
292     if( node->getParent() != _node )
293       return;
294
295     if( getChildFactory(node->getNameString()) )
296     {
297       if( !_transform.valid() )
298         // If transform is destroyed also all children are destroyed, so we can
299         // not do anything here.
300         return;
301
302       ElementPtr child = getChild(node);
303       if( !child )
304         SG_LOG
305         (
306           SG_GL,
307           SG_WARN,
308           "can't removed unknown child " << node->getDisplayName()
309         );
310       else
311       {
312         // Remove child from the scenegraph (this automatically invalidates the
313         // reference on the element hold by the MatrixTransform)
314         child->onDestroy();
315       }
316     }
317     else
318     {
319       _style.erase( node->getNameString() );
320     }
321   }
322
323   //----------------------------------------------------------------------------
324   void Group::childChanged(SGPropertyNode* node)
325   {
326     if(    node->getParent()->getParent() == _node
327         && node->getNameString() == "z-index" )
328       return handleZIndexChanged( getChild(node->getParent()),
329                                   node->getIntValue() );
330   }
331
332   //----------------------------------------------------------------------------
333   void Group::handleZIndexChanged(ElementPtr child, int z_index)
334   {
335     if( !child || !_transform.valid() )
336       return;
337
338     osg::ref_ptr<osg::MatrixTransform> tf = child->getMatrixTransform();
339     size_t index = _transform->getChildIndex(tf),
340            index_new = index;
341
342     for(;; ++index_new)
343     {
344       if( index_new + 1 == _transform->getNumChildren() )
345         break;
346
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 )
350         break;
351     }
352
353     if( index_new == index )
354     {
355       // We were not able to move upwards so now try downwards
356       for(;; --index_new)
357       {
358         if( index_new == 0 )
359           break;
360
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 )
364           break;
365       }
366
367       if( index == index_new )
368         return;
369     }
370
371     _transform->removeChild(index);
372     _transform->insertChild(index_new, tf);
373
374     SG_LOG
375     (
376       SG_GENERAL,
377       SG_INFO,
378       "canvas::Group: Moved element " << index << " to position " << index_new
379     );
380   }
381
382   //----------------------------------------------------------------------------
383   ElementPtr Group::getChildByIndex(size_t index) const
384   {
385     assert(_transform.valid());
386     OSGUserData* ud =
387       static_cast<OSGUserData*>(_transform->getChild(index)->getUserData());
388     assert(ud);
389     return ud->element;
390   }
391
392   //----------------------------------------------------------------------------
393   ElementPtr Group::findChild( const SGPropertyNode* node,
394                                const std::string& id ) const
395   {
396     if( !_transform.valid() )
397     {
398       warnTransformExpired("findChild");
399       return ElementPtr();
400     }
401
402     for(size_t i = 0; i < _transform->getNumChildren(); ++i)
403     {
404       ElementPtr el = getChildByIndex(i);
405
406       if( node )
407       {
408         if( el->getProps() == node )
409           return el;
410       }
411       else
412       {
413         if( el->get<std::string>("id") == id )
414           return el;
415       }
416     }
417
418     return ElementPtr();
419   }
420
421 } // namespace canvas
422 } // namespace simgear