From fb61da809df70cffc9439fdb93dbc8d3093a0b01 Mon Sep 17 00:00:00 2001
From: Andreas Schenk <andreas_schenk@hms.harvard.edu>
Date: Fri, 27 May 2011 16:04:39 -0400
Subject: [PATCH] bugfixes and optimizations for data viewer. Fixes BZDNG-252

---
 .../code_fragments/img/create_split_image.py  |   4 +-
 examples/code_fragments/img/fft_li.py         |   3 +-
 examples/code_fragments/img/modulate_image.py |   4 +-
 examples/code_fragments/img/spoke_pattern.py  |   4 +-
 .../code_fragments/img/view_phase_diff.py     |   4 +-
 modules/gui/src/data_viewer/data_viewer.cc    |   4 +-
 modules/gui/src/data_viewer/data_viewer.hh    |   7 +-
 .../src/data_viewer/data_viewer_panel_base.cc | 280 +++++++-----------
 .../src/data_viewer/data_viewer_panel_base.hh |  14 +-
 modules/gui/src/data_viewer/fft_panel.cc      |   8 +-
 modules/gui/src/file_loader.cc                |   3 +-
 modules/gui/src/gosty_app.cc                  |  14 +-
 modules/gui/src/scene_selection.cc            |   5 +-
 13 files changed, 145 insertions(+), 209 deletions(-)

diff --git a/examples/code_fragments/img/create_split_image.py b/examples/code_fragments/img/create_split_image.py
index 1da640423..6003847e6 100644
--- a/examples/code_fragments/img/create_split_image.py
+++ b/examples/code_fragments/img/create_split_image.py
@@ -41,6 +41,4 @@ else:
   imagelist=io.LoadImageList(sys.argv[1:-1])
 
 result=CreateSplitImage(imagelist)
-v_result=gui.CreateDataViewer(result)
-main_area=gui.GostyApp.Instance().perspective.main_area
-main_area.AddWidget("Split Image", v_result)
+v_result=gui.CreateDataViewer(result,"Split Image")
diff --git a/examples/code_fragments/img/fft_li.py b/examples/code_fragments/img/fft_li.py
index 73681183e..c7d241fd1 100644
--- a/examples/code_fragments/img/fft_li.py
+++ b/examples/code_fragments/img/fft_li.py
@@ -10,7 +10,6 @@ main_area=gui.GostyApp.Instance().perspective.main_area
 for im in images:
    im.CenterSpatialOrigin()
    im.ApplyIP(img.alg.DFT()) 
-   v=gui.CreateDataViewer(im)
+   v=gui.CreateDataViewer(im,"Image")
    viewers.append(v)
-   main_area.AddWidget('Image' + str(i), v)
    i+=1
\ No newline at end of file
diff --git a/examples/code_fragments/img/modulate_image.py b/examples/code_fragments/img/modulate_image.py
index bed29c565..1222f9b16 100644
--- a/examples/code_fragments/img/modulate_image.py
+++ b/examples/code_fragments/img/modulate_image.py
@@ -20,6 +20,4 @@ im3 = img.CreateImage(img.Size(400,400))
 im3.Paste(im)
 im3.Paste(im2)
 
-v=gui.CreateDataViewer(im3)
-main_area=gui.GostyApp.Instance().perspective.main_area
-main_area.AddWidget("Modulated Image", v)
\ No newline at end of file
+v=gui.CreateDataViewer(im3,"Modulated Image")
diff --git a/examples/code_fragments/img/spoke_pattern.py b/examples/code_fragments/img/spoke_pattern.py
index c0a43b001..b4c6dcb9d 100644
--- a/examples/code_fragments/img/spoke_pattern.py
+++ b/examples/code_fragments/img/spoke_pattern.py
@@ -40,6 +40,4 @@ filter=ost.img.alg.GaussianLowPassFilter(threshold)
 image.ApplyIP(filter)
 
 # Viewer is launched to show the result
-v=gui.CreateDataViewer(image)
-main_area=gui.GostyApp.Instance().perspective.main_area
-main_area.AddWidget("Modulated Image", v)
\ No newline at end of file
+v=gui.CreateDataViewer(image,"Modulated Image")
diff --git a/examples/code_fragments/img/view_phase_diff.py b/examples/code_fragments/img/view_phase_diff.py
index 4a395a50b..9a0b6af78 100644
--- a/examples/code_fragments/img/view_phase_diff.py
+++ b/examples/code_fragments/img/view_phase_diff.py
@@ -21,6 +21,4 @@ for pixel in ex_it:
   phase2=img.Phase(image2.GetComplex(pixel))
   phase_diff=phase1-phase2
   diff_image.SetReal(pixel,180.0*float(phase_diff)/math.pi)
-v=gui.CreateDataViewer(diff_image)
-main_area=gui.GostyApp.Instance().perspective.main_area
-main_area.AddWidget("Phase difference (in degrees)", v)
\ No newline at end of file
+v=gui.CreateDataViewer(diff_image,"Phase difference (in degrees)")
diff --git a/modules/gui/src/data_viewer/data_viewer.cc b/modules/gui/src/data_viewer/data_viewer.cc
index a9d9d6a57..14e15380a 100644
--- a/modules/gui/src/data_viewer/data_viewer.cc
+++ b/modules/gui/src/data_viewer/data_viewer.cc
@@ -213,7 +213,7 @@ void DataViewer::build(const Data& data)
 
   AddDockWidget(ov_manager_gui_,"Overlays",true,0);
   info_->SetImageInfo(data);
-  AddDockWidget(info_,"Info",true,1);
+  AddDockWidget(info_,"Info",true,0);
   AddDockWidget(argand_,"Argand",false,1);
   AddDockWidget(fft_,"FFT",false,1);
 
@@ -222,7 +222,7 @@ void DataViewer::build(const Data& data)
 
   connect(panel_,SIGNAL(selected(const Extent&)),info_,SLOT(SetSelection(const Extent&)));
   connect(panel_,SIGNAL(deselected()),info_,SLOT(ClearSelection()));
-  connect(panel_,SIGNAL(released()),this,SLOT(close()));
+  connect(panel_,SIGNAL(released()),this,SIGNAL(released()));
   
   if(!parentWidget()) {
     resize(QApplication::desktop()->availableGeometry().adjusted(20,20,-20,-20).size());
diff --git a/modules/gui/src/data_viewer/data_viewer.hh b/modules/gui/src/data_viewer/data_viewer.hh
index 859a8d8b0..3fd08f977 100644
--- a/modules/gui/src/data_viewer/data_viewer.hh
+++ b/modules/gui/src/data_viewer/data_viewer.hh
@@ -101,9 +101,12 @@ public:
   //! event filter for DataViewerPanel
   virtual bool eventFilter(QObject * object, QEvent *event);
 
-  void SetSlab(int slab);
-  
+  void SetSlab(int slab);  
   int GetSlab() const;
+
+signals:
+  void released();
+
 public slots:
   //! update view
   void UpdateView();
diff --git a/modules/gui/src/data_viewer/data_viewer_panel_base.cc b/modules/gui/src/data_viewer/data_viewer_panel_base.cc
index 622fd7ff9..ec113e525 100644
--- a/modules/gui/src/data_viewer/data_viewer_panel_base.cc
+++ b/modules/gui/src/data_viewer/data_viewer_panel_base.cc
@@ -40,11 +40,9 @@
 #include <QPixmapCache>
 #include <ost/message.hh>
 
-
-#define USE_PIXMAP_CACHE
-
 namespace ost { namespace img { namespace gui {
 
+
 DataViewerPanelBase::DataViewerPanelBase(const Data& data,QWidget* parent):
   QWidget(parent),
   DataObserver(data),
@@ -56,10 +54,9 @@ DataViewerPanelBase::DataViewerPanelBase(const Data& data,QWidget* parent):
   normalizer_(ViewerNormalizerPtr(new ViewerNormalizer)),
   image_(new QImage(1,1,QImage::Format_RGB32)),
   pixmap_(new QPixmap(QPixmap::fromImage(*image_))),
+  rubberband_(new QRubberBand(QRubberBand::Rectangle,this)),
   zoom_level_(0),
   update_raster_image_(false),
-  center_x_(0.0),
-  center_y_(0.0),
   offset_x_(0.0),
   offset_y_(0.0),
   clicked_position_(),
@@ -72,12 +69,7 @@ DataViewerPanelBase::DataViewerPanelBase(const Data& data,QWidget* parent):
   mouseposition_(),
   last_x_(0),last_y_(0),
   right_press_x_(0),right_press_y_(0),
-  selection_dragging_(false),
-  selection_rect_(),
   selection_(),
-  selection_pen_(QPen(QColor(255,255,0),1,Qt::DashLine)),
-  selection_brush_(QBrush(Qt::NoBrush)),
-  selection_state_(false),
   selection_mode_(0),
   cmode_(RasterImage::GREY),
   cursors_(),
@@ -90,16 +82,13 @@ DataViewerPanelBase::DataViewerPanelBase(const Data& data,QWidget* parent):
   update_min_max();
   UpdateNormalizer(data_min_,data_max_,1.0,false);
   // placeholder for image
-  Recenter();
   slab_=GetObservedData().GetExtent().GetCenter()[2];
 
   emit slabChanged(slab_);
 
-  setMinimumSize(200,200);
-  on_resize(size().width(),size().height()); // needed for proper initialization
-
+  Recenter();
   setMouseTracking(true);
-  setFocusPolicy(Qt::StrongFocus);
+  setFocusPolicy(Qt::WheelFocus);
   //TODO cursors
   setCursor(cursor_);
   /*
@@ -128,6 +117,8 @@ DataViewerPanelBase::DataViewerPanelBase(const Data& data,QWidget* parent):
   popupmenu_->AppendCheckItem(ID_DISP_PIXEL_VAL,QT("Display pixel values"));
   popupmenu_->AppendCheckItem(ID_SHOW_CLICK_POS,QT("Show last clicked point"));
   */
+  QPixmapCache::setCacheLimit(51200);
+
 }
 
 DataViewerPanelBase::~DataViewerPanelBase()
@@ -140,6 +131,7 @@ void DataViewerPanelBase::SetData(const Data& d)
 {
   SetObservedData(d);
   update_min_max();
+  Recenter();
   UpdateView(true);
 }
 
@@ -238,7 +230,7 @@ void DataViewerPanelBase::OnUpdateMenu(QUpdateUIEvent& e)
 void DataViewerPanelBase::Renormalize() 
 {
   if(!IsDataValid()) return;
-  if(selection_state_) {    
+  if(HasSelection()) {    
     alg::StatMinMax s;
     ImageHandle tmp=CreateImage(GetSelection());
     tmp.Paste(GetObservedData());
@@ -363,88 +355,83 @@ void DataViewerPanelBase::ObserverRelease()
 
 void DataViewerPanelBase::resizeEvent(QResizeEvent* event)
 {
-  on_resize(event->size().width(),event->size().height());
+  if(event->oldSize().width()<0 || event->oldSize().height()<0){
+   Recenter();
+   }else{
+    offset_x_+=static_cast<Real>((event->oldSize().width()-event->size().width()))*0.5*i_zoom_scale_;
+    offset_y_+=static_cast<Real>((event->oldSize().height()-event->size().height()))*0.5*i_zoom_scale_;
+    UpdateView(false);
+  }
 }
 
 void DataViewerPanelBase::paintEvent(QPaintEvent* event)
 {
-  static const int blocksize=256;
+  static const int blocksize=128;
 
-  if(!IsDataValid()) return;
+  if(!IsDataValid() ) return;
   QPainter painter(this);
-  painter.setRenderHints(QPainter::Antialiasing | QPainter::TextAntialiasing,
-                         antialiasing_);
-#ifdef USE_PIXMAP_CACHE
+  painter.setRenderHints(QPainter::Antialiasing | QPainter::TextAntialiasing, antialiasing_);
   if(update_raster_image_) {
     QPixmapCache::clear();
   }
-
-  int uc=1+static_cast<int>(floor(static_cast<Real>(size().width())/static_cast<Real>(blocksize)));
-  int vc=1+static_cast<int>(floor(static_cast<Real>(size().height())/static_cast<Real>(blocksize)));
-  
-  geom::Vec2 cen=zoom_scale_*geom::Vec2(center_x_+offset_x_,
-                                        center_y_+offset_y_)/static_cast<Real>(blocksize);
-  Point i(static_cast<int>(floor(cen[0])),
-          static_cast<int>(floor(cen[1])));
-  Point i0 = i-Point((uc-1)/2-1,(vc-1)/2-1);
+  painter.fillRect(0,0,width(),height(),Qt::black);
+  Size imsize=GetObservedData().GetSize();
+  Point start=GetObservedData().GetExtent().GetStart();
+  geom::Vec2 cen=zoom_scale_*(geom::Vec2(offset_x_,offset_y_)-start.ToVec2())/static_cast<Real>(blocksize);
+  // o = offet of the block grid compared to (0,0)
   Point o(static_cast<int>(static_cast<Real>(blocksize)*(cen[0]-floor(cen[0]))),
           static_cast<int>(static_cast<Real>(blocksize)*(cen[1]-floor(cen[1]))));
-  
+
+  Point i(static_cast<int>(floor(cen[0])),static_cast<int>(floor(cen[1])));
+  int ustart=std::max<int>(0,std::max<int>((event->rect().left()+o[0])/blocksize,-i[0]));
+  int vstart=std::max<int>(0,std::max<int>((event->rect().top()+o[1])/blocksize,-i[1]));
+  int uend=std::min<int>((event->rect().right()+o[0])/blocksize,
+                  static_cast<int>(zoom_scale_*imsize[0]+1)/blocksize-i[0]);
+  int vend=std::min<int>((event->rect().bottom()+o[1])/blocksize,
+                  static_cast<int>(zoom_scale_*imsize[1]+1)/blocksize-i[1]);
   painter.setPen(QColor(255,255,255));
-  for(int v=0;v<=vc;++v) {
-    for(int u=0;u<=uc;++u) {
+  for(int v=vstart;v<=vend;++v) {
+    for(int u=ustart;u<=uend;++u) {
       QString cache_key = QString("%1,%2,%3,%4")
         .arg(reinterpret_cast<unsigned long>(this))
-        .arg(i0[0]+u)
-        .arg(i0[1]+v)
+        .arg(i[0]+u)
+        .arg(i[1]+v)
         .arg(zoom_level_);
       QPixmap pm;
-      if (update_raster_image_ || !QPixmapCache::find(cache_key, pm)) {
+      #ifdef __APPLE__
+      // Partial fix for buggy MDI on OSX. QMdiSubwindows hidden behind the opaque active window
+      // still receive QPaintEvents. Redrawing the hidden windows may deplete the pixmap cache.
+      // hasFocus() avoids the depletion but doesn't avoid the redraw.
+      if (update_raster_image_ || ( !QPixmapCache::find(cache_key, pm) && hasFocus())) {
+      #else
+      if (update_raster_image_ || ( !QPixmapCache::find(cache_key, pm))) {
+      #endif
         RasterImage ri(blocksize,blocksize);
         ri.Fill(GetObservedData(),zoom_level_,
                 geom::Vec3(WinToFracPoint(u*blocksize-o[0],v*blocksize-o[1])),
                 slab_,normalizer_,cmode_,fast_low_mag_,fast_high_mag_);
-        QImage im(blocksize,blocksize,QImage::Format_RGB32);
-        for(int b=0;b<blocksize;++b) {
-          for(int a=0;a<blocksize;++a) {
-            RasterImage::Pixel p=ri.GetPixel(a,b);
-            im.setPixel(a,b,qRgb(p.r,p.g,p.b));
-          }
-        }
+        QImage im(ri.GetDataPtr(),blocksize,blocksize,blocksize*3,QImage::Format_RGB888);
         pm=QPixmap::fromImage(im);
         QPixmapCache::insert(cache_key,pm);
+        painter.drawPixmap(u*blocksize-o[0],v*blocksize-o[1],pm);
+        // next line only used for cache debugging
+        //painter.drawLine(u*blocksize-o[0],v*blocksize-o[1],(u+1)*blocksize-o[0],(v+1)*blocksize-o[1]);
+      }else{
+        painter.drawPixmap(u*blocksize-o[0],v*blocksize-o[1],pm);
       }
-      painter.drawPixmap(u*blocksize-o[0],v*blocksize-o[1],pm);
+      // next line only used for cache debugging
       //painter.drawRect(u*blocksize-o[0],v*blocksize-o[1],blocksize,blocksize);
     }
   }
   update_raster_image_=false;
-#else
-  if(update_raster_image_) {
-    image_->fill(127);
-    extract_ri();
-    update_raster_image_=false;
-    delete pixmap_;
-    pixmap_ = new QPixmap(QPixmap::fromImage(*image_));
-  }
-  // this is an optimization for overlays
-  //painter.drawImage(QPoint(0,0),*image_);
-  painter.drawPixmap(QPoint(0,0),*pixmap_);
-#endif
 
   if(zoom_level_ >= 7 && display_pixel_values_) {
     draw_pixel_values(painter);
   }
 
-  if(zoom_level_<6) {
+  if(zoom_level_<7) {
     draw_extent(painter);
   }
-
-  if(selection_dragging_ || selection_state_) {
-    painter.setPen(selection_pen_);
-    painter.setBrush(selection_brush_);
-    painter.drawRect(selection_rect_);
-  }
 }
 
 void DataViewerPanelBase::keyPressEvent(QKeyEvent * event)
@@ -486,20 +473,14 @@ void DataViewerPanelBase::mousePressEvent(QMouseEvent* event)
 {
   if(!IsDataValid()) return;
   if(event->button() == Qt::LeftButton && event->modifiers()==Qt::NoModifier) {
-    selection_state_=false;
-    selection_dragging_=false;
     selection_=Extent();
-    selection_rect_.setX(event->x());
-    selection_rect_.setY(event->y());
-    selection_rect_.setWidth(0);
-    selection_rect_.setHeight(0);
+    rubberband_->setGeometry(QRect(event->pos(),QSize(0,0)));
+    rubberband_->hide();
     last_x_=event->x();
     last_y_=event->y();
     QPoint cp=FracPointToWin(geom::Vec2(clicked_position_));
     clicked_position_ = geom::Vec3(WinToFracPoint(QPoint(event->x(),event->y())));
     clicked_position_[2]=slab_;
-    UpdateView(false);
-    
     emit clicked(clicked_position_);
 
   }
@@ -516,13 +497,10 @@ void DataViewerPanelBase::mouseReleaseEvent(QMouseEvent* event)
 {
   if(!IsDataValid()) return;
   if(event->button() == Qt::LeftButton && event->modifiers()==Qt::NoModifier) {
-    if(selection_dragging_) {
-      selection_dragging_=false;
-      selection_state_=true;
-      UpdateView(false);
+    if(HasSelection()){
       emit selected(selection_);
     } else {
-      UpdateView(false);
+      rubberband_->hide();
       emit deselected();
     }
   } else if(event->button() & Qt::RightButton) {
@@ -538,18 +516,18 @@ void DataViewerPanelBase::mouseReleaseEvent(QMouseEvent* event)
 void DataViewerPanelBase::mouseMoveEvent(QMouseEvent* event)
 {
   if(!IsDataValid()) return;
-  static Point drag_start;
   if(event->buttons() == Qt::RightButton && event->modifiers()==Qt::NoModifier) {
     // right mouse drag translates pic
     int dx = event->x()-last_x_;
     int dy = event->y()-last_y_;
     move(dx,dy);
   } else if(event->buttons() == Qt::LeftButton  && event->modifiers()==Qt::NoModifier) {
+    static Point drag_start;
     // left mouse drag does selection box
-    if(!selection_dragging_){
-      drag_start=WinToPoint(event->x(),event->y());
+    if(!rubberband_->isVisible()){
+      drag_start=WinToPoint(rubberband_->geometry().topLeft());
+      rubberband_->show();
     }
-    selection_dragging_=true;
     QSize vsize=size();
     Point mouse_pos=WinToPoint(event->x(),event->y());
     Point max_pos=WinToPoint(vsize.width(),vsize.height());
@@ -577,20 +555,9 @@ void DataViewerPanelBase::mouseMoveEvent(QMouseEvent* event)
         selection_.SetEnd(selection_.GetStart()+Point(minsize,minsize)-Point(1,1));
       }
     }
-    QRect new_selection_rect_(PointToWin(selection_.GetStart()),PointToWin(selection_.GetEnd()+Point(1,1)));
-    selection_rect_=new_selection_rect_;
-    UpdateView(false);
+    update_rubberband_from_selection_();
   } else if((event->buttons() == Qt::MidButton) && HasSelection()) {
-    int dx=static_cast<int>(i_zoom_scale_*(event->x()-last_x_));
-    int dy=static_cast<int>(i_zoom_scale_*(event->y()-last_y_));
-    selection_.SetStart(selection_.GetStart()+Point(dx,dy));
-    QRect new_selection_rect_(PointToWin(selection_.GetStart()),PointToWin(selection_.GetEnd()+Point(1,1)));
-    selection_rect_=new_selection_rect_;
-    UpdateView(false);
-    emit selected(Extent(Point(selection_rect_.left(),
-                                           selection_rect_.top()),
-                                Point(selection_rect_.bottom(),
-                                            selection_rect_.right())));
+    update_rubberband_from_selection_();
   }
   last_x_=event->x();
   last_y_=event->y();
@@ -629,16 +596,16 @@ geom::Vec2 DataViewerPanelBase::WinToFracPointCenter(const QPoint& p) const
 
 geom::Vec2 DataViewerPanelBase::WinToFracPoint(int mx, int my) const
 {
-  Real px = i_zoom_scale_*static_cast<Real>(mx)+(center_x_+offset_x_);
-  Real py = i_zoom_scale_*static_cast<Real>(my)+(center_y_+offset_y_);
+  Real px = i_zoom_scale_*static_cast<Real>(mx)+offset_x_;
+  Real py = i_zoom_scale_*static_cast<Real>(my)+offset_y_;
 
   return geom::Vec2(px,py);
 }
 
 geom::Vec2 DataViewerPanelBase::WinToFracPointCenter(int mx, int my) const
 {
-  Real px = i_zoom_scale_*static_cast<Real>(mx)+(center_x_+offset_x_)-0.5;
-  Real py = i_zoom_scale_*static_cast<Real>(my)+(center_y_+offset_y_)-0.5;
+  Real px = i_zoom_scale_*static_cast<Real>(mx)+offset_x_-0.5;
+  Real py = i_zoom_scale_*static_cast<Real>(my)+offset_y_-0.5;
 
   return geom::Vec2(px,py);
 }
@@ -661,14 +628,14 @@ QPoint DataViewerPanelBase::PointToWin(const Point& p) const
 
 QPoint DataViewerPanelBase::FracPointToWin(const geom::Vec2& v) const
 {
-  return QPoint(static_cast<int>(zoom_scale_*(v[0]-(center_x_+offset_x_))),
-		static_cast<int>(zoom_scale_*(v[1]-(center_y_+offset_y_))));
+  return QPoint(static_cast<int>(zoom_scale_*(v[0]-offset_x_)),
+		static_cast<int>(zoom_scale_*(v[1]-offset_y_)));
 }
 
 QPoint DataViewerPanelBase::FracPointToWinCenter(const geom::Vec2& v) const
 {
-  return QPoint(static_cast<int>(zoom_scale_*(v[0]+0.5-(center_x_+offset_x_))),
-		static_cast<int>(zoom_scale_*(v[1]+0.5-(center_y_+offset_y_))));
+  return QPoint(static_cast<int>(zoom_scale_*(v[0]+0.5-offset_x_)),
+		static_cast<int>(zoom_scale_*(v[1]+0.5-offset_y_)));
 }
 
 Extent DataViewerPanelBase::GetSelection() const 
@@ -724,28 +691,28 @@ void DataViewerPanelBase::SetDisplayPixelValues(bool show)
 void DataViewerPanelBase::Recenter()
 {
   if(!IsDataValid()) return;
+  zoom_scale_ = ldexp(1.0,zoom_level_);
+  i_zoom_scale_ = 1.0/zoom_scale_;
+
   if (!HasSelection())  {
     if(GetObservedData().IsSpatial()) {
-    
-      Point cen = GetObservedData().GetSpatialOrigin();
       Size siz = GetObservedData().GetSize();
-
-      center_x_=static_cast<Real>(siz[0])/2.0+static_cast<Real>(cen[0]);
-      center_y_=static_cast<Real>(siz[1])/2.0+static_cast<Real>(cen[1]);
+      Point start=GetObservedData().GetExtent().GetStart();
+      offset_x_ = static_cast<Real>(siz[0])/2.0+start[0]-0.5*i_zoom_scale_*static_cast<Real>(size().width());
+      offset_y_ = static_cast<Real>(siz[1])/2.0+start[1]-0.5*i_zoom_scale_*static_cast<Real>(size().height());
     } else {
-      center_x_=0.0;
-      center_y_=0.0;
+      offset_x_ = -0.5*i_zoom_scale_*static_cast<Real>(size().width());
+      offset_y_ = -0.5*i_zoom_scale_*static_cast<Real>(size().height());
     }
 
-    offset_x_ = -center_x_;
-    offset_y_ = -center_y_;
   
   } else {
     Extent selection = GetSelection();
-    center_x_=static_cast<Real>(selection.GetCenter()[0]);
-    center_y_=static_cast<Real>(selection.GetCenter()[1]);
+    offset_x_=static_cast<Real>(selection.GetCenter()[0])-0.5*i_zoom_scale_*static_cast<Real>(size().width());
+    offset_y_=static_cast<Real>(selection.GetCenter()[1])-0.5*i_zoom_scale_*static_cast<Real>(size().height());
   }
-  zoom(0);// force refresh without changing zoom level
+ update_rubberband_from_selection_();
+ UpdateView(false);
 }
 
 bool DataViewerPanelBase::IsWithin(const QPoint& qp) const
@@ -766,13 +733,6 @@ void DataViewerPanelBase::draw_extent(QPainter& painter)
                          PointToWin(GetObservedData().GetExtent().GetEnd()+Point(1,1))));
 }
 
-void DataViewerPanelBase::set_clippingregion(QPainter& painter)
-{
-  if(!IsDataValid()) return;
-  QPoint p1=PointToWin(GetObservedData().GetExtent().GetStart());
-  QPoint p2=PointToWin(GetObservedData().GetExtent().GetEnd()+Point(1,1));
-  //painter.SetClippingRegion(p1.x,p1.y,p2.x-p1.x,p2.y-p1.y);
-}
 
 
 void DataViewerPanelBase::extract_ri()
@@ -790,7 +750,7 @@ void DataViewerPanelBase::extract_ri()
     RasterImage ri(w,h,image_->bits());
     ri.Fill(GetObservedData(),
             zoom_level_, 
-            geom::Vec3(center_x_+offset_x_, center_y_+offset_y_, 0.0),
+            geom::Vec3(offset_x_, offset_y_, 0.0),
             slab_,
             normalizer_,cmode_,fast_low_mag_,fast_high_mag_,
             std::min(w,std::max(0,tl.x())),std::min(h,std::max(0,tl.y())),
@@ -799,14 +759,14 @@ void DataViewerPanelBase::extract_ri()
     if(zoom_level_==0) {
       img2qt(GetObservedData(),image_,
               zoom_level_,
-              geom::Vec3(center_x_+offset_x_, center_y_+offset_y_, 0.0),
+              geom::Vec3(offset_x_,offset_y_, 0.0),
               slab_,
               normalizer_);
     } else {
       RasterImage ri(size().width(),size().height());
       ri.Fill(GetObservedData(),
               zoom_level_, 
-              geom::Vec3(center_x_+offset_x_, center_y_+offset_y_, 0.0),
+              geom::Vec3(offset_x_, offset_y_, 0.0),
               slab_,
               normalizer_,cmode_,fast_low_mag_,fast_high_mag_);
       
@@ -822,54 +782,43 @@ void DataViewerPanelBase::extract_ri()
 
 void DataViewerPanelBase::zoom(int d)
 {
-  if((d>0 && INT_MAX-d >zoom_level_)|| (d<0 && INT_MIN-d <zoom_level_)){
-    zoom_level_+=d;
-    // maximal zoom = 2^8
-    zoom_level_=std::min(zoom_level_,8);
-    zoom_level_=std::max(zoom_level_,-8);
-    
-  }
+  // maximal zoom = 2^8, therefore zoom_level_ goes from 8 to 8 and delta from -16 to 16
+  int old_zoom_level=zoom_level_;
+  int dl=std::max(d,-16);
+  dl=std::min(dl,16);
+  zoom_level_+=dl;
+  zoom_level_=std::min(zoom_level_,8);
+  zoom_level_=std::max(zoom_level_,-8);
+  Real old_zoom_scale = ldexp(1.0,old_zoom_level);
+  Real old_i_zoom_scale = 1.0/old_zoom_scale;
 
   zoom_scale_ = ldexp(1.0,zoom_level_);
   i_zoom_scale_ = 1.0/zoom_scale_;
 
-  offset_x_ = -0.5*i_zoom_scale_*static_cast<Real>(size().width());
-  offset_y_ = -0.5*i_zoom_scale_*static_cast<Real>(size().height());
+  offset_x_ += static_cast<Real>(size().width())/2.0*(old_i_zoom_scale-i_zoom_scale_);
+  offset_y_ += static_cast<Real>(size().height())/2.0*(old_i_zoom_scale-i_zoom_scale_);
 
-  selection_rect_=QRect(PointToWin(selection_.GetStart()),
-                        PointToWin(selection_.GetEnd()+Point(1,1)));
-#ifdef USE_PIXMAP_CACHE
+  update_rubberband_from_selection_();
   UpdateView(false);
-#else
-  UpdateView(true);
-#endif
   emit zoomed(zoom_level_);
 }
 
 
 void DataViewerPanelBase::move(int dx, int dy)
 {
-  center_x_-=i_zoom_scale_*(Real)dx;
-  center_y_-=i_zoom_scale_*(Real)dy;
-  selection_rect_=QRect(PointToWin(selection_.GetStart()),
-                        PointToWin(selection_.GetEnd()+Point(1,1)));
-#ifdef USE_PIXMAP_CACHE
+  offset_x_-=i_zoom_scale_*(Real)dx;
+  offset_y_-=i_zoom_scale_*(Real)dy;
+  update_rubberband_from_selection_();
   UpdateView(false);
-#else
-  UpdateView(true);
-#endif
 }
 
 void DataViewerPanelBase::MoveTo(const geom::Vec2& p)
 {
-  center_x_=p[0];
-  center_y_=p[1];
+  offset_x_=p[0];
+  offset_y_=p[1];
 
-#ifdef USE_PIXMAP_CACHE
+  update_rubberband_from_selection_();
   UpdateView(false);
-#else
-  UpdateView(true);
-#endif
 }
 
 int DataViewerPanelBase::GetSlab()
@@ -898,16 +847,6 @@ void DataViewerPanelBase::SetColorMode(RasterImage::Mode m)
   UpdateView(true);
 }
 
-void DataViewerPanelBase::on_resize(int width, int height)
-{
-#ifdef USE_PIXMAP_CACHE
-  UpdateView(true);
-#else
-  delete image_;
-  image_ = new QImage(width,height,QImage::Format_RGB32);
-#endif
-  zoom(0);
-}
 
 ImageHandle DataViewerPanelBase::Extract(const Extent& e)
 {
@@ -942,7 +881,7 @@ geom::Vec3 DataViewerPanelBase::GetClickedPosition()
 
 bool DataViewerPanelBase::HasSelection()
 {
-  return selection_state_;
+  return !rubberband_->geometry().isNull() && rubberband_->isVisible();
 }
 
 Real DataViewerPanelBase::GetDataMin() const
@@ -1005,8 +944,12 @@ void DataViewerPanelBase::draw_pixel_values(QPainter& painter)
   Extent visible = Extent(WinToPoint(0, 0), 
                                       WinToPoint(this->size().width(),
                                                  this->size().height()));
-  Point a = visible.GetStart();
-  Point b = visible.GetEnd();
+  if(!HasOverlap(visible,GetObservedData().GetExtent())){
+    return;
+  }
+  Extent overlap=Overlap(visible,GetObservedData().GetExtent());
+  Point a = overlap.GetStart();
+  Point b = overlap.GetEnd();
 
   QFont fnt("Courier",(zoom_level_-6)*2+7);
   painter.setFont(fnt);
@@ -1045,4 +988,9 @@ void DataViewerPanelBase::draw_pixel_values(QPainter& painter)
   }
 }
 
+void DataViewerPanelBase::update_rubberband_from_selection_()
+{
+  rubberband_->setGeometry(QRect(PointToWin(selection_.GetStart()),PointToWin(selection_.GetEnd()+Point(1,1))));
+}
+
 }}}  //ns
diff --git a/modules/gui/src/data_viewer/data_viewer_panel_base.hh b/modules/gui/src/data_viewer/data_viewer_panel_base.hh
index db836a82c..60999a900 100644
--- a/modules/gui/src/data_viewer/data_viewer_panel_base.hh
+++ b/modules/gui/src/data_viewer/data_viewer_panel_base.hh
@@ -86,6 +86,7 @@ public:
 
   //! update view
   void UpdateView(bool update_raster_image=true);
+  void UpdateView(const QRect& rect,bool update_raster_image=true);
 
   //! retrieve ptr to internal normalizer
   ViewerNormalizerPtr GetNormalizer() const;
@@ -148,8 +149,6 @@ public:
   //! re-center with spatial origin in the middle of the window
   void Recenter();
 
-  //! re-center with center of selection (if any) in the middle of the window
-  void CenterSelection();
   
   /////////////////
   // other methods
@@ -202,11 +201,10 @@ private:
   ViewerNormalizerPtr normalizer_;
   QImage* image_;
   QPixmap* pixmap_;
+  QRubberBand* rubberband_;
   QPoint lastmouse_;
   int zoom_level_;
   bool update_raster_image_;
-
-  Real center_x_, center_y_;
   Real offset_x_, offset_y_;
 
   geom::Vec3 clicked_position_; 
@@ -222,12 +220,7 @@ private:
 
   int last_x_,last_y_;
   int right_press_x_,right_press_y_;
-  bool selection_dragging_;
-  QRect selection_rect_;
   Extent selection_;
-  QPen selection_pen_;
-  QBrush selection_brush_;
-  bool selection_state_;
   int selection_mode_;
   
   RasterImage::Mode cmode_;
@@ -243,13 +236,12 @@ private:
   void move(int dx, int dy);
   void slab(int dz);
   void zoom(int d);
-  void on_resize(int w, int h);
   void extract_ri();
 
   void draw_extent(QPainter& p);
   void draw_pixel_values(QPainter& p);
-  void set_clippingregion(QPainter& p);
   void update_min_max();
+  void update_rubberband_from_selection_();
 };
 
 }}}  //ns
diff --git a/modules/gui/src/data_viewer/fft_panel.cc b/modules/gui/src/data_viewer/fft_panel.cc
index b4d042abd..00d3b60d2 100644
--- a/modules/gui/src/data_viewer/fft_panel.cc
+++ b/modules/gui/src/data_viewer/fft_panel.cc
@@ -26,6 +26,7 @@
 #include "fft_panel.hh"
 
 
+#include <QDebug>
 #include <QInputDialog>
 
 
@@ -33,8 +34,8 @@ namespace ost { namespace img { namespace gui {
     
 FFTPanel::FFTPanel(const Data& parent_data, QWidget* parent):
   DataViewerPanelBase(parent_data,parent),
-  size_(200),
-  parent_position_(0,0),
+  size_(std::min<int>(256,std::min<int>(parent_data.GetSize()[0],parent_data.GetSize()[1]))),
+  parent_position_(parent_data.GetExtent().GetCenter()),
   parent_data_(parent_data),
   fft_data_(CreateImage(Extent(Size(size_,size_),Point(0,0)),
                               COMPLEX,HALF_FREQUENCY))
@@ -75,8 +76,7 @@ void FFTPanel::ShowSizeDialog()
     int i = QInputDialog::getInteger(this, "Set FFT size","FFT size", size_, 1, std::min<int>(parent_data_.GetSize()[0],parent_data_.GetSize()[1]), 1, &ok);
   #endif
   if (ok){
-    size_=i;
-    update_fft();
+    SetFFTSize(i);
   }
 }
 
diff --git a/modules/gui/src/file_loader.cc b/modules/gui/src/file_loader.cc
index ae8e728e8..d31c2f0b8 100644
--- a/modules/gui/src/file_loader.cc
+++ b/modules/gui/src/file_loader.cc
@@ -289,8 +289,7 @@ gfx::GfxObjP FileLoader::TryLoadMap(const QString& filename, io::MapIOHandlerPtr
       //FIXME ImageHandle should not be destroyed at the end of method
       //therefore hack with list
       loaded_images_.append(map);
-      ost::img::gui::DataViewer* viewer = GostyApp::Instance()->CreateDataViewer(loaded_images_.last());
-      gui::GostyApp::Instance()->GetPerspective()->GetMainArea()->AddWidget(filename,viewer);
+      GostyApp::Instance()->CreateDataViewer(loaded_images_.last());
       throw io::IOFileAlreadyLoadedException("Loaded in DataViewer");
     }
   }
diff --git a/modules/gui/src/gosty_app.cc b/modules/gui/src/gosty_app.cc
index 9c666652b..ac5ce3105 100644
--- a/modules/gui/src/gosty_app.cc
+++ b/modules/gui/src/gosty_app.cc
@@ -17,8 +17,6 @@
 // 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 //------------------------------------------------------------------------------
 #include "gosty_app.hh"
-#include <iostream>
-
 
 #include <ost/gui/python_shell/python_shell.hh>
 #include <ost/gui/gl_win.hh>
@@ -37,7 +35,7 @@
 
 #include <QApplication>
 #include <QMainWindow>
-
+#include <QMdiSubWindow>
 #include <QMenuBar>
 #include <QDesktopWidget>
 /*
@@ -113,7 +111,15 @@ SequenceViewer* GostyApp::GetSequenceViewer()
 #if OST_IMG_ENABLED
 ost::img::gui::DataViewer* GostyApp::CreateDataViewer(const ost::img::Data& d, const QString& name)
 {
-  return new ost::img::gui::DataViewer(main_,d,name);
+  ost::img::gui::DataViewer* viewer=new ost::img::gui::DataViewer(main_,d,name);
+  QMdiSubWindow* mdi=new QMdiSubWindow(this->GetPerspective()->GetMainArea());
+  mdi->setWindowTitle(name);
+  mdi->setWidget(viewer);
+  viewer->setParent(mdi);
+  this->GetPerspective()->GetMainArea()->addSubWindow(mdi);
+  mdi->showMaximized();
+  connect(viewer,SIGNAL(released()),mdi,SLOT(close()));
+  return viewer; 
 }
 #endif
   
diff --git a/modules/gui/src/scene_selection.cc b/modules/gui/src/scene_selection.cc
index 2ee945096..f0fea54e2 100644
--- a/modules/gui/src/scene_selection.cc
+++ b/modules/gui/src/scene_selection.cc
@@ -137,10 +137,7 @@ void SceneSelection::ViewDensitySlices() {
         // The following is a hack. I need to pass a reference to an ImagHandle
         // that never goes out of scope, so I get a reference from the MapIso using
         // GetMap and pass it to the CreateDataViewer
-        img::gui::DataViewer* dv = GostyApp::Instance()->CreateDataViewer(obj->GetOriginalMap());
-          MainArea* ma = GostyApp::Instance()->GetPerspective()->GetMainArea();
-          ma->AddWidget(QString(obj->GetName().c_str()), dv) ;
-        dv->show();
+        GostyApp::Instance()->CreateDataViewer(obj->GetOriginalMap(),QString(obj->GetName().c_str()));
       }
     }
   }
-- 
GitLab