changeset 385:6cc3ce74dc05

using message broker in widgets
author Sebastien Jodogne <s.jodogne@gmail.com>
date Wed, 07 Nov 2018 20:49:41 +0100
parents d20d75f20c5d
children e33659decec5
files Applications/Generic/NativeStoneApplicationContext.cpp Applications/Generic/NativeStoneApplicationContext.h Applications/Generic/NativeStoneApplicationRunner.cpp Applications/Qt/QCairoWidget.cpp Applications/Qt/QCairoWidget.h Applications/Qt/QStoneMainWindow.cpp Applications/Qt/QStoneMainWindow.h Applications/Samples/Qt/SampleMainWindow.cpp Applications/Samples/Qt/SampleMainWindowWithButtons.cpp Applications/Samples/SimpleViewer/Qt/SimpleViewerMainWindow.cpp Applications/Sdl/SdlEngine.cpp Applications/Sdl/SdlEngine.h Applications/Sdl/SdlStoneApplicationRunner.cpp Applications/Sdl/SdlStoneApplicationRunner.h Framework/Messages/MessageType.h Framework/Viewport/IViewport.h Framework/Viewport/WidgetViewport.cpp Framework/Viewport/WidgetViewport.h Framework/Widgets/EmptyWidget.h Framework/Widgets/IWidget.h Framework/Widgets/WidgetBase.cpp Framework/Widgets/WidgetBase.h Platforms/Wasm/Defaults.cpp Platforms/Wasm/Defaults.h
diffstat 24 files changed, 295 insertions(+), 208 deletions(-) [+]
line wrap: on
line diff
--- a/Applications/Generic/NativeStoneApplicationContext.cpp	Wed Nov 07 16:17:02 2018 +0100
+++ b/Applications/Generic/NativeStoneApplicationContext.cpp	Wed Nov 07 20:49:41 2018 +0100
@@ -24,9 +24,9 @@
 
 namespace OrthancStone
 {
-  IWidget& NativeStoneApplicationContext::SetCentralWidget(IWidget* widget)   // Takes ownership
+  IWidget& NativeStoneApplicationContext::GlobalMutexLocker::SetCentralWidget(IWidget* widget)
   {
-    centralViewport_->SetCentralWidget(widget);
+    that_.centralViewport_.SetCentralWidget(widget);
     return *widget;
   }
 
@@ -37,7 +37,7 @@
     {
       {
         GlobalMutexLocker locker(*that);
-        that->GetCentralViewport().UpdateContent();
+        locker.GetCentralViewport().UpdateContent();
       }
       
       boost::this_thread::sleep(boost::posix_time::milliseconds(that->updateDelayInMs_));
@@ -45,8 +45,9 @@
   }
   
 
-  NativeStoneApplicationContext::NativeStoneApplicationContext() :
-    centralViewport_(new OrthancStone::WidgetViewport()),
+  NativeStoneApplicationContext::NativeStoneApplicationContext(MessageBroker& broker) :
+    broker_(broker),
+    centralViewport_(broker),
     stopped_(true),
     updateDelayInMs_(100)   // By default, 100ms between each refresh of the content
   {
@@ -56,7 +57,10 @@
 
   void NativeStoneApplicationContext::Start()
   {
-    if (centralViewport_->HasUpdateContent())
+    boost::mutex::scoped_lock lock(globalMutex_);
+    
+    if (stopped_ &&
+        centralViewport_.HasUpdateContent())
     {
       stopped_ = false;
       updateThread_ = boost::thread(UpdateThread, this);
--- a/Applications/Generic/NativeStoneApplicationContext.h	Wed Nov 07 16:17:02 2018 +0100
+++ b/Applications/Generic/NativeStoneApplicationContext.h	Wed Nov 07 20:49:41 2018 +0100
@@ -34,46 +34,51 @@
   class NativeStoneApplicationContext : public StoneApplicationContext
   {
   private:
-
     static void UpdateThread(NativeStoneApplicationContext* that);
 
-    boost::mutex                   globalMutex_;
-    std::auto_ptr<WidgetViewport>  centralViewport_;
-    boost::thread                  updateThread_;
-    bool                           stopped_;
-    unsigned int                   updateDelayInMs_;
+    boost::mutex    globalMutex_;
+    MessageBroker&  broker_;
+    WidgetViewport  centralViewport_;
+    boost::thread   updateThread_;
+    bool            stopped_;
+    unsigned int    updateDelayInMs_;
 
   public:
     class GlobalMutexLocker: public boost::noncopyable
     {
-      boost::mutex::scoped_lock  lock_;
+    private:
+      NativeStoneApplicationContext&  that_;
+      boost::mutex::scoped_lock       lock_;
+      
     public:
-      GlobalMutexLocker(NativeStoneApplicationContext& that):
+      GlobalMutexLocker(NativeStoneApplicationContext& that) :
+        that_(that),
         lock_(that.globalMutex_)
       {
       }
+
+      IWidget& SetCentralWidget(IWidget* widget);   // Takes ownership
+
+      MessageBroker& GetMessageBroker() const
+      {
+        return that_.broker_;
+      }
+    
+      IViewport& GetCentralViewport() 
+      {
+        return that_.centralViewport_;
+      }
+
+      void SetUpdateDelay(unsigned int delayInMs)
+      {
+        that_.updateDelayInMs_ = delayInMs;
+      }
     };
 
-    NativeStoneApplicationContext();
-
-    virtual ~NativeStoneApplicationContext()
-    {
-    }
-
-    virtual IWidget& SetCentralWidget(IWidget* widget);   // Takes ownership
-
-    IViewport& GetCentralViewport() 
-    {
-      return *(centralViewport_.get());
-    }
+    NativeStoneApplicationContext(MessageBroker& broker);
 
     void Start();
 
     void Stop();
-
-    void SetUpdateDelay(unsigned int delayInMs)
-    {
-      updateDelayInMs_ = delayInMs;
-    }
   };
 }
--- a/Applications/Generic/NativeStoneApplicationRunner.cpp	Wed Nov 07 16:17:02 2018 +0100
+++ b/Applications/Generic/NativeStoneApplicationRunner.cpp	Wed Nov 07 20:49:41 2018 +0100
@@ -24,16 +24,18 @@
 #endif
 
 #include "NativeStoneApplicationRunner.h"
-#include "NativeStoneApplicationContext.h"
-#include <boost/program_options.hpp>
 
 #include "../../Framework/Toolbox/MessagingToolbox.h"
+#include "../../Platforms/Generic/OracleWebService.h"
+#include "NativeStoneApplicationContext.h"
 
 #include <Core/Logging.h>
 #include <Core/HttpClient.h>
 #include <Core/Toolbox.h>
+#include <Core/OrthancException.h>
 #include <Plugins/Samples/Common/OrthancHttpConnection.h>
-#include "../../Platforms/Generic/OracleWebService.h"
+
+#include <boost/program_options.hpp>
 
 namespace OrthancStone
 {
@@ -183,7 +185,7 @@
 
       LogStatusBar statusBar;
 
-      NativeStoneApplicationContext context;
+      NativeStoneApplicationContext context(broker_);
 
       {
         Oracle oracle(4); // use 4 threads to download content
@@ -197,8 +199,8 @@
 
           {
             NativeStoneApplicationContext::GlobalMutexLocker locker(context);
-            context.SetCentralWidget(application_.GetCentralWidget());
-            context.GetCentralViewport().SetStatusBar(statusBar);
+            locker.SetCentralWidget(application_.GetCentralWidget());
+            locker.GetCentralViewport().SetStatusBar(statusBar);
           }
 
           std::string title = application_.GetTitle();
--- a/Applications/Qt/QCairoWidget.cpp	Wed Nov 07 16:17:02 2018 +0100
+++ b/Applications/Qt/QCairoWidget.cpp	Wed Nov 07 20:49:41 2018 +0100
@@ -25,6 +25,20 @@
 
 #include <stdexcept>
 
+
+QCairoWidget::StoneObserver::StoneObserver(QCairoWidget& that,
+                                           OrthancStone::IViewport& viewport,
+                                           OrthancStone::MessageBroker& broker) :
+  OrthancStone::IObserver(broker),
+  that_(that)
+{
+  // get notified each time the content of the central viewport changes
+  viewport.RegisterObserverCallback(
+    new OrthancStone::Callable<StoneObserver, OrthancStone::IViewport::ViewportChangedMessage>
+    (*this, &StoneObserver::OnViewportChanged));
+}
+
+
 QCairoWidget::QCairoWidget(QWidget *parent) :
   QWidget(parent),
   context_(NULL)
@@ -32,24 +46,29 @@
   setFocusPolicy(Qt::StrongFocus); // catch keyPressEvents
 }
 
-QCairoWidget::~QCairoWidget()
-{
-}
 
 void QCairoWidget::SetContext(OrthancStone::NativeStoneApplicationContext& context)
 {
   context_ = &context;
-  context_->GetCentralViewport().Register(*this); // get notified each time the content of the central viewport changes
+
+  {
+    OrthancStone::NativeStoneApplicationContext::GlobalMutexLocker locker(*context_);
+    observer_.reset(new StoneObserver(*this,
+                                      locker.GetCentralViewport(),
+                                      locker.GetMessageBroker()));
+  }
 }
 
+
 void QCairoWidget::paintEvent(QPaintEvent* /*event*/)
 {
   QPainter painter(this);
 
-  if (image_.get() != NULL && context_ != NULL)
+  if (image_.get() != NULL &&
+      context_ != NULL)
   {
     OrthancStone::NativeStoneApplicationContext::GlobalMutexLocker locker(*context_);
-    OrthancStone::IViewport& viewport = context_->GetCentralViewport();
+    OrthancStone::IViewport& viewport = locker.GetCentralViewport();
     Orthanc::ImageAccessor a;
     surface_.GetWriteableAccessor(a);
     viewport.Render(a);
@@ -103,35 +122,43 @@
     default:
       return;  // Unsupported button
   }
-  context_->GetCentralViewport().MouseDown(button, event->pos().x(), event->pos().y(), stoneModifiers);
+
+  {
+    OrthancStone::NativeStoneApplicationContext::GlobalMutexLocker locker(*context_);
+    locker.GetCentralViewport().MouseDown(button, event->pos().x(), event->pos().y(), stoneModifiers);
+  }
 }
 
 
 void QCairoWidget::mouseReleaseEvent(QMouseEvent* /*eventNotUsed*/)
 {
-  context_->GetCentralViewport().MouseLeave();
+  OrthancStone::NativeStoneApplicationContext::GlobalMutexLocker locker(*context_);
+  locker.GetCentralViewport().MouseLeave();
 }
 
 
 void QCairoWidget::mouseMoveEvent(QMouseEvent* event)
 {
-  context_->GetCentralViewport().MouseMove(event->pos().x(), event->pos().y());
+  OrthancStone::NativeStoneApplicationContext::GlobalMutexLocker locker(*context_);
+  locker.GetCentralViewport().MouseMove(event->pos().x(), event->pos().y());
 }
 
 
 void QCairoWidget::wheelEvent(QWheelEvent * event)
 {
+  OrthancStone::NativeStoneApplicationContext::GlobalMutexLocker locker(*context_);
+
   OrthancStone::KeyboardModifiers stoneModifiers = GetKeyboardModifiers(event);
 
   if (event->orientation() == Qt::Vertical)
   {
     if (event->delta() < 0)  // TODO: compare direction with SDL and make sure we send the same directions
     {
-       context_->GetCentralViewport().MouseWheel(OrthancStone::MouseWheelDirection_Up, event->pos().x(), event->pos().y(), stoneModifiers);
+       locker.GetCentralViewport().MouseWheel(OrthancStone::MouseWheelDirection_Up, event->pos().x(), event->pos().y(), stoneModifiers);
     }
     else
     {
-      context_->GetCentralViewport().MouseWheel(OrthancStone::MouseWheelDirection_Down, event->pos().x(), event->pos().y(), stoneModifiers);
+      locker.GetCentralViewport().MouseWheel(OrthancStone::MouseWheelDirection_Down, event->pos().x(), event->pos().y(), stoneModifiers);
     }
   }
 }
@@ -158,7 +185,11 @@
       break;
     }
   }
-  context_->GetCentralViewport().KeyPressed(keyType, keyChar, stoneModifiers);
+
+  {
+    OrthancStone::NativeStoneApplicationContext::GlobalMutexLocker locker(*context_);    
+    locker.GetCentralViewport().KeyPressed(keyType, keyChar, stoneModifiers);
+  }
 }
 
 
@@ -177,7 +208,9 @@
                             surface_.GetPitch(),
                             QImage::Format_RGB32));
 
-    context_->GetCentralViewport().SetSize(event->size().width(), event->size().height());
-
+    {
+      OrthancStone::NativeStoneApplicationContext::GlobalMutexLocker locker(*context_);
+      locker.GetCentralViewport().SetSize(event->size().width(), event->size().height());
+    }
   }
 }
--- a/Applications/Qt/QCairoWidget.h	Wed Nov 07 16:17:02 2018 +0100
+++ b/Applications/Qt/QCairoWidget.h	Wed Nov 07 20:49:41 2018 +0100
@@ -1,7 +1,7 @@
 /**
  * Stone of Orthanc
  * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
- * Department, University Hospital of Liege, Belgium
+4 * Department, University Hospital of Liege, Belgium
  * Copyright (C) 2017-2018 Osimis S.A., Belgium
  *
  * This program is free software: you can redistribute it and/or
@@ -20,23 +20,39 @@
 
 #pragma once
 
-#include "../../Framework/Widgets/CairoWidget.h"
 #include "../../Applications/Generic/NativeStoneApplicationContext.h"
 #include "../../Framework/Viewport/CairoSurface.h"
+#include "../../Framework/Widgets/IWidget.h"
 
 #include <QWidget>
-#include <QGestureEvent>
 #include <memory>
 #include <cassert>
 
-class QCairoWidget : public QWidget, public OrthancStone::IViewport::IObserver
+class QCairoWidget : public QWidget
 {
   Q_OBJECT
 
 private:
+  class StoneObserver : public OrthancStone::IObserver
+  {
+  private:
+    QCairoWidget& that_;
+    
+  public:
+    StoneObserver(QCairoWidget& that,
+                  OrthancStone::IViewport& viewport,
+                  OrthancStone::MessageBroker& broker);
+
+    void OnViewportChanged(const OrthancStone::IViewport::ViewportChangedMessage& message)
+    {
+      that_.OnViewportChanged();
+    }
+  };
+  
   std::auto_ptr<QImage>         image_;
   OrthancStone::CairoSurface    surface_;
   OrthancStone::NativeStoneApplicationContext* context_;
+  std::auto_ptr<StoneObserver>  observer_;
 
 protected:
   virtual void paintEvent(QPaintEvent *event);
@@ -56,18 +72,15 @@
 public:
   explicit QCairoWidget(QWidget *parent);
  
-  virtual ~QCairoWidget();
-
   void SetContext(OrthancStone::NativeStoneApplicationContext& context);
 
-  virtual void OnViewportContentChanged(const OrthancStone::IViewport& /*sceneNotUsed*/)
+  void OnViewportChanged()
   {
     update();  // schedule a repaint (handled by Qt)
     emit ContentChanged();
   }
 
 signals:
-
   void ContentChanged();
                                                
 public slots:
--- a/Applications/Qt/QStoneMainWindow.cpp	Wed Nov 07 16:17:02 2018 +0100
+++ b/Applications/Qt/QStoneMainWindow.cpp	Wed Nov 07 20:49:41 2018 +0100
@@ -23,15 +23,17 @@
 namespace OrthancStone
 {
 
-  QStoneMainWindow::QStoneMainWindow(NativeStoneApplicationContext& context, QWidget *parent) :
+  QStoneMainWindow::QStoneMainWindow(NativeStoneApplicationContext& context,
+                                     QWidget *parent) :
     QMainWindow(parent),
-    context_(context)
+    context_(context),
+    cairoCentralWidget_(NULL)
   {
   }
 
-  void QStoneMainWindow::SetCentralStoneWidget(QCairoWidget *centralWidget)
+  void QStoneMainWindow::SetCentralStoneWidget(QCairoWidget& centralWidget)
   {
-    cairoCentralWidget_ = centralWidget;
+    cairoCentralWidget_ = &centralWidget;
     cairoCentralWidget_->SetContext(context_);
   }
 
--- a/Applications/Qt/QStoneMainWindow.h	Wed Nov 07 16:17:02 2018 +0100
+++ b/Applications/Qt/QStoneMainWindow.h	Wed Nov 07 20:49:41 2018 +0100
@@ -36,10 +36,10 @@
 
   protected:  // you must inherit this class
     QStoneMainWindow(NativeStoneApplicationContext& context, QWidget *parent = 0);
-    void SetCentralStoneWidget(QCairoWidget* centralWidget);
+    void SetCentralStoneWidget(QCairoWidget& centralWidget);
+
   public:
     virtual ~QStoneMainWindow();
-
   };
 
 }
--- a/Applications/Samples/Qt/SampleMainWindow.cpp	Wed Nov 07 16:17:02 2018 +0100
+++ b/Applications/Samples/Qt/SampleMainWindow.cpp	Wed Nov 07 20:49:41 2018 +0100
@@ -32,13 +32,16 @@
   namespace Samples
   {
 
-    SampleMainWindow::SampleMainWindow(OrthancStone::NativeStoneApplicationContext& context, OrthancStone::Samples::SampleSingleCanvasApplicationBase& stoneSampleApplication, QWidget *parent) :
+    SampleMainWindow::SampleMainWindow(
+      OrthancStone::NativeStoneApplicationContext& context,
+      OrthancStone::Samples::SampleSingleCanvasApplicationBase& stoneSampleApplication,
+      QWidget *parent) :
       QStoneMainWindow(context, parent),
       ui_(new Ui::SampleMainWindow),
       stoneSampleApplication_(stoneSampleApplication)
     {
       ui_->setupUi(this);
-      SetCentralStoneWidget(ui_->cairoCentralWidget);
+      SetCentralStoneWidget(*ui_->cairoCentralWidget);
     }
 
     SampleMainWindow::~SampleMainWindow()
--- a/Applications/Samples/Qt/SampleMainWindowWithButtons.cpp	Wed Nov 07 16:17:02 2018 +0100
+++ b/Applications/Samples/Qt/SampleMainWindowWithButtons.cpp	Wed Nov 07 20:49:41 2018 +0100
@@ -32,13 +32,16 @@
   namespace Samples
   {
 
-    SampleMainWindowWithButtons::SampleMainWindowWithButtons(OrthancStone::NativeStoneApplicationContext& context, OrthancStone::Samples::SampleSingleCanvasWithButtonsApplicationBase& stoneSampleApplication, QWidget *parent) :
+    SampleMainWindowWithButtons::SampleMainWindowWithButtons(
+      OrthancStone::NativeStoneApplicationContext& context,
+      OrthancStone::Samples::SampleSingleCanvasWithButtonsApplicationBase& stoneSampleApplication,
+      QWidget *parent) :
       QStoneMainWindow(context, parent),
       ui_(new Ui::SampleMainWindowWithButtons),
       stoneSampleApplication_(stoneSampleApplication)
     {
       ui_->setupUi(this);
-      SetCentralStoneWidget(ui_->cairoCentralWidget);
+      SetCentralStoneWidget(*ui_->cairoCentralWidget);
 
 #if QT_VERSION >= 0x050000
       connect(ui_->toolButton1, &QToolButton::clicked, this, &SampleMainWindowWithButtons::tool1Clicked);
--- a/Applications/Samples/SimpleViewer/Qt/SimpleViewerMainWindow.cpp	Wed Nov 07 16:17:02 2018 +0100
+++ b/Applications/Samples/SimpleViewer/Qt/SimpleViewerMainWindow.cpp	Wed Nov 07 20:49:41 2018 +0100
@@ -30,13 +30,16 @@
 namespace SimpleViewer
 {
 
-  SimpleViewerMainWindow::SimpleViewerMainWindow(OrthancStone::NativeStoneApplicationContext& context, SimpleViewerApplication& stoneApplication, QWidget *parent) :
+  SimpleViewerMainWindow::SimpleViewerMainWindow(
+    OrthancStone::NativeStoneApplicationContext& context,
+    SimpleViewerApplication& stoneApplication,
+    QWidget *parent) :
     QStoneMainWindow(context, parent),
     ui_(new Ui::SimpleViewerMainWindow),
     stoneApplication_(stoneApplication)
   {
     ui_->setupUi(this);
-    SetCentralStoneWidget(ui_->cairoCentralWidget);
+    SetCentralStoneWidget(*ui_->cairoCentralWidget);
 
 #if QT_VERSION >= 0x050000
     connect(ui_->toolButtonCrop, &QToolButton::clicked, this, &SimpleViewerMainWindow::cropClicked);
--- a/Applications/Sdl/SdlEngine.cpp	Wed Nov 07 16:17:02 2018 +0100
+++ b/Applications/Sdl/SdlEngine.cpp	Wed Nov 07 20:49:41 2018 +0100
@@ -32,7 +32,8 @@
   void SdlEngine::SetSize(unsigned int width,
                           unsigned int height)
   {
-    context_.GetCentralViewport().SetSize(width, height);
+    NativeStoneApplicationContext::GlobalMutexLocker locker(context_);
+    locker.GetCentralViewport().SetSize(width, height);
     surface_.SetSize(width, height);
   }
 
@@ -42,7 +43,7 @@
     if (viewportChanged_)
     {
       NativeStoneApplicationContext::GlobalMutexLocker locker(context_);
-      surface_.Render(context_.GetCentralViewport());
+      surface_.Render(locker.GetCentralViewport());
 
       viewportChanged_ = false;
     }
@@ -98,7 +99,9 @@
 
 
   SdlEngine::SdlEngine(SdlWindow& window,
-                       NativeStoneApplicationContext& context) :
+                       NativeStoneApplicationContext& context,
+                       MessageBroker& broker) :
+    IObserver(broker),
     window_(window),
     context_(context),
     surface_(window),
@@ -107,20 +110,16 @@
   }
   
 
-  SdlEngine::~SdlEngine()
-  {
-  }
-
-
   void SdlEngine::Run()
   {
     int scancodeCount = 0;
     const uint8_t* keyboardState = SDL_GetKeyboardState(&scancodeCount);
 
+    SetSize(window_.GetWidth(), window_.GetHeight());
+    
     {
       NativeStoneApplicationContext::GlobalMutexLocker locker(context_);
-      SetSize(window_.GetWidth(), window_.GetHeight());
-      context_.GetCentralViewport().FitContent();
+      locker.GetCentralViewport().FitContent();
     }
     
     bool stop = false;
@@ -147,15 +146,15 @@
           switch (event.button.button)
           {
           case SDL_BUTTON_LEFT:
-            context_.GetCentralViewport().MouseDown(MouseButton_Left, event.button.x, event.button.y, modifiers);
+            locker.GetCentralViewport().MouseDown(MouseButton_Left, event.button.x, event.button.y, modifiers);
             break;
             
           case SDL_BUTTON_RIGHT:
-            context_.GetCentralViewport().MouseDown(MouseButton_Right, event.button.x, event.button.y, modifiers);
+            locker.GetCentralViewport().MouseDown(MouseButton_Right, event.button.x, event.button.y, modifiers);
             break;
             
           case SDL_BUTTON_MIDDLE:
-            context_.GetCentralViewport().MouseDown(MouseButton_Middle, event.button.x, event.button.y, modifiers);
+            locker.GetCentralViewport().MouseDown(MouseButton_Middle, event.button.x, event.button.y, modifiers);
             break;
 
           default:
@@ -164,22 +163,22 @@
         }
         else if (event.type == SDL_MOUSEMOTION)
         {
-          context_.GetCentralViewport().MouseMove(event.button.x, event.button.y);
+          locker.GetCentralViewport().MouseMove(event.button.x, event.button.y);
         }
         else if (event.type == SDL_MOUSEBUTTONUP)
         {
-          context_.GetCentralViewport().MouseUp();
+          locker.GetCentralViewport().MouseUp();
         }
         else if (event.type == SDL_WINDOWEVENT)
         {
           switch (event.window.event)
           {
           case SDL_WINDOWEVENT_LEAVE:
-            context_.GetCentralViewport().MouseLeave();
+            locker.GetCentralViewport().MouseLeave();
             break;
 
           case SDL_WINDOWEVENT_ENTER:
-            context_.GetCentralViewport().MouseEnter();
+            locker.GetCentralViewport().MouseEnter();
             break;
 
           case SDL_WINDOWEVENT_SIZE_CHANGED:
@@ -199,11 +198,11 @@
 
           if (event.wheel.y > 0)
           {
-            context_.GetCentralViewport().MouseWheel(MouseWheelDirection_Up, x, y, modifiers);
+            locker.GetCentralViewport().MouseWheel(MouseWheelDirection_Up, x, y, modifiers);
           }
           else if (event.wheel.y < 0)
           {
-            context_.GetCentralViewport().MouseWheel(MouseWheelDirection_Down, x, y, modifiers);
+            locker.GetCentralViewport().MouseWheel(MouseWheelDirection_Down, x, y, modifiers);
           }
         }
         else if (event.type == SDL_KEYDOWN &&
@@ -213,59 +212,59 @@
 
           switch (event.key.keysym.sym)
           {
-          case SDLK_a:    context_.GetCentralViewport().KeyPressed(KeyboardKeys_Generic, 'a', modifiers);  break;
-          case SDLK_b:    context_.GetCentralViewport().KeyPressed(KeyboardKeys_Generic, 'b', modifiers);  break;
-          case SDLK_c:    context_.GetCentralViewport().KeyPressed(KeyboardKeys_Generic, 'c', modifiers);  break;
-          case SDLK_d:    context_.GetCentralViewport().KeyPressed(KeyboardKeys_Generic, 'd', modifiers);  break;
-          case SDLK_e:    context_.GetCentralViewport().KeyPressed(KeyboardKeys_Generic, 'e', modifiers);  break;
+          case SDLK_a:    locker.GetCentralViewport().KeyPressed(KeyboardKeys_Generic, 'a', modifiers);  break;
+          case SDLK_b:    locker.GetCentralViewport().KeyPressed(KeyboardKeys_Generic, 'b', modifiers);  break;
+          case SDLK_c:    locker.GetCentralViewport().KeyPressed(KeyboardKeys_Generic, 'c', modifiers);  break;
+          case SDLK_d:    locker.GetCentralViewport().KeyPressed(KeyboardKeys_Generic, 'd', modifiers);  break;
+          case SDLK_e:    locker.GetCentralViewport().KeyPressed(KeyboardKeys_Generic, 'e', modifiers);  break;
           case SDLK_f:    window_.ToggleMaximize();                         break;
-          case SDLK_g:    context_.GetCentralViewport().KeyPressed(KeyboardKeys_Generic, 'g', modifiers);  break;
-          case SDLK_h:    context_.GetCentralViewport().KeyPressed(KeyboardKeys_Generic, 'h', modifiers);  break;
-          case SDLK_i:    context_.GetCentralViewport().KeyPressed(KeyboardKeys_Generic, 'i', modifiers);  break;
-          case SDLK_j:    context_.GetCentralViewport().KeyPressed(KeyboardKeys_Generic, 'j', modifiers);  break;
-          case SDLK_k:    context_.GetCentralViewport().KeyPressed(KeyboardKeys_Generic, 'k', modifiers);  break;
-          case SDLK_l:    context_.GetCentralViewport().KeyPressed(KeyboardKeys_Generic, 'l', modifiers);  break;
-          case SDLK_m:    context_.GetCentralViewport().KeyPressed(KeyboardKeys_Generic, 'm', modifiers);  break;
-          case SDLK_n:    context_.GetCentralViewport().KeyPressed(KeyboardKeys_Generic, 'n', modifiers);  break;
-          case SDLK_o:    context_.GetCentralViewport().KeyPressed(KeyboardKeys_Generic, 'o', modifiers);  break;
-          case SDLK_p:    context_.GetCentralViewport().KeyPressed(KeyboardKeys_Generic, 'p', modifiers);  break;
+          case SDLK_g:    locker.GetCentralViewport().KeyPressed(KeyboardKeys_Generic, 'g', modifiers);  break;
+          case SDLK_h:    locker.GetCentralViewport().KeyPressed(KeyboardKeys_Generic, 'h', modifiers);  break;
+          case SDLK_i:    locker.GetCentralViewport().KeyPressed(KeyboardKeys_Generic, 'i', modifiers);  break;
+          case SDLK_j:    locker.GetCentralViewport().KeyPressed(KeyboardKeys_Generic, 'j', modifiers);  break;
+          case SDLK_k:    locker.GetCentralViewport().KeyPressed(KeyboardKeys_Generic, 'k', modifiers);  break;
+          case SDLK_l:    locker.GetCentralViewport().KeyPressed(KeyboardKeys_Generic, 'l', modifiers);  break;
+          case SDLK_m:    locker.GetCentralViewport().KeyPressed(KeyboardKeys_Generic, 'm', modifiers);  break;
+          case SDLK_n:    locker.GetCentralViewport().KeyPressed(KeyboardKeys_Generic, 'n', modifiers);  break;
+          case SDLK_o:    locker.GetCentralViewport().KeyPressed(KeyboardKeys_Generic, 'o', modifiers);  break;
+          case SDLK_p:    locker.GetCentralViewport().KeyPressed(KeyboardKeys_Generic, 'p', modifiers);  break;
           case SDLK_q:    stop = true;                                      break;
-          case SDLK_r:    context_.GetCentralViewport().KeyPressed(KeyboardKeys_Generic, 'r', modifiers);  break;
-          case SDLK_s:    context_.GetCentralViewport().KeyPressed(KeyboardKeys_Generic, 's', modifiers);  break;
-          case SDLK_t:    context_.GetCentralViewport().KeyPressed(KeyboardKeys_Generic, 't', modifiers);  break;
-          case SDLK_u:    context_.GetCentralViewport().KeyPressed(KeyboardKeys_Generic, 'u', modifiers);  break;
-          case SDLK_v:    context_.GetCentralViewport().KeyPressed(KeyboardKeys_Generic, 'v', modifiers);  break;
-          case SDLK_w:    context_.GetCentralViewport().KeyPressed(KeyboardKeys_Generic, 'w', modifiers);  break;
-          case SDLK_x:    context_.GetCentralViewport().KeyPressed(KeyboardKeys_Generic, 'x', modifiers);  break;
-          case SDLK_y:    context_.GetCentralViewport().KeyPressed(KeyboardKeys_Generic, 'y', modifiers);  break;
-          case SDLK_z:    context_.GetCentralViewport().KeyPressed(KeyboardKeys_Generic, 'z', modifiers);  break;
-          case SDLK_KP_0: context_.GetCentralViewport().KeyPressed(KeyboardKeys_Generic, '0', modifiers);  break;
-          case SDLK_KP_1: context_.GetCentralViewport().KeyPressed(KeyboardKeys_Generic, '1', modifiers);  break;
-          case SDLK_KP_2: context_.GetCentralViewport().KeyPressed(KeyboardKeys_Generic, '2', modifiers);  break;
-          case SDLK_KP_3: context_.GetCentralViewport().KeyPressed(KeyboardKeys_Generic, '3', modifiers);  break;
-          case SDLK_KP_4: context_.GetCentralViewport().KeyPressed(KeyboardKeys_Generic, '4', modifiers);  break;
-          case SDLK_KP_5: context_.GetCentralViewport().KeyPressed(KeyboardKeys_Generic, '5', modifiers);  break;
-          case SDLK_KP_6: context_.GetCentralViewport().KeyPressed(KeyboardKeys_Generic, '6', modifiers);  break;
-          case SDLK_KP_7: context_.GetCentralViewport().KeyPressed(KeyboardKeys_Generic, '7', modifiers);  break;
-          case SDLK_KP_8: context_.GetCentralViewport().KeyPressed(KeyboardKeys_Generic, '8', modifiers);  break;
-          case SDLK_KP_9: context_.GetCentralViewport().KeyPressed(KeyboardKeys_Generic, '9', modifiers);  break;
+          case SDLK_r:    locker.GetCentralViewport().KeyPressed(KeyboardKeys_Generic, 'r', modifiers);  break;
+          case SDLK_s:    locker.GetCentralViewport().KeyPressed(KeyboardKeys_Generic, 's', modifiers);  break;
+          case SDLK_t:    locker.GetCentralViewport().KeyPressed(KeyboardKeys_Generic, 't', modifiers);  break;
+          case SDLK_u:    locker.GetCentralViewport().KeyPressed(KeyboardKeys_Generic, 'u', modifiers);  break;
+          case SDLK_v:    locker.GetCentralViewport().KeyPressed(KeyboardKeys_Generic, 'v', modifiers);  break;
+          case SDLK_w:    locker.GetCentralViewport().KeyPressed(KeyboardKeys_Generic, 'w', modifiers);  break;
+          case SDLK_x:    locker.GetCentralViewport().KeyPressed(KeyboardKeys_Generic, 'x', modifiers);  break;
+          case SDLK_y:    locker.GetCentralViewport().KeyPressed(KeyboardKeys_Generic, 'y', modifiers);  break;
+          case SDLK_z:    locker.GetCentralViewport().KeyPressed(KeyboardKeys_Generic, 'z', modifiers);  break;
+          case SDLK_KP_0: locker.GetCentralViewport().KeyPressed(KeyboardKeys_Generic, '0', modifiers);  break;
+          case SDLK_KP_1: locker.GetCentralViewport().KeyPressed(KeyboardKeys_Generic, '1', modifiers);  break;
+          case SDLK_KP_2: locker.GetCentralViewport().KeyPressed(KeyboardKeys_Generic, '2', modifiers);  break;
+          case SDLK_KP_3: locker.GetCentralViewport().KeyPressed(KeyboardKeys_Generic, '3', modifiers);  break;
+          case SDLK_KP_4: locker.GetCentralViewport().KeyPressed(KeyboardKeys_Generic, '4', modifiers);  break;
+          case SDLK_KP_5: locker.GetCentralViewport().KeyPressed(KeyboardKeys_Generic, '5', modifiers);  break;
+          case SDLK_KP_6: locker.GetCentralViewport().KeyPressed(KeyboardKeys_Generic, '6', modifiers);  break;
+          case SDLK_KP_7: locker.GetCentralViewport().KeyPressed(KeyboardKeys_Generic, '7', modifiers);  break;
+          case SDLK_KP_8: locker.GetCentralViewport().KeyPressed(KeyboardKeys_Generic, '8', modifiers);  break;
+          case SDLK_KP_9: locker.GetCentralViewport().KeyPressed(KeyboardKeys_Generic, '9', modifiers);  break;
 
           case SDLK_PLUS:
           case SDLK_KP_PLUS:
-            context_.GetCentralViewport().KeyPressed(KeyboardKeys_Generic, '+', modifiers);  break;
+            locker.GetCentralViewport().KeyPressed(KeyboardKeys_Generic, '+', modifiers);  break;
 
           case SDLK_MINUS:
           case SDLK_KP_MINUS:
-            context_.GetCentralViewport().KeyPressed(KeyboardKeys_Generic, '-', modifiers);  break;
+            locker.GetCentralViewport().KeyPressed(KeyboardKeys_Generic, '-', modifiers);  break;
 
           case SDLK_RIGHT:
-            context_.GetCentralViewport().KeyPressed(KeyboardKeys_Right, 0, modifiers);  break;
+            locker.GetCentralViewport().KeyPressed(KeyboardKeys_Right, 0, modifiers);  break;
           case SDLK_LEFT:
-            context_.GetCentralViewport().KeyPressed(KeyboardKeys_Left, 0, modifiers);  break;
+            locker.GetCentralViewport().KeyPressed(KeyboardKeys_Left, 0, modifiers);  break;
           case SDLK_UP:
-            context_.GetCentralViewport().KeyPressed(KeyboardKeys_Up, 0, modifiers);  break;
+            locker.GetCentralViewport().KeyPressed(KeyboardKeys_Up, 0, modifiers);  break;
           case SDLK_DOWN:
-            context_.GetCentralViewport().KeyPressed(KeyboardKeys_Down, 0, modifiers);  break;
+            locker.GetCentralViewport().KeyPressed(KeyboardKeys_Down, 0, modifiers);  break;
           default:
             break;
           }
--- a/Applications/Sdl/SdlEngine.h	Wed Nov 07 16:17:02 2018 +0100
+++ b/Applications/Sdl/SdlEngine.h	Wed Nov 07 20:49:41 2018 +0100
@@ -28,7 +28,7 @@
 
 namespace OrthancStone
 {
-  class SdlEngine : public IViewport::IObserver
+  class SdlEngine : public IObserver
   {
   private:
     SdlWindow&                window_;
@@ -46,11 +46,10 @@
 
   public:
     SdlEngine(SdlWindow& window,
-              NativeStoneApplicationContext& context);
+              NativeStoneApplicationContext& context,
+              MessageBroker& broker);
   
-    virtual ~SdlEngine();
-
-    virtual void OnViewportContentChanged(const IViewport& viewport)
+    void OnViewportChanged(const IViewport::ViewportChangedMessage& message)
     {
       viewportChanged_ = true;
     }
--- a/Applications/Sdl/SdlStoneApplicationRunner.cpp	Wed Nov 07 16:17:02 2018 +0100
+++ b/Applications/Sdl/SdlStoneApplicationRunner.cpp	Wed Nov 07 20:49:41 2018 +0100
@@ -24,16 +24,18 @@
 #endif
 
 #include "SdlStoneApplicationRunner.h"
-#include <boost/program_options.hpp>
 
 #include "../../Framework/Toolbox/MessagingToolbox.h"
+#include "../../Platforms/Generic/OracleWebService.h"
 #include "SdlEngine.h"
 
 #include <Core/Logging.h>
 #include <Core/HttpClient.h>
 #include <Core/Toolbox.h>
+#include <Core/OrthancException.h>
 #include <Plugins/Samples/Common/OrthancHttpConnection.h>
-#include "../../Platforms/Generic/OracleWebService.h"
+
+#include <boost/program_options.hpp>
 
 namespace OrthancStone
 {
@@ -42,6 +44,7 @@
     SdlWindow::GlobalInitialize();
   }
 
+  
   void SdlStoneApplicationRunner::DeclareCommandLineOptions(boost::program_options::options_description& options)
   {
     boost::program_options::options_description sdl("SDL options");
@@ -54,6 +57,7 @@
     options.add(sdl);
   }
 
+  
   void SdlStoneApplicationRunner::ParseCommandLineOptions(const boost::program_options::variables_map& parameters)
   {
     if (!parameters.count("width") ||
@@ -85,10 +89,13 @@
     {
       LOG(WARNING) << "OpenGL is disabled, enable it with option \"--opengl=on\" for best performance";
     }
-
   }
 
-  void SdlStoneApplicationRunner::Run(NativeStoneApplicationContext& context, const std::string& title, int argc, char* argv[])
+  
+  void SdlStoneApplicationRunner::Run(NativeStoneApplicationContext& context,
+                                      const std::string& title,
+                                      int argc,
+                                      char* argv[])
   {
     /**************************************************************
      * Run the application inside a SDL window
@@ -97,11 +104,16 @@
     LOG(WARNING) << "Starting the application";
 
     SdlWindow window(title.c_str(), width_, height_, enableOpenGl_);
-    SdlEngine sdl(window, context);
+    SdlEngine sdl(window, context, broker_);
 
     {
       NativeStoneApplicationContext::GlobalMutexLocker locker(context);
-      context.GetCentralViewport().Register(sdl);  // (*)
+
+      locker.GetCentralViewport().RegisterObserverCallback(
+        new Callable<SdlEngine, IViewport::ViewportChangedMessage>
+        (sdl, &SdlEngine::OnViewportChanged));
+
+      //context.GetCentralViewport().Register(sdl);  // (*)
     }
 
     context.Start();
@@ -115,13 +127,14 @@
     // update thread started by "context.Start()" would call a
     // destructed object (the "SdlEngine" is deleted with the
     // lexical scope).
+
+    // TODO Is this still true with message broker?
     context.Stop();
   }
 
+  
   void SdlStoneApplicationRunner::Finalize()
   {
     SdlWindow::GlobalFinalize();
   }
-
-
 }
--- a/Applications/Sdl/SdlStoneApplicationRunner.h	Wed Nov 07 16:17:02 2018 +0100
+++ b/Applications/Sdl/SdlStoneApplicationRunner.h	Wed Nov 07 20:49:41 2018 +0100
@@ -33,21 +33,29 @@
 {
   class SdlStoneApplicationRunner : public NativeStoneApplicationRunner
   {
-    unsigned int width_;
-    unsigned int height_;
-    bool enableOpenGl_;
+  private:
+    unsigned int  width_;
+    unsigned int  height_;
+    bool          enableOpenGl_;
+    
   public:
     SdlStoneApplicationRunner(MessageBroker& broker,
-                                 IStoneApplication& application)
-      : NativeStoneApplicationRunner(broker, application)
+                              IStoneApplication& application) :
+      NativeStoneApplicationRunner(broker, application)
     {
     }
 
     virtual void Initialize();
+    
     virtual void DeclareCommandLineOptions(boost::program_options::options_description& options);
-    virtual void Run(NativeStoneApplicationContext& context, const std::string& title, int argc, char* argv[]);
+    
+    virtual void Run(NativeStoneApplicationContext& context,
+                     const std::string& title,
+                     int argc,
+                     char* argv[]);
+    
     virtual void ParseCommandLineOptions(const boost::program_options::variables_map& parameters);
+    
     virtual void Finalize();
   };
-
 }
--- a/Framework/Messages/MessageType.h	Wed Nov 07 16:17:02 2018 +0100
+++ b/Framework/Messages/MessageType.h	Wed Nov 07 20:49:41 2018 +0100
@@ -51,6 +51,8 @@
     MessageType_OrthancApi_GenericHttpError_Ready,
     MessageType_OrthancApi_GenericEmptyResponse_Ready,
 
+    MessageType_ViewportChanged,
+
     // used in unit tests only
     MessageType_Test1,
     MessageType_Test2,
--- a/Framework/Viewport/IViewport.h	Wed Nov 07 16:17:02 2018 +0100
+++ b/Framework/Viewport/IViewport.h	Wed Nov 07 20:49:41 2018 +0100
@@ -23,6 +23,7 @@
 
 #include "IStatusBar.h"
 #include "../StoneEnumerations.h"
+#include "../Messages/IObservable.h"
 
 #include <Core/Images/ImageAccessor.h>
 
@@ -30,27 +31,22 @@
 {
   class IWidget;   // Forward declaration
   
-  class IViewport : public boost::noncopyable
+  class IViewport : public IObservable
   {
   public:
-    class IObserver : public boost::noncopyable
+    typedef OriginMessage<MessageType_ViewportChanged, IViewport> ViewportChangedMessage;
+
+    IViewport(MessageBroker& broker) :
+      IObservable(broker)
     {
-    public:
-      virtual ~IObserver()
-      {
-      }
-
-      virtual void OnViewportContentChanged(const IViewport& scene) = 0;
-    };
-
+    }
+    
     virtual ~IViewport()
     {
     }
 
     virtual void FitContent() = 0;
 
-    virtual void Register(IObserver& observer) = 0;
-
     virtual void SetStatusBar(IStatusBar& statusBar) = 0;
 
     virtual void SetSize(unsigned int width,
@@ -87,6 +83,10 @@
     virtual void UpdateContent() = 0;
 
     // Should only be called from IWidget
-    virtual void NotifyContentChanged(const IWidget& widget) = 0;
+    virtual void NotifyContentChanged()
+    {
+      ViewportChangedMessage message(*this);
+      EmitMessage(message);
+    }
   };
 }
--- a/Framework/Viewport/WidgetViewport.cpp	Wed Nov 07 16:17:02 2018 +0100
+++ b/Framework/Viewport/WidgetViewport.cpp	Wed Nov 07 20:49:41 2018 +0100
@@ -26,7 +26,8 @@
 
 namespace OrthancStone
 {
-  WidgetViewport::WidgetViewport() :
+  WidgetViewport::WidgetViewport(MessageBroker& broker) :
+    IViewport(broker),
     statusBar_(NULL),
     isMouseOver_(false),
     lastMouseX_(0),
@@ -73,17 +74,16 @@
       centralWidget_->SetStatusBar(*statusBar_);
     }
 
-    backgroundChanged_ = true;
-    observers_.Apply(*this, &IObserver::OnViewportContentChanged);
+    NotifyBackgroundChanged();
 
     return *widget;
   }
 
 
-  void WidgetViewport::NotifyContentChanged(const IWidget& widget)
+  void WidgetViewport::NotifyBackgroundChanged()
   {
     backgroundChanged_ = true;
-    observers_.Apply(*this, &IObserver::OnViewportContentChanged);
+    NotifyContentChanged();
   }
 
 
@@ -97,7 +97,7 @@
       centralWidget_->SetSize(width, height);
     }
 
-    observers_.Apply(*this, &IObserver::OnViewportContentChanged);
+    NotifyBackgroundChanged();
   }
 
 
@@ -155,7 +155,7 @@
       mouseTracker_.reset(NULL);
     }      
 
-    observers_.Apply(*this, &IObserver::OnViewportContentChanged);
+    NotifyContentChanged();
   }
 
 
@@ -165,7 +165,7 @@
     {
       mouseTracker_->MouseUp();
       mouseTracker_.reset(NULL);
-      observers_.Apply(*this, &IObserver::OnViewportContentChanged);
+      NotifyContentChanged();
     }
   }
 
@@ -196,7 +196,7 @@
     if (repaint)
     {
       // The scene must be repainted, notify the observers
-      observers_.Apply(*this, &IObserver::OnViewportContentChanged);
+      NotifyContentChanged();
     }
   }
 
@@ -204,7 +204,7 @@
   void WidgetViewport::MouseEnter()
   {
     isMouseOver_ = true;
-    observers_.Apply(*this, &IObserver::OnViewportContentChanged);
+    NotifyContentChanged();
   }
 
 
@@ -218,7 +218,7 @@
       mouseTracker_.reset(NULL);
     }
 
-    observers_.Apply(*this, &IObserver::OnViewportContentChanged);
+    NotifyContentChanged();
   }
 
 
--- a/Framework/Viewport/WidgetViewport.h	Wed Nov 07 16:17:02 2018 +0100
+++ b/Framework/Viewport/WidgetViewport.h	Wed Nov 07 20:49:41 2018 +0100
@@ -22,7 +22,6 @@
 #pragma once
 
 #include "IViewport.h"
-#include "../Toolbox/ObserversRegistry.h"
 #include "../Widgets/IWidget.h"
 
 #include <memory>
@@ -34,7 +33,6 @@
   private:
     std::auto_ptr<IWidget>        centralWidget_;
     IStatusBar*                   statusBar_;
-    ObserversRegistry<IViewport>  observers_;
     std::auto_ptr<IMouseTracker>  mouseTracker_;
     bool                          isMouseOver_;
     int                           lastMouseX_;
@@ -43,7 +41,7 @@
     bool                          backgroundChanged_;
 
   public:
-    WidgetViewport();
+    WidgetViewport(MessageBroker& broker);
 
     virtual void FitContent();
 
@@ -51,12 +49,7 @@
 
     IWidget& SetCentralWidget(IWidget* widget);  // Takes ownership
 
-    virtual void NotifyContentChanged(const IWidget& widget);
-
-    virtual void Register(IObserver& observer)
-    {
-      observers_.Register(observer);
-    }
+    virtual void NotifyBackgroundChanged();
 
     virtual void SetSize(unsigned int width,
                          unsigned int height);
--- a/Framework/Widgets/EmptyWidget.h	Wed Nov 07 16:17:02 2018 +0100
+++ b/Framework/Widgets/EmptyWidget.h	Wed Nov 07 20:49:41 2018 +0100
@@ -26,9 +26,9 @@
 namespace OrthancStone
 {
   /**
-     * This is a test widget that simply fills its surface with an
-     * uniform color.
-     **/
+   * This is a test widget that simply fills its surface with an
+   * uniform color.
+   **/
   class EmptyWidget : public IWidget
   {
   private:
@@ -54,7 +54,7 @@
     {
     }
 
-    virtual void SetViewport(IViewport& viewport)
+    virtual void SetViewport(WidgetViewport& viewport)
     {
     }
 
--- a/Framework/Widgets/IWidget.h	Wed Nov 07 16:17:02 2018 +0100
+++ b/Framework/Widgets/IWidget.h	Wed Nov 07 20:49:41 2018 +0100
@@ -24,10 +24,11 @@
 #include "../StoneEnumerations.h"
 #include "../Viewport/IMouseTracker.h"
 #include "../Viewport/IStatusBar.h"
-#include "../Viewport/IViewport.h"
 
 namespace OrthancStone
 {
+  class WidgetViewport;  // Forward declaration
+  
   class IWidget : public boost::noncopyable
   {
   public:
@@ -39,7 +40,7 @@
 
     virtual void SetParent(IWidget& parent) = 0;
     
-    virtual void SetViewport(IViewport& viewport) = 0;
+    virtual void SetViewport(WidgetViewport& viewport) = 0;
 
     virtual void SetStatusBar(IStatusBar& statusBar) = 0;
 
--- a/Framework/Widgets/WidgetBase.cpp	Wed Nov 07 16:17:02 2018 +0100
+++ b/Framework/Widgets/WidgetBase.cpp	Wed Nov 07 20:49:41 2018 +0100
@@ -36,7 +36,7 @@
 
     if (viewport_ != NULL)
     {
-      viewport_->NotifyContentChanged(*this);
+      viewport_->NotifyBackgroundChanged();
     }
   }
 
@@ -115,7 +115,7 @@
   }
 
 
-  void WidgetBase::SetViewport(IViewport& viewport)
+  void WidgetBase::SetViewport(WidgetViewport& viewport)
   {
     if (viewport_ != NULL)
     {
--- a/Framework/Widgets/WidgetBase.h	Wed Nov 07 16:17:02 2018 +0100
+++ b/Framework/Widgets/WidgetBase.h	Wed Nov 07 20:49:41 2018 +0100
@@ -24,19 +24,20 @@
 #include "IWidget.h"
 
 #include "../Viewport/CairoContext.h"
+#include "../Viewport/WidgetViewport.h"
 
 namespace OrthancStone
 {
   class WidgetBase : public IWidget
   {
   private:
-    IWidget*     parent_;
-    IViewport*   viewport_;
-    IStatusBar*  statusBar_;
-    bool         backgroundCleared_;
-    uint8_t      backgroundColor_[3];
-    bool         transmitMouseOver_;
-    std::string  name_;
+    IWidget*         parent_;
+    WidgetViewport*  viewport_;
+    IStatusBar*      statusBar_;
+    bool             backgroundCleared_;
+    uint8_t          backgroundColor_[3];
+    bool             transmitMouseOver_;
+    std::string      name_;
 
   protected:
     void ClearBackgroundOrthanc(Orthanc::ImageAccessor& target) const;
@@ -61,7 +62,7 @@
   
     virtual void SetParent(IWidget& parent);
     
-    virtual void SetViewport(IViewport& viewport);
+    virtual void SetViewport(WidgetViewport& viewport);
 
     void SetBackgroundCleared(bool clear)
     {
--- a/Platforms/Wasm/Defaults.cpp	Wed Nov 07 16:17:02 2018 +0100
+++ b/Platforms/Wasm/Defaults.cpp	Wed Nov 07 20:49:41 2018 +0100
@@ -20,7 +20,7 @@
 static OrthancStone::StartupParametersBuilder startupParametersBuilder;
 static OrthancStone::MessageBroker broker;
 
-static OrthancStone::ViewportContentChangedObserver viewportContentChangedObserver_;
+static OrthancStone::ViewportContentChangedObserver viewportContentChangedObserver_(broker);
 static OrthancStone::StatusBar statusBar_;
 
 static std::list<std::shared_ptr<OrthancStone::WidgetViewport>> viewports_;
@@ -44,7 +44,7 @@
   // when WASM needs a C++ viewport
   ViewportHandle EMSCRIPTEN_KEEPALIVE CreateCppViewport() {
     
-    std::shared_ptr<OrthancStone::WidgetViewport> viewport(new OrthancStone::WidgetViewport);
+    std::shared_ptr<OrthancStone::WidgetViewport> viewport(new OrthancStone::WidgetViewport(broker));
     printf("viewport %x\n", (int)viewport.get());
 
     viewports_.push_back(viewport);
@@ -52,7 +52,10 @@
     printf("There are now %d viewports in C++\n", viewports_.size());
 
     viewport->SetStatusBar(statusBar_);
-    viewport->Register(viewportContentChangedObserver_);
+
+    viewport->RegisterObserverCallback(
+      new Callable<ViewportContentChangedObserver, IViewport::ViewportChangedMessage>
+      (viewportContentChangedObserver_, &ViewportContentChangedObserver::OnViewportChanged));
 
     return viewport.get();
   }
--- a/Platforms/Wasm/Defaults.h	Wed Nov 07 16:17:02 2018 +0100
+++ b/Platforms/Wasm/Defaults.h	Wed Nov 07 20:49:41 2018 +0100
@@ -35,15 +35,15 @@
 namespace OrthancStone {
 
   // default Observer to trigger Viewport redraw when something changes in the Viewport
-  class ViewportContentChangedObserver :
-    public OrthancStone::IViewport::IObserver
+  class ViewportContentChangedObserver : public IObserver
   {
   private:
     // Flag to avoid flooding JavaScript with redundant Redraw requests
     bool isScheduled_; 
 
   public:
-    ViewportContentChangedObserver() :
+    ViewportContentChangedObserver(MessageBroker& broker) :
+      IObserver(broker),
       isScheduled_(false)
     {
     }
@@ -53,11 +53,11 @@
       isScheduled_ = false;
     }
 
-    virtual void OnViewportContentChanged(const OrthancStone::IViewport &viewport)
+    void OnViewportChanged(const IViewport::ViewportChangedMessage& message)
     {
       if (!isScheduled_)
       {
-        ScheduleWebViewportRedrawFromCpp((ViewportHandle)&viewport);  // loosing constness when transmitted to Web
+        ScheduleWebViewportRedrawFromCpp((ViewportHandle)&message.GetOrigin());  // loosing constness when transmitted to Web
         isScheduled_ = true;
       }
     }
@@ -76,4 +76,4 @@
       printf("%s\n", message.c_str());
     }
   };
-}
\ No newline at end of file
+}