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