]> git.mxchange.org Git - flightgear.git/blob - src/Canvas/gui_mgr.cxx
208d209c9a98a53cdc609838f49e826552017306
[flightgear.git] / src / Canvas / gui_mgr.cxx
1 // Canvas gui/dialog manager
2 //
3 // Copyright (C) 2012  Thomas Geymayer <tomgey@gmail.com>
4 //
5 // This program is free software; you can redistribute it and/or
6 // modify it under the terms of the GNU General Public License as
7 // published by the Free Software Foundation; either version 2 of the
8 // License, or (at your option) any later version.
9 //
10 // This program is distributed in the hope that it will be useful, but
11 // WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13 // General Public License for more details.
14 //
15 // You should have received a copy of the GNU General Public License
16 // along with this program; if not, write to the Free Software
17 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
18
19 #include "gui_mgr.hxx"
20 #include "mouse_event.hxx"
21 #include <Canvas/window.hxx>
22 #include <Canvas/canvas.hxx>
23
24 #include <Main/globals.hxx>
25 #include <Viewer/CameraGroup.hxx>
26 #include <Viewer/renderer.hxx>
27
28 #include <osgViewer/Viewer>
29 #include <osgGA/GUIEventHandler>
30
31 #include <boost/bind.hpp>
32
33 /**
34  * Event handler
35  */
36 class GUIEventHandler:
37   public osgGA::GUIEventHandler
38 {
39   public:
40     GUIEventHandler(GUIMgr* gui_mgr):
41       _gui_mgr( gui_mgr )
42     {}
43
44     bool handle( const osgGA::GUIEventAdapter& ea,
45                  osgGA::GUIActionAdapter& aa,
46                  osg::Object*,
47                  osg::NodeVisitor* )
48     {
49       return _gui_mgr->handleEvent(ea);
50     }
51
52   protected:
53     GUIMgr *_gui_mgr;
54 };
55
56 /**
57  * Track a canvas placement on a window
58  */
59 class WindowPlacement:
60   public canvas::Placement
61 {
62   public:
63     WindowPlacement( canvas::WindowPtr window,
64                      CanvasPtr canvas ):
65       _window(window),
66       _canvas(canvas)
67     {}
68
69     /**
70      * Remove placement from window
71      */
72     virtual ~WindowPlacement()
73     {
74       canvas::WindowPtr window = _window.lock();
75       CanvasPtr canvas = _canvas.lock();
76
77       if( window && canvas && canvas == window->getCanvas().lock() )
78         window->setCanvas( CanvasPtr() );
79     }
80
81   private:
82     canvas::WindowWeakPtr _window;
83     CanvasWeakPtr _canvas;
84 };
85
86 //------------------------------------------------------------------------------
87 typedef boost::shared_ptr<canvas::Window> WindowPtr;
88 WindowPtr windowFactory(SGPropertyNode* node)
89 {
90   return WindowPtr(new canvas::Window(node));
91 }
92
93 //------------------------------------------------------------------------------
94 GUIMgr::GUIMgr():
95   PropertyBasedMgr("/sim/gui/canvas", "window", &windowFactory),
96   _event_handler( new GUIEventHandler(this) ),
97   _transform( new osg::MatrixTransform ),
98   _geode_windows( new osg::Geode ),
99   _width(_props, "size[0]"),
100   _height(_props, "size[1]"),
101   _last_push(-1)
102 {
103   _width = _height = -1;
104
105   osg::Camera* camera =
106     flightgear::getGUICamera( flightgear::CameraGroup::getDefault() );
107   assert(camera);
108
109   osg::Viewport* vp = camera->getViewport();
110   handleResize(vp->x(), vp->y(), vp->width(), vp->height());
111
112   _transform->addChild(_geode_windows);
113   camera->addChild(_transform);
114
115   Canvas::addPlacementFactory
116   (
117     "window",
118     boost::bind(&GUIMgr::addPlacement, this, _1, _2)
119   );
120 }
121
122 //------------------------------------------------------------------------------
123 void GUIMgr::init()
124 {
125   PropertyBasedMgr::init();
126
127   globals->get_renderer()
128          ->getViewer()
129          ->addEventHandler( _event_handler );
130 }
131
132 //------------------------------------------------------------------------------
133 void GUIMgr::shutdown()
134 {
135   PropertyBasedMgr::shutdown();
136
137   globals->get_renderer()
138          ->getViewer()
139          ->removeEventHandler( _event_handler );
140 }
141
142 //------------------------------------------------------------------------------
143 void GUIMgr::elementCreated(PropertyBasedElementPtr element)
144 {
145   _geode_windows->addDrawable
146   (
147     static_cast<canvas::Window*>(element.get())->getDrawable()
148   );
149 }
150
151 //------------------------------------------------------------------------------
152 bool GUIMgr::handleEvent(const osgGA::GUIEventAdapter& ea)
153 {
154   switch( ea.getEventType() )
155   {
156     case osgGA::GUIEventAdapter::PUSH:
157     case osgGA::GUIEventAdapter::RELEASE:
158 //    case osgGA::GUIEventAdapter::DOUBLECLICK:
159 //    // DOUBLECLICK doesn't seem to be triggered...
160     case osgGA::GUIEventAdapter::DRAG:
161     case osgGA::GUIEventAdapter::MOVE:
162     case osgGA::GUIEventAdapter::SCROLL:
163       return handleMouse(ea);
164 //        case osgGA::GUIEventAdapter::MOVE:
165 //          std::cout << "MOVE" << std::endl;
166 //          break;
167     case osgGA::GUIEventAdapter::RESIZE:
168       handleResize( ea.getWindowX(),
169                     ea.getWindowY(),
170                     ea.getWindowWidth(),
171                     ea.getWindowHeight() );
172       return true;
173     default:
174       return false;
175   }
176 }
177
178 //------------------------------------------------------------------------------
179 canvas::WindowPtr GUIMgr::getWindow(size_t i)
180 {
181   return boost::shared_static_cast<canvas::Window>(_elements[i]);
182 }
183
184 //------------------------------------------------------------------------------
185 canvas::Placements GUIMgr::addPlacement( const SGPropertyNode* node,
186                                          CanvasPtr canvas )
187 {
188   int placement_index = node->getIntValue("index", -1);
189
190   canvas::Placements placements;
191   for( size_t i = 0; i < _elements.size(); ++i )
192   {
193     if( placement_index > 0 && static_cast<int>(i) != placement_index )
194       continue;
195
196     canvas::WindowPtr window = getWindow(i);
197     if( !window )
198       continue;
199
200     window->setCanvas(canvas);
201     placements.push_back(
202       canvas::PlacementPtr(new WindowPlacement(window, canvas))
203     );
204   }
205   return placements;
206 }
207
208 //------------------------------------------------------------------------------
209 bool GUIMgr::handleMouse(const osgGA::GUIEventAdapter& ea)
210 {
211   canvas::MouseEvent event( ea.getEventType() );
212
213   event.x = 0.5 * (ea.getXnormalized() + 1) * _width + 0.5;
214   event.y = 0.5 * (ea.getYnormalized() + 1) * _height + 0.5;
215   if(    ea.getMouseYOrientation()
216       != osgGA::GUIEventAdapter::Y_INCREASING_DOWNWARDS )
217     event.y = _height - event.y;
218
219   event.button = ea.getButton();
220   event.state = ea.getButtonMask();
221   event.mod = ea.getModKeyMask();
222   event.scroll = ea.getScrollingMotion();
223
224   int window_at_cursor = -1;
225   for( size_t i = 0; i < _elements.size(); ++i )
226   {
227     if(    _elements[i]
228         && getWindow(i)->getRegion().contains(event.x, event.y) )
229     {
230       window_at_cursor = i;
231       break;
232     }
233   }
234
235   int target_window = window_at_cursor;
236   switch( ea.getEventType() )
237   {
238     case osgGA::GUIEventAdapter::PUSH:
239       _last_push = window_at_cursor;
240       break;
241     case osgGA::GUIEventAdapter::SCROLL:
242     case osgGA::GUIEventAdapter::MOVE:
243       break;
244
245     case osgGA::GUIEventAdapter::RELEASE:
246       if( _last_push < 0 )
247         return false;
248
249       target_window = _last_push;
250       _last_push = -1;
251       break;
252
253     case osgGA::GUIEventAdapter::DRAG:
254       target_window = _last_push;
255       break;
256
257     default:
258       return false;
259   }
260
261   if( target_window >= 0 )
262   {
263     canvas::WindowPtr window = getWindow(target_window);
264
265     event.dx = event.x - _last_x;
266     event.dy = event.y - _last_y;
267
268     _last_x = event.x;
269     _last_y = event.y;
270
271     // Let the event position be always relative to the top left window corner
272     event.x -= window->getRegion().x();
273     event.y -= window->getRegion().y();
274
275     return window->handleMouseEvent(event);
276   }
277   else
278     return false;
279 }
280
281 //------------------------------------------------------------------------------
282 void GUIMgr::handleResize(int x, int y, int width, int height)
283 {
284   if( _width == width && _height == height )
285     return;
286
287   _width = width;
288   _height = height;
289
290   // Origin should be at top left corner, therefore we need to mirror the y-axis
291   _transform->setMatrix(osg::Matrix(
292     1,  0, 0, 0,
293     0, -1, 0, 0,
294     0,  0, 1, 0,
295     0, _height, 0, 1
296   ));
297 }