]> git.mxchange.org Git - simgear.git/blob - simgear/canvas/elements/CanvasGroup.cxx
Canvas: More helper functions and cleanup.
[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    * Create an canvas Element of type T
37    */
38   template<typename T>
39   ElementPtr createElement( const CanvasWeakPtr& canvas,
40                             const SGPropertyNode_ptr& node,
41                             const Style& style,
42                             Element* parent )
43   {
44     ElementPtr el( new T(canvas, node, style, parent) );
45     el->setSelf(el);
46     return el;
47   }
48
49   /**
50    * Add canvas Element type to factory map
51    */
52   template<typename T>
53   void add(ElementFactories& factories)
54   {
55     factories[T::TYPE_NAME] = &createElement<T>;
56   }
57
58   //----------------------------------------------------------------------------
59   ElementFactories Group::_child_factories;
60   const std::string Group::TYPE_NAME = "group";
61
62   //----------------------------------------------------------------------------
63   Group::Group( const CanvasWeakPtr& canvas,
64                 const SGPropertyNode_ptr& node,
65                 const Style& parent_style,
66                 Element* parent ):
67     Element(canvas, node, parent_style, parent)
68   {
69     if( !isInit<Group>() )
70     {
71       add<Group>(_child_factories);
72       add<Image>(_child_factories);
73       add<Map  >(_child_factories);
74       add<Path >(_child_factories);
75       add<Text >(_child_factories);
76     }
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     ChildList::iterator child = findChild(node);
100     if( child == _children.end() )
101       return ElementPtr();
102
103     return child->second;
104   }
105
106   //----------------------------------------------------------------------------
107   ElementPtr Group::getChild(const std::string& id)
108   {
109     for( ChildList::iterator child = _children.begin();
110                              child != _children.end();
111                            ++child )
112     {
113       if( child->second->get<std::string>("id") == id )
114         return child->second;
115     }
116
117     return ElementPtr();
118   }
119
120   //----------------------------------------------------------------------------
121   ElementPtr Group::getOrCreateChild( const std::string& type,
122                                       const std::string& id )
123   {
124     ElementPtr child = getChild(id);
125     if( child )
126     {
127       if( child->getProps()->getNameString() == type )
128         return child;
129
130       SG_LOG
131       (
132         SG_GENERAL,
133         SG_WARN,
134         "Group::getOrCreateChild: type missmatch! "
135         "('" << type << "' != '" << child->getProps()->getName() << "', "
136         "id = '" << id << "')"
137       );
138
139       return ElementPtr();
140     }
141
142     return createChild(type, id);
143   }
144
145   //----------------------------------------------------------------------------
146   ElementPtr Group::getElementById(const std::string& id)
147   {
148     std::vector<GroupPtr> groups;
149
150     BOOST_FOREACH( ChildList::value_type child, _children )
151     {
152       const ElementPtr& el = child.second;
153       if( el->getProps()->getStringValue("id") == id )
154         return el;
155
156       GroupPtr group = boost::dynamic_pointer_cast<Group>(el);
157       if( group )
158         groups.push_back(group);
159     }
160
161     BOOST_FOREACH( GroupPtr group, groups )
162     {
163       ElementPtr el = group->getElementById(id);
164       if( el )
165         return el;
166     }
167
168     return ElementPtr();
169   }
170
171   //----------------------------------------------------------------------------
172   void Group::clearEventListener()
173   {
174     BOOST_FOREACH( ChildList::value_type child, _children )
175       child.second->clearEventListener();
176
177     Element::clearEventListener();
178   }
179
180   //----------------------------------------------------------------------------
181   void Group::update(double dt)
182   {
183     BOOST_FOREACH( ChildList::value_type child, _children )
184       child.second->update(dt);
185
186     Element::update(dt);
187   }
188
189   //----------------------------------------------------------------------------
190   bool Group::traverse(EventVisitor& visitor)
191   {
192     // Iterate in reverse order as last child is displayed on top
193     BOOST_REVERSE_FOREACH( ChildList::value_type child, _children )
194     {
195       if( child.second->accept(visitor) )
196         return true;
197     }
198     return false;
199   }
200
201   //----------------------------------------------------------------------------
202   bool Group::setStyle(const SGPropertyNode* style)
203   {
204     // Don't propagate styles directly applicable to this group
205     if( Element::setStyle(style) )
206       return true;
207
208     if(    style->getParent() != _node
209         && _style.find(style->getNameString()) != _style.end() )
210       return false;
211
212     bool handled = false;
213     BOOST_FOREACH( ChildList::value_type child, _children )
214     {
215       if( child.second->setStyle(style) )
216         handled = true;
217     }
218
219     return handled;
220   }
221
222   //----------------------------------------------------------------------------
223   osg::BoundingBox Group::getTransformedBounds(const osg::Matrix& m) const
224   {
225     osg::BoundingBox bb;
226
227     BOOST_FOREACH( ChildList::value_type child, _children )
228     {
229       if( !child.second->getMatrixTransform()->getNodeMask() )
230         continue;
231
232       bb.expandBy
233       (
234         child.second->getTransformedBounds
235         (
236           child.second->getMatrixTransform()->getMatrix() * m
237         )
238       );
239     }
240
241     return bb;
242   }
243
244   //----------------------------------------------------------------------------
245   void Group::childAdded(SGPropertyNode* child)
246   {
247     if( child->getParent() != _node )
248       return;
249
250     ElementFactories::iterator child_factory =
251       _child_factories.find( child->getNameString() );
252     if( child_factory != _child_factories.end() )
253     {
254       ElementPtr element = child_factory->second(_canvas, child, _style, this);
255
256       // Add to osg scene graph...
257       _transform->addChild( element->getMatrixTransform() );
258       _children.push_back( ChildList::value_type(child, element) );
259
260       // ...and ensure correct ordering
261       handleZIndexChanged( --_children.end() );
262
263       return;
264     }
265
266     if( !Element::setStyle(child) )
267     {
268       // Only add style if not applicable to group itself
269       _style[ child->getNameString() ] = child;
270       setStyle(child);
271     }
272   }
273
274   //----------------------------------------------------------------------------
275   void Group::childRemoved(SGPropertyNode* node)
276   {
277     if( node->getParent() != _node )
278       return;
279
280     if( _child_factories.find(node->getNameString()) != _child_factories.end() )
281     {
282       ChildList::iterator child = findChild(node);
283       if( child == _children.end() )
284         SG_LOG
285         (
286           SG_GL,
287           SG_WARN,
288           "can't removed unknown child " << node->getDisplayName()
289         );
290       else
291       {
292         _transform->removeChild( child->second->getMatrixTransform() );
293         _children.erase(child);
294       }
295     }
296     else
297     {
298       Style::iterator style = _style.find(node->getNameString());
299       if( style != _style.end() )
300         _style.erase(style);
301     }
302   }
303
304   //----------------------------------------------------------------------------
305   void Group::childChanged(SGPropertyNode* node)
306   {
307     if(    node->getParent()->getParent() == _node
308         && node->getNameString() == "z-index" )
309       return handleZIndexChanged( findChild(node->getParent()),
310                                   node->getIntValue() );
311   }
312
313   //----------------------------------------------------------------------------
314   void Group::handleZIndexChanged(ChildList::iterator child, int z_index)
315   {
316     if( child == _children.end() )
317       return;
318
319     osg::Node* tf = child->second->getMatrixTransform();
320     int index = _transform->getChildIndex(tf),
321         index_new = index;
322
323     ChildList::iterator next = child;
324     ++next;
325
326     while(    next != _children.end()
327            && next->first->getIntValue("z-index", 0) <= z_index )
328     {
329       ++index_new;
330       ++next;
331     }
332
333     if( index_new != index )
334     {
335       _children.insert(next, *child);
336     }
337     else
338     {
339       ChildList::iterator prev = child,
340                           check = child;
341       while(    check != _children.begin()
342              && (--check)->first->getIntValue("z-index", 0) > z_index )
343       {
344         --index_new;
345         --prev;
346       }
347
348       if( index == index_new )
349         return;
350
351       _children.insert(prev, *child);
352     }
353
354     _transform->removeChild(index);
355     _transform->insertChild(index_new, tf);
356
357     _children.erase(child);
358
359     SG_LOG
360     (
361       SG_GENERAL,
362       SG_INFO,
363       "canvas::Group: Moved element " << index << " to position " << index_new
364     );
365   }
366
367   //----------------------------------------------------------------------------
368   Group::ChildList::iterator Group::findChild(const SGPropertyNode* node)
369   {
370     return std::find_if
371     (
372       _children.begin(),
373       _children.end(),
374       boost::bind(&ChildList::value_type::first, _1) == node
375     );
376   }
377
378 } // namespace canvas
379 } // namespace simgear