From 8a6a2346539aa901e4f1301dba5331c40269188b Mon Sep 17 00:00:00 2001
From: Thomas Geymayer <tomgey@gmail.com>
Date: Fri, 27 Jul 2012 13:17:42 +0200
Subject: [PATCH] Canvas: First version of new Canvas GUI system.

 - Refactor CanvasMgr into PropertyBasedMgr to be also used
   for the Canvas GUI system.
 - Get rid of tied properties in the Canvas system.
 - Add new placement type 'window' for placing canvases onto
   windows
 - Pass mouse events to Window class (only if cursor is over
   window)
 - Refactor canvas placement clean up
---
 src/Canvas/CMakeLists.txt             |  10 +
 src/Canvas/canvas.cxx                 | 366 +++++++++++---------------
 src/Canvas/canvas.hxx                 | 101 +++++--
 src/Canvas/canvas_fwd.hpp             |  53 ++++
 src/Canvas/canvas_mgr.cxx             | 135 ++--------
 src/Canvas/canvas_mgr.hxx             |  44 +---
 src/Canvas/elements/text.cxx          |  66 ++---
 src/Canvas/elements/text.hxx          |   4 +-
 src/Canvas/gui_mgr.cxx                | 297 +++++++++++++++++++++
 src/Canvas/gui_mgr.hxx                |  71 +++++
 src/Canvas/mouse_event.hxx            |  52 ++++
 src/Canvas/placement.cxx              |  36 +++
 src/Canvas/placement.hxx              |  38 +++
 src/Canvas/property_based_element.cxx |  32 +++
 src/Canvas/property_based_element.hxx |  46 ++++
 src/Canvas/property_based_mgr.cxx     | 118 +++++++++
 src/Canvas/property_based_mgr.hxx     |  75 ++++++
 src/Canvas/property_helper.cxx        |  12 +
 src/Canvas/property_helper.hxx        |   6 +
 src/Canvas/rect.hxx                   |  68 +++++
 src/Canvas/window.cxx                 | 177 +++++++++++++
 src/Canvas/window.hxx                 |  66 +++++
 src/Instrumentation/od_gauge.cxx      |  49 +++-
 src/Instrumentation/od_gauge.hxx      |  17 +-
 src/Main/fg_init.cxx                  |   2 +
 25 files changed, 1477 insertions(+), 464 deletions(-)
 create mode 100644 src/Canvas/canvas_fwd.hpp
 create mode 100644 src/Canvas/gui_mgr.cxx
 create mode 100644 src/Canvas/gui_mgr.hxx
 create mode 100644 src/Canvas/mouse_event.hxx
 create mode 100644 src/Canvas/placement.cxx
 create mode 100644 src/Canvas/placement.hxx
 create mode 100644 src/Canvas/property_based_element.cxx
 create mode 100644 src/Canvas/property_based_element.hxx
 create mode 100644 src/Canvas/property_based_mgr.cxx
 create mode 100644 src/Canvas/property_based_mgr.hxx
 create mode 100644 src/Canvas/rect.hxx
 create mode 100644 src/Canvas/window.cxx
 create mode 100644 src/Canvas/window.hxx

diff --git a/src/Canvas/CMakeLists.txt b/src/Canvas/CMakeLists.txt
index c2c52e251..d8c45bde1 100644
--- a/src/Canvas/CMakeLists.txt
+++ b/src/Canvas/CMakeLists.txt
@@ -8,7 +8,12 @@ set(SOURCES
   elements/map.cxx
   elements/path.cxx
   elements/text.cxx
+  gui_mgr.cxx
+  placement.cxx
+  property_based_element.cxx
+  property_based_mgr.cxx
   property_helper.cxx
+  window.cxx
 )
 
 set(HEADERS
@@ -19,7 +24,12 @@ set(HEADERS
   elements/map.hxx
   elements/path.hxx
   elements/text.hxx
+  gui_mgr.hxx
+  placement.hxx
+  property_based_element.hxx
+  property_based_mgr.hxx
   property_helper.hxx
+  window.hxx
 )
 
 flightgear_component(Canvas "${SOURCES}" "${HEADERS}")
diff --git a/src/Canvas/canvas.cxx b/src/Canvas/canvas.cxx
index 673e9f12c..4c6504fbc 100644
--- a/src/Canvas/canvas.cxx
+++ b/src/Canvas/canvas.cxx
@@ -18,6 +18,7 @@
 
 #include "canvas.hxx"
 #include "elements/group.hxx"
+#include "mouse_event.hxx"
 
 #include <Canvas/property_helper.hxx>
 #include <Main/globals.hxx>
@@ -31,172 +32,95 @@
 #include <osgText/Text>
 #include <osgViewer/Viewer>
 
+#include <boost/algorithm/string/predicate.hpp>
 #include <iostream>
 
-//------------------------------------------------------------------------------
-/**
- * Callback used to disable/enable rendering to the texture if it is not
- * visible
- */
-class CameraCullCallback:
-  public osg::NodeCallback
+//----------------------------------------------------------------------------
+Canvas::CameraCullCallback::CameraCullCallback():
+  _render( true ),
+  _render_frame( 0 )
 {
-  public:
 
-    CameraCullCallback():
-      _render( true ),
-      _render_frame( 0 )
-    {}
+}
 
-    /**
-     * Enable rendering for the next frame
-     */
-    void enableRendering()
-    {
-      _render = true;
-    }
-
-  private:
-
-    bool _render;
-    unsigned int _render_frame;
-
-    virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
-    {
-      if( !_render && nv->getTraversalNumber() != _render_frame )
-        return;
-
-      traverse(node, nv);
-
-      _render = false;
-      _render_frame = nv->getTraversalNumber();
-    }
-};
-
-/**
- * This callback is installed on every placement of the canvas in the scene to
- * only render the canvas if at least one placement is visible
- */
-class PlacementCullCallback:
-  public osg::NodeCallback
+//----------------------------------------------------------------------------
+void Canvas::CameraCullCallback::enableRendering()
 {
-  public:
+  _render = true;
+}
 
-    PlacementCullCallback(Canvas* canvas, CameraCullCallback* camera_cull):
-      _canvas( canvas ),
-      _camera_cull( camera_cull )
-    {}
+//----------------------------------------------------------------------------
+void Canvas::CameraCullCallback::operator()( osg::Node* node,
+                                             osg::NodeVisitor* nv )
+{
+  if( !_render && nv->getTraversalNumber() != _render_frame )
+    return;
 
-  private:
+  traverse(node, nv);
 
-    Canvas *_canvas;
-    CameraCullCallback *_camera_cull;
+  _render = false;
+  _render_frame = nv->getTraversalNumber();
+}
 
-    virtual void operator()(osg::Node* node, osg::NodeVisitor* nv)
-    {
-      if( nv->getTraversalMask() & simgear::MODEL_BIT )
-        _camera_cull->enableRendering();
+//----------------------------------------------------------------------------
+Canvas::CullCallback::CullCallback(CameraCullCallback* camera_cull):
+  _camera_cull( camera_cull )
+{
 
-      traverse(node, nv);
-    }
-};
+}
+
+//----------------------------------------------------------------------------
+void Canvas::CullCallback::operator()( osg::Node* node,
+                                       osg::NodeVisitor* nv )
+{
+  if( nv->getTraversalMask() & simgear::MODEL_BIT )
+    _camera_cull->enableRendering();
+
+  traverse(node, nv);
+}
 
 //------------------------------------------------------------------------------
-Canvas::Canvas():
+Canvas::Canvas(SGPropertyNode* node):
+  PropertyBasedElement(node),
   _size_x(-1),
   _size_y(-1),
   _view_width(-1),
   _view_height(-1),
-  _status(0),
+  _status(node, "status"),
+  _status_msg(node, "status-msg"),
+  _mouse_x(node, "mouse/x"),
+  _mouse_y(node, "mouse/y"),
+  _mouse_dx(node, "mouse/dx"),
+  _mouse_dy(node, "mouse/dy"),
+  _mouse_button(node, "mouse/button"),
+  _mouse_state(node, "mouse/state"),
+  _mouse_mod(node, "mouse/mod"),
+  _mouse_scroll(node, "mouse/scroll"),
+  _mouse_event(node, "mouse/event"),
   _sampling_dirty(false),
   _color_dirty(true),
-  _node(0),
+  _root_group( new canvas::Group(node) ),
   _render_always(false)
 {
+  _status = 0;
   setStatusFlags(MISSING_SIZE_X | MISSING_SIZE_Y);
 
   _camera_callback = new CameraCullCallback;
-  _cull_callback = new PlacementCullCallback(this, _camera_callback);
+  _cull_callback = new CullCallback(_camera_callback);
+
+  canvas::linkColorNodes
+  (
+    "color-background",
+    _node,
+    _color_background,
+    osg::Vec4f(0,0,0,1)
+  );
 }
 
 //------------------------------------------------------------------------------
 Canvas::~Canvas()
 {
-  clearPlacements();
 
-  unbind();
-  _node = 0;
-}
-
-//------------------------------------------------------------------------------
-int Canvas::getStatus() const
-{
-  return _status;
-}
-
-//------------------------------------------------------------------------------
-void Canvas::reset(SGPropertyNode* node)
-{
-  if( node )
-    SG_LOG
-    (
-      SG_GL,
-      SG_INFO,
-      "Canvas::reset() texture[" << node->getIndex() << "]"
-    );
-
-  unbind();
-
-  _node = node;
-  setStatusFlags(MISSING_SIZE_X | MISSING_SIZE_Y);
-
-  if( _node )
-  {
-    _root_group.reset( new canvas::Group(_node) );
-    _node->tie
-    (
-      "size[0]",
-      SGRawValueMethods<Canvas, int>( *this, &Canvas::getSizeX,
-                                             &Canvas::setSizeX )
-    );
-    _node->tie
-    (
-      "size[1]",
-      SGRawValueMethods<Canvas, int>( *this, &Canvas::getSizeY,
-                                             &Canvas::setSizeY )
-    );
-    _node->tie
-    (
-      "view[0]",
-      SGRawValueMethods<Canvas, int>( *this, &Canvas::getViewWidth,
-                                             &Canvas::setViewWidth )
-    );
-    _node->tie
-    (
-      "view[1]",
-      SGRawValueMethods<Canvas, int>( *this, &Canvas::getViewHeight,
-                                             &Canvas::setViewHeight )
-    );
-    _node->tie
-    (
-      "status",
-      SGRawValueMethods<Canvas, int>(*this, &Canvas::getStatus)
-    );
-    _node->tie
-    (
-      "status-msg",
-      SGRawValueMethods<Canvas, const char*>(*this, &Canvas::getStatusMsg)
-    );
-    _node->addChangeListener(this);
-
-    canvas::linkColorNodes
-    (
-      "color-background",
-      _node,
-      _color_background,
-      osg::Vec4f(0,0,0,1)
-    );
-  }
 }
 
 //------------------------------------------------------------------------------
@@ -257,15 +181,29 @@ void Canvas::update(double delta_time_sec)
       // New placement
       _placements.resize(node->getIndex() + 1);
     else
-      // Remove maybe already existing placements
-      clearPlacements(node->getIndex());
+      // Remove possibly existing placements
+      _placements[ node->getIndex() ].clear();
 
-    // add new placements
-    _placements[node->getIndex()] = _texture.set_texture(
-      node,
-      _texture.getTexture(),
-      _cull_callback
-    );
+    // Get new placements
+    PlacementFactoryMap::const_iterator placement_factory =
+      _placement_factories.find( node->getStringValue("type", "object") );
+    if( placement_factory != _placement_factories.end() )
+    {
+      canvas::Placements& placements =
+        _placements[ node->getIndex() ] =
+          placement_factory->second
+          (
+            node,
+            boost::shared_static_cast<Canvas>(_self.lock())
+          );
+      node->setStringValue
+      (
+        "status-msg",
+        placements.empty() ? "No match" : "Ok"
+      );
+    }
+    else
+      node->setStringValue("status-msg", "Unknown placement type");
   }
 
   if( _render_always )
@@ -290,12 +228,6 @@ void Canvas::setSizeX(int sx)
   setStatusFlags(CREATE_FAILED, false);
 }
 
-//------------------------------------------------------------------------------
-int Canvas::getSizeX() const
-{
-  return _size_x;
-}
-
 //------------------------------------------------------------------------------
 void Canvas::setSizeY(int sy)
 {
@@ -314,12 +246,6 @@ void Canvas::setSizeY(int sy)
   setStatusFlags(CREATE_FAILED, false);
 }
 
-//------------------------------------------------------------------------------
-int Canvas::getSizeY() const
-{
-  return _size_y;
-}
-
 //------------------------------------------------------------------------------
 void Canvas::setViewWidth(int w)
 {
@@ -330,12 +256,6 @@ void Canvas::setViewWidth(int w)
   _texture.setViewSize(_view_width, _view_height);
 }
 
-//------------------------------------------------------------------------------
-int Canvas::getViewWidth() const
-{
-  return _view_width;
-}
-
 //------------------------------------------------------------------------------
 void Canvas::setViewHeight(int h)
 {
@@ -347,15 +267,19 @@ void Canvas::setViewHeight(int h)
 }
 
 //------------------------------------------------------------------------------
-int Canvas::getViewHeight() const
+bool Canvas::handleMouseEvent(const canvas::MouseEvent& event)
 {
-  return _view_height;
-}
-
-//------------------------------------------------------------------------------
-const char* Canvas::getStatusMsg() const
-{
-  return _status_msg.c_str();
+  _mouse_x = event.x;
+  _mouse_y = event.y;
+  _mouse_dx = event.dx;
+  _mouse_dy = event.dy;
+  _mouse_button = event.button;
+  _mouse_state = event.state;
+  _mouse_mod = event.mod;
+  _mouse_scroll = event.scroll;
+  // Always set event type last because all listeners are attached to it
+  _mouse_event = event.type;
+  return true;
 }
 
 //------------------------------------------------------------------------------
@@ -380,7 +304,7 @@ void Canvas::childRemoved( SGPropertyNode * parent,
     return;
 
   if( child->getNameString() == "placement" )
-    clearPlacements(child->getIndex());
+    _placements[ child->getIndex() ].clear();
   else
     static_cast<canvas::Element*>(_root_group.get())
       ->childRemoved(parent, child);
@@ -389,6 +313,9 @@ void Canvas::childRemoved( SGPropertyNode * parent,
 //----------------------------------------------------------------------------
 void Canvas::valueChanged(SGPropertyNode* node)
 {
+  if( boost::starts_with(node->getNameString(), "status") )
+    return;
+
   if( node->getParent()->getParent() == _node )
   {
     if(    !_color_background.empty()
@@ -416,11 +343,31 @@ void Canvas::valueChanged(SGPropertyNode* node)
       _sampling_dirty = true;
     else if( node->getNameString() == "render-always" )
       _render_always = node->getBoolValue();
+    else if( node->getNameString() == "size" )
+    {
+      if( node->getIndex() == 0 )
+        setSizeX( node->getIntValue() );
+      else if( node->getIndex() == 1 )
+        setSizeY( node->getIntValue() );
+    }
+    else if( node->getNameString() == "view" )
+    {
+      if( node->getIndex() == 0 )
+        setViewWidth( node->getIntValue() );
+      else if( node->getIndex() == 1 )
+        setViewHeight( node->getIntValue() );
+    }
   }
 
   _root_group->valueChanged(node);
 }
 
+//------------------------------------------------------------------------------
+osg::Texture2D* Canvas::getTexture() const
+{
+  return _texture.getTexture();
+}
+
 //------------------------------------------------------------------------------
 GLuint Canvas::getTexId() const
 {
@@ -449,13 +396,41 @@ GLuint Canvas::getTexId() const
   return tobj->_id;
 }
 
+//------------------------------------------------------------------------------
+Canvas::CameraCullCallbackPtr Canvas::getCameraCullCallback() const
+{
+  return _camera_callback;
+}
+
+//----------------------------------------------------------------------------
+Canvas::CullCallbackPtr Canvas::getCullCallback() const
+{
+  return _cull_callback;
+}
+
+//------------------------------------------------------------------------------
+void Canvas::addPlacementFactory( const std::string& type,
+                                  canvas::PlacementFactory factory )
+{
+  if( _placement_factories.find(type) != _placement_factories.end() )
+    SG_LOG
+    (
+      SG_GENERAL,
+      SG_WARN,
+      "Canvas::addPlacementFactory: replace existing factor for type " << type
+    );
+
+  _placement_factories[type] = factory;
+}
+
 //------------------------------------------------------------------------------
 void Canvas::setStatusFlags(unsigned int flags, bool set)
 {
   if( set )
-    _status |= flags;
+    _status = _status | flags;
   else
-    _status &= ~flags;
+    _status = _status & ~flags;
+  // TODO maybe extend simgear::PropertyObject to allow |=, &= etc.
 
   if( (_status & MISSING_SIZE_X) && (_status & MISSING_SIZE_Y) )
     _status_msg = "Missing size";
@@ -472,47 +447,4 @@ void Canvas::setStatusFlags(unsigned int flags, bool set)
 }
 
 //------------------------------------------------------------------------------
-void Canvas::clearPlacements(int index)
-{
-  Placements& placements = _placements.at(index);
-  while( !placements.empty() )
-  {
-    osg::ref_ptr<osg::Group> group = placements.back();
-    placements.pop_back();
-
-    assert( group->getNumChildren() == 1 );
-    osg::Node *child = group->getChild(0);
-
-    if( group->getNumParents() )
-    {
-      osg::Group *parent = group->getParent(0);
-      parent->addChild(child);
-      parent->removeChild(group);
-    }
-
-    group->removeChild(child);
-  }
-}
-
-//------------------------------------------------------------------------------
-void Canvas::clearPlacements()
-{
-  for(size_t i = 0; i < _placements.size(); ++i)
-    clearPlacements(i);
-  _placements.clear();
-}
-
-//------------------------------------------------------------------------------
-void Canvas::unbind()
-{
-  if( !_node )
-    return;
-
-  _node->untie("size[0]");
-  _node->untie("size[1]");
-  _node->untie("view[0]");
-  _node->untie("view[1]");
-  _node->untie("status");
-  _node->untie("status-msg");
-  _node->removeChangeListener(this);
-}
+Canvas::PlacementFactoryMap Canvas::_placement_factories;
diff --git a/src/Canvas/canvas.hxx b/src/Canvas/canvas.hxx
index a10713bd4..fa75f95d0 100644
--- a/src/Canvas/canvas.hxx
+++ b/src/Canvas/canvas.hxx
@@ -19,21 +19,20 @@
 #ifndef CANVAS_HXX_
 #define CANVAS_HXX_
 
+#include "placement.hxx"
+#include "property_based_element.hxx"
+
+#include <Canvas/canvas_fwd.hpp>
 #include <Instrumentation/od_gauge.hxx>
-#include <simgear/props/props.hxx>
+
+#include <simgear/props/propertyObject.hxx>
 #include <osg/NodeCallback>
 
 #include <memory>
 #include <string>
 
-namespace canvas
-{
-  class Group;
-}
-
-class CameraCullCallback;
 class Canvas:
-  public SGPropertyChangeListener
+  public PropertyBasedElement
 {
   public:
 
@@ -45,26 +44,58 @@ class Canvas:
       CREATE_FAILED  = 0x0004
     };
 
-    Canvas();
+    /**
+     * Callback used to disable/enable rendering to the texture if it is not
+     * visible
+     */
+    class CameraCullCallback:
+      public osg::NodeCallback
+    {
+      public:
+        CameraCullCallback();
+
+        /**
+         * Enable rendering for the next frame
+         */
+        void enableRendering();
+
+      private:
+        bool _render;
+        unsigned int _render_frame;
+
+        virtual void operator()(osg::Node* node, osg::NodeVisitor* nv);
+    };
+    typedef osg::ref_ptr<CameraCullCallback> CameraCullCallbackPtr;
+
+    /**
+     * This callback is installed on every placement of the canvas in the
+     * scene to only render the canvas if at least one placement is visible
+     */
+    class CullCallback:
+      public osg::NodeCallback
+    {
+      public:
+        CullCallback(CameraCullCallback* camera_cull);
+
+      private:
+        CameraCullCallback *_camera_cull;
+
+        virtual void operator()(osg::Node* node, osg::NodeVisitor* nv);
+    };
+    typedef osg::ref_ptr<CullCallback> CullCallbackPtr;
+
+    Canvas(SGPropertyNode* node);
     virtual ~Canvas();
 
-    void reset(SGPropertyNode* node);
     void update(double delta_time_sec);
 
     void setSizeX(int sx);
-    int getSizeX() const;
-
     void setSizeY(int sy);
-    int getSizeY() const;
 
     void setViewWidth(int w);
-    int getViewWidth() const;
-
     void setViewHeight(int h);
-    int getViewHeight() const;
 
-    int getStatus() const;
-    const char* getStatusMsg() const;
+    bool handleMouseEvent(const canvas::MouseEvent& event);
 
     virtual void childAdded( SGPropertyNode * parent,
                              SGPropertyNode * child );
@@ -72,8 +103,15 @@ class Canvas:
                                SGPropertyNode * child );
     virtual void valueChanged (SGPropertyNode * node);
 
+    osg::Texture2D* getTexture() const;
     GLuint getTexId() const;
 
+    CameraCullCallbackPtr getCameraCullCallback() const;
+    CullCallbackPtr getCullCallback() const;
+
+    static void addPlacementFactory( const std::string& type,
+                                     canvas::PlacementFactory factory );
+
   private:
 
     Canvas(const Canvas&); // = delete;
@@ -84,8 +122,16 @@ class Canvas:
         _view_width,
         _view_height;
 
-    int         _status;
-    std::string _status_msg;
+    simgear::PropertyObject<int>            _status;
+    simgear::PropertyObject<std::string>    _status_msg;
+
+    simgear::PropertyObject<int>    _mouse_x, _mouse_y,
+                                    _mouse_dx, _mouse_dy,
+                                    _mouse_button,
+                                    _mouse_state,
+                                    _mouse_mod,
+                                    _mouse_scroll,
+                                    _mouse_event;
 
     bool _sampling_dirty,
          _color_dirty;
@@ -93,22 +139,19 @@ class Canvas:
     FGODGauge _texture;
     std::auto_ptr<canvas::Group> _root_group;
 
-    SGPropertyNode_ptr              _node;
     std::vector<SGPropertyNode_ptr> _color_background;
 
-    osg::ref_ptr<CameraCullCallback> _camera_callback;
-    osg::ref_ptr<osg::NodeCallback> _cull_callback;
-
+    CameraCullCallbackPtr _camera_callback;
+    CullCallbackPtr _cull_callback;
     bool _render_always; //<! Used to disable automatic lazy rendering (culling)
 
     std::vector<SGPropertyNode*> _dirty_placements;
-    std::vector<Placements> _placements;
+    std::vector<canvas::Placements> _placements;
+
+    typedef std::map<std::string, canvas::PlacementFactory> PlacementFactoryMap;
+    static PlacementFactoryMap _placement_factories;
 
     void setStatusFlags(unsigned int flags, bool set = true);
-    void clearPlacements(int index);
-    void clearPlacements();
-
-    void unbind();
 };
 
 #endif /* CANVAS_HXX_ */
diff --git a/src/Canvas/canvas_fwd.hpp b/src/Canvas/canvas_fwd.hpp
new file mode 100644
index 000000000..c56612fd9
--- /dev/null
+++ b/src/Canvas/canvas_fwd.hpp
@@ -0,0 +1,53 @@
+// Canvas forward declarations
+//
+// Copyright (C) 2012  Thomas Geymayer <tomgey@gmail.com>
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License as
+// published by the Free Software Foundation; either version 2 of the
+// License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+
+#ifndef CANVAS_FWD_HPP_
+#define CANVAS_FWD_HPP_
+
+#include <boost/function.hpp>
+#include <boost/shared_ptr.hpp>
+#include <boost/weak_ptr.hpp>
+#include <vector>
+
+class SGPropertyNode;
+
+class Canvas;
+typedef boost::shared_ptr<Canvas> CanvasPtr;
+typedef boost::weak_ptr<Canvas> CanvasWeakPtr;
+
+class PropertyBasedElement;
+typedef boost::shared_ptr<PropertyBasedElement> PropertyBasedElementPtr;
+typedef boost::weak_ptr<PropertyBasedElement> PropertyBasedElementWeakPtr;
+
+namespace canvas
+{
+  class Group;
+  class MouseEvent;
+
+  class Placement;
+  typedef boost::shared_ptr<Placement> PlacementPtr;
+  typedef std::vector<PlacementPtr> Placements;
+  typedef boost::function<Placements( const SGPropertyNode*,
+                                      CanvasPtr )> PlacementFactory;
+
+  class Window;
+  typedef boost::shared_ptr<Window> WindowPtr;
+  typedef boost::weak_ptr<Window> WindowWeakPtr;
+}
+
+#endif /* CANVAS_FWD_HPP_ */
diff --git a/src/Canvas/canvas_mgr.cxx b/src/Canvas/canvas_mgr.cxx
index b8300654a..21fcccf80 100644
--- a/src/Canvas/canvas_mgr.cxx
+++ b/src/Canvas/canvas_mgr.cxx
@@ -19,131 +19,38 @@
 #include "canvas_mgr.hxx"
 #include "canvas.hxx"
 
-#include <Main/fg_props.hxx>
+#include <boost/bind.hpp>
 
-#include <osg/Camera>
-#include <osg/Texture2D>
+typedef boost::shared_ptr<Canvas> CanvasPtr;
+CanvasPtr canvasFactory(SGPropertyNode* node)
+{
+  return CanvasPtr(new Canvas(node));
+}
 
-#include <stdexcept>
-#include <string>
 
 //------------------------------------------------------------------------------
 CanvasMgr::CanvasMgr():
-  _props( fgGetNode("/canvas", true) )
+  PropertyBasedMgr("/canvas", "texture", &canvasFactory)
 {
-
-}
-
-//------------------------------------------------------------------------------
-CanvasMgr::~CanvasMgr()
-{
-
-}
-
-//------------------------------------------------------------------------------
-void CanvasMgr::init()
-{
-  _props->addChangeListener(this);
-  triggerChangeRecursive(_props);
-}
-
-//------------------------------------------------------------------------------
-void CanvasMgr::reinit()
-{
-
-}
-
-//------------------------------------------------------------------------------
-void CanvasMgr::shutdown()
-{
-  _props->removeChangeListener(this);
-}
-
-//------------------------------------------------------------------------------
-void CanvasMgr::bind()
-{
-}
-
-//------------------------------------------------------------------------------
-void CanvasMgr::unbind()
-{
-}
-
-//------------------------------------------------------------------------------
-void CanvasMgr::update(double delta_time_sec)
-{
- for( size_t i = 0; i < _canvases.size(); ++i )
-   if( _canvases[i] )
-     _canvases[i]->update(delta_time_sec);
-}
-
-//------------------------------------------------------------------------------
-void CanvasMgr::childAdded( SGPropertyNode * parent,
-                            SGPropertyNode * child )
-{
-  if( parent != _props )
-    return;
-
-  if( child->getNameString() == "texture" )
-    textureAdded(child);
-}
-
-//------------------------------------------------------------------------------
-void CanvasMgr::childRemoved( SGPropertyNode * parent,
-                              SGPropertyNode * child )
-{
-  if( parent != _props )
-    return;
-
-  if( child->getNameString() == "texture" )
-  {
-    size_t index = child->getIndex();
-
-    if( index >= _canvases.size() )
-      SG_LOG(SG_GL, SG_WARN, "can't removed unknown texture[" << index << "]!");
-    else
-      // remove the canvas...
-      _canvases[index].reset();
-  }
+  Canvas::addPlacementFactory
+  (
+    "object",
+    boost::bind
+    (
+      &FGODGauge::set_texture,
+      _1,
+      boost::bind(&Canvas::getTexture, _2),
+      boost::bind(&Canvas::getCullCallback, _2)
+    )
+  );
 }
 
 //------------------------------------------------------------------------------
 unsigned int CanvasMgr::getCanvasTexId(size_t index) const
 {
-  if(    index >= _canvases.size()
-      || !_canvases[index] )
+  if(    index >= _elements.size()
+      || !_elements[index] )
     return 0;
 
-  return _canvases[index]->getTexId();
-}
-
-//------------------------------------------------------------------------------
-void CanvasMgr::textureAdded(SGPropertyNode* node)
-{
-  size_t index = node->getIndex();
-
-  if( index >= _canvases.size() )
-  {
-    if( index > _canvases.size() )
-      SG_LOG(SG_GL, SG_WARN, "Skipping unused texture slot(s)!");
-
-    _canvases.resize(index + 1);
-  }
-  else if( _canvases[index] )
-    SG_LOG(SG_GL, SG_WARN, "texture[" << index << "] already exists!");
-
-  _canvases[index].reset( new Canvas() );
-  _canvases[index]->reset(node);
-}
-
-//------------------------------------------------------------------------------
-void CanvasMgr::triggerChangeRecursive(SGPropertyNode* node)
-{
-  node->getParent()->fireChildAdded(node);
-
-  if( node->nChildren() == 0 && node->getType() != simgear::props::NONE )
-    return node->fireValueChanged();
-
-  for( int i = 0; i < node->nChildren(); ++i )
-    triggerChangeRecursive( node->getChild(i) );
+  return static_cast<Canvas*>(_elements[index].get())->getTexId();
 }
diff --git a/src/Canvas/canvas_mgr.hxx b/src/Canvas/canvas_mgr.hxx
index d1ae92603..10cbe9a76 100644
--- a/src/Canvas/canvas_mgr.hxx
+++ b/src/Canvas/canvas_mgr.hxx
@@ -19,36 +19,13 @@
 #ifndef CANVAS_MGR_H_
 #define CANVAS_MGR_H_
 
-#include <simgear/props/props.hxx>
-#include <simgear/structure/subsystem_mgr.hxx>
-
-#include <boost/shared_ptr.hpp>
-#include <vector>
-
-class Canvas;
-typedef boost::shared_ptr<Canvas> CanvasPtr;
+#include "property_based_mgr.hxx"
 
 class CanvasMgr:
-  public SGSubsystem,
-  public SGPropertyChangeListener
+  public PropertyBasedMgr
 {
   public:
     CanvasMgr();
-    virtual ~CanvasMgr();
-
-    virtual void init();
-    virtual void reinit();
-    virtual void shutdown();
-
-    virtual void bind();
-    virtual void unbind();
-
-    virtual void update(double delta_time_sec);
-
-    virtual void childAdded( SGPropertyNode * parent,
-                             SGPropertyNode * child );
-    virtual void childRemoved( SGPropertyNode * parent,
-                               SGPropertyNode * child );
 
     /**
      * Get OpenGL texture name for given canvas
@@ -57,23 +34,6 @@ class CanvasMgr:
      * @return OpenGL texture name
      */
     unsigned int getCanvasTexId(size_t index) const;
-
-  private:
-
-    /** Root node for everything concerning the canvas system */
-    SGPropertyNode_ptr _props;
-
-    /** The actual canvases */
-    std::vector<CanvasPtr> _canvases;
-
-    void textureAdded(SGPropertyNode* node);
-
-    /**
-     * Trigger a childAdded and valueChanged event for every child of node
-     * (Unlimited depth) and node itself.
-     */
-    void triggerChangeRecursive(SGPropertyNode* node);
-
 };
 
 #endif /* CANVAS_MGR_H_ */
diff --git a/src/Canvas/elements/text.cxx b/src/Canvas/elements/text.cxx
index 725d67e12..518e78378 100644
--- a/src/Canvas/elements/text.cxx
+++ b/src/Canvas/elements/text.cxx
@@ -126,54 +126,12 @@ namespace canvas
 
     _font_size = getChildDefault<float>(_node, "character-size", 32);
     _font_aspect = getChildDefault<float>(_node, "character-aspect-ratio", 1);
-
-    _node->tie
-    (
-      "alignment",
-      SGRawValueMethods<Text, const char*>
-      (
-        *this,
-        &Text::getAlignment,
-        &Text::setAlignment
-      )
-    );
-    _node->tie
-    (
-      "padding",
-      SGRawValueMethods<osgText::Text, float>
-      (
-        *_text.get(),
-        &osgText::Text::getBoundingBoxMargin,
-        &osgText::Text::setBoundingBoxMargin
-      )
-    );
-    typedef SGRawValueMethods<osgText::Text, int> TextIntMethods;
-    _node->tie
-    (
-      "draw-mode",
-      //  TEXT              = 1 default
-      //  BOUNDINGBOX       = 2
-      //  FILLEDBOUNDINGBOX = 4
-      //  ALIGNMENT         = 8
-      TextIntMethods
-      (
-        *_text.get(),
-        (TextIntMethods::getter_t)&osgText::Text::getDrawMode,
-        (TextIntMethods::setter_t)&osgText::Text::setDrawMode
-      )
-    );
   }
 
   //----------------------------------------------------------------------------
   Text::~Text()
   {
-    if( _node )
-    {
-      _node->untie("alignment");
-      _node->untie("padding");
-      _node->untie("draw-mode");
-    }
-    _node = 0;
+
   }
 
   //----------------------------------------------------------------------------
@@ -210,10 +168,20 @@ namespace canvas
 #include "text-alignment.hxx"
 #undef ENUM_MAPPING
     else
+    {
+      if( !align_string.empty() )
+        SG_LOG
+        (
+          SG_GENERAL,
+          SG_WARN,
+          "canvas::Text: unknown alignment '" << align_string << "'"
+        );
       _text->setAlignment(osgText::Text::LEFT_BASE_LINE);
+    }
   }
 
   //----------------------------------------------------------------------------
+#if 0
   const char* Text::getAlignment() const
   {
     switch( _text->getAlignment() )
@@ -227,7 +195,7 @@ namespace canvas
         return "unknown";
     }
   }
-
+#endif
   //----------------------------------------------------------------------------
   void Text::childChanged(SGPropertyNode* child)
   {
@@ -247,10 +215,20 @@ namespace canvas
         osgText::String( child->getStringValue(),
                          osgText::String::ENCODING_UTF8 )
       );
+    else if( name == "padding" )
+      _text->setBoundingBoxMargin( child->getFloatValue() );
+    else if( name == "draw-mode" )
+      //  TEXT              = 1 default
+      //  BOUNDINGBOX       = 2
+      //  FILLEDBOUNDINGBOX = 4
+      //  ALIGNMENT         = 8
+      _text->setDrawMode( child->getIntValue() );
     else if( name == "max-width" )
       _text->setMaximumWidth( child->getFloatValue() );
     else if( name == "font" )
       setFont( child->getStringValue() );
+    else if( name == "alignment" )
+      setAlignment( child->getStringValue() );
   }
 
   //----------------------------------------------------------------------------
diff --git a/src/Canvas/elements/text.hxx b/src/Canvas/elements/text.hxx
index 6147e4367..630e717cf 100644
--- a/src/Canvas/elements/text.hxx
+++ b/src/Canvas/elements/text.hxx
@@ -33,14 +33,12 @@ namespace canvas
   {
     public:
       Text(SGPropertyNode_ptr node);
-      virtual ~Text();
+      ~Text();
 
       virtual void update(double dt);
 
       void setFont(const char* name);
-
       void setAlignment(const char* align);
-      const char* getAlignment() const;
 
     protected:
 
diff --git a/src/Canvas/gui_mgr.cxx b/src/Canvas/gui_mgr.cxx
new file mode 100644
index 000000000..208d209c9
--- /dev/null
+++ b/src/Canvas/gui_mgr.cxx
@@ -0,0 +1,297 @@
+// Canvas gui/dialog manager
+//
+// Copyright (C) 2012  Thomas Geymayer <tomgey@gmail.com>
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License as
+// published by the Free Software Foundation; either version 2 of the
+// License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+
+#include "gui_mgr.hxx"
+#include "mouse_event.hxx"
+#include <Canvas/window.hxx>
+#include <Canvas/canvas.hxx>
+
+#include <Main/globals.hxx>
+#include <Viewer/CameraGroup.hxx>
+#include <Viewer/renderer.hxx>
+
+#include <osgViewer/Viewer>
+#include <osgGA/GUIEventHandler>
+
+#include <boost/bind.hpp>
+
+/**
+ * Event handler
+ */
+class GUIEventHandler:
+  public osgGA::GUIEventHandler
+{
+  public:
+    GUIEventHandler(GUIMgr* gui_mgr):
+      _gui_mgr( gui_mgr )
+    {}
+
+    bool handle( const osgGA::GUIEventAdapter& ea,
+                 osgGA::GUIActionAdapter& aa,
+                 osg::Object*,
+                 osg::NodeVisitor* )
+    {
+      return _gui_mgr->handleEvent(ea);
+    }
+
+  protected:
+    GUIMgr *_gui_mgr;
+};
+
+/**
+ * Track a canvas placement on a window
+ */
+class WindowPlacement:
+  public canvas::Placement
+{
+  public:
+    WindowPlacement( canvas::WindowPtr window,
+                     CanvasPtr canvas ):
+      _window(window),
+      _canvas(canvas)
+    {}
+
+    /**
+     * Remove placement from window
+     */
+    virtual ~WindowPlacement()
+    {
+      canvas::WindowPtr window = _window.lock();
+      CanvasPtr canvas = _canvas.lock();
+
+      if( window && canvas && canvas == window->getCanvas().lock() )
+        window->setCanvas( CanvasPtr() );
+    }
+
+  private:
+    canvas::WindowWeakPtr _window;
+    CanvasWeakPtr _canvas;
+};
+
+//------------------------------------------------------------------------------
+typedef boost::shared_ptr<canvas::Window> WindowPtr;
+WindowPtr windowFactory(SGPropertyNode* node)
+{
+  return WindowPtr(new canvas::Window(node));
+}
+
+//------------------------------------------------------------------------------
+GUIMgr::GUIMgr():
+  PropertyBasedMgr("/sim/gui/canvas", "window", &windowFactory),
+  _event_handler( new GUIEventHandler(this) ),
+  _transform( new osg::MatrixTransform ),
+  _geode_windows( new osg::Geode ),
+  _width(_props, "size[0]"),
+  _height(_props, "size[1]"),
+  _last_push(-1)
+{
+  _width = _height = -1;
+
+  osg::Camera* camera =
+    flightgear::getGUICamera( flightgear::CameraGroup::getDefault() );
+  assert(camera);
+
+  osg::Viewport* vp = camera->getViewport();
+  handleResize(vp->x(), vp->y(), vp->width(), vp->height());
+
+  _transform->addChild(_geode_windows);
+  camera->addChild(_transform);
+
+  Canvas::addPlacementFactory
+  (
+    "window",
+    boost::bind(&GUIMgr::addPlacement, this, _1, _2)
+  );
+}
+
+//------------------------------------------------------------------------------
+void GUIMgr::init()
+{
+  PropertyBasedMgr::init();
+
+  globals->get_renderer()
+         ->getViewer()
+         ->addEventHandler( _event_handler );
+}
+
+//------------------------------------------------------------------------------
+void GUIMgr::shutdown()
+{
+  PropertyBasedMgr::shutdown();
+
+  globals->get_renderer()
+         ->getViewer()
+         ->removeEventHandler( _event_handler );
+}
+
+//------------------------------------------------------------------------------
+void GUIMgr::elementCreated(PropertyBasedElementPtr element)
+{
+  _geode_windows->addDrawable
+  (
+    static_cast<canvas::Window*>(element.get())->getDrawable()
+  );
+}
+
+//------------------------------------------------------------------------------
+bool GUIMgr::handleEvent(const osgGA::GUIEventAdapter& ea)
+{
+  switch( ea.getEventType() )
+  {
+    case osgGA::GUIEventAdapter::PUSH:
+    case osgGA::GUIEventAdapter::RELEASE:
+//    case osgGA::GUIEventAdapter::DOUBLECLICK:
+//    // DOUBLECLICK doesn't seem to be triggered...
+    case osgGA::GUIEventAdapter::DRAG:
+    case osgGA::GUIEventAdapter::MOVE:
+    case osgGA::GUIEventAdapter::SCROLL:
+      return handleMouse(ea);
+//        case osgGA::GUIEventAdapter::MOVE:
+//          std::cout << "MOVE" << std::endl;
+//          break;
+    case osgGA::GUIEventAdapter::RESIZE:
+      handleResize( ea.getWindowX(),
+                    ea.getWindowY(),
+                    ea.getWindowWidth(),
+                    ea.getWindowHeight() );
+      return true;
+    default:
+      return false;
+  }
+}
+
+//------------------------------------------------------------------------------
+canvas::WindowPtr GUIMgr::getWindow(size_t i)
+{
+  return boost::shared_static_cast<canvas::Window>(_elements[i]);
+}
+
+//------------------------------------------------------------------------------
+canvas::Placements GUIMgr::addPlacement( const SGPropertyNode* node,
+                                         CanvasPtr canvas )
+{
+  int placement_index = node->getIntValue("index", -1);
+
+  canvas::Placements placements;
+  for( size_t i = 0; i < _elements.size(); ++i )
+  {
+    if( placement_index > 0 && static_cast<int>(i) != placement_index )
+      continue;
+
+    canvas::WindowPtr window = getWindow(i);
+    if( !window )
+      continue;
+
+    window->setCanvas(canvas);
+    placements.push_back(
+      canvas::PlacementPtr(new WindowPlacement(window, canvas))
+    );
+  }
+  return placements;
+}
+
+//------------------------------------------------------------------------------
+bool GUIMgr::handleMouse(const osgGA::GUIEventAdapter& ea)
+{
+  canvas::MouseEvent event( ea.getEventType() );
+
+  event.x = 0.5 * (ea.getXnormalized() + 1) * _width + 0.5;
+  event.y = 0.5 * (ea.getYnormalized() + 1) * _height + 0.5;
+  if(    ea.getMouseYOrientation()
+      != osgGA::GUIEventAdapter::Y_INCREASING_DOWNWARDS )
+    event.y = _height - event.y;
+
+  event.button = ea.getButton();
+  event.state = ea.getButtonMask();
+  event.mod = ea.getModKeyMask();
+  event.scroll = ea.getScrollingMotion();
+
+  int window_at_cursor = -1;
+  for( size_t i = 0; i < _elements.size(); ++i )
+  {
+    if(    _elements[i]
+        && getWindow(i)->getRegion().contains(event.x, event.y) )
+    {
+      window_at_cursor = i;
+      break;
+    }
+  }
+
+  int target_window = window_at_cursor;
+  switch( ea.getEventType() )
+  {
+    case osgGA::GUIEventAdapter::PUSH:
+      _last_push = window_at_cursor;
+      break;
+    case osgGA::GUIEventAdapter::SCROLL:
+    case osgGA::GUIEventAdapter::MOVE:
+      break;
+
+    case osgGA::GUIEventAdapter::RELEASE:
+      if( _last_push < 0 )
+        return false;
+
+      target_window = _last_push;
+      _last_push = -1;
+      break;
+
+    case osgGA::GUIEventAdapter::DRAG:
+      target_window = _last_push;
+      break;
+
+    default:
+      return false;
+  }
+
+  if( target_window >= 0 )
+  {
+    canvas::WindowPtr window = getWindow(target_window);
+
+    event.dx = event.x - _last_x;
+    event.dy = event.y - _last_y;
+
+    _last_x = event.x;
+    _last_y = event.y;
+
+    // Let the event position be always relative to the top left window corner
+    event.x -= window->getRegion().x();
+    event.y -= window->getRegion().y();
+
+    return window->handleMouseEvent(event);
+  }
+  else
+    return false;
+}
+
+//------------------------------------------------------------------------------
+void GUIMgr::handleResize(int x, int y, int width, int height)
+{
+  if( _width == width && _height == height )
+    return;
+
+  _width = width;
+  _height = height;
+
+  // Origin should be at top left corner, therefore we need to mirror the y-axis
+  _transform->setMatrix(osg::Matrix(
+    1,  0, 0, 0,
+    0, -1, 0, 0,
+    0,  0, 1, 0,
+    0, _height, 0, 1
+  ));
+}
diff --git a/src/Canvas/gui_mgr.hxx b/src/Canvas/gui_mgr.hxx
new file mode 100644
index 000000000..6b010a020
--- /dev/null
+++ b/src/Canvas/gui_mgr.hxx
@@ -0,0 +1,71 @@
+// Canvas gui/dialog manager
+//
+// Copyright (C) 2012  Thomas Geymayer <tomgey@gmail.com>
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License as
+// published by the Free Software Foundation; either version 2 of the
+// License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+
+#ifndef CANVAS_GUI_MGR_HXX_
+#define CANVAS_GUI_MGR_HXX_
+
+#include "property_based_mgr.hxx"
+#include <Canvas/canvas_fwd.hpp>
+#include <Canvas/placement.hxx>
+
+#include <simgear/props/propertyObject.hxx>
+
+#include <osg/ref_ptr>
+#include <osg/Geode>
+#include <osg/MatrixTransform>
+
+namespace osgGA
+{
+  class GUIEventAdapter;
+}
+
+class GUIEventHandler;
+class GUIMgr:
+  public PropertyBasedMgr
+{
+  public:
+    GUIMgr();
+
+    virtual void init();
+    virtual void shutdown();
+
+    virtual void elementCreated(PropertyBasedElementPtr element);
+
+    bool handleEvent(const osgGA::GUIEventAdapter& ea);
+
+  protected:
+    osg::ref_ptr<GUIEventHandler>       _event_handler;
+    osg::ref_ptr<osg::MatrixTransform>  _transform;
+    osg::ref_ptr<osg::Geode>            _geode_windows;
+
+    simgear::PropertyObject<int>        _width,
+                                        _height;
+
+    int _last_push,
+        _last_x,
+        _last_y;
+
+    canvas::WindowPtr getWindow(size_t i);
+    canvas::Placements addPlacement( const SGPropertyNode*,
+                                     CanvasPtr canvas );
+
+    bool handleMouse(const osgGA::GUIEventAdapter& ea);
+    void handleResize(int x, int y, int width, int height);
+};
+
+#endif /* CANVAS_GUI_MGR_HXX_ */
diff --git a/src/Canvas/mouse_event.hxx b/src/Canvas/mouse_event.hxx
new file mode 100644
index 000000000..851f0f65d
--- /dev/null
+++ b/src/Canvas/mouse_event.hxx
@@ -0,0 +1,52 @@
+// Mouse event
+//
+// Copyright (C) 2012  Thomas Geymayer <tomgey@gmail.com>
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License as
+// published by the Free Software Foundation; either version 2 of the
+// License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+
+#ifndef CANVAS_MOUSE_EVENT_HXX_
+#define CANVAS_MOUSE_EVENT_HXX_
+
+#include <osgGA/GUIEventAdapter>
+
+namespace canvas
+{
+
+  struct MouseEvent
+  {
+    typedef osgGA::GUIEventAdapter::EventType EventType;
+    typedef osgGA::GUIEventAdapter::ScrollingMotion Scroll;
+
+    MouseEvent(EventType type):
+      type(type),
+      x(-1), y(-1),
+      dx(0), dy(0),
+      button(-1),
+      state(-1),
+      mod(-1)
+    {}
+
+    EventType   type;
+    int         x, y,
+                dx, dy,
+                button, //<! Button for this event
+                state,  //<! Current button state
+                mod;    //<! Keyboard modifier state
+    Scroll      scroll;
+  };
+
+} // namespace canvas
+
+#endif /* CANVAS_MOUSE_EVENT_HXX_ */
diff --git a/src/Canvas/placement.cxx b/src/Canvas/placement.cxx
new file mode 100644
index 000000000..c2634b62b
--- /dev/null
+++ b/src/Canvas/placement.cxx
@@ -0,0 +1,36 @@
+// Base class for canvas placements
+//
+// Copyright (C) 2012  Thomas Geymayer <tomgey@gmail.com>
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License as
+// published by the Free Software Foundation; either version 2 of the
+// License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+
+#include "placement.hxx"
+
+namespace canvas
+{
+
+  //----------------------------------------------------------------------------
+  Placement::Placement()
+  {
+
+  }
+
+  //----------------------------------------------------------------------------
+  Placement::~Placement()
+  {
+
+  }
+
+} // namespace canvas
diff --git a/src/Canvas/placement.hxx b/src/Canvas/placement.hxx
new file mode 100644
index 000000000..e424f2129
--- /dev/null
+++ b/src/Canvas/placement.hxx
@@ -0,0 +1,38 @@
+// Base class for canvas placements
+//
+// Copyright (C) 2012  Thomas Geymayer <tomgey@gmail.com>
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License as
+// published by the Free Software Foundation; either version 2 of the
+// License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+
+#ifndef CANVAS_PLACEMENT_HXX_
+#define CANVAS_PLACEMENT_HXX_
+
+namespace canvas
+{
+
+  class Placement
+  {
+    public:
+      Placement();
+      virtual ~Placement() = 0;
+
+    private:
+      Placement(const Placement&) /* = delete */;
+      Placement& operator=(const Placement&) /* = delete */;
+  };
+
+} // namespace canvas
+
+#endif /* CANVAS_PLACEMENT_HXX_ */
diff --git a/src/Canvas/property_based_element.cxx b/src/Canvas/property_based_element.cxx
new file mode 100644
index 000000000..3b801a7c1
--- /dev/null
+++ b/src/Canvas/property_based_element.cxx
@@ -0,0 +1,32 @@
+// Base class for elements of property controlled subsystems
+//
+// Copyright (C) 2012  Thomas Geymayer <tomgey@gmail.com>
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License as
+// published by the Free Software Foundation; either version 2 of the
+// License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+
+#include "property_based_element.hxx"
+
+//------------------------------------------------------------------------------
+PropertyBasedElement::PropertyBasedElement(SGPropertyNode* node):
+  _node(node)
+{
+  _node->addChangeListener(this);
+}
+
+//------------------------------------------------------------------------------
+PropertyBasedElement::~PropertyBasedElement()
+{
+  _node->removeChangeListener(this);
+}
diff --git a/src/Canvas/property_based_element.hxx b/src/Canvas/property_based_element.hxx
new file mode 100644
index 000000000..358ae7d0c
--- /dev/null
+++ b/src/Canvas/property_based_element.hxx
@@ -0,0 +1,46 @@
+// Base class for elements of property controlled subsystems
+//
+// Copyright (C) 2012  Thomas Geymayer <tomgey@gmail.com>
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License as
+// published by the Free Software Foundation; either version 2 of the
+// License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+
+#ifndef PROPERTY_BASED_ELEMENT_HXX_
+#define PROPERTY_BASED_ELEMENT_HXX_
+
+#include <Canvas/canvas_fwd.hpp>
+#include <simgear/props/props.hxx>
+
+/**
+ * Base class for a property controlled element
+ */
+class PropertyBasedElement:
+  public SGPropertyChangeListener
+{
+  public:
+    PropertyBasedElement(SGPropertyNode* node);
+    virtual ~PropertyBasedElement();
+
+    virtual void update(double delta_time_sec) = 0;
+
+  protected:
+
+    friend class PropertyBasedMgr;
+
+    SGPropertyNode_ptr _node;
+    PropertyBasedElementWeakPtr _self;
+};
+
+
+#endif /* PROPERTY_BASED_ELEMENT_HXX_ */
diff --git a/src/Canvas/property_based_mgr.cxx b/src/Canvas/property_based_mgr.cxx
new file mode 100644
index 000000000..1d57228df
--- /dev/null
+++ b/src/Canvas/property_based_mgr.cxx
@@ -0,0 +1,118 @@
+// Base class for all property controlled subsystems
+//
+// Copyright (C) 2012  Thomas Geymayer <tomgey@gmail.com>
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License as
+// published by the Free Software Foundation; either version 2 of the
+// License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+
+#include "property_based_mgr.hxx"
+#include "property_helper.hxx"
+#include <Main/fg_props.hxx>
+
+#include <stdexcept>
+#include <string>
+
+//------------------------------------------------------------------------------
+void PropertyBasedMgr::init()
+{
+  _props->addChangeListener(this);
+  canvas::triggerChangeRecursive(_props);
+}
+
+//------------------------------------------------------------------------------
+void PropertyBasedMgr::shutdown()
+{
+  _props->removeChangeListener(this);
+}
+
+//------------------------------------------------------------------------------
+void PropertyBasedMgr::update(double delta_time_sec)
+{
+  for( size_t i = 0; i < _elements.size(); ++i )
+    if( _elements[i] )
+      _elements[i]->update(delta_time_sec);
+}
+
+//------------------------------------------------------------------------------
+void PropertyBasedMgr::childAdded( SGPropertyNode * parent,
+                                   SGPropertyNode * child )
+{
+  if( parent != _props || child->getNameString() != _name_elements )
+    return;
+
+  size_t index = child->getIndex();
+
+  if( index >= _elements.size() )
+  {
+    if( index > _elements.size() )
+      SG_LOG
+      (
+        SG_GENERAL,
+        SG_WARN,
+        "Skipping unused " << _name_elements << " slot(s)!"
+      );
+
+    _elements.resize(index + 1);
+  }
+  else if( _elements[index] )
+    SG_LOG
+    (
+      SG_GENERAL,
+      SG_WARN,
+      _name_elements << "[" << index << "] already exists!"
+    );
+
+  PropertyBasedElementPtr el = _element_factory(child);
+  el->_self = el;
+  _elements[index] = el;
+  elementCreated( el );
+}
+
+//------------------------------------------------------------------------------
+void PropertyBasedMgr::childRemoved( SGPropertyNode * parent,
+                                     SGPropertyNode * child )
+{
+  if( parent != _props || child->getNameString() != _name_elements )
+    return;
+
+  size_t index = child->getIndex();
+
+  if( index >= _elements.size() )
+    SG_LOG
+    (
+      SG_GENERAL,
+      SG_WARN,
+      "can't removed unknown " << _name_elements << "[" << index << "]!"
+    );
+  else
+    // remove the element...
+    _elements[index].reset();
+}
+
+//------------------------------------------------------------------------------
+PropertyBasedMgr::PropertyBasedMgr( const std::string& path_root,
+                                    const std::string& name_elements,
+                                    ElementFactory element_factory ):
+  _props( fgGetNode(path_root, true) ),
+  _name_elements( name_elements ),
+  _element_factory( element_factory )
+{
+
+}
+
+//------------------------------------------------------------------------------
+PropertyBasedMgr::~PropertyBasedMgr()
+{
+
+}
diff --git a/src/Canvas/property_based_mgr.hxx b/src/Canvas/property_based_mgr.hxx
new file mode 100644
index 000000000..904b3c619
--- /dev/null
+++ b/src/Canvas/property_based_mgr.hxx
@@ -0,0 +1,75 @@
+// Base class for all property controlled subsystems
+//
+// Copyright (C) 2012  Thomas Geymayer <tomgey@gmail.com>
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License as
+// published by the Free Software Foundation; either version 2 of the
+// License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+
+#ifndef PROPERTY_BASED_MGR_HXX_
+#define PROPERTY_BASED_MGR_HXX_
+
+#include "property_based_element.hxx"
+#include <simgear/structure/subsystem_mgr.hxx>
+
+#include <boost/shared_ptr.hpp>
+#include <boost/function.hpp>
+#include <vector>
+
+class PropertyBasedMgr:
+  public SGSubsystem,
+  public SGPropertyChangeListener
+{
+  public:
+    virtual void init();
+    virtual void shutdown();
+
+    virtual void update (double delta_time_sec);
+
+    virtual void childAdded( SGPropertyNode * parent,
+                             SGPropertyNode * child );
+    virtual void childRemoved( SGPropertyNode * parent,
+                               SGPropertyNode * child );
+
+    virtual void elementCreated(PropertyBasedElementPtr element) {}
+
+  protected:
+
+    typedef boost::function<PropertyBasedElementPtr(SGPropertyNode*)>
+            ElementFactory;
+
+    /** Branch in the property tree for this property managed subsystem */
+    SGPropertyNode*      _props;
+
+    /** Property name of managed elements */
+    const std::string       _name_elements;
+
+    /** The actually managed elements */
+    std::vector<PropertyBasedElementPtr> _elements;
+
+    /** Function object which creates a new element */
+    ElementFactory          _element_factory;
+
+    /**
+     * @param path_root     Path to property branch used for controlling this
+     *                      subsystem
+     * @param name_elements The name of the nodes for the managed elements
+     */
+    PropertyBasedMgr( const std::string& path_root,
+                      const std::string& name_elements,
+                      ElementFactory element_factory );
+    virtual ~PropertyBasedMgr() = 0;
+
+};
+
+#endif /* PROPERTY_BASED_MGR_HXX_ */
diff --git a/src/Canvas/property_helper.cxx b/src/Canvas/property_helper.cxx
index 7bf65404e..795dd72d0 100644
--- a/src/Canvas/property_helper.cxx
+++ b/src/Canvas/property_helper.cxx
@@ -44,4 +44,16 @@ namespace canvas
     for( size_t i = 0; i < num_channels; ++i )
       nodes.push_back( getChildDefault(color, channels[i], def[i]) );
   }
+
+  //----------------------------------------------------------------------------
+  void triggerChangeRecursive(SGPropertyNode* node)
+  {
+    node->getParent()->fireChildAdded(node);
+
+    if( node->nChildren() == 0 && node->getType() != simgear::props::NONE )
+      return node->fireValueChanged();
+
+    for( int i = 0; i < node->nChildren(); ++i )
+      triggerChangeRecursive( node->getChild(i) );
+  }
 }
diff --git a/src/Canvas/property_helper.hxx b/src/Canvas/property_helper.hxx
index 429cc636e..dbafdd77e 100644
--- a/src/Canvas/property_helper.hxx
+++ b/src/Canvas/property_helper.hxx
@@ -78,6 +78,12 @@ namespace canvas
                        std::vector<SGPropertyNode_ptr>& nodes,
                        const osg::Vec4& def = osg::Vec4(0,0,0,1) );
 
+  /**
+   * Trigger a childAdded and valueChanged event for every child of node
+   * (Unlimited depth) and node itself.
+   */
+  void triggerChangeRecursive(SGPropertyNode* node);
+
 } // namespace canvas
 
 #endif /* PROPERTY_HELPER_HXX_ */
diff --git a/src/Canvas/rect.hxx b/src/Canvas/rect.hxx
new file mode 100644
index 000000000..b3b42cc4e
--- /dev/null
+++ b/src/Canvas/rect.hxx
@@ -0,0 +1,68 @@
+// Class representing a rectangular region
+//
+// Copyright (C) 2012  Thomas Geymayer <tomgey@gmail.com>
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License as
+// published by the Free Software Foundation; either version 2 of the
+// License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+
+#ifndef CANVAS_RECT_HXX_
+#define CANVAS_RECT_HXX_
+
+#include <osg/Vec2>
+
+namespace canvas
+{
+  template<typename T>
+  class Rect
+  {
+    public:
+      Rect() {}
+
+      Rect(T x, T y, T w, T h):
+        _x1(x),
+        _x2(x + w),
+        _y1(y),
+        _y2(y + h)
+      {}
+
+      void set(T x, T y, T w, T h)
+      {
+        _x1 = x;
+        _x2 = x + w;
+        _y1 = y;
+        _y2 = y + h;
+      }
+
+      T x() const { return _x1; }
+      T y() const { return _y1; }
+      T width() const { return _x2 - _x1; }
+      T height() const { return _y2 - _y1; }
+
+      T l() const { return _x1; }
+      T r() const { return _x2; }
+      T t() const { return _y1; }
+      T b() const { return _y2; }
+
+      bool contains(T x, T y) const
+      {
+        return _x1 <= x && x <= _x2
+            && _y1 <= y && y <= _y2;
+      }
+
+    private:
+      T _x1, _x2, _y1, _y2;
+  };
+} // namespace canvas
+
+#endif /* CANVAS_RECT_HXX_ */
diff --git a/src/Canvas/window.cxx b/src/Canvas/window.cxx
new file mode 100644
index 000000000..40dc85500
--- /dev/null
+++ b/src/Canvas/window.cxx
@@ -0,0 +1,177 @@
+// Window for placing a Canvas onto it (for dialogs, menus, etc.)
+//
+// Copyright (C) 2012  Thomas Geymayer <tomgey@gmail.com>
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License as
+// published by the Free Software Foundation; either version 2 of the
+// License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+
+#include "window.hxx"
+#include <Canvas/canvas.hxx>
+
+#include <osg/BlendFunc>
+#include <osg/Geometry>
+#include <osg/Texture2D>
+#include <osgGA/GUIEventHandler>
+
+/**
+ * Callback to enable/disable rendering of canvas displayed inside windows
+ */
+class CullCallback:
+  public osg::Drawable::CullCallback
+{
+  public:
+    CullCallback(Canvas::CameraCullCallback* camera_cull);
+
+  private:
+    Canvas::CameraCullCallback *_camera_cull;
+
+    virtual bool cull( osg::NodeVisitor* nv,
+                       osg::Drawable* drawable,
+                       osg::RenderInfo* renderInfo ) const;
+};
+
+//------------------------------------------------------------------------------
+CullCallback::CullCallback(Canvas::CameraCullCallback* camera_cull):
+  _camera_cull( camera_cull )
+{
+
+}
+
+//------------------------------------------------------------------------------
+bool CullCallback::cull( osg::NodeVisitor* nv,
+                         osg::Drawable* drawable,
+                         osg::RenderInfo* renderInfo ) const
+{
+  _camera_cull->enableRendering();
+  return false;
+}
+
+namespace canvas
+{
+  //----------------------------------------------------------------------------
+  Window::Window(SGPropertyNode* node):
+    PropertyBasedElement(node),
+    _dirty(true),
+    _geometry( new osg::Geometry ),
+    _vertices( new osg::Vec3Array(4) ),
+    _tex_coords( new osg::Vec2Array(4) ),
+    _x(node, "x"),
+    _y(node, "y"),
+    _width(node, "size[0]"),
+    _height(node, "size[1]")
+  {
+    _x = 50;
+    _y = 100;
+    _width = 400;
+    _height = 300;
+
+    _geometry->setVertexArray(_vertices);
+    _geometry->setTexCoordArray(0,_tex_coords);
+
+    osg::Vec4Array* colors = new osg::Vec4Array(1);
+    (*colors)[0].set(1.0f,1.0f,1.0,1.0f);
+    _geometry->setColorArray(colors);
+    _geometry->setColorBinding(osg::Geometry::BIND_OVERALL);
+
+    _geometry->addPrimitiveSet(
+      new osg::DrawArrays(osg::PrimitiveSet::QUADS,0,4)
+    );
+    _geometry->setDataVariance(osg::Object::DYNAMIC);
+
+    osg::StateSet* stateSet = _geometry->getOrCreateStateSet();
+    stateSet->setRenderBinDetails(1000, "RenderBin");
+
+    // speed optimization?
+    stateSet->setMode(GL_CULL_FACE, osg::StateAttribute::OFF);
+    stateSet->setAttribute(new osg::BlendFunc(
+      osg::BlendFunc::SRC_ALPHA,
+      osg::BlendFunc::ONE_MINUS_SRC_ALPHA)
+    );
+    stateSet->setMode(GL_BLEND, osg::StateAttribute::ON);
+    stateSet->setMode(GL_LIGHTING, osg::StateAttribute::OFF);
+    stateSet->setMode(GL_FOG, osg::StateAttribute::OFF);
+    stateSet->setMode(GL_DEPTH_TEST, osg::StateAttribute::OFF);
+    stateSet->setTextureMode(0, GL_TEXTURE_2D, osg::StateAttribute::ON);
+  }
+
+  //----------------------------------------------------------------------------
+  Window::~Window()
+  {
+
+  }
+
+  //----------------------------------------------------------------------------
+  void Window::update(double delta_time_sec)
+  {
+    if( !_dirty )
+      return;
+    _dirty = false;
+
+    _region.set(_x, _y, _width, _height);
+
+    int z = 0; // TODO do we need to use z for depth ordering?
+
+    (*_vertices)[0].set(_region.l(), _region.t(), z);
+    (*_vertices)[1].set(_region.r(), _region.t(), z);
+    (*_vertices)[2].set(_region.r(), _region.b(), z);
+    (*_vertices)[3].set(_region.l(), _region.b(), z);
+
+    float l = 0, t = 1, b = 0, r = 1;
+    (*_tex_coords)[0].set(l,t);
+    (*_tex_coords)[1].set(r,t);
+    (*_tex_coords)[2].set(r,b);
+    (*_tex_coords)[3].set(l,b);
+
+    _geometry->dirtyDisplayList();
+  }
+
+  //----------------------------------------------------------------------------
+  void Window::valueChanged (SGPropertyNode * node)
+  {
+    if( node->getParent() != _node )
+      return;
+
+    const std::string& name = node->getNameString();
+    if(    name == "x" || name == "y" || name == "size" )
+      _dirty = true;
+  }
+
+  //----------------------------------------------------------------------------
+  void Window::setCanvas(CanvasPtr canvas)
+  {
+    _canvas = canvas;
+    _geometry->getOrCreateStateSet()
+             ->setTextureAttribute(0, canvas ? canvas->getTexture() : 0);
+    _geometry->dirtyDisplayList();
+    _geometry->setCullCallback(
+      canvas ? new CullCallback(canvas->getCameraCullCallback()) : 0
+    );
+  }
+
+  //----------------------------------------------------------------------------
+  CanvasWeakPtr Window::getCanvas() const
+  {
+    return _canvas;
+  }
+
+  //----------------------------------------------------------------------------
+  bool Window::handleMouseEvent(const MouseEvent& event)
+  {
+    if( !_canvas.expired() )
+      return _canvas.lock()->handleMouseEvent(event);
+    else
+      return false;
+  }
+
+} // namespace canvas
diff --git a/src/Canvas/window.hxx b/src/Canvas/window.hxx
new file mode 100644
index 000000000..afefbe515
--- /dev/null
+++ b/src/Canvas/window.hxx
@@ -0,0 +1,66 @@
+// Window for placing a Canvas onto it (for dialogs, menus, etc.)
+//
+// Copyright (C) 2012  Thomas Geymayer <tomgey@gmail.com>
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License as
+// published by the Free Software Foundation; either version 2 of the
+// License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+
+#ifndef CANVAS_WINDOW_HXX_
+#define CANVAS_WINDOW_HXX_
+
+#include "mouse_event.hxx"
+#include "property_based_element.hxx"
+#include "rect.hxx"
+
+#include <simgear/props/propertyObject.hxx>
+
+#include <osg/Geometry>
+
+namespace canvas
+{
+  class Window:
+    public PropertyBasedElement
+  {
+    public:
+      Window(SGPropertyNode* node);
+      virtual ~Window();
+
+      virtual void update(double delta_time_sec);
+      virtual void valueChanged (SGPropertyNode * node);
+
+      osg::Drawable* getDrawable() { return _geometry; }
+      const Rect<int>& getRegion() const { return _region; }
+
+      void setCanvas(CanvasPtr canvas);
+      CanvasWeakPtr getCanvas() const;
+
+      bool handleMouseEvent(const MouseEvent& event);
+
+    protected:
+
+      bool _dirty;
+
+      osg::ref_ptr<osg::Geometry>   _geometry;
+      osg::ref_ptr<osg::Vec3Array>  _vertices;
+      osg::ref_ptr<osg::Vec2Array>  _tex_coords;
+
+      simgear::PropertyObject<int>  _x, _y,
+                                    _width, _height;
+      Rect<int>                     _region;
+
+      CanvasWeakPtr _canvas;
+  };
+} // namespace canvas
+
+#endif /* CANVAS_WINDOW_HXX_ */
diff --git a/src/Instrumentation/od_gauge.cxx b/src/Instrumentation/od_gauge.cxx
index 6314e734a..2a003706f 100644
--- a/src/Instrumentation/od_gauge.cxx
+++ b/src/Instrumentation/od_gauge.cxx
@@ -311,7 +311,7 @@ class ReplaceStaticTextureVisitor:
      * Get a list of groups which have been inserted into the scene graph to
      * replace the given texture
      */
-    Placements& getPlacements()
+    canvas::Placements& getPlacements()
     {
       return _placements;
     }
@@ -386,7 +386,9 @@ class ReplaceStaticTextureVisitor:
         if( _cull_callback )
           group->setCullCallback(_cull_callback);
 
-        _placements.push_back(group);
+        _placements.push_back(
+          canvas::PlacementPtr(new ObjectPlacement(group))
+        );
 
         osg::StateSet* stateSet = group->getOrCreateStateSet();
         stateSet->setTextureAttribute( unit, _new_texture,
@@ -407,10 +409,38 @@ class ReplaceStaticTextureVisitor:
       }
     }
 
-
-
   protected:
 
+    class ObjectPlacement:
+      public canvas::Placement
+    {
+      public:
+        ObjectPlacement(osg::ref_ptr<osg::Group> group):
+          _group(group)
+        {}
+
+        /**
+         * Remove placement from the scene
+         */
+        virtual ~ObjectPlacement()
+        {
+          assert( _group->getNumChildren() == 1 );
+          osg::Node *child = _group->getChild(0);
+
+          if( _group->getNumParents() )
+          {
+            osg::Group *parent = _group->getParent(0);
+            parent->addChild(child);
+            parent->removeChild(_group);
+          }
+
+          _group->removeChild(child);
+        }
+
+      private:
+        osg::ref_ptr<osg::Group> _group;
+    };
+
     std::string _tex_name,      ///<! Name of texture to be replaced
                 _node_name,     ///<! Only replace if node name matches
                 _parent_name;   ///<! Only replace if any parent node matches
@@ -418,11 +448,12 @@ class ReplaceStaticTextureVisitor:
     osg::Texture2D     *_new_texture;
     osg::NodeCallback  *_cull_callback;
 
-    Placements _placements;
+    canvas::Placements _placements;
 };
 
 //------------------------------------------------------------------------------
-  Placements FGODGauge::set_texture(const char* name, osg::Texture2D* new_texture)
+canvas::Placements FGODGauge::set_texture( const char* name,
+                                           osg::Texture2D* new_texture )
 {
   osg::Group* root = globals->get_scenery()->get_aircraft_branch();
   ReplaceStaticTextureVisitor visitor(name, new_texture);
@@ -431,9 +462,9 @@ class ReplaceStaticTextureVisitor:
 }
 
 //------------------------------------------------------------------------------
-Placements FGODGauge::set_texture( const SGPropertyNode* placement,
-                             osg::Texture2D* new_texture,
-                             osg::NodeCallback* cull_callback )
+canvas::Placements FGODGauge::set_texture( const SGPropertyNode* placement,
+                                           osg::Texture2D* new_texture,
+                                           osg::NodeCallback* cull_callback )
 {
   osg::Group* root = globals->get_scenery()->get_aircraft_branch();
   ReplaceStaticTextureVisitor visitor(placement, new_texture, cull_callback);
diff --git a/src/Instrumentation/od_gauge.hxx b/src/Instrumentation/od_gauge.hxx
index 7d5ebf4ee..13ae90751 100644
--- a/src/Instrumentation/od_gauge.hxx
+++ b/src/Instrumentation/od_gauge.hxx
@@ -29,6 +29,9 @@
 #ifndef _OD_GAUGE_HXX
 #define _OD_GAUGE_HXX
 
+#include <Canvas/canvas_fwd.hpp>
+#include <Canvas/placement.hxx>
+
 #include <osg/NodeCallback>
 #include <osg/Group>
 
@@ -38,14 +41,13 @@ namespace osg {
 }
 
 class SGPropertyNode;
-typedef std::vector<osg::ref_ptr<osg::Group> > Placements;
 
 /**
  * Owner Drawn Gauge helper class.
  */
 class FGODGauge
 {
-public:
+  public:
     FGODGauge();
     virtual ~FGODGauge();
 
@@ -109,7 +111,9 @@ public:
      * @param new_texture dynamic texture to replace the old one
      * @return A list of groups which override the given texture
      */
-    Placements set_texture(const char * name, osg::Texture2D* new_texture);
+    static
+    canvas::Placements set_texture( const char * name,
+                                    osg::Texture2D* new_texture );
 
     /**
      * Replace an opengl texture name inside the aircraft scene graph.
@@ -125,9 +129,10 @@ public:
      *        object
      * @return A list of groups which override the given texture
      */
-    Placements set_texture( const SGPropertyNode* placement,
-                            osg::Texture2D* new_texture,
-                            osg::NodeCallback* cull_callback = 0 );
+    static
+    canvas::Placements set_texture( const SGPropertyNode* placement,
+                                    osg::Texture2D* new_texture,
+                                    osg::NodeCallback* cull_callback = 0 );
 
     /**
      * Get the OSG camera for drawing this gauge.
diff --git a/src/Main/fg_init.cxx b/src/Main/fg_init.cxx
index 55d5581a5..7b7f7fff9 100644
--- a/src/Main/fg_init.cxx
+++ b/src/Main/fg_init.cxx
@@ -81,6 +81,7 @@
 #include <Cockpit/panel_io.hxx>
 
 #include <Canvas/canvas_mgr.hxx>
+#include <Canvas/gui_mgr.hxx>
 #include <GUI/new_gui.hxx>
 #include <Input/input.hxx>
 #include <Instrumentation/instrument_mgr.hxx>
@@ -1164,6 +1165,7 @@ bool fgInitSubsystems() {
     // Initialize the canvas 2d drawing subsystem.
     ////////////////////////////////////////////////////////////////////
     globals->add_subsystem("Canvas", new CanvasMgr, SGSubsystemMgr::DISPLAY);
+    globals->add_subsystem("CanvasGUI", new GUIMgr, SGSubsystemMgr::DISPLAY);
 
     ////////////////////////////////////////////////////////////////////
     // Initialise the ATIS Manager