]> git.mxchange.org Git - simgear.git/blob - simgear/canvas/elements/CanvasElement.cxx
Implement Canvas single/double/tripple click handling.
[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/CanvasEventListener.hxx>
22 #include <simgear/canvas/CanvasEventVisitor.hxx>
23 #include <simgear/canvas/MouseEvent.hxx>
24
25 #include <osg/Drawable>
26 #include <osg/Geode>
27
28 #include <boost/foreach.hpp>
29 #include <boost/make_shared.hpp>
30
31 #include <cassert>
32 #include <cstring>
33
34 namespace simgear
35 {
36 namespace canvas
37 {
38   const std::string NAME_TRANSFORM = "tf";
39
40   //----------------------------------------------------------------------------
41   void Element::removeListener()
42   {
43     _node->removeChangeListener(this);
44   }
45
46   //----------------------------------------------------------------------------
47   Element::~Element()
48   {
49     removeListener();
50
51     BOOST_FOREACH(osg::Group* parent, _transform->getParents())
52     {
53       parent->removeChild(_transform);
54     }
55   }
56
57   //----------------------------------------------------------------------------
58   ElementWeakPtr Element::getWeakPtr() const
59   {
60     return boost::static_pointer_cast<Element>(_self.lock());
61   }
62
63   //----------------------------------------------------------------------------
64   void Element::update(double dt)
65   {
66     if( !_transform->getNodeMask() )
67       // Don't do anything if element is hidden
68       return;
69
70     if( _transform_dirty )
71     {
72       osg::Matrix m;
73       for( size_t i = 0; i < _transform_types.size(); ++i )
74       {
75         // Skip unused indizes...
76         if( _transform_types[i] == TT_NONE )
77           continue;
78
79         SGPropertyNode* tf_node = _node->getChild("tf", i, true);
80
81         // Build up the matrix representation of the current transform node
82         osg::Matrix tf;
83         switch( _transform_types[i] )
84         {
85           case TT_MATRIX:
86             tf = osg::Matrix( tf_node->getDoubleValue("m[0]", 1),
87                               tf_node->getDoubleValue("m[1]", 0), 0, 0,
88
89                               tf_node->getDoubleValue("m[2]", 0),
90                               tf_node->getDoubleValue("m[3]", 1), 0, 0,
91
92                               0,    0,    1, 0,
93                               tf_node->getDoubleValue("m[4]", 0),
94                               tf_node->getDoubleValue("m[5]", 0), 0, 1 );
95             break;
96           case TT_TRANSLATE:
97             tf.makeTranslate( osg::Vec3f( tf_node->getDoubleValue("t[0]", 0),
98                                           tf_node->getDoubleValue("t[1]", 0),
99                                           0 ) );
100             break;
101           case TT_ROTATE:
102             tf.makeRotate( tf_node->getDoubleValue("rot", 0), 0, 0, 1 );
103             break;
104           case TT_SCALE:
105           {
106             float sx = tf_node->getDoubleValue("s[0]", 1);
107             // sy defaults to sx...
108             tf.makeScale( sx, tf_node->getDoubleValue("s[1]", sx), 1 );
109             break;
110           }
111           default:
112             break;
113         }
114         m.postMult( tf );
115       }
116       _transform->setMatrix(m);
117       _transform_dirty = false;
118     }
119   }
120
121   //----------------------------------------------------------------------------
122   naRef Element::addEventListener(const nasal::CallContext& ctx)
123   {
124     const std::string type_str = ctx.requireArg<std::string>(0);
125     naRef code = ctx.requireArg<naRef>(1);
126
127     SG_LOG
128     (
129       SG_NASAL,
130       SG_INFO,
131       "addEventListener(" << _node->getPath() << ", " << type_str << ")"
132     );
133
134     Event::Type type = Event::strToType(type_str);
135     if( type == Event::UNKNOWN )
136       naRuntimeError( ctx.c,
137                       "addEventListener: Unknown event type %s",
138                       type_str.c_str() );
139
140     _listener[ type ].push_back
141     (
142       boost::make_shared<EventListener>( code,
143                                          _canvas.lock()->getSystemAdapter() )
144     );
145
146     return naNil();
147   }
148
149   //----------------------------------------------------------------------------
150   bool Element::accept(EventVisitor& visitor)
151   {
152     return visitor.apply(*this);
153   }
154
155   //----------------------------------------------------------------------------
156   bool Element::ascend(EventVisitor& visitor)
157   {
158     if( _parent )
159       return _parent->accept(visitor);
160     return true;
161   }
162
163   //----------------------------------------------------------------------------
164   bool Element::traverse(EventVisitor& visitor)
165   {
166     return true;
167   }
168
169   //----------------------------------------------------------------------------
170   void Element::callListeners(const canvas::EventPtr& event)
171   {
172     ListenerMap::iterator listeners = _listener.find(event->getType());
173     if( listeners == _listener.end() )
174       return;
175
176     BOOST_FOREACH(EventListenerPtr listener, listeners->second)
177       listener->call(event);
178   }
179
180   //----------------------------------------------------------------------------
181   bool Element::hitBound(const osg::Vec2f& pos) const
182   {
183     const osg::Vec3f pos3(pos, 0);
184
185     // Drawables have a bounding box...
186     if( _drawable )
187     {
188       if( !_drawable->getBound().contains(pos3) )
189         return false;
190     }
191     // ... for other elements, i.e. groups only a bounding sphere is available
192     else if( !_transform->getBound().contains(pos3) )
193       return false;
194
195     return true;
196   }
197
198   //----------------------------------------------------------------------------
199   osg::ref_ptr<osg::MatrixTransform> Element::getMatrixTransform()
200   {
201     return _transform;
202   }
203
204   //----------------------------------------------------------------------------
205   void Element::childAdded(SGPropertyNode* parent, SGPropertyNode* child)
206   {
207     if(    parent == _node
208         && child->getNameString() == NAME_TRANSFORM )
209     {
210       if( child->getIndex() >= static_cast<int>(_transform_types.size()) )
211         _transform_types.resize( child->getIndex() + 1 );
212
213       _transform_types[ child->getIndex() ] = TT_NONE;
214       _transform_dirty = true;
215       return;
216     }
217     else if(    parent->getParent() == _node
218              && parent->getNameString() == NAME_TRANSFORM )
219     {
220       assert(parent->getIndex() < static_cast<int>(_transform_types.size()));
221
222       const std::string& name = child->getNameString();
223
224       TransformType& type = _transform_types[parent->getIndex()];
225
226       if(      name == "m" )
227         type = TT_MATRIX;
228       else if( name == "t" )
229         type = TT_TRANSLATE;
230       else if( name == "rot" )
231         type = TT_ROTATE;
232       else if( name == "s" )
233         type = TT_SCALE;
234
235       _transform_dirty = true;
236       return;
237     }
238
239     childAdded(child);
240   }
241
242   //----------------------------------------------------------------------------
243   void Element::childRemoved(SGPropertyNode* parent, SGPropertyNode* child)
244   {
245     if( parent == _node && child->getNameString() == NAME_TRANSFORM )
246     {
247       if( child->getIndex() >= static_cast<int>(_transform_types.size()) )
248       {
249         SG_LOG
250         (
251           SG_GENERAL,
252           SG_WARN,
253           "Element::childRemoved: unknown transform: " << child->getPath()
254         );
255         return;
256       }
257
258       _transform_types[ child->getIndex() ] = TT_NONE;
259
260       while( !_transform_types.empty() && _transform_types.back() == TT_NONE )
261         _transform_types.pop_back();
262
263       _transform_dirty = true;
264       return;
265     }
266
267     childRemoved(child);
268   }
269
270   //----------------------------------------------------------------------------
271   void Element::valueChanged(SGPropertyNode* child)
272   {
273     SGPropertyNode *parent = child->getParent();
274     if( parent == _node )
275     {
276       if( setStyle(child) )
277         return;
278       else if( child->getNameString() == "update" )
279         return update(0);
280       else if( child->getNameString() == "visible" )
281         // TODO check if we need another nodemask
282         return _transform->setNodeMask( child->getBoolValue() ? 0xffffffff : 0 );
283     }
284     else if(   parent->getParent() == _node
285             && parent->getNameString() == NAME_TRANSFORM )
286     {
287       _transform_dirty = true;
288       return;
289     }
290
291     childChanged(child);
292   }
293
294   //----------------------------------------------------------------------------
295   bool Element::setStyle(const SGPropertyNode* child)
296   {
297     StyleSetters::const_iterator setter =
298       _style_setters.find(child->getNameString());
299     if( setter == _style_setters.end() )
300       return false;
301
302     setter->second(child);
303     return true;
304   }
305
306   //----------------------------------------------------------------------------
307   void Element::setBoundingBox(const osg::BoundingBox& bb)
308   {
309     if( _bounding_box.empty() )
310     {
311       SGPropertyNode* bb_node = _node->getChild("bounding-box", 0, true);
312       _bounding_box.resize(4);
313       _bounding_box[0] = bb_node->getChild("min-x", 0, true);
314       _bounding_box[1] = bb_node->getChild("min-y", 0, true);
315       _bounding_box[2] = bb_node->getChild("max-x", 0, true);
316       _bounding_box[3] = bb_node->getChild("max-y", 0, true);
317     }
318
319     _bounding_box[0]->setFloatValue(bb._min.x());
320     _bounding_box[1]->setFloatValue(bb._min.y());
321     _bounding_box[2]->setFloatValue(bb._max.x());
322     _bounding_box[3]->setFloatValue(bb._max.y());
323   }
324
325   //----------------------------------------------------------------------------
326   osg::BoundingBox Element::getTransformedBounds(const osg::Matrix& m) const
327   {
328     return osg::BoundingBox();
329   }
330
331   //----------------------------------------------------------------------------
332   Element::Element( const CanvasWeakPtr& canvas,
333                     const SGPropertyNode_ptr& node,
334                     const Style& parent_style,
335                     Element* parent ):
336     PropertyBasedElement(node),
337     _canvas( canvas ),
338     _parent( parent ),
339     _transform_dirty( false ),
340     _transform( new osg::MatrixTransform ),
341     _style( parent_style ),
342     _drawable( 0 )
343   {
344     SG_LOG
345     (
346       SG_GL,
347       SG_DEBUG,
348       "New canvas element " << node->getPath()
349     );
350   }
351
352   //----------------------------------------------------------------------------
353   void Element::setDrawable( osg::Drawable* drawable )
354   {
355     _drawable = drawable;
356     assert( _drawable );
357
358     osg::ref_ptr<osg::Geode> geode = new osg::Geode;
359     geode->addDrawable(_drawable);
360     _transform->addChild(geode);
361   }
362
363   //----------------------------------------------------------------------------
364   void Element::setupStyle()
365   {
366     BOOST_FOREACH( Style::value_type style, _style )
367       setStyle(style.second);
368   }
369
370 } // namespace canvas
371 } // namespace simgear