]> git.mxchange.org Git - simgear.git/blob - simgear/canvas/elements/CanvasGroup.cxx
Canvas: Fix removing text elements.
[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                         const StyleInfo* style_info )
179   {
180     if( !canApplyStyle(style) )
181       return false;
182
183     bool handled = setStyleImpl(style, style_info);
184     if( style_info->inheritable )
185     {
186       for(size_t i = 0; i < _transform->getNumChildren(); ++i)
187         handled |= getChildByIndex(i)->setStyle(style, style_info);
188     }
189
190     return handled;
191   }
192
193   //----------------------------------------------------------------------------
194   osg::BoundingBox Group::getTransformedBounds(const osg::Matrix& m) const
195   {
196     osg::BoundingBox bb;
197
198     for(size_t i = 0; i < _transform->getNumChildren(); ++i)
199     {
200       const ElementPtr& child = getChildByIndex(i);
201       if( !child->getMatrixTransform()->getNodeMask() )
202         continue;
203
204       bb.expandBy
205       (
206         child->getTransformedBounds
207         (
208           child->getMatrixTransform()->getMatrix() * m
209         )
210       );
211     }
212
213     return bb;
214   }
215
216   //----------------------------------------------------------------------------
217   ElementFactory Group::getChildFactory(const std::string& type) const
218   {
219     ElementFactories::iterator child_factory = _child_factories.find(type);
220     if( child_factory != _child_factories.end() )
221       return child_factory->second;
222
223     return ElementFactory();
224   }
225
226   //----------------------------------------------------------------------------
227   void Group::childAdded(SGPropertyNode* child)
228   {
229     if( child->getParent() != _node )
230       return;
231
232     ElementFactory child_factory = getChildFactory( child->getNameString() );
233     if( child_factory )
234     {
235       ElementPtr element = child_factory(_canvas, child, _style, this);
236
237       // Add to osg scene graph...
238       _transform->addChild( element->getMatrixTransform() );
239
240       // ...and ensure correct ordering
241       handleZIndexChanged(element);
242
243       return;
244     }
245
246     StyleInfo const* style = getStyleInfo(child->getNameString());
247     if( style && style->inheritable )
248       _style[ child->getNameString() ] = child;
249   }
250
251   //----------------------------------------------------------------------------
252   void Group::childRemoved(SGPropertyNode* node)
253   {
254     if( node->getParent() != _node )
255       return;
256
257     if( getChildFactory(node->getNameString()) )
258     {
259       if( !_transform.valid() )
260         // If transform is destroyed also all children are destroyed, so we can
261         // not do anything here.
262         return;
263
264       ElementPtr child = getChild(node);
265       if( !child )
266         SG_LOG
267         (
268           SG_GL,
269           SG_WARN,
270           "can't removed unknown child " << node->getDisplayName()
271         );
272       else
273       {
274         // Remove child from the scenegraph (this automatically invalidates the
275         // reference on the element hold by the MatrixTransform)
276         child->onDestroy();
277       }
278     }
279     else
280     {
281       _style.erase( node->getNameString() );
282     }
283   }
284
285   //----------------------------------------------------------------------------
286   void Group::childChanged(SGPropertyNode* node)
287   {
288     if(    node->getParent()->getParent() == _node
289         && node->getNameString() == "z-index" )
290       return handleZIndexChanged( getChild(node->getParent()),
291                                   node->getIntValue() );
292   }
293
294   //----------------------------------------------------------------------------
295   void Group::handleZIndexChanged(ElementPtr child, int z_index)
296   {
297     if( !child )
298       return;
299
300     osg::ref_ptr<osg::MatrixTransform> tf = child->getMatrixTransform();
301     size_t index = _transform->getChildIndex(tf),
302            index_new = index;
303
304     for(;; ++index_new)
305     {
306       if( index_new + 1 == _transform->getNumChildren() )
307         break;
308
309       // Move to end of block with same index (= move upwards until the next
310       // element has a higher index)
311       if( getChildByIndex(index_new + 1)->get<int>("z-index", 0) > z_index )
312         break;
313     }
314
315     if( index_new == index )
316     {
317       // We were not able to move upwards so now try downwards
318       for(;; --index_new)
319       {
320         if( index_new == 0 )
321           break;
322
323         // Move to end of block with same index (= move downwards until the
324         // previous element has the same or a lower index)
325         if( getChildByIndex(index_new - 1)->get<int>("z-index", 0) <= z_index )
326           break;
327       }
328
329       if( index == index_new )
330         return;
331     }
332
333     _transform->removeChild(index);
334     _transform->insertChild(index_new, tf);
335
336     SG_LOG
337     (
338       SG_GENERAL,
339       SG_INFO,
340       "canvas::Group: Moved element " << index << " to position " << index_new
341     );
342   }
343
344   //----------------------------------------------------------------------------
345   ElementPtr Group::getChildByIndex(size_t index) const
346   {
347     assert(_transform.valid());
348     OSGUserData* ud =
349       static_cast<OSGUserData*>(_transform->getChild(index)->getUserData());
350     assert(ud);
351     return ud->element;
352   }
353
354   //----------------------------------------------------------------------------
355   ElementPtr Group::findChild( const SGPropertyNode* node,
356                                const std::string& id ) const
357   {
358     for(size_t i = 0; i < _transform->getNumChildren(); ++i)
359     {
360       ElementPtr el = getChildByIndex(i);
361
362       if( node )
363       {
364         if( el->getProps() == node )
365           return el;
366       }
367       else
368       {
369         if( el->get<std::string>("id") == id )
370           return el;
371       }
372     }
373
374     return ElementPtr();
375   }
376
377 } // namespace canvas
378 } // namespace simgear