/*=========================================================================

  Copyright (c) Kitware, Inc.
  All rights reserved.
  See Copyright.txt or http://www.kitware.com/VolViewCopyright.htm for details.

     This software is distributed WITHOUT ANY WARRANTY; without even
     the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
     PURPOSE.  See the above copyright notice for more information.

=========================================================================*/
#include "vtkVVDataItemVolume.h"

#include "vtkCornerAnnotation.h"
#include "vtkDataArray.h"
#include "vtkImageData.h"
#include "vtkImageMapToColors.h"
#include "vtkImplicitPlaneWidget.h"
#include "vtkLargeInteger.h"
#include "vtkCollection.h"
#include "vtkMedicalImageProperties.h"
#include "vtkMedicalImageReader2.h"
#include "vtkObjectFactory.h"
#include "vtkPointData.h"
#include "vtkSideAnnotation.h"
#include "vtkTextProperty.h"
#include "vtkVolumeProperty.h"
#include "vtkVolumeMapper.h"
#include "vtkGarbageCollector.h"

#include "vtkKWApplication.h"
#include "vtkKWCroppingRegionsWidget.h"
#include "vtkKWCursorWidget.h"
#include "vtkKWEvent.h"
#include "vtkKWHistogram.h"
#include "vtkKWHistogramSet.h"
#include "vtkKWImageWidget.h"
#include "vtkKWInteractorStyle2DView.h"
#include "vtkKWInternationalization.h"
#include "vtkKWLightboxWidget.h"
#include "vtkKWMessageDialog.h"
#include "vtkKWOpenWizard.h"
#include "vtkKWOpenFileProperties.h"
#include "vtkKWOrientationWidget.h"
#include "vtkKWProbeImageWidget.h"
#include "vtkKWProcessStatistics.h"
#include "vtkKWProgressCommand.h"
#include "vtkKWRadioButton.h"
#include "vtkKWScale.h"
#include "vtkKWScaleWithEntry.h"
#include "vtkKWToolbar.h"
#include "vtkKWVolumePropertyPresetSelector.h"
#include "vtkKWVolumePropertyHelper.h"
#include "vtkKWVolumeWidget.h"
#include "vtkKWOpenFileProperties.h"
#include "vtkKWApplicationPro.h"

#include "vtkVVSelectionFrame.h"
#include "vtkVVSelectionFrameLayoutManager.h"
#include "vtkVVWindow.h"
#include "vtkVVDisplayInterface.h"
#include "vtkVVFileInstance.h"
#include "vtkVVLODDataItemVolumeHelper.h"
#include "vtkVVDataItemVolumeContourCollection.h"

#if VTK_MAJOR_VERSION > 5 || (VTK_MAJOR_VERSION == 5 && VTK_MINOR_VERSION > 0)
#include "vtkAbstractWidget.h"
#endif

#include <vtksys/stl/string>
#include <vtksys/stl/vector>
#include <vtksys/ios/sstream>
#include <vtksys/SystemTools.hxx>

#ifdef KWCommonPro_USE_XML_RW
#include "XML/vtkXMLVVDataItemVolumeReader.h"
#include "XML/vtkXMLVVDataItemVolumeWriter.h"
#include "XML/vtkXMLObjectWriter.h"
#include "XML/vtkXMLObjectReader.h"
#endif
vtkKWGetXMLReaderWriterObjectsImplementationMacro(vtkVVDataItemVolume, vtkXMLVVDataItemVolumeReader, vtkXMLVVDataItemVolumeWriter);

#define VTK_VV_DIV_SEP " : "

//---------------------------------------------------------------------------
vtkStandardNewMacro(vtkVVDataItemVolume);
vtkCxxRevisionMacro(vtkVVDataItemVolume, "$Revision: 1.118 $");

//----------------------------------------------------------------------------
class vtkVVDataItemVolumeInternals
{
public:

  // Render Widgets

  typedef vtksys_stl::vector<vtkKWRenderWidgetPro*> RenderWidgetPoolType;
  typedef vtksys_stl::vector<vtkKWRenderWidgetPro*>::iterator RenderWidgetPoolIterator;
  typedef vtksys_stl::vector<vtkKWRenderWidgetPro*>::reverse_iterator RenderWidgetPoolReverseIterator;
  RenderWidgetPoolType RenderWidgetPool;
};

//----------------------------------------------------------------------------
vtkVVDataItemVolume::vtkVVDataItemVolume()
{ 
  this->ImageData = NULL;
  this->UndoRedoImageData = NULL;
  this->UndoRedoPluginName = 0;
  this->UndoRedoImageDataType = vtkVVDataItemVolume::Nothing;

  this->VolumeProperty = vtkVolumeProperty::New();	
  this->VolumeProperty->SetInterpolationTypeToLinear();

  this->MedicalImageProperties = NULL;
  this->Internals = new vtkVVDataItemVolumeInternals;
  
  this->HistogramSet = vtkKWHistogramSet::New();

  int i;
  for (i = 0; i < VTK_MAX_VRCOMP; i++)
    {
    this->ScalarUnits[i] = NULL;
    }

  this->DisplayMode = 
    vtkVVDataItemVolume::ModeVolume | 
    vtkVVDataItemVolume::ModeLightBox | 
    vtkVVDataItemVolume::ModeAxial | 
    vtkVVDataItemVolume::ModeCoronal | 
    vtkVVDataItemVolume::ModeSagittal;

  this->LODHelper = NULL;

  // Contours
  this->Contours  = vtkVVDataItemVolumeContourCollection::New();
  this->Contours->SetDataItemVolume(this);
}
  
//----------------------------------------------------------------------------
vtkVVDataItemVolume::~vtkVVDataItemVolume()
{
  this->SetImageData(NULL);
  this->SetUndoRedoImageData(NULL);
  this->SetVolumeProperty(NULL);
  this->SetMedicalImageProperties(NULL);

  if (this->HistogramSet)
    {
    this->HistogramSet->Delete();
    this->HistogramSet = NULL;
    }

  int i;
  for (i = 0; i < VTK_MAX_VRCOMP; i++)
    {
    this->SetScalarUnits(i, NULL);
    }

  if (this->LODHelper)
    {
    this->LODHelper->Delete();
    }

  if (this->Contours)
    {
    this->Contours->SetDataItemVolume(NULL);
    this->Contours->Delete();
    }
  delete this->Internals;
}

//----------------------------------------------------------------------------
void vtkVVDataItemVolume::ReleaseData()
{
  this->SetMedicalImageProperties(NULL);
  this->SetImageData(NULL);
  this->SetUndoRedoImageData(NULL);
  this->Internals->RenderWidgetPool.clear();
}

//----------------------------------------------------------------------------
void vtkVVDataItemVolume::SetImageData(vtkImageData *arg)
{
  if (this->ImageData == arg)
    {
    return;
    }

  if (this->ImageData)
    {
    this->ImageData->UnRegister(this);
    }
    
  this->ImageData = arg;

  if (this->ImageData)
    {
    this->ImageData->Register(this);
    }

  this->Modified();
}

//----------------------------------------------------------------------------
int vtkVVDataItemVolume::SetUndoRedoImageData(vtkImageData *arg)
{
  if (this->UndoRedoImageData == arg)
    {
    return 0;
    }

  if (this->UndoRedoImageData)
    {
    this->UndoRedoImageData->UnRegister(this);
    }
    
  this->UndoRedoImageData = arg;

  if (this->UndoRedoImageData)
    {
    this->UndoRedoImageData->Register(this);
    
    // this->UndoRedoImageData->Modified(); 
    // FIXME: Check if needed.. I think changing undo data should not trigger 
    // any updates now, only later when the undo data is actually used.
    }

  //  this->Modified(); // FIXME: Verify if this is needed
  
  if (arg == NULL) 
    {
    this->UndoRedoImageDataType = vtkVVDataItemVolume::Nothing;
    this->UndoRedoPluginName = 0;
    }

  return 1;  
}

//----------------------------------------------------------------------------
void vtkVVDataItemVolume::SetUndoRedoImageDataType(unsigned int arg)
{
  if (arg == this->UndoRedoImageDataType)
    {
    return;
    }
  if (arg >= vtkVVDataItemVolume::Nothing)
    {
    this->SetUndoRedoImageDataTypeToNothing();
    this->SetUndoRedoImageData(NULL); 
    }
  else
    {
    this->UndoRedoImageDataType = arg;
    }
  this->Modified();
}

//----------------------------------------------------------------------------
void vtkVVDataItemVolume::SetVolumeProperty(vtkVolumeProperty *arg)
{
  if (this->VolumeProperty == arg)
    {
    return;
    }

  if (this->VolumeProperty)
    {
    this->VolumeProperty->UnRegister(this);
    }
    
  this->VolumeProperty = arg;

  if (this->VolumeProperty)
    {
    this->VolumeProperty->Register(this);
    }

  this->Modified();
}

//----------------------------------------------------------------------------
vtkVolumeProperty* vtkVVDataItemVolume::GetVolumeProperty()
{
  return this->VolumeProperty;
}

//----------------------------------------------------------------------------
const char* vtkVVDataItemVolume::GetDescriptiveName()
{
  if (!this->DescriptiveName)
    {
    vtkMedicalImageProperties *med_prop = this->GetMedicalImageProperties();
    if (!med_prop)
      {
      return this->Superclass::GetDescriptiveName();
      }
    
    vtksys_stl::string desc_name;

    const char *pat_name = med_prop->GetPatientName();
    const char *pat_id   = med_prop->GetPatientID();
    const char *study_id = med_prop->GetStudyID();
    const char *series_nb = med_prop->GetSeriesNumber();
    const char *image_nb = med_prop->GetImageNumber();
    const char *modality = med_prop->GetModality();
    const char *acq_date = med_prop->GetAcquisitionDate();
    const char *acq_time = med_prop->GetAcquisitionTime();

    if (pat_name || pat_id || study_id || series_nb || image_nb)
      {
      if (pat_name && *pat_name)
        {
        desc_name = pat_name;
        }
      if (pat_id && *pat_id)
        {
        if (desc_name.size())
          {
          desc_name += VTK_VV_DIV_SEP;
          }
        desc_name += pat_id;
        }
      if (study_id && *study_id && 
          strcmp(study_id, "NA") && strcmp(study_id, "N/A"))
        {
        if (desc_name.size())
          {
          desc_name += VTK_VV_DIV_SEP;
          }
        desc_name += study_id;
        if (series_nb && *series_nb)
          {
          if (desc_name.size())
            {
            desc_name += VTK_VV_DIV_SEP;
            }
          desc_name += series_nb;
          }
        // Now that we can load a single 2D slice from a series, it is
        // important to put the slice number so that we can load two
        // different slices from the same series.
        if (image_nb && *image_nb)
          {
          if (desc_name.size())
            {
            desc_name += VTK_VV_DIV_SEP;
            }
          desc_name += image_nb;
          }
        }
      else
        {
        if (acq_date && *acq_date && acq_time && *acq_time)
          {
          if (desc_name.size())
            {
            desc_name += VTK_VV_DIV_SEP;
            }
          char locale[200];
          if( vtkMedicalImageProperties::GetDateAsLocale(acq_date, locale) )
            {
            desc_name += locale;
            }
          desc_name += ' ';
          desc_name += acq_time;
          }
        }
      if (modality && *modality)
        {
        if (desc_name.size())
          {
          desc_name += VTK_VV_DIV_SEP;
          }
        desc_name += modality;
        }
      }
    if (!desc_name.size())
      {
      return this->Superclass::GetDescriptiveName();
      }
    if (this->GetName())
      {
      vtksys_stl::string short_data_name = 
        vtksys::SystemTools::GetFilenameName(this->GetName());
      desc_name = vtksys::SystemTools::CropString(short_data_name.c_str(), 28) 
        + VTK_VV_DIV_SEP + desc_name;
      }
    this->SetDescriptiveName(desc_name.c_str());
    }

  return this->DescriptiveName;
}

//----------------------------------------------------------------------------
void vtkVVDataItemVolume::SetMedicalImageProperties(
  vtkMedicalImageProperties *arg)
{
  if (this->MedicalImageProperties == arg)
    {
    return;
    }

  if (this->MedicalImageProperties)
    {
    this->MedicalImageProperties->UnRegister(this);
    }
    
  this->MedicalImageProperties = arg;

  if (this->MedicalImageProperties)
    {
    this->MedicalImageProperties->Register(this);
    }

  this->Modified();
}

//----------------------------------------------------------------------------
const char *vtkVVDataItemVolume::GetScalarUnits(int i)
{
  if (i < 0 || i >= VTK_MAX_VRCOMP)
    {
    return NULL;
    }
  return this->ScalarUnits[i];
}

//----------------------------------------------------------------------------
void vtkVVDataItemVolume::SetScalarUnits(int i, const char *arg)
{
  if (i < 0 || i >= VTK_MAX_VRCOMP ||
      (this->ScalarUnits[i] == NULL && arg == NULL) ||
      (this->ScalarUnits[i] && arg && (!strcmp(this->ScalarUnits[i], arg))))
    { 
    return;
    } 

  if (this->ScalarUnits[i]) 
    { 
    delete [] this->ScalarUnits[i]; 
    } 

  if (arg) 
    { 
    this->ScalarUnits[i] = new char[strlen(arg) + 1]; 
    strcpy(this->ScalarUnits[i], arg); 
    } 
  else 
    { 
    this->ScalarUnits[i] = NULL; 
    } 

  this->Modified(); 
}

//----------------------------------------------------------------------------
int vtkVVDataItemVolume::GetScalarRange(int c, double range[2])
{
  if (this->ImageData)
    {
    vtkDataArray *scalars = this->ImageData->GetPointData()->GetScalars();
    if (scalars && c >= 0 && c < scalars->GetNumberOfComponents())
      {
      scalars->GetRange(range, c);
      return 1;
      }
    }
  return 0;
}

//----------------------------------------------------------------------------
void vtkVVDataItemVolume::GetBounds(double bounds[6])
{
  if (this->ImageData)
    {
    this->ImageData->GetBounds(bounds);
    }
}

//----------------------------------------------------------------------------
vtkKWHistogram* vtkVVDataItemVolume::GetHistogram(int component)
{
  // No set, bye

  if (!this->HistogramSet)
    {
    return NULL;
    }

  // Check if we have an histogram, we are up to date

  vtkDataArray *array = this->ImageData->GetPointData()->GetScalars();
  if (!array)
    {
    return NULL;
    }

  char hist_name[1024];
  if (!this->HistogramSet->ComputeHistogramName(
        array->GetName(), component, NULL, hist_name))
    {
    return NULL;
    }

  vtkKWHistogram *hist = this->HistogramSet->GetHistogramWithName(hist_name);
  if (hist)
    {
    return hist;
    }

  // No histogram, then create one

  hist = this->HistogramSet->AllocateAndAddHistogram(hist_name);
  if (!hist)
    {
    return NULL;
    }

  hist->SetLogMode(1);
  hist->BuildHistogram(array, component);
  hist->SetMaximumNumberOfBins(100);

  return hist;
}

//----------------------------------------------------------------------------
int vtkVVDataItemVolume::CreateRenderWidget(vtkVVWindowBase *win,
                                            vtkKWRenderWidgetPro *rwp)
{
  if (!win || !rwp)
    {
    return 0;
    }

  vtkMedicalImageProperties *med_prop = this->GetMedicalImageProperties();
  
  vtkVVSelectionFrameLayoutManager *mgr = win->GetDataSetWidgetLayoutManager();

  vtkVVSelectionFrame *sel_frame = vtkVVSelectionFrame::SafeDownCast(
    mgr->AllocateWidget());

  vtkKW2DRenderWidget *rw2d = vtkKW2DRenderWidget::SafeDownCast(rwp);
  vtkKWImageWidget *iw = vtkKWImageWidget::SafeDownCast(rwp);
  vtkKWVolumeWidget *vw = vtkKWVolumeWidget::SafeDownCast(rwp);
  vtkKWLightboxWidget *lw = vtkKWLightboxWidget::SafeDownCast(rwp);
  vtkKWProbeImageWidget *ow = vtkKWProbeImageWidget::SafeDownCast(rwp);

  vtkKWVolumeWidget *vw_stored = this->GetVolumeWidget(win);

  vtkVVWindow *vvwin = vtkVVWindow::SafeDownCast(win);

  if (rw2d)
    {
    if (this->GetScope() == vtkVVDataItem::ScopeMedical)
      {
      rw2d->SetSliceTypeToMedical();
      }
    else
      {
      rw2d->SetSliceTypeToDefault();
      }
    }

  // Each widget has a tag and group (the group is the data name itself)

  vtksys_stl::string tag;
  if (vw)
    {
    tag = ks_("View|Volume");
    }
  else if (ow)
    {
    tag = ks_("View|Oblique");
    }
  else if (lw)
    {
    tag = ks_("View|Lightbox");
    }
  else if (rw2d)
    {
    tag = rw2d->GetSliceOrientationAsString();
    }


  vtksys_stl::string title(this->GetDescriptiveName());
  if (!title.size())
    {
    vtksys_stl::string short_data_name = 
      vtksys::SystemTools::GetFilenameName(this->GetName());
    title = vtksys::SystemTools::CropString(short_data_name.c_str(), 25);
    }
  title += VTK_VV_DIV_SEP;
  title += tag.c_str();
  sel_frame->SetTitle(title.c_str());

  sel_frame->SetDataItem(this);
    
  rwp->RenderStateOff();
  rwp->SetRenderModeToDisabled();
  rwp->GetOrientationWidget()->RepositionableOff();
  rwp->GetOrientationWidget()->ResizeableOff();
  if (this->GetScope() == vtkVVDataItem::ScopeMedical)
    {
    rwp->GetOrientationWidget()->SetAnnotationTypeToMedical();
    }
  else
    {
    rwp->GetOrientationWidget()->SetAnnotationTypeToGeneral();
    }
  
  // Our 2D render widget displays the slice scale on the left side, 
  // we need to set its new parent before it is created

  if (rw2d)
    {
    vtkKWScaleWithEntry *scale = rw2d->GetSliceScale();
    scale->SetParent(sel_frame->GetLeftUserFrame());
    scale->SetOrientationToVertical();
    }

  // Add to the manager

  int res = 
    mgr->AddWidgetWithTagAndGroup(sel_frame, tag.c_str(), this->GetName());
  sel_frame->Delete();
  if (!res)
    {
    return 0;
    }

  // This will set rw's parent and Register

  sel_frame->SetRenderWidget(rwp); 

  if (!rwp->IsCreated())
    {
    rwp->Create();
    }

  // Various units

  rwp->SetDistanceUnits(this->GetDistanceUnits());
  int c;
  for (c = 0; c < VTK_MAX_VRCOMP; c++)
    {
    rwp->SetScalarUnits(c, this->GetScalarUnits(c));
    }

  if (vw)
    {
    vtkKWRadioButton *rb = vtkKWRadioButton::SafeDownCast(
      win->GetInteractionMode3DToolbar()->GetWidget("Zoom"));
    if(rb)
      {
      vw->SetInteractionMode(rb->GetVariableValueAsInt());
      }
    else
      {
      vw->SetInteractionModeToRotate();
      }
    vw->SetCropping(1);
    }

  // Configure the slice scale to be vertical and dark

  if (rw2d)
    {
    vtkKWRadioButton *rb = vtkKWRadioButton::SafeDownCast(
      win->GetInteractionMode2DToolbar()->GetWidget("Zoom"));
    if(rb)
      {
      rw2d->SetInteractionMode(rb->GetVariableValueAsInt());
      }
    else
      {
      rw2d->SetInteractionModeToWindowLevel();
      }
    vtkKWScaleWithEntry *scale = rw2d->GetSliceScale();
    scale->RangeVisibilityOff();
    scale->LabelVisibilityOff();
    scale->EntryVisibilityOff();
    scale->GetScale()->SetBackgroundColor(0.2, 0.2, 0.2);
    scale->GetScale()->SetTroughColor(0.1, 0.1, 0.1);
    scale->GetScale()->SetWidth(10);
    }

  if (ow)
    {
    vtkKWInteractorStyle2DView *style = ow->GetInteractorStyle();
    if (style)
      {
      this->AddCallbackCommandObserver(
        style, vtkKWEvent::ResliceChangedEvent);
      this->AddCallbackCommandObserver(
        style, vtkKWEvent::ResliceChangingEvent);
      }
    }
    
  if (iw)
    {
    vtkKWCroppingRegionsWidget *cropping_w = iw->GetCroppingWidget();
    if (vw_stored)
      {
      vtkCollection *mappers = vtkCollection::New();
      vw_stored->GetAllVolumeMappers(mappers);
      cropping_w->SetVolumeMapper(
        vtkVolumeMapper::SafeDownCast(mappers->GetItemAsObject(0)));
      mappers->Delete();
      }
    this->AddCallbackCommandObserver(
      cropping_w, vtkKWEvent::CroppingPlanesPositionChangedEvent);
    this->AddCallbackCommandObserver(
      cropping_w, vtkCommand::StartInteractionEvent);
    this->AddCallbackCommandObserver(
      cropping_w, vtkCommand::EndInteractionEvent);

    iw->SetCursor3DInteractiveState(1);
    this->AddCallbackCommandObserver(
      iw, vtkKWEvent::Cursor3DPositionChangedEvent);

    vtkKWCursorWidget *cursor_w = iw->GetCursor3DWidget();
    this->AddCallbackCommandObserver(
      cursor_w, vtkKWEvent::Cursor3DPositionChangingEvent);
    this->AddCallbackCommandObserver(
      cursor_w, vtkCommand::StartInteractionEvent);
    this->AddCallbackCommandObserver(
      cursor_w, vtkCommand::EndInteractionEvent);

    if (vw_stored)
      {
      iw->SetCursor3DXColor(vw_stored->GetCursor3DXColor());
      iw->SetCursor3DYColor(vw_stored->GetCursor3DYColor());
      iw->SetCursor3DZColor(vw_stored->GetCursor3DZColor());
      }
    }

  rwp->AddBindings();
  rwp->UseContextMenuOn();
  win->AddCallbackCommandObserver(
    rwp, vtkKWEvent::InteractionModeChangedEvent);

  vtkVolumeProperty *volumeprop = this->GetVolumeProperty();
  // the volumeprop's IndependentComponents is set in LoadFromOpenWizard
  rwp->SetIndependentComponents(volumeprop->GetIndependentComponents());

  // To be done *before* setting the Input, which would apply some
  // default preset to the VolumeWidget's transfer functions.
  rwp->SetVolumeProperty(volumeprop);

  rwp->SetInput(this->GetImageData());

  if (vw)
    {
    vw->SetStandardCameraView(vtkKWVolumeWidget::STANDARD_VIEW_MINUS_Y);
    this->AddCallbackCommandObserver(
      vw, vtkKWEvent::ObliqueProbeMovementEvent);
    }

  if (vvwin && med_prop)
    {
    vtkKWVolumePropertyPresetSelector *prop_selector = 
      vvwin->GetDisplayInterface()->GetVolumePropertyPresetSelector();
    if (med_prop->GetModality())
      {
      int id = -1;
      if (!strcmp(med_prop->GetModality(), "CT"))
        {
        id = prop_selector->GetIdOfPresetWithFileName("CT-Muscle.vvt");
        }
      else if (!strcmp(med_prop->GetModality(), "MR"))
        {
        id = prop_selector->GetIdOfPresetWithFileName("MR-Default.vvt");
        }
      if (id != -1)
        {
        vtkKWVolumePropertyHelper::CopyVolumeProperty(
          volumeprop, 
          prop_selector->GetPresetVolumeProperty(id), 
          vtkKWVolumePropertyHelper::CopySkipOpacityUnitDistance
          );
        }
      }
    }

  // Apply the first auto window/level presets

  if (med_prop)
    {
    double w, l;
    for (int idx = 0; idx < med_prop->GetNumberOfWindowLevelPresets(); idx++)
      {
      const char *comment = med_prop->GetNthWindowLevelPresetComment(idx);
      if (comment && !strncmp(comment, "Auto ", 5) &&
          med_prop->GetNthWindowLevelPreset(idx, &w, &l))
        {
        rwp->SetWindowLevel(w, l);
        break;
        }
      }
    }

  // Update the annotations

  this->UpdateRenderWidgetAnnotations(rwp);
  rwp->SetCornerAnnotationVisibility(mgr->GetCornerAnnotationsVisibility());
  if (iw)
    {
    iw->SetScaleBarVisibility(mgr->GetScaleBarsVisibility());
    }

  return 1;
}

//----------------------------------------------------------------------------
int vtkVVDataItemVolume::AddAutoWindowLevelPresets()
{ 
  if (!this->MedicalImageProperties)
    {
    return 0;
    }

  // Add some auto-presets based on the histogram
  // First we need to know how many total count in the histogram

  vtkKWHistogram *histogram = this->GetHistogram(0);
  if (!histogram)
    {
    return 0;
    }
  
  int total = static_cast<int>(histogram->GetTotalOccurence());

  // Create a few variables, comment strings we'll need

  double low_val, high_val, auto_window, auto_level;

  typedef struct
  {
    double low;
    double high;
    const char *comment;
  } entry_type;
  
  // If you change the comment make sure you modify CreateRenderWidget
  // accordingly since we are looking for the first auto-preset by
  // searching for the comment.

  entry_type entries[] =
    {
      { 0.01, 0.99, "Auto 1" }, // from 1% to 99% of the histogram
      { 0.05, 0.95, "Auto 2" }, // from 5% to 95% of the histogram
      { 0.10, 0.90, "Auto 3" }  // from 10% to 90% of the histogram
    };

  size_t nb_entries = 1; // sizeof(entries) / sizeof(entries[0]);
  int nb_presets = 
    this->MedicalImageProperties->GetNumberOfWindowLevelPresets();
  
  for (size_t i = 0; i < nb_entries; i++)
    {
    low_val = 
      histogram->GetValueAtAccumulatedOccurence(entries[i].low * total);
    high_val = 
      histogram->GetValueAtAccumulatedOccurence(entries[i].high * total);
    auto_window = static_cast<int>(high_val - low_val);
    auto_level = static_cast<int>(0.5 * (high_val + low_val));
    if (!this->MedicalImageProperties->HasWindowLevelPreset(
          auto_window, auto_level))
      {
      this->MedicalImageProperties->AddWindowLevelPreset(
        auto_window, auto_level);
      this->MedicalImageProperties->SetNthWindowLevelPresetComment(
        this->MedicalImageProperties->GetNumberOfWindowLevelPresets() - 1,
        entries[i].comment);
      }
    }

  return this->MedicalImageProperties->GetNumberOfWindowLevelPresets() 
    - nb_presets;
}

const char *TruncatePrecision(const char *in)
{
  static vtksys_stl::string out;
  vtksys_ios::stringstream ss;
  ss << in;
  double val;
  if( ss >> val )
    {
    const int precision = 1000;
    if ( val >= (0.5 / precision) )
      {
      val = floor((val * precision) + 0.5) / precision;
      }
    vtksys_ios::ostringstream os;
    os << val;
    out = os.str();
    }
  return out.c_str(); 
}

//----------------------------------------------------------------------------
void vtkVVDataItemVolume::UpdateRenderWidgetAnnotations(
  vtkKWRenderWidgetPro *rwp)
{
  char buffer[256];
  vtkMedicalImageProperties *med_prop = this->GetMedicalImageProperties();

  if (!rwp)
    {
    return;
    }

  vtkKW2DRenderWidget *rw2d = vtkKW2DRenderWidget::SafeDownCast(rwp);
  vtkKWImageWidget *iw = vtkKWImageWidget::SafeDownCast(rwp);
  vtkKWVolumeWidget *vw = vtkKWVolumeWidget::SafeDownCast(rwp);
  vtkKWLightboxWidget *lw = vtkKWLightboxWidget::SafeDownCast(rwp);
  vtkKWProbeImageWidget *ow = vtkKWProbeImageWidget::SafeDownCast(rwp);
  
  rwp->GetCornerAnnotation()->ClearAllTexts();
  rwp->GetCornerAnnotation()->GetTextProperty()->ShadowOn();

  if (rw2d)
    {
    rw2d->GetSideAnnotation()->GetTextProperty()->ShadowOn();
    }

  vtksys_stl::string anno;

  // Top left

  if (vw)
    {
    anno = ks_("View|Volume");
    }
  else if (ow)
    {
    anno = ks_("View|Oblique");
    }
  else if (rw2d)
    {
    if (lw)
      {
      anno = ks_("View|Lightbox");
      anno += ": ";
      }
    anno += rw2d->GetSliceOrientationAsString();
    }

  if (med_prop)
    {
    anno += "\n";
    if (med_prop->GetModality() ||
        med_prop->GetManufacturer() ||
        med_prop->GetManufacturerModelName() ||
        med_prop->GetStationName())
      {
      if (med_prop->GetModality())
        {
        anno += med_prop->GetModality();
        anno += ' ';
        }
      if (med_prop->GetManufacturer())
        {
        anno += med_prop->GetManufacturer();
        anno += ' ';
        }
      if (med_prop->GetManufacturerModelName())
        {
        anno += med_prop->GetManufacturerModelName();
        anno += ' ';
        }
      if (med_prop->GetStationName())
        {
        anno += med_prop->GetStationName();
        anno += ' ';
        }
      }
    anno += "\n";
    anno += ks_("Annotation|Exam");
    anno += ": ";
    if (med_prop->GetStudyID())
      {
      if (anno.size())
        {
        }
      anno += med_prop->GetStudyID();
      }
    anno += "\n";
    anno += ks_("Annotation|Series");
    anno += ": ";
    if (med_prop->GetSeriesNumber())
      {
      anno += med_prop->GetSeriesNumber();
      }
    if (med_prop->GetSeriesDescription())
      {
      // Separate from Series Number if present
      if (med_prop->GetSeriesNumber())
        {
        anno += " ";
        }
      anno += "(";
      anno += med_prop->GetSeriesDescription();
      anno += ")";
      }
    }

  if (rw2d && !ow)
    {
    if (anno.size())
      {
      anno += "\n";
      }
    anno += "<image_and_max>";

    anno += "\n<slice_pos>";
    if (this->DistanceUnits)
      {
      anno += ' ';
      anno += this->DistanceUnits;
      }
    }

  rwp->GetCornerAnnotation()->SetText(2, anno.c_str());

  // Top right

  anno = "";
  if (this->GetName())
    {
    vtksys_stl::string short_data_name = 
      vtksys::SystemTools::GetFilenameName(this->GetName());
    anno += vtksys::SystemTools::CropString(short_data_name.c_str(), 25);
    anno += "\n";
    }
  if (med_prop)
    {
    if (med_prop->GetInstitutionName())
      {
      anno += med_prop->GetInstitutionName();
      }
    anno += "\n";
    const char *pat_name = med_prop->GetPatientName();
    if (pat_name && *pat_name)
      {
      anno += pat_name;
      }
    anno += "\nID: ";
    const char *pat_id = med_prop->GetPatientID();
    if (pat_id && *pat_id)
      {
      anno += pat_id;
      }
    anno += "\n";
    const char *pat_age = med_prop->GetPatientAge();
    const char *pat_sex = med_prop->GetPatientSex();
    const char *pat_birthdate = med_prop->GetPatientBirthDate();
    if ((pat_age && *pat_age) ||
        (pat_sex && *pat_sex) ||
        (pat_birthdate && *pat_birthdate))
      {
      if (pat_age && *pat_age)
        {
        int year, month, week, day;
        if (vtkMedicalImageProperties::GetAgeAsFields(
              pat_age, year, month, week, day))
          {
          if (year != -1)
            {
            sprintf(buffer, ks_("Patient Age|%d year(s)"), year);
            }
          else if (month != -1)
            {
            sprintf(buffer, ks_("Patient Age|%d month(s)"), month);
            }
          else if (week != -1)
            {
            sprintf(buffer, ks_("Patient Age|%d week(s)"), week);
            }
          else if (day != -1)
            {
            sprintf(buffer, ks_("Patient Age|%d day(s)"), day);
            }
          else
            {
            vtkGenericWarningMacro("Problem with parsing Patient Age"); // should not happen
            }
          }
        else
          {
          // Something was written in the Patient Age, but cannot be recognized
          strcpy(buffer, pat_age);
          }
        anno += buffer;
        anno += ' ';
        }
      if (pat_sex && *pat_sex)
        {
        anno += pat_sex;
        anno += ' ';
        }
      if (pat_birthdate && *pat_birthdate)
        {
        char locale[200];
        if( vtkMedicalImageProperties::GetDateAsLocale(pat_birthdate, locale) )
          {
          anno += locale;
          }
        }
      }
    anno += "\n";
    if (med_prop->GetAcquisitionDate())
      {
      const char *acqdate = med_prop->GetAcquisitionDate();
      char locale[200];
      if( vtkMedicalImageProperties::GetDateAsLocale(acqdate, locale) )
        {
        anno += locale;
        }
      if (med_prop->GetAcquisitionTime())
        {
        anno += ' ';
        anno += med_prop->GetAcquisitionTime();
        }
      }
    }
  rwp->GetCornerAnnotation()->SetText(3, anno.c_str());

  // Bottom right

  if (rw2d)
    {
    rwp->GetCornerAnnotation()->SetText(1, "<window_level>");
    }

  // Bottom left

  anno = "";
  if (med_prop)
    {
    if (med_prop->GetModality() && !strcmp(med_prop->GetModality(), "CT"))
      {
      anno += "mA: ";
      if (med_prop->GetXRayTubeCurrent() ||
          med_prop->GetExposure())
        {
        if (med_prop->GetXRayTubeCurrent())
          {
          anno += med_prop->GetXRayTubeCurrent();
          }
        /*
          if (med_prop->GetExposure())
          {
          if (med_prop->GetXRayTubeCurrent())
          {
          anno += " / ";
          }
          anno += med_prop->GetExposure();
          anno += " mAs";
          }
        */
        }
      anno += "\nkVp: ";
      if (med_prop->GetKVP())
        {
        anno += med_prop->GetKVP();
        }
      anno += "\n";
      anno += ks_("Annotation|CT|Thickness|Thick");
      anno += ": ";
      if (med_prop->GetSliceThickness())
        {
        sprintf(buffer, "%g", med_prop->GetSliceThicknessAsDouble());
        anno += buffer;
        if (this->DistanceUnits)
          {
          anno += ' ';
          anno += this->DistanceUnits;
          }
        }
      anno += "\n";
      anno += ks_("Annotation|CT|Kernel");
      anno += ": ";
      if (med_prop->GetConvolutionKernel())
        {
        anno += med_prop->GetConvolutionKernel();
        }
      }
    else if (med_prop->GetModality() && !strcmp(med_prop->GetModality(), "MR"))
      {
      anno += "TR: ";
      if (med_prop->GetRepetitionTime())
        {
        anno += TruncatePrecision(med_prop->GetRepetitionTime());
        }
      anno += "\nTE: ";
      if (med_prop->GetEchoTime())
        {
        anno += TruncatePrecision(med_prop->GetEchoTime());
        }
      anno += "\n";
      anno += ks_("Annotation|MR|Thickness|Thick");
      anno += ": ";
      if (med_prop->GetSliceThickness())
        {
        sprintf(buffer, "%g", med_prop->GetSliceThicknessAsDouble());
        anno += buffer;
        if (this->DistanceUnits)
          {
          anno += ' ';
          anno += this->DistanceUnits;
          }
        }
      }
    }
  if (anno.size())
    {
    anno += "\n";
    }
  if (this->GetApplication())
    {
    anno += this->GetApplication()->GetPrettyName();
    }
  rwp->GetCornerAnnotation()->SetText(0, anno.c_str());

  // If the orientation cannot be trusted, do not show side annotations on
  // the 2DRenderWidgets.

  if (rw2d && 
      med_prop && med_prop->GetModality() &&
      (!strcmp(med_prop->GetModality(), "CR") ||
       !strcmp(med_prop->GetModality(), "MG")))
    {
    rw2d->SupportSideAnnotationOff();
    }
  if (iw)
    {
    iw->SupportScalarBarOff();
    }
}

//----------------------------------------------------------------------------
void vtkVVDataItemVolume::AddDefaultRenderWidgets(vtkVVWindowBase *win)
{
  if (!win)
    {
    return;
    }

  // Set the app, if not already

  if (!this->GetApplication())
    {
    this->SetApplication(win->GetApplication());
    }

  // Save the layout manager resolution first, as it expands its grid
  // automatically when new widgets are added. Restore it later

  vtkVVSelectionFrameLayoutManager *mgr =
    win->GetDataSetWidgetLayoutManager();

  int old_m, old_n;
  mgr->GetResolution(old_m, old_n);
  
  vtksys_stl::string old_sel_changed_cmd;
  if (mgr->GetSelectionChangedCommand())
    {
    old_sel_changed_cmd = mgr->GetSelectionChangedCommand();
    mgr->SetSelectionChangedCommand(NULL, NULL);
    }

  // Call the super, just in case

  this->Superclass::AddDefaultRenderWidgets(win);

  // Allocate the renderwidgets

  int *dims = this->GetImageData()->GetDimensions();
  int is_3d = ((dims[0] > 1) && dims[1] > 1 && dims[2] > 1);

  vtkKWRenderWidget *widget_to_select = NULL;

  // Allocate the volume widget

  if (is_3d && win->GetSupportVolumeWidget() && 
      (this->DisplayMode & vtkVVDataItemVolume::ModeVolume))
    {
    vtkKWVolumeWidget *vw = vtkKWVolumeWidget::New();
    vw->SetHistogramSet(this->HistogramSet);
    if (this->CreateRenderWidget(win, vw))
      {
      this->Internals->RenderWidgetPool.push_back(vw);
      }
    vw->Delete();
    }

  // Create 2D widgets

  // XY - Axial

  if (dims[0] != 1 && dims[1] != 1 && 
      (this->DisplayMode & vtkVVDataItemVolume::ModeAxial))
    {
    vtkKWImageWidget *iw = vtkKWImageWidget::New();
    iw->SetSliceOrientationToXY();
    if (this->CreateRenderWidget(win, iw))
      {
      this->Internals->RenderWidgetPool.push_back(iw);
      widget_to_select = iw; // as convenience
      }
    iw->Delete();
    }

  // XZ - Coronal

  if (dims[0] != 1 && dims[2] != 1 && 
      (this->DisplayMode & vtkVVDataItemVolume::ModeCoronal))
    {
    vtkKWImageWidget *iw = vtkKWImageWidget::New();
    iw->SetSliceOrientationToXZ();
    if (this->CreateRenderWidget(win, iw))
      {
      this->Internals->RenderWidgetPool.push_back(iw);
      }
    iw->Delete();
    }

  // YZ - Sagittal

  if (dims[1] != 1 && dims[2] != 1 && 
      (this->DisplayMode & vtkVVDataItemVolume::ModeSagittal))
    {
    vtkKWImageWidget *iw = vtkKWImageWidget::New();
    iw->SetSliceOrientationToYZ();
    if (this->CreateRenderWidget(win, iw))
      {
      this->Internals->RenderWidgetPool.push_back(iw);
      }
    iw->Delete();
    }

  // LightBox

  if (is_3d && win->GetSupportLightboxWidget() && 
      (this->DisplayMode & vtkVVDataItemVolume::ModeLightBox))
    {
    vtkKWLightboxWidget *lw = vtkKWLightboxWidget::New();
    lw->SetSliceOrientationToXY();
    if (this->CreateRenderWidget(win, lw))
      {
      this->Internals->RenderWidgetPool.push_back(lw);
      }
    lw->Delete();
    }

  // Probe Image

  if (is_3d && win->GetSupportObliqueProbeWidget())
    {
    vtkKWVolumeWidget *vw = this->GetVolumeWidget(win);
    if (vw)
      {
      vtkKWProbeImageWidget *ow = vtkKWProbeImageWidget::New();
      if (this->CreateRenderWidget(win, ow))
        {
        this->Internals->RenderWidgetPool.push_back(ow);
        ow->SetProbeInputAlgorithm(
          vw->GetPlaneWidget()->GetPolyDataAlgorithm());
        ow->SupportSideAnnotationOff();
        }
      ow->Delete();
      }
    }

  // Restore layout manager resolution

  int m, n;
  mgr->GetResolution(m, n);
  if (m * n > old_m * old_n && old_m * old_n)
    {
    mgr->SetResolution(old_m, old_n);
    }

  // Time to show everybody

  mgr->ShowWidgetsWithGroup(this->GetName());

  // As a convenience, select a widget (here, an image widget; selecting
  // a volume widget would trigger too many extra computations (gradient,
  // thumbnail) and would delay the time it would take the application
  // to give control to the user.

  if (widget_to_select && !mgr->GetSelectedWidget())
    {
    vtkKWSelectionFrame *sel_frame = 
      mgr->GetContainingSelectionFrame(widget_to_select);
    mgr->SelectWidget(sel_frame);
    }

  if (old_sel_changed_cmd.size())
    {
    mgr->SetSelectionChangedCommand(NULL, old_sel_changed_cmd.c_str());
    }

  // Monitor any progress events coming from the histograms

  if (this->HistogramSet)
    {
    vtkKWProgressCommand *cb = vtkKWProgressCommand::New();
    cb->SetWindow(vtkKWWindowBase::SafeDownCast(mgr->GetParentTopLevel()));
    cb->SetStartMessage(
      ks_("Progress|Updating histograms"));
    cb->SetRetrieveProgressMethodToCallData();
    this->HistogramSet->AddObserver(vtkCommand::StartEvent, cb);
    this->HistogramSet->AddObserver(vtkCommand::ProgressEvent, cb);
    this->HistogramSet->AddObserver(vtkCommand::EndEvent, cb);
    cb->Delete();
    }

  // Render everybody

  vtkVVDataItemVolumeInternals::RenderWidgetPoolIterator it = 
    this->Internals->RenderWidgetPool.begin();
  vtkVVDataItemVolumeInternals::RenderWidgetPoolIterator end = 
    this->Internals->RenderWidgetPool.end();
  for (; it != end; ++it)
    {
    vtkKWRenderWidgetPro *rwp = *it;
    if (rwp->GetParentTopLevel() == win)
      {
      rwp->RenderStateOn();
      rwp->SetRenderModeToStill();
      rwp->Render();
      rwp->ResetCamera();
      }
    }
}

//----------------------------------------------------------------------------
void vtkVVDataItemVolume::UpdateRenderWidgetsAnnotations()
{
  vtkVVDataItemVolumeInternals::RenderWidgetPoolIterator it = 
    this->Internals->RenderWidgetPool.begin();
  vtkVVDataItemVolumeInternals::RenderWidgetPoolIterator end = 
    this->Internals->RenderWidgetPool.end();
  for (; it != end; ++it)
    {
    this->UpdateRenderWidgetAnnotations(*it);
    }
}

//----------------------------------------------------------------------------
void vtkVVDataItemVolume::RemoveDefaultRenderWidgets(
  vtkVVWindowBase *win)
{
  if (!win)
    {
    return;
    }

  this->Superclass::RemoveDefaultRenderWidgets(win);

  if (this->HistogramSet)
    {
    this->HistogramSet->RemoveObservers(vtkCommand::StartEvent);
    this->HistogramSet->RemoveObservers(vtkCommand::ProgressEvent);
    this->HistogramSet->RemoveObservers(vtkCommand::EndEvent);
    }

  // Remove widgets
  // To avoid circular loops, and the renderwidget to be destroyed before
  // its renderwindow, we have to make sure the renderwidget is closed,
  // released, and destroyed *now*

  int done = 0;

  while (!done)
    {
    done = 1;
    vtkVVDataItemVolumeInternals::RenderWidgetPoolIterator it = 
      this->Internals->RenderWidgetPool.begin();
    vtkVVDataItemVolumeInternals::RenderWidgetPoolIterator end = 
      this->Internals->RenderWidgetPool.end();
    for (; it != end; ++it)
      {
      vtkKWRenderWidgetPro *rwp = vtkKWRenderWidgetPro::SafeDownCast(*it);
      if (rwp && rwp->GetParentTopLevel() == win)
        {
        vtkKWImageWidget *iw = vtkKWImageWidget::SafeDownCast(rwp);
        vtkKWVolumeWidget *vw = vtkKWVolumeWidget::SafeDownCast(rwp);
        vtkKWProbeImageWidget *ow = vtkKWProbeImageWidget::SafeDownCast(rwp);
        win->RemoveCallbackCommandObserver(
          rwp, vtkKWEvent::InteractionModeChangedEvent);
        if (iw)
          {
          vtkKWCroppingRegionsWidget *cropping_w = iw->GetCroppingWidget();
          this->RemoveCallbackCommandObserver(
            cropping_w, vtkKWEvent::CroppingPlanesPositionChangedEvent);
          this->RemoveCallbackCommandObserver(
            cropping_w, vtkCommand::StartInteractionEvent);
          this->RemoveCallbackCommandObserver(
            cropping_w, vtkCommand::EndInteractionEvent);
        
          this->RemoveCallbackCommandObserver(
            iw, vtkKWEvent::Cursor3DPositionChangingEvent);
          vtkKWCursorWidget *cursor_w = iw->GetCursor3DWidget();
          this->RemoveCallbackCommandObserver(
            cursor_w, vtkKWEvent::Cursor3DPositionChangingEvent);
          this->RemoveCallbackCommandObserver(
            cursor_w, vtkCommand::StartInteractionEvent);
          this->RemoveCallbackCommandObserver(
            cursor_w, vtkCommand::EndInteractionEvent);
          }
        if (vw)
          {
          this->RemoveCallbackCommandObserver(
            vw, vtkKWEvent::ObliqueProbeMovementEvent);
          }
        if (ow)
          {
          ow->SetProbeInputAlgorithm(NULL);
          }
        rwp->SetInput(NULL);
        rwp->SetParent(NULL);

        this->Internals->RenderWidgetPool.erase(it);
        done = 0;
        break;
        }
      }
    }

  vtkVVSelectionFrameLayoutManager *mgr = win->GetDataSetWidgetLayoutManager();
  mgr->RemoveAllWidgetsWithGroup(this->GetName());
}

//----------------------------------------------------------------------------
vtkKWLightboxWidget* vtkVVDataItemVolume::GetLightboxWidget(vtkVVWindowBase *win)
{ 
  vtkVVDataItemVolumeInternals::RenderWidgetPoolIterator it = 
    this->Internals->RenderWidgetPool.begin();
  vtkVVDataItemVolumeInternals::RenderWidgetPoolIterator end = 
    this->Internals->RenderWidgetPool.end();
  for (; it != end; ++it)
    {
    vtkKWLightboxWidget *lw = vtkKWLightboxWidget::SafeDownCast(*it);
    if (lw && lw->GetParentTopLevel() == win)
      {
      return lw;
      }
    }
  return NULL;
}

//----------------------------------------------------------------------------
vtkKWVolumeWidget* vtkVVDataItemVolume::GetVolumeWidget(vtkVVWindowBase *win)
{ 
  vtkVVDataItemVolumeInternals::RenderWidgetPoolIterator it = 
    this->Internals->RenderWidgetPool.begin();
  vtkVVDataItemVolumeInternals::RenderWidgetPoolIterator end = 
    this->Internals->RenderWidgetPool.end();
  for (; it != end; ++it)
    {
    vtkKWVolumeWidget *vw = vtkKWVolumeWidget::SafeDownCast(*it);
    if (vw && vw->GetParentTopLevel() == win)
      {
      return vw;
      }
    }
  return NULL;
}

//----------------------------------------------------------------------------
vtkKWProbeImageWidget* vtkVVDataItemVolume::GetObliqueProbeWidget(
  vtkVVWindowBase *win)
{ 
  vtkVVDataItemVolumeInternals::RenderWidgetPoolIterator it = 
    this->Internals->RenderWidgetPool.begin();
  vtkVVDataItemVolumeInternals::RenderWidgetPoolIterator end = 
    this->Internals->RenderWidgetPool.end();
  for (; it != end; ++it)
    {
    vtkKWProbeImageWidget *ow = vtkKWProbeImageWidget::SafeDownCast(*it);
    if (ow && ow->GetParentTopLevel() == win)
      {
      return ow;
      }
    }
  return NULL;
}

//----------------------------------------------------------------------------
vtkKWProbeImageWidget* 
vtkVVDataItemVolume::GetObliqueProbeWidgetUsingInteractorStyle(
  vtkKWInteractorStyle2DView *style)
{ 
  vtkVVDataItemVolumeInternals::RenderWidgetPoolIterator it = 
    this->Internals->RenderWidgetPool.begin();
  vtkVVDataItemVolumeInternals::RenderWidgetPoolIterator end = 
    this->Internals->RenderWidgetPool.end();
  for (; it != end; ++it)
    {
    vtkKWProbeImageWidget *ow = vtkKWProbeImageWidget::SafeDownCast(*it);
    if (ow && ow->GetInteractorStyle() == style)
      {
      return ow;
      }
    }
  return NULL;
}

//----------------------------------------------------------------------------
int vtkVVDataItemVolume::GetNumberOfRenderWidgets()
{
  if (this->Internals)
    {
    return (int)this->Internals->RenderWidgetPool.size();
    }
  return 0;
}

//----------------------------------------------------------------------------
vtkKWRenderWidget* vtkVVDataItemVolume::GetNthRenderWidget(int idx)
{ 
  if (this->Internals && idx >= 0 && idx < this->GetNumberOfRenderWidgets())
    {
    return this->Internals->RenderWidgetPool[idx];
    }
  return NULL;
}

//----------------------------------------------------------------------------
vtkKWRenderWidget* vtkVVDataItemVolume::GetNthRenderWidget(
  vtkVVWindowBase *win, int idx)
{ 
  vtkVVDataItemVolumeInternals::RenderWidgetPoolIterator it = 
    this->Internals->RenderWidgetPool.begin();
  vtkVVDataItemVolumeInternals::RenderWidgetPoolIterator end = 
    this->Internals->RenderWidgetPool.end();
  
  int i = 0;
  for (; it != end; ++it)
    {
    if ((*it) && (*it)->GetParentTopLevel() == win)
      {
      if (i == idx)
        {
        return (*it);
        }
      ++i;
      }
    }
  return NULL;
}

//----------------------------------------------------------------------------
vtkKWImageWidget* 
vtkVVDataItemVolume::GetImageWidgetUsingCroppingRegionsWidget(
  vtkKWCroppingRegionsWidget *cropping_w)
{ 
  vtkVVDataItemVolumeInternals::RenderWidgetPoolIterator it = 
    this->Internals->RenderWidgetPool.begin();
  vtkVVDataItemVolumeInternals::RenderWidgetPoolIterator end = 
    this->Internals->RenderWidgetPool.end();
  for (; it != end; ++it)
    {
    vtkKWImageWidget *iw = vtkKWImageWidget::SafeDownCast(*it);
    if (iw && iw->GetCroppingWidget() == cropping_w)
      {
      return iw;
      }
    }
  return NULL;
}

//----------------------------------------------------------------------------
vtkKWImageWidget* 
vtkVVDataItemVolume::GetImageWidgetUsingCursorWidget(
    vtkKWCursorWidget *cursor_w)
{ 
  vtkVVDataItemVolumeInternals::RenderWidgetPoolIterator it = 
    this->Internals->RenderWidgetPool.begin();
  vtkVVDataItemVolumeInternals::RenderWidgetPoolIterator end = 
    this->Internals->RenderWidgetPool.end();
  for (; it != end; ++it)
    {
    vtkKWImageWidget *iw = vtkKWImageWidget::SafeDownCast(*it);
    if (iw && iw->GetCursor3DWidget() == cursor_w)
      {
      return iw;
      }
    }
  return NULL;
}

//----------------------------------------------------------------------------
int vtkVVDataItemVolume::HasRenderWidgetInWindow(vtkVVWindowBase *win)
{
  vtkVVDataItemVolumeInternals::RenderWidgetPoolIterator it = 
    this->Internals->RenderWidgetPool.begin();
  vtkVVDataItemVolumeInternals::RenderWidgetPoolIterator end = 
    this->Internals->RenderWidgetPool.end();
  for (; it != end; ++it)
    {
    if ((*it) && (*it)->GetParentTopLevel() == win)
      {
      return 1;
      }
    }
  return 0;
}
  
//----------------------------------------------------------------------------
void vtkVVDataItemVolume::ResetRenderWidgetsInput()
{
  // Write out the state of each widget to a stream and load that back in
  // This will ensure that we preserve any changes the user made on the 
  // old data set and propagate them to the new dataset.
    
  vtkVVDataItemVolumeInternals::RenderWidgetPoolIterator it;
  vtkVVDataItemVolumeInternals::RenderWidgetPoolIterator end = 
    this->Internals->RenderWidgetPool.end();

  vtksys_stl::vector<vtksys_stl::string> serialized_states;

  // First save the state of all the widgets, *THEN* change the input and
  // restore the state 
  // You can't save, change and restore one by one, because each widget
  // may react to its input's change by resetting Window/Level (for example)
  // and sending events that will be intercepted by the *other* widgets that
  // need to be change later on.

  it = this->Internals->RenderWidgetPool.begin();
  for (; it != end; ++it)
    {
    vtkKWRenderWidgetPro *rwp = (*it);

    vtksys_stl::ostringstream stream;
    vtkXMLObjectWriter *xmlw = rwp->GetNewXMLWriter();
    xmlw->WriteToStream(stream);

    serialized_states.push_back(stream.str());

    xmlw->Delete();
    }

  vtksys_stl::vector<vtksys_stl::string>::iterator states_it = 
    serialized_states.begin();
  it = this->Internals->RenderWidgetPool.begin();
  for (; it != end; ++it, ++states_it)
    {
    vtkKWRenderWidgetPro *rwp = (*it);

    rwp->SetInput(this->GetImageData());
    rwp->UpdateAccordingToInput();
    
    vtkXMLObjectReader *xmlr = rwp->GetNewXMLReader();
    xmlr->ParseString((*states_it).c_str());

    rwp->Render();

    xmlr->Delete();
    }
}
  
//----------------------------------------------------------------------------
void vtkVVDataItemVolume::SetCroppingPlanes(
  vtkVVWindowBase *win,
  double p0, double p1, double p2, double p3, double p4, double p5)
{
  vtkVVDataItemVolumeInternals::RenderWidgetPoolIterator it = 
    this->Internals->RenderWidgetPool.begin();
  vtkVVDataItemVolumeInternals::RenderWidgetPoolIterator end = 
    this->Internals->RenderWidgetPool.end();
  for (; it != end; ++it)
    {
    vtkKWImageWidget *iw = vtkKWImageWidget::SafeDownCast(*it);
    vtkKWVolumeWidget *vw = vtkKWVolumeWidget::SafeDownCast(*it);
    vtkKWProbeImageWidget *pw = vtkKWProbeImageWidget::SafeDownCast(*it);
    if (iw && !pw && iw->GetParentTopLevel() == win)
      {
      iw->SetCroppingPlanes(p0, p1, p2, p3, p4, p5);
      }
    else if (vw && vw->GetParentTopLevel() == win)
      {
      vw->SetCroppingPlanes(p0, p1, p2, p3, p4, p5);
      }
    }
}

//----------------------------------------------------------------------------
void vtkVVDataItemVolume::ResetCroppingPlanes(
  vtkVVWindowBase *win)
{
  vtkVVDataItemVolumeInternals::RenderWidgetPoolIterator it = 
    this->Internals->RenderWidgetPool.begin();
  vtkVVDataItemVolumeInternals::RenderWidgetPoolIterator end = 
    this->Internals->RenderWidgetPool.end();
  for (; it != end; ++it)
    {
    vtkKWImageWidget *iw = vtkKWImageWidget::SafeDownCast(*it);
    vtkKWVolumeWidget *vw = vtkKWVolumeWidget::SafeDownCast(*it);
    vtkKWProbeImageWidget *pw = vtkKWProbeImageWidget::SafeDownCast(*it);
    if (iw && !pw && iw->GetParentTopLevel() == win)
      {
      iw->ResetCroppingPlanes();
      }
    else if (vw && vw->GetParentTopLevel() == win)
      {
      vw->ResetCroppingPlanes();
      }
    }
}

//----------------------------------------------------------------------------
void vtkVVDataItemVolume::SetCroppingMode(vtkVVWindowBase *win, int state)
{
  vtkVVDataItemVolumeInternals::RenderWidgetPoolIterator it = 
    this->Internals->RenderWidgetPool.begin();
  vtkVVDataItemVolumeInternals::RenderWidgetPoolIterator end = 
    this->Internals->RenderWidgetPool.end();
  for (; it != end; ++it)
    {
    vtkKWImageWidget *iw = vtkKWImageWidget::SafeDownCast(*it);
    vtkKWProbeImageWidget *pw = vtkKWProbeImageWidget::SafeDownCast(*it);
    if (iw && !pw && iw->GetParentTopLevel() == win)
      {
      iw->SetCroppingRegionsVisibility(state);
      }
    }
}

//----------------------------------------------------------------------------
int vtkVVDataItemVolume::GetCroppingMode(vtkVVWindowBase *win)
{
  vtkVVDataItemVolumeInternals::RenderWidgetPoolIterator it = 
    this->Internals->RenderWidgetPool.begin();
  vtkVVDataItemVolumeInternals::RenderWidgetPoolIterator end = 
    this->Internals->RenderWidgetPool.end();
  for (; it != end; ++it)
    {
    vtkKWImageWidget *iw = vtkKWImageWidget::SafeDownCast(*it);
    vtkKWProbeImageWidget *pw = vtkKWProbeImageWidget::SafeDownCast(*it);
    if (iw && !pw && iw->GetParentTopLevel() == win)
      {
      return iw->GetCroppingRegionsVisibility();
      }
    }
  return 0;
}

//----------------------------------------------------------------------------
void vtkVVDataItemVolume::SetCursorPosition(
  vtkVVWindowBase *win, double x, double y, double z)
{
  vtkVVDataItemVolumeInternals::RenderWidgetPoolIterator it = 
    this->Internals->RenderWidgetPool.begin();
  vtkVVDataItemVolumeInternals::RenderWidgetPoolIterator end = 
    this->Internals->RenderWidgetPool.end();
  for (; it != end; ++it)
    {
    vtkKWImageWidget *iw = vtkKWImageWidget::SafeDownCast(*it);
    vtkKWVolumeWidget *vw = vtkKWVolumeWidget::SafeDownCast(*it);
    if (iw && iw->GetParentTopLevel() == win)
      {
      iw->SetCursor3DPosition(x, y, z);
      }
      else if (vw && vw->GetParentTopLevel() == win)
      {
      vw->SetCursor3DPosition(x, y, z);
      }
    }
}

//----------------------------------------------------------------------------
void vtkVVDataItemVolume::SetCursorVisibility(vtkVVWindowBase *win, int state)
{
  vtkVVDataItemVolumeInternals::RenderWidgetPoolIterator it = 
    this->Internals->RenderWidgetPool.begin();
  vtkVVDataItemVolumeInternals::RenderWidgetPoolIterator end = 
    this->Internals->RenderWidgetPool.end();
  for (; it != end; ++it)
    {
    vtkKWImageWidget *iw = vtkKWImageWidget::SafeDownCast(*it);
    vtkKWProbeImageWidget *pw = vtkKWProbeImageWidget::SafeDownCast(*it);
    vtkKWVolumeWidget *vw = vtkKWVolumeWidget::SafeDownCast(*it);
    if (iw && !pw && iw->GetParentTopLevel() == win)
      {
      iw->SetCursor3DVisibility(state);
      }
    else if (vw && vw->GetParentTopLevel() == win)
      {
      vw->SetCursor3DVisibility(state);
      if (state)
        {
        vw->StartUsingCursor3D();
        }
      else
        {
        vw->StopUsingCursor3D();
        }
      }
    }
}

//----------------------------------------------------------------------------
int vtkVVDataItemVolume::GetCursorVisibility(vtkVVWindowBase *win)
{
  vtkVVDataItemVolumeInternals::RenderWidgetPoolIterator it = 
    this->Internals->RenderWidgetPool.begin();
  vtkVVDataItemVolumeInternals::RenderWidgetPoolIterator end = 
    this->Internals->RenderWidgetPool.end();
  for (; it != end; ++it)
    {
    vtkKWImageWidget *iw = vtkKWImageWidget::SafeDownCast(*it);
    vtkKWVolumeWidget *vw = vtkKWVolumeWidget::SafeDownCast(*it);
    if (iw && iw->GetParentTopLevel() == win)
      {
      return iw->GetCursor3DVisibility();
      }
    else if (vw && vw->GetParentTopLevel() == win)
      {
      return vw->GetCursor3DVisibility();
      }
    }
  return 0;
}

//----------------------------------------------------------------------------
void vtkVVDataItemVolume::SetObliqueProbeVisibility(
  vtkVVWindowBase *win, int state)
{
  vtkKWVolumeWidget *vw = this->GetVolumeWidget(win);
  if (vw)
    {
    vw->SetPlaneWidgetVisibility(state);
    }
}

//----------------------------------------------------------------------------
int vtkVVDataItemVolume::GetObliqueProbeVisibility(vtkVVWindowBase *win)
{
  vtkKWVolumeWidget *vw = this->GetVolumeWidget(win);
  if (vw)
    {
    return vw->GetPlaneWidgetVisibility();
    }
  return 0;
}

//----------------------------------------------------------------------------
int vtkVVDataItemVolume::CheckForMemory(vtkKWOpenFileProperties *open_prop, 
                                        vtkKWApplication *app)
{
  if (!open_prop)
    {
    return 0;
    }
  
  vtkImageData *img = vtkImageData::New();
  open_prop->CopyToImageData(img);
  int res = vtkVVDataItemVolume::CheckForMemory(img, app);
  img->Delete();
  return res;
}

//----------------------------------------------------------------------------
int vtkVVDataItemVolume::InvokeMemoryDialog( vtkKWApplication *app, 
                            const char *message, int icon, int style )
{
  if (!app)
    {
    return 0;
    }

  // Quiet mode for testing of large datasets.
  bool quietMode = false;
  if (vtkKWApplicationPro *appPro = 
        vtkKWApplicationPro::SafeDownCast(app))
    {
    quietMode = appPro->GetTestingMode() ? true : false;
    }

  if (style == vtkKWMessageDialog::StyleYesNo)
    {
    // In quiet mode, silently try to load it, otherwise go through the dialog
    return ((quietMode || 
             vtkKWMessageDialog::PopupYesNo(
                 app, app->GetNthWindow(0), 
                 ks_("Memory Dialog|Title|Check For Memory"),
                 message, icon)) ? 1 : 0);
    }
  else if (style == vtkKWMessageDialog::StyleMessage)
    {

    // In quiet mode, silently fail on error.
    if (quietMode)
      {
      return 0;
      }
  
    vtkKWMessageDialog::PopupMessage(
        app, app->GetNthWindow(0), 
        ks_("Memory Dialog|Title|Check For Memory"),
        message, icon);
    }

  return 0;
}

//----------------------------------------------------------------------------
int vtkVVDataItemVolume::CheckForMemory(vtkImageData *input, 
                                        vtkKWApplication *app)
{
  if (!input)
    {
    return 0;
    }
  
  int *ext = input->GetWholeExtent();
  
  vtkLargeInteger num_voxels;
  
  // First consider the extent

  num_voxels = (ext[1] - ext[0] + 1);
  num_voxels *= (ext[3] - ext[2] + 1); 
  num_voxels *= (ext[5] - ext[4] + 1);
  
  // Then the number of components

  vtkLargeInteger mem_estimate;
  mem_estimate = num_voxels;
  mem_estimate *= input->GetNumberOfScalarComponents();
  mem_estimate *= input->GetScalarSize();
  
  vtkLargeInteger mem_estimate_with_gradients;
  if (!app)
    {
    mem_estimate_with_gradients = 0;
    }
  else
    {
    mem_estimate_with_gradients = mem_estimate;
    mem_estimate_with_gradients += (num_voxels * 3);
    }
  
  // Convert into K

  mem_estimate = mem_estimate / 1024;
  mem_estimate_with_gradients = mem_estimate_with_gradients / 1024;
  
  vtkKWProcessStatistics *pr = vtkKWProcessStatistics::New();

  long available_virtual = pr->GetAvailableVirtualMemory();
  long available_physical = pr->GetAvailablePhysicalMemory();
  long total_virtual = pr->GetTotalVirtualMemory();
  long total_physical = pr->GetTotalPhysicalMemory();
  
  if (total_virtual < total_physical)
    {
    total_virtual = total_physical;
    }

  pr->Delete();

  // If we received bogus results from pr just return
  // and assume everything is OK

  if (available_virtual < 0 || available_physical < 0 || 
      total_virtual < 0 || total_physical < 0)
    {
    return 1;
    }

  int res = 1;

  // The volume itself *has* to fit in contiguous memory
  // The gradients do not need to, they are slice-based

  vtksys_stl::string msg;
  msg += "Trying to load a ";
  char buffer[256];
  sprintf(buffer, "%d x %d x %d", 
          (ext[1] - ext[0] + 1), (ext[3] - ext[2] + 1), (ext[5] - ext[4] + 1));
  msg += buffer;
  msg += " dataset.\n\n";

#ifdef _WIN32
  void *ptr = malloc(mem_estimate.CastToUnsignedLong());
  if (!ptr)
    {
    if (app)
      {
      msg += k_("The data you are trying to process will not fit in "
                "your system memory. That is why this application will not "
                "even attempt to process it. Please close some applications, "
                "increase the amount of swap space, or increase the amount "
                "of memory in the computer.");
      res = vtkVVDataItemVolume::InvokeMemoryDialog( app, msg.c_str(),
        vtkKWMessageDialog::ErrorIcon, vtkKWMessageDialog::StyleMessage);
      }
    }
  free(ptr);
  if (!res)
    {
    return res;
    }
#endif
  
  if (mem_estimate_with_gradients.CastToUnsignedLong() <= 
      .7 * available_physical)
    {
    return 1;
    }
  
  // The full estimate will not fit in physical memory

  if (mem_estimate.CastToUnsignedLong() <= 
      .7 * available_physical && 
      mem_estimate_with_gradients.CastToUnsignedLong() <= 
      .8 * available_virtual)
    {
    msg += k_("The data you are about to process is relatively large "
              "compared to your system memory. Processing such data "
              "may result in system becoming slow when using lighting "
              "or gradient opacity. Are you sure you want to do that?");
    res = vtkVVDataItemVolume::InvokeMemoryDialog( app, msg.c_str(),
        vtkKWMessageDialog::WarningIcon, vtkKWMessageDialog::StyleYesNo);
    }
  else if (mem_estimate.CastToUnsignedLong() < 
           .8 * available_virtual && 
           mem_estimate_with_gradients.CastToUnsignedLong() < 
           .8 * available_virtual)
    {
    msg += k_("The data you are about to process is relatively large "
              "and it will not fit in the physical memory. Processing "
              "such a data may result in system becoming slow during "
              "normal operation. Are you sure you want to do that?");
    res = vtkVVDataItemVolume::InvokeMemoryDialog( app, msg.c_str(),
        vtkKWMessageDialog::WarningIcon, vtkKWMessageDialog::StyleYesNo);
    }
  else if (mem_estimate.CastToUnsignedLong() < 
           .8 * available_virtual && 
           mem_estimate_with_gradients.CastToUnsignedLong() > 
           .8 * available_virtual)
    {
    msg += k_("The data you are about to process is relatively large "
              "and it will not fit in the physical memory. Processing "
              "such  a data may result in system becoming slow during "
              "normal operation. You will also not be able to use "
              "lighting or gradient opacity for 3D data. Are you sure "
              "you want to do that?");
    res = vtkVVDataItemVolume::InvokeMemoryDialog( app, msg.c_str(),
        vtkKWMessageDialog::WarningIcon, vtkKWMessageDialog::StyleYesNo);
    }
  else
    {
    msg += k_("The data you are trying to process will not fit in "
              "your system memory. That is why this application will not "
              "even attempt to process it. Please close some applications, "
              "increase the amount of swap space, or increase the amount "
              "of memory in the computer.");
    res = vtkVVDataItemVolume::InvokeMemoryDialog( app, msg.c_str(),
      vtkKWMessageDialog::ErrorIcon, vtkKWMessageDialog::StyleMessage);
    }

  return res;
}

//----------------------------------------------------------------------------
void vtkVVDataItemVolume::ProcessCallbackCommandEvents(vtkObject *caller,
                                                       unsigned long event,
                                                       void *calldata)
{
  vtkKWCroppingRegionsWidget *cropping_w = 
    vtkKWCroppingRegionsWidget::SafeDownCast(caller);
  vtkKWCursorWidget *cursor_w = 
    vtkKWCursorWidget::SafeDownCast(caller);
  vtkKWVolumeWidget *vw = vtkKWVolumeWidget::SafeDownCast(caller);
  vtkKWImageWidget *iw = vtkKWImageWidget::SafeDownCast(caller);
  vtkKWImageWidget *iw_temp = vtkKWImageWidget::SafeDownCast(caller);
  vtkKWProbeImageWidget *ow = NULL;
  vtkKWInteractorStyle2DView *style = 
    vtkKWInteractorStyle2DView::SafeDownCast(caller);

  float *fargs = (float*)calldata;

  vtkVVDataItemVolumeInternals::RenderWidgetPoolIterator it = 
    this->Internals->RenderWidgetPool.begin();
  vtkVVDataItemVolumeInternals::RenderWidgetPoolIterator end = 
    this->Internals->RenderWidgetPool.end();

  // When interacting with some 3D widgets, make sure the
  // volume widget is in interactive mode

  if (cropping_w || cursor_w)
    {
    iw_temp = cropping_w 
      ? this->GetImageWidgetUsingCroppingRegionsWidget(cropping_w) 
      : this->GetImageWidgetUsingCursorWidget(cursor_w);
    switch (event)
      {
      case vtkCommand::StartInteractionEvent:
      case vtkCommand::EndInteractionEvent:
        for (; it != end; ++it)
          {
          vtkKWVolumeWidget *vw3 = vtkKWVolumeWidget::SafeDownCast(*it);
          if (vw3 && vw3->GetParentTopLevel() == iw_temp->GetParentTopLevel())
            {
            if (event == vtkCommand::StartInteractionEvent)
              {
              vw3->SetRenderModeToInteractive();
              }
            else
              {
              vw3->SetRenderModeToStill();
              vw3->Render();
              }
            }
          }
        break;
      }
    }

  // The cropping widget is sending

  if (cropping_w)
    {
    switch (event)
      {
      // The planes position has changed, propagate to everyone
      // using them

      case vtkKWEvent::CroppingPlanesPositionChangedEvent:
        iw_temp = 
          this->GetImageWidgetUsingCroppingRegionsWidget(cropping_w);
        if (iw_temp)
          {
          this->SetCroppingPlanes(
            vtkVVWindowBase::SafeDownCast(iw_temp->GetParentTopLevel()),
            fargs[0], fargs[1], fargs[2], fargs[3], fargs[4], fargs[5]);
          }
        break;
      }
    }

  // The cursor widget is sending

  if (cursor_w)
    {
    switch (event)
      {
      // The cursor position has changed, propagate to everyone
      // using them

      case vtkKWEvent::Cursor3DPositionChangingEvent:
        iw_temp = 
          this->GetImageWidgetUsingCursorWidget(cursor_w);
        if (iw_temp)
          {
          this->SetCursorPosition(
            vtkVVWindowBase::SafeDownCast(iw_temp->GetParentTopLevel()), 
            fargs[0], fargs[1], fargs[2]);
          }
        break;
      }
    }

  // The image widget is sending

  if (iw)
    {
    switch (event)
      {
      // The cursor position has changed by virtue of changing the
      // slice, propagate to everyone using them

      case vtkKWEvent::Cursor3DPositionChangedEvent:
        this->SetCursorPosition(
          vtkVVWindowBase::SafeDownCast(iw->GetParentTopLevel()), 
          fargs[0], fargs[1], fargs[2]);
        break;
      }
    }
  
  if (style)
    {
    ow = this->GetObliqueProbeWidgetUsingInteractorStyle(style);
    switch (event)
      {
      case vtkKWEvent::ResliceChangingEvent:
        for (; it != end; ++it)
          {
          vtkKWVolumeWidget *vw3 = vtkKWVolumeWidget::SafeDownCast(*it);
          if (vw3 && vw3->GetParentTopLevel() == ow->GetParentTopLevel())
            {
            vw3->GetPlaneWidget()->UpdatePlacement();
            if (vw3->GetPlaneWidgetVisibility())
              {
              vw3->SetRenderModeToInteractive();
              vw3->Render();
              }
            }
          }
        break;
      case vtkKWEvent::ResliceChangedEvent:
        for (; it != end; ++it)
          {
          vtkKWVolumeWidget *vw3 = vtkKWVolumeWidget::SafeDownCast(*it);
          if (vw3 && vw3->GetParentTopLevel() == ow->GetParentTopLevel())
            {
            vw3->GetPlaneWidget()->UpdatePlacement();
            if (vw3->GetPlaneWidgetVisibility())
              {
              vw3->SetRenderModeToStill();
              vw3->Render();
              }
            }
          }
        break;
      }
    }

  // The volume widget is sending

  if (vw)
    {
    switch (event)
      {
      // The oblique probe plane is moving, render the oblique probe widget

      case vtkKWEvent::ObliqueProbeMovementEvent:
        ow = this->GetObliqueProbeWidget(
          vtkVVWindowBase::SafeDownCast(vw->GetParentTopLevel()));
        if (ow)
          {
          ow->Render();
          }
        break;
      }
    }

  this->Superclass::ProcessCallbackCommandEvents(caller, event, calldata);
}

//----------------------------------------------------------------------------
int vtkVVDataItemVolume::LoadFromOpenWizard(
  vtkKWOpenWizard *openwizard, int output_port)
{
  if (!openwizard)
    {
    vtkErrorMacro("Failed loading data, empty wizard!");
    return 0;
    }

  // We do not have the data, load it

  if (openwizard->GetReadyToLoad() == vtkKWOpenWizard::DATA_IS_UNAVAILABLE)
    {
    vtkErrorMacro("Failed loading data, data unavailable!");
    return 0;
    }

  // Check for memory, then load

  if (openwizard->GetReadyToLoad() == vtkKWOpenWizard::DATA_IS_READY_TO_LOAD)
    {
    if (!vtkVVDataItemVolume::CheckForMemory(
          openwizard->GetOpenFileProperties(), 
          this->GetApplication()))
      {
      return 0;
      }

    vtkKWWindowBase *win = vtkKWWindowBase::SafeDownCast(
      openwizard->GetMasterWindow());
    if (win)
      {
      vtksys_stl::string msg("Loading ");
      if (this->GetName())
        {
        msg += this->GetName();
        }
      win->SetStatusText(msg.c_str());
      }
    openwizard->Load(output_port);
    }

  if (openwizard->GetReadyToLoad() != vtkKWOpenWizard::DATA_IS_LOADED)
    {
    vtkErrorMacro("Failed loading data, data not loaded!");
    return 0;
    }

  vtkImageData *input = openwizard->GetOutput(output_port);
  if (!input)
    {
    vtkErrorMacro("Failed loading data, reader output is empty!");
    return 0;
    }

  // Update the name

  bool is_part_of_multiple_output_ports = false;
  vtkAlgorithm *alg = vtkAlgorithm::SafeDownCast(openwizard->GetLastReader());
  if (alg)
    {
    if (!this->GetName() || !strstr(this->GetName(), VTK_VV_DIV_SEP))
      {
      int nb_output_ports = alg->GetNumberOfOutputPorts();
      vtksys_ios::ostringstream os;
      os << (this->GetName() ? this->GetName() : openwizard->GetFileName());
      if (output_port < nb_output_ports && nb_output_ports > 1)
        {
        is_part_of_multiple_output_ports = true;
        os << VTK_VV_DIV_SEP << output_port; // if that changes, up date the test above
        }
      this->SetName(os.str().c_str());
      this->SetDescriptiveName(NULL); // force automatic re-creation
      }
    }
  else
    {
    if (!this->GetName())
      {
      this->SetName(openwizard->GetFileName());
      this->SetDescriptiveName(NULL); // force automatic re-creation
      }
    }

  // Since we support multi-data now, we can not rely on the open wizard
  // to keep the data for us, let's create our own image data, shallow
  // copy from the open wizard's input, and assign it to our data item

  vtkImageData *new_input = vtkImageData::New();
  this->SetImageData(new_input);
  new_input->Delete();
  new_input->ShallowCopy(input);

  // Propagate the current openwizard settings to our data item
  // Again, we can not rely on the openwizard to keep data around for us

  vtkVolumeProperty *volumeprop = this->GetVolumeProperty();
  if (volumeprop)
    {
    volumeprop->SetIndependentComponents(
      openwizard->GetOpenFileProperties()->GetIndependentComponents());
    }
  this->SetDistanceUnits(
    openwizard->GetOpenFileProperties()->GetDistanceUnits());
  switch (openwizard->GetOpenFileProperties()->GetScope())
    {
    case vtkKWOpenFileProperties::ScopeMedical:
      this->SetScopeToMedical();
      break;
    case vtkKWOpenFileProperties::ScopeScientific:
      this->SetScopeToScientific();
      break;
    default:
    case vtkKWOpenFileProperties::ScopeUnknown:
      this->SetScopeToUnknown();
      break;
    }
  for (int c = 0; c < VTK_MAX_VRCOMP; c++)
    {
    this->SetScalarUnits(
      c, openwizard->GetOpenFileProperties()->GetScalarUnits(c));
    }

  // Copy the medical properties

  vtkMedicalImageReader2 *med_reader = 
    vtkMedicalImageReader2::SafeDownCast(openwizard->GetLastReader());
  if (med_reader)
    {
    if (!this->MedicalImageProperties)
      {
      this->MedicalImageProperties = vtkMedicalImageProperties::New();
      }
    this->GetMedicalImageProperties()->DeepCopy(
      med_reader->GetMedicalImageProperties());
    this->SetDescriptiveName(NULL); // force automatic re-creation
    }
 
  // Add some auto window/level presets

  this->AddAutoWindowLevelPresets();

  // We do not want to duplicate too much data, let's release the wizard data

  openwizard->Release(output_port);

  return 1;
}

//----------------------------------------------------------------------------
vtkVVLODDataItemVolumeHelper * vtkVVDataItemVolume::GetLODHelper()
{
  if (!this->LODHelper)
    {
    this->LODHelper = vtkVVLODDataItemVolumeHelper::New();
    this->LODHelper->SetDataItemVolume(this);
    }
  return this->LODHelper;
}

//----------------------------------------------------------------------
void vtkVVDataItemVolume::Register( vtkObjectBase *o )
{
  this->RegisterInternal(o, 1);
}

//----------------------------------------------------------------------
void vtkVVDataItemVolume::UnRegister( vtkObjectBase *o )
{
  this->UnRegisterInternal(o, 1);
}

//----------------------------------------------------------------------
void vtkVVDataItemVolume::ReportReferences(vtkGarbageCollector* collector)
{
  this->Superclass::ReportReferences(collector);
  vtkGarbageCollectorReport(collector, this->Contours, "Contours");    
}

//----------------------------------------------------------------------------
void vtkVVDataItemVolume::PrintSelf(ostream& os, vtkIndent indent)
{
  this->Superclass::PrintSelf(os, indent);
  
  os << indent << "HistogramSet: " << this->HistogramSet << endl;
  os << indent << "DistanceUnits: " << (this->DistanceUnits ? this->DistanceUnits : "(NULL)") << endl;

  os << indent << "ImageData: ";
  if (this->ImageData)
    {
    int *dims = this->ImageData->GetDimensions();
    os << this->ImageData 
       << " (" << dims[0] << "x" << dims[1] << "x" << dims[2] << ")" << endl;
    }
  else
    {
    os << "(NULL)" << endl;
    }

  os << indent << "UndoRedoImageData: ";
  if (this->UndoRedoImageData)
    {
    os << this->UndoRedoImageData << endl;
    }
  else
    {
    os << "(NULL)" << endl;
    }

  os << indent << "UndoRedoImageDataType: " << this->UndoRedoImageDataType << endl;

  if (this->UndoRedoPluginName)
    {
    os << indent << this->UndoRedoPluginName << endl;
    }
  else
    {
    os << indent << "(NULL)" << endl;
    }

  os << indent << "VolumeProperty: ";
  if (this->VolumeProperty)
    {
    os << this->VolumeProperty << endl;
    }
  else
    {
    os << "(NULL)" << endl;
    }

  os << indent << "MedicalImageProperties: ";
  if (this->MedicalImageProperties)
    {
    os << this->MedicalImageProperties << endl;
    }
  else
    {
    os << "(NULL)" << endl;
    }
}
