]> git.mxchange.org Git - simgear.git/blob - simgear/canvas/elements/CanvasElement.cxx
canvas::Layout: support for contents margins.
[simgear.git] / simgear / canvas / elements / CanvasElement.cxx
1 // Interface for 2D Canvas element
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 "CanvasElement.hxx"
20 #include <simgear/canvas/Canvas.hxx>
21 #include <simgear/canvas/CanvasEventVisitor.hxx>
22 #include <simgear/canvas/events/MouseEvent.hxx>
23 #include <simgear/math/SGMisc.hxx>
24 #include <simgear/misc/strutils.hxx>
25 #include <simgear/scene/material/parseBlendFunc.hxx>
26
27 #include <osg/Drawable>
28 #include <osg/Geode>
29 #include <osg/StateAttribute>
30
31 #include <boost/algorithm/string/predicate.hpp>
32 #include <boost/foreach.hpp>
33 #include <boost/make_shared.hpp>
34
35 #include <cassert>
36 #include <cmath>
37 #include <cstring>
38
39 namespace simgear
40 {
41 namespace canvas
42 {
43   const std::string NAME_TRANSFORM = "tf";
44
45   /**
46    * glScissor with coordinates relative to different reference frames.
47    */
48   class Element::RelativeScissor:
49     public osg::StateAttribute
50   {
51     public:
52
53       ReferenceFrame                _coord_reference;
54       osg::observer_ptr<osg::Node>  _node;
55
56       explicit RelativeScissor(osg::Node* node = NULL):
57         _coord_reference(GLOBAL),
58         _node(node),
59         _x(0),
60         _y(0),
61         _width(0),
62         _height(0)
63       {
64
65       }
66
67       /** Copy constructor using CopyOp to manage deep vs shallow copy. */
68       RelativeScissor( const RelativeScissor& vp,
69                        const osg::CopyOp& copyop = osg::CopyOp::SHALLOW_COPY ):
70         StateAttribute(vp, copyop),
71         _coord_reference(vp._coord_reference),
72         _node(vp._node),
73         _x(vp._x),
74         _y(vp._y),
75         _width(vp._width),
76         _height(vp._height)
77       {}
78
79       META_StateAttribute(simgear, RelativeScissor, SCISSOR);
80
81       /** Return -1 if *this < *rhs, 0 if *this==*rhs, 1 if *this>*rhs. */
82       virtual int compare(const StateAttribute& sa) const
83       {
84         // check the types are equal and then create the rhs variable
85         // used by the COMPARE_StateAttribute_Parameter macros below.
86         COMPARE_StateAttribute_Types(RelativeScissor,sa)
87
88         // compare each parameter in turn against the rhs.
89         COMPARE_StateAttribute_Parameter(_x)
90         COMPARE_StateAttribute_Parameter(_y)
91         COMPARE_StateAttribute_Parameter(_width)
92         COMPARE_StateAttribute_Parameter(_height)
93         COMPARE_StateAttribute_Parameter(_coord_reference)
94         COMPARE_StateAttribute_Parameter(_node)
95
96         return 0; // passed all the above comparison macros, must be equal.
97       }
98
99       virtual bool getModeUsage(StateAttribute::ModeUsage& usage) const
100       {
101         usage.usesMode(GL_SCISSOR_TEST);
102         return true;
103       }
104
105       inline float& x() { return _x; }
106       inline float x() const { return _x; }
107
108       inline float& y() { return _y; }
109       inline float y() const { return _y; }
110
111       inline float& width() { return _width; }
112       inline float width() const { return _width; }
113
114       inline float& height() { return _height; }
115       inline float height() const { return _height; }
116
117       virtual void apply(osg::State& state) const
118       {
119         if( _width <= 0 || _height <= 0 )
120           return;
121
122         const osg::Viewport* vp = state.getCurrentViewport();
123         float w2 = 0.5 * vp->width(),
124               h2 = 0.5 * vp->height();
125
126         osg::Matrix model_view
127         (
128           w2, 0,  0, 0,
129           0,  h2, 0, 0,
130           0,  0,  1, 0,
131           w2, h2, 0, 1
132         );
133         model_view.preMult(state.getProjectionMatrix());
134
135         if( _coord_reference != GLOBAL )
136         {
137           osg::Node* ref_obj = _node.get();
138
139           if( _coord_reference == PARENT )
140           {
141             if( _node->getNumParents() < 1 )
142             {
143               SG_LOG(SG_GL, SG_WARN, "RelativeScissor: missing parent.");
144               return;
145             }
146
147             ref_obj = _node->getParent(0);
148           }
149
150           osg::MatrixList const& parent_matrices = ref_obj->getWorldMatrices();
151           assert( !parent_matrices.empty() );
152           model_view.preMult(parent_matrices.front());
153         }
154
155         const osg::Vec2 scale( model_view(0,0), model_view(1,1)),
156                         offset(model_view(3,0), model_view(3,1));
157
158         // TODO check/warn for rotation?
159         GLint x = SGMiscf::roundToInt(scale.x() * _x + offset.x()),
160               y = SGMiscf::roundToInt(scale.y() * _y + offset.y()),
161               w = SGMiscf::roundToInt(std::fabs(scale.x()) * _width),
162               h = SGMiscf::roundToInt(std::fabs(scale.y()) * _height);
163
164         if( scale.x() < 0 )
165           x -= w;
166         if( scale.y() < 0 )
167           y -= h;
168
169         glScissor(x, y, w, h);
170       }
171
172       bool contains(const osg::Vec2f& pos) const
173       {
174         return _x <= pos.x() && pos.x() <= _x + _width
175             && _y <= pos.y() && pos.y() <= _y + _height;
176       }
177
178       bool contains( const osg::Vec2f& global_pos,
179                      const osg::Vec2f& parent_pos,
180                      const osg::Vec2f& local_pos ) const
181       {
182         switch( _coord_reference )
183         {
184           case GLOBAL: return contains(global_pos);
185           case PARENT: return contains(parent_pos);
186           case LOCAL:  return contains(local_pos);
187         }
188
189         return false;
190       }
191
192     protected:
193       float _x,
194             _y,
195             _width,
196             _height;
197   };
198
199   //----------------------------------------------------------------------------
200   Element::OSGUserData::OSGUserData(ElementPtr element):
201     element(element)
202   {
203
204   }
205
206   //----------------------------------------------------------------------------
207   Element::~Element()
208   {
209     if( !_transform.valid() )
210       return;
211
212     for(unsigned int i = 0; i < _transform->getNumChildren(); ++i)
213     {
214       OSGUserData* ud =
215         static_cast<OSGUserData*>(_transform->getChild(i)->getUserData());
216
217       if( ud )
218         // Ensure parent is cleared to prevent accessing released memory if an
219         // element somehow survives longer than his parent.
220         ud->element->_parent = 0;
221     }
222   }
223
224   //----------------------------------------------------------------------------
225   void Element::onDestroy()
226   {
227     if( !_transform.valid() )
228       return;
229
230     // The transform node keeps a reference on this element, so ensure it is
231     // deleted.
232     BOOST_FOREACH(osg::Group* parent, _transform->getParents())
233     {
234       parent->removeChild(_transform.get());
235     }
236
237     // Hide in case someone still holds a reference
238     setVisible(false);
239     removeListener();
240
241     _parent = 0;
242     _transform = 0;
243   }
244
245   //----------------------------------------------------------------------------
246   ElementPtr Element::getParent() const
247   {
248     return _parent;
249   }
250
251   //----------------------------------------------------------------------------
252   CanvasWeakPtr Element::getCanvas() const
253   {
254     return _canvas;
255   }
256
257   //----------------------------------------------------------------------------
258   void Element::update(double dt)
259   {
260     if( !isVisible() )
261       return;
262
263     // Trigger matrix update
264     getMatrix();
265
266     // Update bounding box on manual update (manual updates pass zero dt)
267     if( dt == 0 && _drawable )
268       _drawable->getBound();
269
270     if( (_attributes_dirty & BLEND_FUNC) && _transform.valid() )
271     {
272       parseBlendFunc(
273         _transform->getOrCreateStateSet(),
274         _node->getChild("blend-source"),
275         _node->getChild("blend-destination"),
276         _node->getChild("blend-source-rgb"),
277         _node->getChild("blend-destination-rgb"),
278         _node->getChild("blend-source-alpha"),
279         _node->getChild("blend-destination-alpha")
280       );
281       _attributes_dirty &= ~BLEND_FUNC;
282     }
283   }
284
285   //----------------------------------------------------------------------------
286   bool Element::addEventListener( const std::string& type_str,
287                                   const EventListener& cb )
288   {
289     SG_LOG
290     (
291       SG_GENERAL,
292       SG_INFO,
293       "addEventListener(" << _node->getPath() << ", " << type_str << ")"
294     );
295
296     _listener[ Event::getOrRegisterType(type_str) ].push_back(cb);
297     return true;
298   }
299
300   //----------------------------------------------------------------------------
301   void Element::clearEventListener()
302   {
303     _listener.clear();
304   }
305
306   //----------------------------------------------------------------------------
307   bool Element::accept(EventVisitor& visitor)
308   {
309     if( !isVisible() )
310       return false;
311
312     return visitor.apply(*this);
313   }
314
315   //----------------------------------------------------------------------------
316   bool Element::ascend(EventVisitor& visitor)
317   {
318     if( _parent )
319       return _parent->accept(visitor);
320     return true;
321   }
322
323   //----------------------------------------------------------------------------
324   bool Element::traverse(EventVisitor& visitor)
325   {
326     return true;
327   }
328
329   //----------------------------------------------------------------------------
330   bool Element::handleEvent(const EventPtr& event)
331   {
332     ListenerMap::iterator listeners = _listener.find(event->getType());
333     if( listeners == _listener.end() )
334       return false;
335
336     BOOST_FOREACH(EventListener const& listener, listeners->second)
337       try
338       {
339         listener(event);
340       }
341       catch( std::exception const& ex )
342       {
343         SG_LOG(
344           SG_GENERAL,
345           SG_WARN,
346           "canvas::Element: event handler error: '" << ex.what() << "'"
347         );
348       }
349
350     return true;
351   }
352
353   //----------------------------------------------------------------------------
354   bool Element::dispatchEvent(const EventPtr& event)
355   {
356     EventPropagationPath path;
357     path.push_back( EventTarget(this) );
358
359     for( Element* parent = _parent;
360                   parent != NULL;
361                   parent = parent->_parent )
362       path.push_front( EventTarget(parent) );
363
364     CanvasPtr canvas = _canvas.lock();
365     if( !canvas )
366       return false;
367
368     return canvas->propagateEvent(event, path);
369   }
370
371   //----------------------------------------------------------------------------
372   bool Element::hitBound( const osg::Vec2f& global_pos,
373                           const osg::Vec2f& parent_pos,
374                           const osg::Vec2f& local_pos ) const
375   {
376     if( _scissor && !_scissor->contains(global_pos, parent_pos, local_pos) )
377       return false;
378
379     const osg::Vec3f pos3(parent_pos, 0);
380
381     // Drawables have a bounding box...
382     if( _drawable )
383       return _drawable->getBound().contains(osg::Vec3f(local_pos, 0));
384     else if( _transform.valid() )
385       // ... for other elements, i.e. groups only a bounding sphere is available
386       return _transform->getBound().contains(osg::Vec3f(parent_pos, 0));
387     else
388       return false;
389   }
390
391
392   //----------------------------------------------------------------------------
393   void Element::setVisible(bool visible)
394   {
395     if( _transform.valid() )
396       // TODO check if we need another nodemask
397       _transform->setNodeMask(visible ? 0xffffffff : 0);
398   }
399
400   //----------------------------------------------------------------------------
401   bool Element::isVisible() const
402   {
403     return _transform.valid() && _transform->getNodeMask() != 0;
404   }
405
406   //----------------------------------------------------------------------------
407   osg::MatrixTransform* Element::getMatrixTransform()
408   {
409     return _transform.get();
410   }
411
412   //----------------------------------------------------------------------------
413   osg::MatrixTransform const* Element::getMatrixTransform() const
414   {
415     return _transform.get();
416   }
417
418   //----------------------------------------------------------------------------
419   osg::Vec2f Element::posToLocal(const osg::Vec2f& pos) const
420   {
421     getMatrix();
422     const osg::Matrix& m = _transform->getInverseMatrix();
423     return osg::Vec2f
424     (
425       m(0, 0) * pos[0] + m(1, 0) * pos[1] + m(3, 0),
426       m(0, 1) * pos[0] + m(1, 1) * pos[1] + m(3, 1)
427     );
428   }
429
430   //----------------------------------------------------------------------------
431   void Element::childAdded(SGPropertyNode* parent, SGPropertyNode* child)
432   {
433     if(    parent == _node
434         && child->getNameString() == NAME_TRANSFORM )
435     {
436       if( child->getIndex() >= static_cast<int>(_transform_types.size()) )
437         _transform_types.resize( child->getIndex() + 1 );
438
439       _transform_types[ child->getIndex() ] = TT_NONE;
440       _attributes_dirty |= TRANSFORM;
441       return;
442     }
443     else if(    parent->getParent() == _node
444              && parent->getNameString() == NAME_TRANSFORM )
445     {
446       assert(parent->getIndex() < static_cast<int>(_transform_types.size()));
447
448       const std::string& name = child->getNameString();
449
450       TransformType& type = _transform_types[parent->getIndex()];
451
452       if(      name == "m" )
453         type = TT_MATRIX;
454       else if( name == "t" )
455         type = TT_TRANSLATE;
456       else if( name == "rot" )
457         type = TT_ROTATE;
458       else if( name == "s" )
459         type = TT_SCALE;
460
461       _attributes_dirty |= TRANSFORM;
462       return;
463     }
464
465     childAdded(child);
466   }
467
468   //----------------------------------------------------------------------------
469   void Element::childRemoved(SGPropertyNode* parent, SGPropertyNode* child)
470   {
471     if( parent == _node )
472     {
473       if( child->getNameString() == NAME_TRANSFORM )
474       {
475         if( !_transform.valid() )
476           return;
477
478         if( child->getIndex() >= static_cast<int>(_transform_types.size()) )
479         {
480           SG_LOG
481           (
482             SG_GENERAL,
483             SG_WARN,
484             "Element::childRemoved: unknown transform: " << child->getPath()
485           );
486           return;
487         }
488
489         _transform_types[ child->getIndex() ] = TT_NONE;
490
491         while( !_transform_types.empty() && _transform_types.back() == TT_NONE )
492           _transform_types.pop_back();
493
494         _attributes_dirty |= TRANSFORM;
495         return;
496       }
497       else if( StyleInfo const* style = getStyleInfo(child->getNameString()) )
498       {
499         if( setStyle(getParentStyle(child), style) )
500           return;
501       }
502     }
503
504     childRemoved(child);
505   }
506
507   //----------------------------------------------------------------------------
508   void Element::valueChanged(SGPropertyNode* child)
509   {
510     SGPropertyNode *parent = child->getParent();
511     if( parent == _node )
512     {
513       const std::string& name = child->getNameString();
514       if( boost::starts_with(name, "data-") )
515         return;
516       else if( StyleInfo const* style_info = getStyleInfo(name) )
517       {
518         SGPropertyNode const* style = child;
519         if( isStyleEmpty(child) )
520         {
521           child->clearValue();
522           style = getParentStyle(child);
523         }
524         setStyle(style, style_info);
525         return;
526       }
527       else if( name == "update" )
528         return update(0);
529       else if( boost::starts_with(name, "blend-") )
530         return (void)(_attributes_dirty |= BLEND_FUNC);
531     }
532     else if(   parent
533             && parent->getParent() == _node
534             && parent->getNameString() == NAME_TRANSFORM )
535     {
536       _attributes_dirty |= TRANSFORM;
537       return;
538     }
539
540     childChanged(child);
541   }
542
543   //----------------------------------------------------------------------------
544   bool Element::setStyle( const SGPropertyNode* child,
545                           const StyleInfo* style_info )
546   {
547     return canApplyStyle(child) && setStyleImpl(child, style_info);
548   }
549
550   //----------------------------------------------------------------------------
551   void Element::setClip(const std::string& clip)
552   {
553     osg::StateSet* ss = getOrCreateStateSet();
554     if( !ss )
555       return;
556
557     if( clip.empty() || clip == "auto" )
558     {
559       ss->removeAttribute(osg::StateAttribute::SCISSOR);
560       _scissor = 0;
561       return;
562     }
563
564     // TODO generalize CSS property parsing
565     const std::string RECT("rect(");
566     if(    !boost::ends_with(clip, ")")
567         || !boost::starts_with(clip, RECT) )
568     {
569       SG_LOG(SG_GENERAL, SG_WARN, "Canvas: invalid clip: " << clip);
570       return;
571     }
572
573     const std::string sep(", \t\npx");
574     int comp = 0;
575     float values[4];
576
577     for(size_t pos = RECT.size(); comp < 4; ++comp)
578     {
579       pos = clip.find_first_not_of(sep, pos);
580       if( pos == std::string::npos || pos == clip.size() - 1 )
581         break;
582
583       char *end = 0;
584       values[comp] = strtod(&clip[pos], &end);
585       if( end == &clip[pos] || !end )
586         break;
587
588       pos = end - &clip[0];
589     }
590
591     if( comp < 4 )
592     {
593       SG_LOG(SG_GENERAL, SG_WARN, "Canvas: invalid clip: " << clip);
594       return;
595     }
596
597     float width = values[1] - values[3],
598           height = values[2] - values[0];
599
600     if( width < 0 || height < 0 )
601     {
602       SG_LOG(SG_GENERAL, SG_WARN, "Canvas: negative clip size: " << clip);
603       return;
604     }
605
606     if( !_scissor )
607       _scissor = new RelativeScissor(_transform.get());
608
609     // <top>, <right>, <bottom>, <left>
610     _scissor->x() = values[3];
611     _scissor->y() = values[0];
612     _scissor->width() = width;
613     _scissor->height() = height;
614
615     SGPropertyNode* clip_frame = _node->getChild("clip-frame", 0);
616     if( clip_frame )
617       valueChanged(clip_frame);
618     else
619       _scissor->_coord_reference = GLOBAL;
620
621     ss->setAttributeAndModes(_scissor);
622   }
623
624   //----------------------------------------------------------------------------
625   void Element::setClipFrame(ReferenceFrame rf)
626   {
627     if( _scissor )
628       _scissor->_coord_reference = rf;
629   }
630
631   //----------------------------------------------------------------------------
632   osg::BoundingBox Element::getBoundingBox() const
633   {
634     if( _drawable )
635       return _drawable->getBound();
636
637     osg::BoundingBox bb;
638
639     if( _transform.valid() )
640       bb.expandBy(_transform->getBound());
641
642     return bb;
643   }
644
645   //----------------------------------------------------------------------------
646   osg::BoundingBox Element::getTightBoundingBox() const
647   {
648     return getTransformedBounds(getMatrix());
649   }
650
651   //----------------------------------------------------------------------------
652   osg::BoundingBox Element::getTransformedBounds(const osg::Matrix& m) const
653   {
654     if( !_drawable )
655       return osg::BoundingBox();
656
657     osg::BoundingBox transformed;
658     const osg::BoundingBox& bb = _drawable->getBound();
659     for(int i = 0; i < 4; ++i)
660       transformed.expandBy( bb.corner(i) * m );
661
662     return transformed;
663   }
664
665   //----------------------------------------------------------------------------
666   osg::Matrix Element::getMatrix() const
667   {
668     if( !_transform )
669       return osg::Matrix::identity();
670
671     if( !(_attributes_dirty & TRANSFORM) )
672       return _transform->getMatrix();
673
674     osg::Matrix m;
675     for( size_t i = 0; i < _transform_types.size(); ++i )
676     {
677       // Skip unused indizes...
678       if( _transform_types[i] == TT_NONE )
679         continue;
680
681       SGPropertyNode* tf_node = _node->getChild("tf", i, true);
682
683       // Build up the matrix representation of the current transform node
684       osg::Matrix tf;
685       switch( _transform_types[i] )
686       {
687         case TT_MATRIX:
688           tf = osg::Matrix( tf_node->getDoubleValue("m[0]", 1),
689                             tf_node->getDoubleValue("m[1]", 0),
690                             0,
691                             tf_node->getDoubleValue("m[6]", 0),
692
693                             tf_node->getDoubleValue("m[2]", 0),
694                             tf_node->getDoubleValue("m[3]", 1),
695                             0,
696                             tf_node->getDoubleValue("m[7]", 0),
697
698                             0,
699                             0,
700                             1,
701                             0,
702
703                             tf_node->getDoubleValue("m[4]", 0),
704                             tf_node->getDoubleValue("m[5]", 0),
705                             0,
706                             tf_node->getDoubleValue("m[8]", 1) );
707           break;
708         case TT_TRANSLATE:
709           tf.makeTranslate( osg::Vec3f( tf_node->getDoubleValue("t[0]", 0),
710                                         tf_node->getDoubleValue("t[1]", 0),
711                                         0 ) );
712           break;
713         case TT_ROTATE:
714           tf.makeRotate( tf_node->getDoubleValue("rot", 0), 0, 0, 1 );
715           break;
716         case TT_SCALE:
717         {
718           float sx = tf_node->getDoubleValue("s[0]", 1);
719           // sy defaults to sx...
720           tf.makeScale( sx, tf_node->getDoubleValue("s[1]", sx), 1 );
721           break;
722         }
723         default:
724           break;
725       }
726       m.postMult( tf );
727     }
728     _transform->setMatrix(m);
729     _attributes_dirty &= ~TRANSFORM;
730
731     return m;
732   }
733
734   //----------------------------------------------------------------------------
735   Element::StyleSetters Element::_style_setters;
736
737   //----------------------------------------------------------------------------
738   Element::Element( const CanvasWeakPtr& canvas,
739                     const SGPropertyNode_ptr& node,
740                     const Style& parent_style,
741                     Element* parent ):
742     PropertyBasedElement(node),
743     _canvas( canvas ),
744     _parent( parent ),
745     _attributes_dirty( 0 ),
746     _transform( new osg::MatrixTransform ),
747     _style( parent_style ),
748     _scissor( 0 ),
749     _drawable( 0 )
750   {
751     staticInit();
752
753     SG_LOG
754     (
755       SG_GL,
756       SG_DEBUG,
757       "New canvas element " << node->getPath()
758     );
759
760     // Ensure elements are drawn in order they appear in the element tree
761     _transform->getOrCreateStateSet()
762               ->setRenderBinDetails
763               (
764                 0,
765                 "PreOrderBin",
766                 osg::StateSet::OVERRIDE_RENDERBIN_DETAILS
767               );
768
769     _transform->setUserData( new OSGUserData(this) );
770   }
771
772   //----------------------------------------------------------------------------
773   void Element::staticInit()
774   {
775     if( isInit<Element>() )
776       return;
777
778     addStyle("clip", "", &Element::setClip, false);
779     addStyle("clip-frame", "", &Element::setClipFrame, false);
780     addStyle("visible", "", &Element::setVisible, false);
781   }
782
783   //----------------------------------------------------------------------------
784   bool Element::isStyleEmpty(const SGPropertyNode* child) const
785   {
786     return !child
787         || simgear::strutils::strip(child->getStringValue()).empty();
788   }
789
790   //----------------------------------------------------------------------------
791   bool Element::canApplyStyle(const SGPropertyNode* child) const
792   {
793     if( _node == child->getParent() )
794       return true;
795
796     // Parent values do not override if element has own value
797     return isStyleEmpty( _node->getChild(child->getName()) );
798   }
799
800   //----------------------------------------------------------------------------
801   bool Element::setStyleImpl( const SGPropertyNode* child,
802                               const StyleInfo* style_info )
803   {
804     const StyleSetter* style_setter = style_info
805                                     ? &style_info->setter
806                                     : getStyleSetter(child->getNameString());
807     while( style_setter )
808     {
809       if( style_setter->func(*this, child) )
810         return true;
811       style_setter = style_setter->next;
812     }
813     return false;
814   }
815
816   //----------------------------------------------------------------------------
817   const Element::StyleInfo*
818   Element::getStyleInfo(const std::string& name) const
819   {
820     StyleSetters::const_iterator setter = _style_setters.find(name);
821     if( setter == _style_setters.end() )
822       return 0;
823
824     return &setter->second;
825   }
826
827   //----------------------------------------------------------------------------
828   const Element::StyleSetter*
829   Element::getStyleSetter(const std::string& name) const
830   {
831     const StyleInfo* info = getStyleInfo(name);
832     return info ? &info->setter : 0;
833   }
834
835   //----------------------------------------------------------------------------
836   const SGPropertyNode*
837   Element::getParentStyle(const SGPropertyNode* child) const
838   {
839     // Try to get value from parent...
840     if( _parent )
841     {
842       Style::const_iterator style =
843         _parent->_style.find(child->getNameString());
844       if( style != _parent->_style.end() )
845         return style->second;
846     }
847
848     // ...or reset to default if none is available
849     return child; // TODO somehow get default value for each style?
850   }
851
852   //----------------------------------------------------------------------------
853   void Element::setDrawable( osg::Drawable* drawable )
854   {
855     _drawable = drawable;
856     assert( _drawable );
857
858     osg::ref_ptr<osg::Geode> geode = new osg::Geode;
859     geode->addDrawable(_drawable);
860     _transform->addChild(geode);
861   }
862
863   //----------------------------------------------------------------------------
864   osg::StateSet* Element::getOrCreateStateSet()
865   {
866     if( _drawable.valid() )
867       return _drawable->getOrCreateStateSet();
868     if( _transform.valid() )
869       return _transform->getOrCreateStateSet();
870
871     return 0;
872   }
873
874   //----------------------------------------------------------------------------
875   void Element::setupStyle()
876   {
877     BOOST_FOREACH( Style::value_type style, _style )
878       setStyle(style.second);
879   }
880
881 } // namespace canvas
882 } // namespace simgear