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