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

  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 "vtkVVDisplayInterface.h"

#include "vtkCamera.h"
#include "vtkDirectory.h"
#include "vtkImageData.h"
#include "vtkMath.h"
#include "vtkMedicalImageProperties.h"
#include "vtkMutexLock.h"
#include "vtkObjectFactory.h"
#include "vtkPiecewiseFunction.h"
#include "vtkPointData.h"
#include "vtkRenderer.h"
#include "vtkRenderWindow.h"
#include "vtkVolumeProperty.h"
#include "vtkXMLDataElement.h"
#include "vtkXMLUtilities.h"

#include "vtkKWApplication.h"
#include "vtkKWColorTransferFunctionEditor.h"
#include "vtkKWEntry.h"
#include "vtkKWEntryWithLabel.h"
#include "vtkKWEvent.h"
#include "vtkKWFrame.h"
#include "vtkKWFrameWithLabel.h"
#include "vtkKWIcon.h"
#include "vtkKWImageMapToWindowLevelColors.h"
#include "vtkKWImageWidget.h"
#include "vtkKWInteractorStyle2DView.h"
#include "vtkKWInternationalization.h"
#include "vtkKWLabel.h"
#include "vtkKWLightboxWidget.h"
#include "vtkKWLoadSaveDialog.h"
#include "vtkKWMenu.h"
#include "vtkKWMenuButton.h"
#include "vtkKWMenuButtonWithLabel.h"
#include "vtkKWMenuButtonWithSpinButtons.h"
#include "vtkKWMenuButtonWithSpinButtonsWithLabel.h"
#include "vtkKWMessageDialog.h"
#include "vtkKWMultiColumnList.h"
#include "vtkKWMultiColumnListWithScrollbars.h"
#include "vtkKWPiecewiseFunctionEditor.h"
#include "vtkKWPopupButtonWithLabel.h"
#include "vtkKWPushButton.h"
#include "vtkKWVolumeMaterialPropertyWidget.h"
#include "vtkKWVolumePropertyHelper.h"
#include "vtkKWVolumePropertyPresetSelector.h"
#include "vtkKWVolumePropertyWidget.h"
#include "vtkKWVolumeWidget.h"
#include "vtkKWWindowLevelPresetSelector.h"

#include "vtkVVDataItemVolume.h"
#include "vtkVVDataItemPool.h"
#include "vtkVVWindow.h"

#include "vtkKWERepresentativeVolumeImageCreator.h"

#include "XML/vtkXMLKWVolumeWidgetReader.h"
#include "XML/vtkXMLKWVolumeWidgetWriter.h"

#include <vtksys/SystemTools.hxx>
#include <vtksys/stl/vector>
#include <vtksys/stl/map>

#include "time.h"

#include "vtkKWVolViewBuildConfigure.h" // KWVolView_SOURCE_DIR, KWVolView_INSTALL_DATA_DIR, KWVolView_PRESETS_TFUNCS_SUBDIR, KWVolView_PRESETS_WLS_SUBDIR

//----------------------------------------------------------------------------
class vtkVVDisplayInterfaceInternals
{
public:

  vtksys_stl::string UpdateVolumePropertyPresetThumbnailsTimerId;
  vtksys_stl::string UpdateWindowLevelPresetThumbnailsTimerId;
  static const int UpdatePresetThumbnailsDelay = 10;
  
  int Page3DId;
  int Page2DId;

  vtksys_stl::map<vtksys_stl::string,vtksys_stl::string> VolumePropertyPresetSelectorFilters;
  vtksys_stl::map<vtksys_stl::string,vtksys_stl::string> WindowLevelPresetSelectorFilters;
};

//---------------------------------------------------------------------------
vtkStandardNewMacro(vtkVVDisplayInterface);
vtkCxxRevisionMacro(vtkVVDisplayInterface, "$Revision: 1.104 $");

//---------------------------------------------------------------------------
vtkVVDisplayInterface::vtkVVDisplayInterface()
{
  this->Internals = new vtkVVDisplayInterfaceInternals;

  this->ThumbnailCreator = vtkKWERepresentativeVolumeImageCreator::New();
  this->WaitingOnThumbnailCreator = 0;
  
  this->SetName(ks_("Display Panel|Title|Display & Presets"));

  // Display: Window/Level Settings

  this->WindowLevelSettingsFrame = NULL;
  this->WindowLevelEntriesFrame = NULL;
  this->WindowEntry = NULL;
  this->LevelEntry = NULL;
  this->WindowLevelResetButton = NULL;
  this->WindowLevelFunctionEditor = NULL;

  // Display: Volume Appearance Settings

  this->VolumePropertyWidget = NULL;
  this->BlendModeMenuButton = NULL;

  // Presets: Volume Appearance Presets

  this->VolumePropertyPresetFrame = NULL;
  this->VolumePropertyPresetSelector = NULL;

  // Presets: Window/Level Presets

  this->WindowLevelPresetFrame = NULL;
  this->WindowLevelPresetSelector = NULL;

}

//---------------------------------------------------------------------------
vtkVVDisplayInterface::~vtkVVDisplayInterface()
{
  // Display: Window/Level Settings

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

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

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

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

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

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

  // Display: Volume Appearance Settings

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

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

  // Presets: Volume Appearance Presets

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

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

  // Presets: Window/Level Presets

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

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

  if (this->ThumbnailCreator)
    {
    this->ThumbnailCreator->Stop();
    this->ThumbnailCreator->Delete();
    }
  
  // Delete our pool

  delete this->Internals;
}

// --------------------------------------------------------------------------
void vtkVVDisplayInterface::Create()
{
  if (this->IsCreated())
    {
    vtkErrorMacro("The panel is already created.");
    return;
    }

  // Create the superclass instance (and set the application)

  this->Superclass::Create();

  ostrstream tk_cmd;

  int index;

  vtkKWFrame *frame;

  char command[512];
  char buffer[2048];
  
  // --------------------------------------------------------------
  // Add a "Display" page

  this->Internals->Page3DId = this->AddPage(NULL);
  
  // --------------------------------------------------------------
  // Add a "Presets" page

  this->Internals->Page2DId = this->AddPage(NULL);

  // --------------------------------------------------------------
  // Display: Volume Appearance Settings

  if (!this->VolumePropertyWidget)
    {
    this->VolumePropertyWidget = vtkKWVolumePropertyWidget::New();
    }

  this->VolumePropertyWidget->SetParent(this->GetPagesParentWidget());
  this->VolumePropertyWidget->Create();
  this->VolumePropertyWidget->GetEditorFrame()->SetLabelText(
    k_("Color/Opacity Settings"));
  this->VolumePropertyWidget->EnableShadingForAllComponentsOn();
  this->VolumePropertyWidget->InterpolationTypeVisibilityOff();
  this->VolumePropertyWidget->HSVColorSelectorVisibilityOff();
  this->VolumePropertyWidget->GradientOpacityFunctionVisibilityOff();
  this->VolumePropertyWidget->InteractiveApplyButtonVisibilityOff();
  this->VolumePropertyWidget->ScalarOpacityUnitDistanceVisibilityOff();
  this->VolumePropertyWidget->ComponentSelectionVisibilityOn();
  this->VolumePropertyWidget->ComponentWeightsVisibilityOn();
  this->VolumePropertyWidget->MaterialPropertyVisibilityOn();
  this->VolumePropertyWidget->InteractiveApplyModeOn();
  this->VolumePropertyWidget->SetMaterialPropertyPositionToBottomFrame();
  this->VolumePropertyWidget->SetWholeRangeComputationMethodToDataAndFunctionPoints();

  vtkKWPiecewiseFunctionEditor *opacity_editor = 
    this->VolumePropertyWidget->GetScalarOpacityFunctionEditor();
  opacity_editor->SetCanvasHeight(105);
  opacity_editor->SetParameterTicksFormat("%-#6.0f");
  opacity_editor->SetNumberOfParameterTicks(5);
  opacity_editor->LockEndPointsParameterOff();
  opacity_editor->LabelVisibilityOn();
  opacity_editor->ParameterRangeLabelVisibilityOn();
  opacity_editor->ParameterRangeVisibilityOn();
  opacity_editor->WindowLevelModeButtonVisibilityOff();
  opacity_editor->ValueRangeVisibilityOn();
  opacity_editor->MidPointGuidelineVisibilityOff();
  opacity_editor->ParameterTicksVisibilityOn();
  opacity_editor->UserFrameVisibilityOff();
  opacity_editor->SetRangeLabelPositionToDefault();

  vtkKWColorTransferFunctionEditor *color_editor = 
    this->VolumePropertyWidget->GetScalarColorFunctionEditor();
  color_editor->SetLockEndPointsParameter(
    opacity_editor->GetLockEndPointsParameter());
  color_editor->SetRangeLabelPosition(
    opacity_editor->GetRangeLabelPosition());
  color_editor->ColorSpaceOptionMenuVisibilityOff();
  color_editor->UserFrameVisibilityOff();

  // It was very easy to override the scalar unit opacity distance with
  // any of the presets. This has been fixed, but let's make sure we
  // can visually keep track of this value, which has an impact on the
  // rendering quality. Debug mode only.

  if (!this->GetApplication()->GetReleaseMode())
    {
    this->VolumePropertyWidget->ScalarOpacityUnitDistanceVisibilityOn();
    }

  this->AddCallbackCommandObserver(
    this->VolumePropertyWidget, vtkKWEvent::WindowLevelChangedEvent);
  this->AddCallbackCommandObserver(
    this->VolumePropertyWidget, vtkKWEvent::WindowLevelChangingEvent);
  this->AddCallbackCommandObserver(
    this->VolumePropertyWidget, vtkKWEvent::VolumePropertyChangedEvent);
  this->AddCallbackCommandObserver(
    this->VolumePropertyWidget, vtkKWEvent::VolumePropertyChangingEvent);

  //this->VolumePropertyWidget->MergeScalarOpacityAndColorEditors();

  vtkKWPiecewiseFunctionEditor *grad_editor = 
    this->VolumePropertyWidget->GetGradientOpacityFunctionEditor();
  grad_editor->WindowLevelModeOn();
  grad_editor->WindowLevelModeLockEndPointValueOn();

  vtkKWVolumeMaterialPropertyWidget *mat_prop = 
    this->VolumePropertyWidget->GetMaterialPropertyWidget();
  mat_prop->SetPopupPreviewSize(20);
  mat_prop->LightingParametersVisibilityOn();
  mat_prop->GetPopupButton()->LabelVisibilityOff();

  // --------------------------------------------------------------
  // Display: Blend mode : radiobutton set

  if (!this->BlendModeMenuButton)
    {
    this->BlendModeMenuButton = vtkKWMenuButtonWithSpinButtons::New();
    }

  this->BlendModeMenuButton->SetParent(
    this->VolumePropertyWidget->GetBottomFrame());
  this->BlendModeMenuButton->Create();
  this->BlendModeMenuButton->SetBalloonHelpString(
    k_("Select a blending/compositing mode."));

  tk_cmd << "pack " << this->BlendModeMenuButton->GetWidgetName()
         << " -side right -expand y -fill both -padx 2 -pady 2 -anchor ne"
         << " -before " << mat_prop->GetWidgetName() << endl;

  sprintf(command, "BlendModeCallback %d", 
          vtkKWVolumeWidget::BLEND_MODE_MIP);
  index = this->BlendModeMenuButton->GetWidget()->GetMenu()->AddRadioButton(
    ks_("Blend Mode|Maximum Intensity Projection"), this, command);
  this->BlendModeMenuButton->GetWidget()->GetMenu()->SetItemHelpString(
    index, k_("Select a maximum intensity projection method."));
  
  sprintf(command, "BlendModeCallback %d", 
          vtkKWVolumeWidget::BLEND_MODE_COMPOSITE);
  index = this->BlendModeMenuButton->GetWidget()->GetMenu()->AddRadioButton(
    ks_("Blend Mode|Volume Rendering"), this, command);
  this->BlendModeMenuButton->GetWidget()->GetMenu()->SetItemHelpString(
    index, k_("Select an alpha compositing projection method."));

  // --------------------------------------------------------------
  // Display: Window/Level Settings : frame

  if (!this->WindowLevelSettingsFrame)
    {
    this->WindowLevelSettingsFrame = vtkKWFrameWithLabel::New();
    }

  this->WindowLevelSettingsFrame->SetParent(this->GetPagesParentWidget());
  this->WindowLevelSettingsFrame->Create();
  this->WindowLevelSettingsFrame->SetLabelText(k_("Window/Level Settings"));
    
  frame = this->WindowLevelSettingsFrame->GetFrame();

  // --------------------------------------------------------------
  // Display: Window/Level Settings : entries frame

  if (!this->WindowLevelEntriesFrame)
    {
    this->WindowLevelEntriesFrame = vtkKWFrame::New();
    }

  this->WindowLevelEntriesFrame->SetParent(frame);
  this->WindowLevelEntriesFrame->Create();
    
  tk_cmd << "pack " << this->WindowLevelEntriesFrame->GetWidgetName()
         << " -side top -anchor nw -expand y -fill x -padx 0 -pady 0" << endl;
  
  // --------------------------------------------------------------
  // Display: Window/Level Settings : window

  if (!this->WindowEntry)
    {
    this->WindowEntry = vtkKWEntryWithLabel::New();
    }

  this->WindowEntry->SetParent(this->WindowLevelEntriesFrame);
  this->WindowEntry->Create();
  this->WindowEntry->SetLabelText(ks_("Window/Level|Window:"));
  this->WindowEntry->GetWidget()->SetValueAsDouble(0.0);
  this->WindowEntry->GetWidget()->SetWidth(6);
  this->WindowEntry->GetWidget()->SetRestrictValueToDouble();
  this->WindowEntry->GetWidget()->SetCommand(this, "WindowLevelCallback");
  this->WindowEntry->SetBalloonHelpString(
    k_("Enter the window value manually. This value can also be controlled "
       "interactively within the view."));

  tk_cmd << "pack " << this->WindowEntry->GetWidgetName()
         << " -side left -anchor nw -fill x -expand t -padx 2 -pady 2" << endl;

  // --------------------------------------------------------------
  // Display: Window/Level Settings : level

  if (!this->LevelEntry)
    {
    this->LevelEntry = vtkKWEntryWithLabel::New();
    }

  this->LevelEntry->SetParent(this->WindowLevelEntriesFrame);
  this->LevelEntry->Create();
  this->LevelEntry->SetLabelText(ks_("Window/Level|Level:"));
  this->LevelEntry->GetWidget()->SetRestrictValueToDouble();
  this->LevelEntry->GetWidget()->SetValueAsDouble(0.0);
  this->LevelEntry->GetWidget()->SetWidth(
    this->WindowEntry->GetWidget()->GetWidth());
  this->LevelEntry->GetWidget()->SetCommand(this, "WindowLevelCallback");
  this->LevelEntry->SetBalloonHelpString(
    k_("Enter the level value manually. This value can also be controlled "
       "interactively within the view."));

  tk_cmd << "pack " << this->LevelEntry->GetWidgetName()
         << " -side left -anchor nw -fill x -expand t -padx 2 -pady 2" << endl;

  // --------------------------------------------------------------
  // Display: Window/Level Settings : reset

  if (!this->WindowLevelResetButton)
    {
    this->WindowLevelResetButton = vtkKWPushButton::New();
    }

  this->WindowLevelResetButton->SetParent(this->WindowLevelEntriesFrame);
  this->WindowLevelResetButton->Create();
  this->WindowLevelResetButton->SetText(ks_("Window/Level|Reset"));
  this->WindowLevelResetButton->SetImageToPredefinedIcon(
    vtkKWIcon::IconResetContrast);
  this->WindowLevelResetButton->SetCompoundModeToLeft();
  this->WindowLevelResetButton->SetBalloonHelpString(
    k_("Reset the window/level to view the full data range"));
  this->WindowLevelResetButton->SetCommand(this, "WindowLevelResetCallback");

  tk_cmd << "pack " << this->WindowLevelResetButton->GetWidgetName()
         << " -side left -anchor nw -padx 2 -pady 2 -fill x -expand t" << endl;

  // --------------------------------------------------------------
  // Display: Window/Level Settings : Tfunc

  if (!this->WindowLevelFunctionEditor)
    {
    this->WindowLevelFunctionEditor = vtkKWPiecewiseFunctionEditor::New();
    }

  this->WindowLevelFunctionEditor->SetParent(frame);
  this->WindowLevelFunctionEditor->Create();
  this->WindowLevelFunctionEditor->WindowLevelModeOn();
  this->WindowLevelFunctionEditor->WindowLevelModeButtonVisibilityOff();
  this->WindowLevelFunctionEditor->ComputePointColorFromValueOff();
  this->WindowLevelFunctionEditor->LockEndPointsParameterOn();
  this->WindowLevelFunctionEditor->ValueRangeVisibilityOff();
  this->WindowLevelFunctionEditor->ValueEntryVisibilityOff();
  this->WindowLevelFunctionEditor->MidPointEntryVisibilityOff();
  this->WindowLevelFunctionEditor->SharpnessEntryVisibilityOff();
  this->WindowLevelFunctionEditor->WindowLevelModeLockEndPointValueOn();
  this->WindowLevelFunctionEditor->SetCanvasHeight(
  this->VolumePropertyWidget->GetScalarOpacityFunctionEditor()->GetCanvasHeight());

  vtkPiecewiseFunction *fun = vtkPiecewiseFunction::New();
  this->WindowLevelFunctionEditor->SetPiecewiseFunction(fun);
  fun->Delete();

  this->AddCallbackCommandObserver(
    this->WindowLevelFunctionEditor, vtkKWEvent::WindowLevelChangedEvent);
  this->AddCallbackCommandObserver(
    this->WindowLevelFunctionEditor, vtkKWEvent::WindowLevelChangingEvent);

  tk_cmd << "pack " << this->WindowLevelFunctionEditor->GetWidgetName()
         << " -side top -anchor nw -fill x -expand t -padx 2 -pady 2" << endl;

  // --------------------------------------------------------------
  // Presets: Volume Appearance Presets : frame

  if (!this->VolumePropertyPresetFrame)
    {
    this->VolumePropertyPresetFrame = vtkKWFrameWithLabel::New();
    }

  this->VolumePropertyPresetFrame->SetParent(this->GetPagesParentWidget());
  this->VolumePropertyPresetFrame->Create();
  this->VolumePropertyPresetFrame->SetLabelText(k_("Color/Opacity Presets"));
  this->VolumePropertyPresetFrame->SetChangePackingOnCollapse(1);
    
  frame = this->VolumePropertyPresetFrame->GetFrame();

  // --------------------------------------------------------------
  // Presets: Volume Appearance Presets : preset selector

  if (!this->VolumePropertyPresetSelector)
    {
    this->VolumePropertyPresetSelector = 
      vtkKWVolumePropertyPresetSelector::New();
    }

  this->VolumePropertyPresetSelector->SetParent(frame);
  this->VolumePropertyPresetSelector->Create();
  this->VolumePropertyPresetSelector->SetListHeight(1);
  this->VolumePropertyPresetSelector->ThumbnailColumnVisibilityOn();
  this->VolumePropertyPresetSelector->TypeColumnVisibilityOn();
  this->VolumePropertyPresetSelector->FilterButtonVisibilityOn();
  this->VolumePropertyPresetSelector->LocateButtonVisibilityOn();
  this->VolumePropertyPresetSelector->EmailButtonVisibilityOn();
  this->VolumePropertyPresetSelector->RemovePresetAlsoRemoveFileOn();
  this->VolumePropertyPresetSelector->SetPresetAddCommand(
    this, "VolumePropertyPresetAddCallback");
  this->VolumePropertyPresetSelector->SetPresetUpdateCommand(
    this, "VolumePropertyPresetUpdateCallback");
  this->VolumePropertyPresetSelector->SetPresetApplyCommand(
    this, "VolumePropertyPresetApplyCallback ");
  this->VolumePropertyPresetSelector->SetPresetHasChangedCommand(
    this, "VolumePropertyPresetHasChangedCallback");
  this->VolumePropertyPresetSelector->SetPresetLoadCommand(
    this, "VolumePropertyPresetLoadCallback");
  this->VolumePropertyPresetSelector->SetPresetFilteringHasChangedCommand(
    this, "VolumePropertyPresetFilteringHasChangedCallback");
  this->VolumePropertyPresetSelector->SetHelpLabelText(
    k_("Use the filter button to the right for more presets."));
  this->VolumePropertyPresetSelector->SetHelpLabelVisibility(1);

  // Temporary switch to single mode until the Tablelist bug is fixed: 
  // selecting a preset re-renders the view, which takes about 1s., and even
  // though it's async, it produces a delay long enough that moving the
  // mouse will select another row.
  // Drawback: one can not use the up/down arrow key to go from one preset
  // to the other. This is ok here since it takes too long to render anyway
  this->VolumePropertyPresetSelector->GetPresetList()->GetWidget()->SetSelectionModeToSingle();

  sprintf(
    buffer, 
    k_("To use that Volume Appearance preset, place it in the \"%s\" "
       "subdirectory in your User Data directory. The location of your User "
       "Data directory can be found by accessing the \"About...\" dialog from "
       "the \"Help\" menu.\nAlternatively, you can load the preset directly "
       "from the Volume Appearance user interface by simply clicking "
       "on the \"Load a preset\" button at the bottom of the preset list."),
    KWVolView_PRESETS_TFUNCS_SUBDIR);
  this->VolumePropertyPresetSelector->SetEmailBody(buffer);

  this->VolumePropertyPresetSelector->AddDefaultNormalizedPresets(
    k_("Default"));
  this->LoadVolumePropertyPresets();

  tk_cmd << "pack " << this->VolumePropertyPresetSelector->GetWidgetName()
         << " -side top -anchor w -fill both -expand t" << endl;

  // --------------------------------------------------------------
  // Presets: Window/Level Presets : frame

  if (!this->WindowLevelPresetFrame)
    {
    this->WindowLevelPresetFrame = vtkKWFrameWithLabel::New();
    }

  this->WindowLevelPresetFrame->SetParent(this->GetPagesParentWidget());
  this->WindowLevelPresetFrame->Create();
  this->WindowLevelPresetFrame->SetLabelText(k_("Window/Level Presets"));
  this->WindowLevelPresetFrame->SetChangePackingOnCollapse(1);
    
  frame = this->WindowLevelPresetFrame->GetFrame();

  // --------------------------------------------------------------
  // Presets: Window/Level Presets : preset selector

  if (!this->WindowLevelPresetSelector)
    {
    this->WindowLevelPresetSelector = vtkKWWindowLevelPresetSelector::New();
    }

  this->WindowLevelPresetSelector->SetParent(frame);
  this->WindowLevelPresetSelector->Create();
  this->WindowLevelPresetSelector->SetListHeight(
    this->VolumePropertyPresetSelector->GetListHeight());
  this->WindowLevelPresetSelector->TypeColumnVisibilityOn();
  this->WindowLevelPresetSelector->FilterButtonVisibilityOn();
  this->WindowLevelPresetSelector->ThumbnailColumnVisibilityOn();
  this->WindowLevelPresetSelector->LocateButtonVisibilityOn();
  this->WindowLevelPresetSelector->EmailButtonVisibilityOn();
  this->WindowLevelPresetSelector->RemovePresetAlsoRemoveFileOn();
  this->WindowLevelPresetSelector->SetPresetAddCommand(
    this, "WindowLevelPresetAddCallback");
  this->WindowLevelPresetSelector->SetPresetUpdateCommand(
    this, "WindowLevelPresetUpdateCallback");
  this->WindowLevelPresetSelector->SetPresetHasChangedCommand(
    this, "WindowLevelPresetHasChangedCallback");
  this->WindowLevelPresetSelector->SetPresetRemoveCommand(
    this, "WindowLevelPresetRemoveCallback");
  this->WindowLevelPresetSelector->SetPresetApplyCommand(
    this, "WindowLevelPresetApplyCallback ");
  this->WindowLevelPresetSelector->SetPresetLoadCommand(
    this, "WindowLevelPresetLoadCallback");
  this->WindowLevelPresetSelector->SetPresetFilteringHasChangedCommand(
    this, "WindowLevelPresetFilteringHasChangedCallback");
  this->WindowLevelPresetSelector->SetHelpLabelText(
    k_("Use the filter button to the right for more presets."));
  this->WindowLevelPresetSelector->SetHelpLabelVisibility(1);
  
  // Set the selection mode to single so that double-click on a comment still
  // works (tablelist bug).
  // Drawback: one can not use the up/down arrow key to go from one preset
  // to the other, but such arrows are available as icons anyway below the
  // list (select next/previous preset).

  this->WindowLevelPresetSelector->GetPresetList()->GetWidget()->SetSelectionModeToSingle();
  
  sprintf(
    buffer, 
    k_("To use that Window/Level preset, place it in the \"%s\" "
       "subdirectory in your User Data directory. The location of your User "
       "Data directory can be found by accessing the \"About...\" dialog from "
       "the \"Help\" menu.\nAlternatively, you can load the preset directly "
       "from the Window/Level user interface by simply clicking "
       "on the \"Load a preset\" button at the bottom of the preset list."),
    KWVolView_PRESETS_WLS_SUBDIR);
  this->WindowLevelPresetSelector->SetEmailBody(buffer);

  this->LoadWindowLevelPresets();

  tk_cmd << "pack " << this->WindowLevelPresetSelector->GetWidgetName()
         << " -side top -anchor w -fill both -expand t" << endl;

  // --------------------------------------------------------------
  // Pack 

  this->Pack();

  tk_cmd << ends;
  this->Script(tk_cmd.str());
  tk_cmd.rdbuf()->freeze(0);

  // Update according to the current Window

  this->Update();
}

// --------------------------------------------------------------------------
void vtkVVDisplayInterface::Pack()
{
  if (!this->IsCreated())
    {
    return;
    }

  ostrstream tk_cmd;

  vtkKWWidget *page1 = this->GetPageWidget(this->Internals->Page3DId);
  vtkKWWidget *page2 = this->GetPageWidget(this->Internals->Page2DId);

  this->SetPageIconToPredefinedIcon(
    this->Internals->Page3DId, vtkKWIcon::IconEditVolumeProperties);
  this->SetPageTitle(this->Internals->Page3DId, NULL);
  this->SetPageBalloonHelpString(
    this->Internals->Page3DId, "Color/Opacity Settings");
  
  this->SetPageIconToPredefinedIcon(
    this->Internals->Page2DId, vtkKWIcon::IconEditContrast);
  this->SetPageTitle(this->Internals->Page2DId, NULL);
  this->SetPageBalloonHelpString(
    this->Internals->Page2DId, "Window/Level Settings");

  // --------------------------------------------------------------
  // Presets: Volume Appearance Presets : frame

  tk_cmd << "pack " << this->VolumePropertyPresetFrame->GetWidgetName()
         << " -side top -anchor nw -fill both -expand t -padx 2 -pady 2 " 
         << " -in " << page1->GetWidgetName() << endl;

  // --------------------------------------------------------------
  // Display: Volume Appearance Settings

  tk_cmd << "pack " << this->VolumePropertyWidget->GetWidgetName()
         << " -side top -anchor nw -fill x -expand n -padx 2 -pady 2 " 
         << " -in " << page1->GetWidgetName() << endl;

  // --------------------------------------------------------------
  // Presets: Window/Level Presets : frame

  tk_cmd << "pack " << this->WindowLevelPresetFrame->GetWidgetName()
         << " -side top -anchor nw -fill both -expand t -padx 2 -pady 2 " 
         << " -in " << page2->GetWidgetName() << endl;

  // --------------------------------------------------------------
  // Display: Window/Level Settings : frame

  tk_cmd 
    << "pack " << this->WindowLevelSettingsFrame->GetWidgetName()
    << " -side top -anchor nw -fill x -expand n -padx 2 -pady 2 " 
    << " -in " << page2->GetWidgetName() << endl;

  // --------------------------------------------------------------
  // Pack 

  tk_cmd << ends;
  this->Script(tk_cmd.str());
  tk_cmd.rdbuf()->freeze(0);
}

//---------------------------------------------------------------------------
void vtkVVDisplayInterface::Update()
{
  this->Superclass::Update();
  
  if (!this->IsCreated())
    {
    return;
    }

  vtkVVDataItem *data = 
    this->Window ? this->Window->GetSelectedDataItem() : NULL;
  vtkVVDataItemVolume *volume_data = vtkVVDataItemVolume::SafeDownCast(data);

  vtkKWRenderWidget *rw = 
    this->Window ? this->Window->GetSelectedRenderWidget() : NULL;

  vtkKWRenderWidgetPro *rwp = vtkKWRenderWidgetPro::SafeDownCast(rw); 
  vtkKW2DRenderWidget *rw2d = vtkKW2DRenderWidget::SafeDownCast(rw); 
  vtkKWImageWidget *iw = vtkKWImageWidget::SafeDownCast(rw); 

  // Unique widgets

  vtkKWVolumeWidget *vw = 
    volume_data ? volume_data->GetVolumeWidget(this->Window) : NULL;

  vtkKWLightboxWidget *lw = 
    volume_data ? volume_data->GetLightboxWidget(this->Window) : NULL;

  // --------------------------------------------------------------
  // Observers

  if (rw)
    {
    this->AddCallbackCommandObserver(
      rw, vtkKWRenderWidget::RendererGradientBackgroundChangedEvent);
    this->AddCallbackCommandObserver(
      rw, vtkKWRenderWidget::RendererBackgroundColorChangedEvent);
    this->AddCallbackCommandObserver(
      rw, vtkKWRenderWidget::RendererBackgroundColor2ChangedEvent);
    }

  if (rwp)
    {
    this->AddCallbackCommandObserver(
      rwp, vtkKWRenderWidgetPro::DisplayChannelsChangedEvent);
    this->AddCallbackCommandObserver(
      rwp, vtkKWRenderWidgetPro::UseOpacityModulationChangedEvent);
    }

  if (rw2d)
    {
    vtkKWInteractorStyle2DView *style = rw2d->GetInteractorStyle();
    if (style)
      {
      this->AddCallbackCommandObserver(
        style, vtkKWEvent::WindowLevelChangedEvent);
      this->AddCallbackCommandObserver(
        style, vtkKWEvent::WindowLevelChangingEvent);
      //this->AddCallbackCommandObserver(
      //  style, vtkKWEvent::WindowLevelResetEvent);
      }
    this->AddCallbackCommandObserver(
      rw2d, vtkKWEvent::WindowLevelResetEvent);
    }
  
  if (iw)
    {
    this->AddCallbackCommandObserver(
      iw, vtkKWEvent::ImageInterpolateEvent);
    } 

  if (lw)
    {
    this->AddCallbackCommandObserver(
      lw, vtkKW2DRenderWidget::SliceOrientationChangedEvent);
    } 

  if (vw)
    {
    this->AddCallbackCommandObserver(
      vw, vtkKWEvent::ProjectionTypeChangedEvent);
    } 

  // --------------------------------------------------------------
  // Display: Window/Level : frame

  if (this->WindowEntry && this->WindowEntry->IsCreated())
    {
    if (rwp)
      {
      this->WindowEntry->GetWidget()->SetValueAsDouble(rwp->GetWindow());
      this->WindowEntry->SetEnabled(this->GetEnabled());
      }
    else
      {
      this->WindowEntry->SetEnabled(0);
      }
    }
  if (this->LevelEntry && this->LevelEntry->IsCreated())
    {
    if (rwp)
      {
      this->LevelEntry->GetWidget()->SetValueAsDouble(rwp->GetLevel());
      this->LevelEntry->SetEnabled(this->GetEnabled());
      }
    else
      {
      this->LevelEntry->SetEnabled(0);
      }
    }

  if (this->WindowLevelResetButton && 
      this->WindowLevelResetButton->IsCreated())
    {
    this->WindowLevelResetButton->SetEnabled(rwp ? this->GetEnabled() : 0);
    }

  if (this->WindowLevelFunctionEditor)
    {
    if (volume_data)
      {
      int scalar_field = 0;
      vtkDataArray *scalars = volume_data->GetImageData() ?
        volume_data->GetImageData()->GetPointData()->GetScalars() : NULL;
      if (scalars)
        {
        double p_range[2];
        if (vtkMath::GetAdjustedScalarRange(scalars, scalar_field, p_range))
          {
          this->WindowLevelFunctionEditor->SetWholeParameterRange(p_range);
          }
        this->WindowLevelFunctionEditor->SetWholeValueRange(0.0, 1.0);
        this->WindowLevelFunctionEditor->SetHistogram(
          volume_data->GetHistogram(scalar_field));
        }
      }
    
    if (rwp)
      {
      this->WindowLevelFunctionEditor->SetWindowLevel(
        rwp->GetWindow(), rwp->GetLevel());
      }

    this->WindowLevelFunctionEditor->SetEnabled(
      rwp || volume_data ? this->GetEnabled() : 0);
    }

  // --------------------------------------------------------------
  // Display: Volume Appearance Settings

  if (this->VolumePropertyWidget && 
      this->VolumePropertyWidget->IsCreated())
    {
    if (volume_data)
      {
      this->VolumePropertyWidget->SetVolumeProperty(
        volume_data->GetVolumeProperty());
      this->VolumePropertyWidget->SetDataSet(
        volume_data->GetImageData());
      this->VolumePropertyWidget->SetHistogramSet(
        volume_data->GetHistogramSet());
      }
    else
      {
      this->VolumePropertyWidget->SetVolumeProperty(NULL);
      this->VolumePropertyWidget->SetDataSet(NULL);
      this->VolumePropertyWidget->SetHistogramSet(NULL);
      }
    if (rwp)
      {
      this->VolumePropertyWidget->SetWindowLevel(
        rwp->GetWindow(), rwp->GetLevel());
      }
    this->VolumePropertyWidget->Update();

    this->VolumePropertyWidget->SetEnabled(
      rwp || volume_data ? this->GetEnabled() : 0);
    }

  // --------------------------------------------------------------
  // Display: Blend mode

  if (this->BlendModeMenuButton && 
      this->BlendModeMenuButton->IsCreated())
    {
    if (vw)
      {
      switch (vw->GetBlendMode())
        {
        case vtkKWVolumeWidget::BLEND_MODE_MIP:
          this->BlendModeMenuButton->GetWidget()->SetValue(
            ks_("Blend Mode|Maximum Intensity Projection"));
          if (this->VolumePropertyWidget)
            {
            this->VolumePropertyWidget->GetMaterialPropertyWidget()->SetEnabled(
              0);
            }
          break;
        case vtkKWVolumeWidget::BLEND_MODE_COMPOSITE:
          this->BlendModeMenuButton->GetWidget()->SetValue(
            ks_("Blend Mode|Volume Rendering"));
          if (this->VolumePropertyWidget)
            {
            this->VolumePropertyWidget->GetMaterialPropertyWidget()->SetEnabled(
              this->VolumePropertyWidget->GetEnabled());
            }
          break;
        }
      this->BlendModeMenuButton->SetEnabled(this->GetEnabled());
      }
    else
      {
      this->BlendModeMenuButton->SetEnabled(0);
      this->VolumePropertyWidget->GetMaterialPropertyWidget()->SetEnabled(0);
      }
    }

  // --------------------------------------------------------------
  // Presets: Volume Appearance Presets

  if (this->VolumePropertyPresetSelector)
    {
    this->VolumePropertyPresetSelector->Update();
    if (volume_data && 
        this->VolumePropertyPresetSelector->GetNumberOfPresets())
      {
      vtkMedicalImageProperties *med_prop = 
        volume_data->GetMedicalImageProperties();
     
      // We are filtering the presets by type, but different datasets
      // can have different filtering, depending on the dataset type.
      // Therefore, keep track of the filter to apply on the type field
      // *per* dataset.

      vtksys_stl::map<vtksys_stl::string,vtksys_stl::string>::iterator it = 
        this->Internals->VolumePropertyPresetSelectorFilters.find(
          volume_data->GetName());
      vtksys_stl::string *regexp;
      if (it == this->Internals->VolumePropertyPresetSelectorFilters.end())
        {
        regexp = &this->Internals->VolumePropertyPresetSelectorFilters[
          volume_data->GetName()];
        // We want to display the presets that apply to any type, + 
        // the one that apply to this specific type (if there was one)
        *regexp = "^(";
        // not an error here, nothing before the first | to match empty string
        *regexp += '|';
        *regexp += k_("Default"); // all the default presets
        *regexp += '|';
        *regexp += k_("User"); // all the user presets
        if (med_prop && med_prop->GetModality())
          {
          *regexp += '|';
          *regexp += med_prop->GetModality();
          }
        *regexp += ")$";
        }
      else
        {
        regexp = &it->second;
        }

      const char *constraint = 
        this->VolumePropertyPresetSelector->GetPresetFilterUserSlotConstraint(
          this->VolumePropertyPresetSelector->GetPresetTypeSlotName());
      this->VolumePropertyPresetSelector->SetPresetFilterUserSlotConstraint(
        this->VolumePropertyPresetSelector->GetPresetTypeSlotName(),
        regexp->c_str());
      if (!constraint)
        {
        this->VolumePropertyPresetSelector->SetPresetFilterUserSlotConstraintToRegularExpression(
          this->VolumePropertyPresetSelector->GetPresetTypeSlotName());
        }

      // We also want to filter by IndependentComponents, i.e. not display
      // the presets that were designed for a different data structure

      char buffer[10];
      sprintf(buffer, "%d", 
              volume_data->GetVolumeProperty()->GetIndependentComponents());
      this->VolumePropertyPresetSelector->SetPresetFilterUserSlotConstraint(
        this->VolumePropertyPresetSelector->GetPresetIndependentComponentsSlotName(), buffer);

#if 0
      // We also want to filter by range, i.e. not display
      // the presets that were designed for a range outside our current
      // data range
      // This is not working, presets can have valid transfer functions defined
      // slightly outside the range

      vtkDataArray *scalars = volume_data->GetImageData() ?
        volume_data->GetImageData()->GetPointData()->GetScalars() : NULL;
      if (scalars)
        {
        double volume_data_range[2];
        if (vtkMath::GetAdjustedScalarRange(scalars, 0, volume_data_range))
          {
          this->VolumePropertyPresetSelector->SetPresetFilterRangeConstraint(
            volume_data_range);
          }
        }
#endif

      // Each preset has a different screenshot *per* dataset

      vtksys_stl::string slot_name(volume_data->GetName());
      slot_name += "Thumbnail";
      this->VolumePropertyPresetSelector->SetPresetThumbnailSlotName(
        slot_name.c_str());

      slot_name = volume_data->GetName();
      slot_name += "Screenshot";
      this->VolumePropertyPresetSelector->SetPresetScreenshotSlotName(
        slot_name.c_str());

      this->ScheduleUpdateVolumePropertyPresetThumbnails();
      }
    this->VolumePropertyPresetSelector->SetEnabled(
      data ? this->GetEnabled() : 0);
    }

  // --------------------------------------------------------------
  // Presets: Window/Level presets
  
  if (this->WindowLevelPresetSelector)
    {
    if (volume_data)
      {
      vtkMedicalImageProperties *med_prop = 
        volume_data->GetMedicalImageProperties();

      // We are filtering the presets by type, but different datasets
      // can have different filtering, depending on the dataset type.
      // Therefore, keep track of the filter to apply on the type field
      // *per* dataset.

      vtksys_stl::map<vtksys_stl::string,vtksys_stl::string>::iterator it = 
        this->Internals->WindowLevelPresetSelectorFilters.find(
          volume_data->GetName());
      vtksys_stl::string *regexp;
      if (it == this->Internals->WindowLevelPresetSelectorFilters.end())
        {
        regexp = &this->Internals->WindowLevelPresetSelectorFilters[
          volume_data->GetName()];
        // We want to display the presets that apply to any type, + 
        // the one that apply to this specific type (if there was one)
        *regexp = "^(";
        // not an error here, nothing before the first | to match empty string
        //*regexp += '|';
        //*regexp += k_("Default"); // all the default presets
        *regexp += '|';
        *regexp += k_("User"); // all the user presets
        if (med_prop && med_prop->GetModality())
          {
          *regexp += '|';
          *regexp += med_prop->GetModality();
          }
        *regexp += ")$";
        }
      else
        {
        regexp = &it->second;
        }

      const char *constraint = 
        this->WindowLevelPresetSelector->GetPresetFilterUserSlotConstraint(
          this->WindowLevelPresetSelector->GetPresetTypeSlotName());
      this->WindowLevelPresetSelector->SetPresetFilterUserSlotConstraint(
        this->WindowLevelPresetSelector->GetPresetTypeSlotName(),
        regexp->c_str());
      if (!constraint)
        {
        this->WindowLevelPresetSelector->SetPresetFilterUserSlotConstraintToRegularExpression(
          this->WindowLevelPresetSelector->GetPresetTypeSlotName());
        }

      // Each preset has a different screenshot *per* dataset

      vtksys_stl::string slot_name(volume_data->GetName());
      slot_name += "Thumbnail";
      this->WindowLevelPresetSelector->SetPresetThumbnailSlotName(
        slot_name.c_str());

      slot_name = volume_data->GetName();
      slot_name += "Screenshot";
      this->WindowLevelPresetSelector->SetPresetScreenshotSlotName(
        slot_name.c_str());
      }

    this->PopulateWindowLevelPresets();
    this->WindowLevelPresetSelector->Update();
    if (volume_data)
      {
      this->ScheduleUpdateWindowLevelPresetThumbnails(
        this->Internals->UpdatePresetThumbnailsDelay);
      }
    this->WindowLevelPresetSelector->SetEnabled(
      data ? this->GetEnabled() : 0);
    }
}

// --------------------------------------------------------------------------
int vtkVVDisplayInterface::GetPage3DId()
{
  return this->Internals->Page3DId;
}

// --------------------------------------------------------------------------
int vtkVVDisplayInterface::GetPage2DId()
{
  return this->Internals->Page2DId;
}

//---------------------------------------------------------------------------
void vtkVVDisplayInterface::UpdateEnableState()
{
  this->Superclass::UpdateEnableState();

  // Window/Level Settings

  if (this->WindowLevelSettingsFrame)
    {
    this->WindowLevelSettingsFrame->SetEnabled(this->GetEnabled());
    }

  if (this->WindowLevelEntriesFrame)
    {
    this->WindowLevelEntriesFrame->SetEnabled(this->GetEnabled());
    }

  if (this->WindowEntry)
    {
    this->WindowEntry->SetEnabled(this->GetEnabled());
    }

  if (this->LevelEntry)
    {
    this->LevelEntry->SetEnabled(this->GetEnabled());
    }

  if (this->WindowLevelResetButton)
    {
    this->WindowLevelResetButton->SetEnabled(this->GetEnabled());
    }

  if (this->WindowLevelFunctionEditor)
    {
    this->WindowLevelFunctionEditor->SetEnabled(this->GetEnabled());
    }

  // Window/Level Presets

  if (this->WindowLevelPresetFrame)
    {
    this->WindowLevelPresetFrame->SetEnabled(this->GetEnabled());
    }

  if (this->WindowLevelPresetSelector)
    {
    this->WindowLevelPresetSelector->SetEnabled(this->GetEnabled());
    }

  // Volume Appearance Settings

  if (this->VolumePropertyWidget)
    {
    this->VolumePropertyWidget->SetEnabled(this->GetEnabled());
    }

  if (this->BlendModeMenuButton)
    {
    this->BlendModeMenuButton->SetEnabled(this->GetEnabled());
    }

  // Volume Appearance Presets

  if (this->VolumePropertyPresetFrame)
    {
    this->VolumePropertyPresetFrame->SetEnabled(this->GetEnabled());
    }

  if (this->VolumePropertyPresetSelector)
    {
    this->VolumePropertyPresetSelector->SetEnabled(this->GetEnabled());
    }
}

//---------------------------------------------------------------------------
void vtkVVDisplayInterface::SetWindowLevelInInterface(
  double window, double level)
{
  if (this->WindowEntry)
    {
    this->WindowEntry->GetWidget()->SetValueAsDouble(window);
    }
  if (this->LevelEntry)
    {
    this->LevelEntry->GetWidget()->SetValueAsDouble(level);
    }
  if (this->WindowLevelFunctionEditor)
    {
    this->WindowLevelFunctionEditor->SetWindowLevel(window, level);
    }
  if (this->VolumePropertyWidget)
    {
    this->VolumePropertyWidget->SetWindowLevel(window, level);
    }
  // In order not to confuse the user about which preset is selected,
  // clear the selection as soon as the W/L is different than the selected
  // preset.

  if (this->WindowLevelPresetSelector)
    {
    int id = this->WindowLevelPresetSelector->GetIdOfSelectedPreset();
    if (id >= 0 && 
        window != this->WindowLevelPresetSelector->GetPresetWindow(id) ||
        level != this->WindowLevelPresetSelector->GetPresetLevel(id))
      {
      this->WindowLevelPresetSelector->ClearSelection();
      }
    }
}

//---------------------------------------------------------------------------
void vtkVVDisplayInterface::SetInteractiveWindowLevelInInterface(
  double window, double level)
{
  if (this->WindowEntry)
    {
    this->WindowEntry->GetWidget()->SetValueAsDouble(window);
    }
  if (this->LevelEntry)
    {
    this->LevelEntry->GetWidget()->SetValueAsDouble(level);
    }
  if (this->WindowLevelFunctionEditor)
    {
    this->WindowLevelFunctionEditor->SetInteractiveWindowLevel(window, level);
    }
  if (this->VolumePropertyWidget)
    {
    this->VolumePropertyWidget->SetInteractiveWindowLevel(window, level);
    }
}

//---------------------------------------------------------------------------
void vtkVVDisplayInterface::SetWindowLevelInRenderWidgetsUsingSelectedDataItem(
  double w, double l)
{
  if (this->Window)
    {
    int nb_rw = this->Window->GetNumberOfRenderWidgetsUsingSelectedDataItem();
    for (int i = 0; i < nb_rw; i++)
      {
      vtkKWRenderWidgetPro *rwp = vtkKWRenderWidgetPro::SafeDownCast(
        this->Window->GetNthRenderWidgetUsingSelectedDataItem(i));
      if (rwp)
        {
        rwp->SetWindowLevel(w, l);
        }
      }
    }
}

//---------------------------------------------------------------------------
void vtkVVDisplayInterface::SetInteractiveWindowLevelInRenderWidgetsUsingSelectedDataItem(
  double w, double l)
{
  if (this->Window)
    {
    int nb_rw = this->Window->GetNumberOfRenderWidgetsUsingSelectedDataItem();
    for (int i = 0; i < nb_rw; i++)
      {
      // In interactive mode, we only update the image widget
      // which is fast enough. The lightbox and volume widgets are too slow
      // to update. 
      vtkKWImageWidget *iw = vtkKWImageWidget::SafeDownCast(
        this->Window->GetNthRenderWidgetUsingSelectedDataItem(i));
      if (iw)
        {
        iw->SetWindowLevel(w, l);
        }
      }
    }
}

//---------------------------------------------------------------------------
void vtkVVDisplayInterface::WindowLevelCallback(const char*)
{
  if (this->WindowEntry && this->LevelEntry)
    {
    double w = this->WindowEntry->GetWidget()->GetValueAsDouble();
    double l = this->LevelEntry->GetWidget()->GetValueAsDouble();
    this->SetWindowLevelInInterface(w, l); // before
    this->SetWindowLevelInRenderWidgetsUsingSelectedDataItem(w, l);
    }
}

//---------------------------------------------------------------------------
void vtkVVDisplayInterface::WindowLevelResetCallback()
{
  if (this->Window)
    {
    int nb_rw = this->Window->GetNumberOfRenderWidgetsUsingSelectedDataItem();
    vtkKWRenderWidgetPro *rwp = NULL;
    for (int i = 0; i < nb_rw; i++)
      {
      rwp = vtkKWRenderWidgetPro::SafeDownCast(
        this->Window->GetNthRenderWidgetUsingSelectedDataItem(i));
      if (rwp)
        {
        rwp->ResetWindowLevel();        
        }
      }
    this->Update();
    }
}

//----------------------------------------------------------------------------
void vtkVVDisplayInterface::LoadWindowLevelPresets()
{
  if (!this->WindowLevelPresetSelector)
    {
    return;
    }

  // Try to load wl from a KWVolView_PRESETS_WLS_SUBDIR dir found inside:
  // - the user data dir
  // - the KWVolView_INSTALL_DATA_DIR dir one and two level up from the binary

  vtksys_stl::vector<vtksys_stl::string> dirs;

  dirs.push_back(this->GetApplication()->GetUserDataDirectory());

  vtksys_stl::string install_dir(
    this->GetApplication()->GetInstallationDirectory());
  install_dir += "/..";

  vtksys_stl::string try_dir;

  try_dir = install_dir;
  try_dir += KWVolView_INSTALL_DATA_DIR;
  dirs.push_back(try_dir);
  
  install_dir += "/..";

  try_dir = install_dir;
  try_dir += KWVolView_INSTALL_DATA_DIR;
  dirs.push_back(try_dir);

  dirs.push_back(KWVolView_SOURCE_DIR);

  vtksys_stl::map<vtksys_stl::string, int> loaded_filenames;
  
  // Iterate over all dirs

  vtksys_stl::vector<vtksys_stl::string>::iterator it = dirs.begin();
  vtksys_stl::vector<vtksys_stl::string>::iterator end = dirs.end();
  for (; it != end; it++)
    {
    vtksys_stl::string tfuncs_dir(*it);
    tfuncs_dir += "/";
    tfuncs_dir += KWVolView_PRESETS_WLS_SUBDIR;
    tfuncs_dir += "/";

    vtkDirectory *dir = vtkDirectory::New();
    dir->Open(tfuncs_dir.c_str());

    int i, num_files = dir->GetNumberOfFiles();
    for (i = 0; i < num_files; ++i)
      {
      vtksys_stl::string filename(tfuncs_dir);
      filename += dir->GetFile(i);
      vtksys_stl::string ext = 
        vtksys::SystemTools::GetFilenameExtension(filename);
      if (!ext.compare(".vvt"))
        {
        filename = vtksys::SystemTools::CollapseFullPath(filename.c_str());
        loaded_filenames[filename] = 1;
        }
      }
    dir->Delete();
    }

  vtksys_stl::map<vtksys_stl::string, int>::iterator loaded_it = 
    loaded_filenames.begin();
  vtksys_stl::map<vtksys_stl::string, int>::iterator loaded_end = 
    loaded_filenames.end();
  for (; loaded_it != loaded_end; loaded_it++)
    {
    this->LoadWindowLevelPreset(loaded_it->first.c_str());
    }
}

//----------------------------------------------------------------------------
int vtkVVDisplayInterface::LoadWindowLevelPreset(const char *filename)
{
  int id = -1;

  if (filename && *filename && this->WindowLevelPresetSelector)
    {
    vtkXMLDataElement *wl_elem = 
      vtkXMLUtilities::ReadElementFromFile(
        filename, this->GetApplication()->GetCharacterEncoding());
    if (wl_elem)
      {
      double window, level;
      if (!strcmp(wl_elem->GetName(), "WindowLevelPreset") &&
          wl_elem->GetScalarAttribute("Window", window) &&
          wl_elem->GetScalarAttribute("Level", level))
        {
        id = this->WindowLevelPresetSelector->AddPreset();
        if (id >= 0)
          {
          const char *type = wl_elem->GetAttribute("Type");
          if (!type)
            {
            type = wl_elem->GetAttribute("Modality"); // backward compat
            }
          this->WindowLevelPresetSelector->SetPresetType(id, type);
          this->WindowLevelPresetSelector->SetPresetComment(
            id, wl_elem->GetAttribute("Comment"));
          this->WindowLevelPresetSelector->SetPresetFileName(id, filename);
          // Force the group to be an empty (but non NULL) string to
          // match the regular expression in PopulateWindowLevelPresets
          // thus allowing the Window/Level preset selector to display
          // presets common to all files/data (empty group), and presets
          // specific to a file/data (group assigned to file/data name).
          this->WindowLevelPresetSelector->SetPresetGroup(id, "");
          this->WindowLevelPresetSelector->SetPresetWindow(id, window);
          this->WindowLevelPresetSelector->SetPresetLevel(id, level);
          }
        }
      wl_elem->Delete();
      }
    }
  
  return id;
}

//----------------------------------------------------------------------------
void vtkVVDisplayInterface::SaveWindowLevelPreset(int id)
{
  if (!this->WindowLevelPresetSelector || id < 0)
    {
    return;
    }

  const char *filename = 
    this->WindowLevelPresetSelector->GetPresetFileName(id);
  if (!filename)
    {
    return;
    }

  vtksys_stl::string filename_dir = 
    vtksys::SystemTools::GetFilenamePath(filename);
  if (!vtksys::SystemTools::FileExists(filename_dir.c_str()))
    {
    vtksys::SystemTools::MakeDirectory(filename_dir.c_str());
    }

  vtkXMLDataElement *wl_elem = vtkXMLDataElement::New();
  wl_elem->SetName("WindowLevelPreset");
  const char *type = 
    this->WindowLevelPresetSelector->GetPresetType(id);
  wl_elem->SetAttribute("Type", type);
  wl_elem->SetAttribute(
    "Comment", this->WindowLevelPresetSelector->GetPresetComment(id));
  wl_elem->SetDoubleAttribute(
    "Window", this->WindowLevelPresetSelector->GetPresetWindow(id));
  wl_elem->SetDoubleAttribute(
    "Level", this->WindowLevelPresetSelector->GetPresetLevel(id));
  
  vtkIndent indent;
  if (!vtkXMLUtilities::WriteElementToFile(wl_elem, filename, &indent))
    {
    vtkKWMessageDialog::PopupMessage(
      this->GetApplication(), 
      this->Window, 
      ks_("Save Preset Dialog|Title|Write Error!"),
      k_("There was a problem writing the window level preset.\n"
         "Please check the location and make sure you have write\n"
         "permissions and enough disk space."),
      vtkKWMessageDialog::ErrorIcon);
    }

  wl_elem->Delete();
}

//---------------------------------------------------------------------------
void vtkVVDisplayInterface::PopulateWindowLevelPresets()
{
  vtkVVDataItem *data = 
    this->Window ? this->Window->GetSelectedDataItem() : NULL;
  vtkVVDataItemVolume *volume_data = vtkVVDataItemVolume::SafeDownCast(data);

  if (!volume_data || !this->WindowLevelPresetSelector)
    {
    return;
    }

  // Add or remove presets 
  
  // The selector will display the presets that are associated to the selected
  // data (which group was assigned the data name), and the presets that
  // are common to all data (which group is empty).
  // We use the ^(data|)$ regular expression.
  // Since we can not use the data name reliably because it can be disrupt
  // the regular expression, clean the name.

  vtksys_stl::string clean_name = 
    vtksys::SystemTools::MakeCindentifier(data->GetName());

  vtksys_stl::string regexp;
  regexp += ("^(");
  regexp += clean_name;
  regexp += "|)$";
  this->WindowLevelPresetSelector->SetPresetFilterGroupConstraint(
    regexp.c_str());
  this->WindowLevelPresetSelector->SetPresetFilterUserSlotConstraintToRegularExpression(
    this->WindowLevelPresetSelector->GetPresetGroupSlotName());
  
  vtkMedicalImageProperties *med_prop = 
    volume_data->GetMedicalImageProperties();
  if (med_prop)
    {
    double w, l;
    int i, nb_presets;

    // Add the one in the med props that are not in the preset selector

    nb_presets = med_prop->GetNumberOfWindowLevelPresets();
    for (i = 0; i < nb_presets; i++)
      {
      if (med_prop->GetNthWindowLevelPreset(i, &w, &l))
        {
        if (!this->WindowLevelPresetSelector->HasPresetWithGroupWithWindowLevel
            (clean_name.c_str(), w, l))
          {
          int id = this->WindowLevelPresetSelector->InsertPreset(
            this->WindowLevelPresetSelector->GetIdOfNthPreset(0));
          this->WindowLevelPresetSelector->SetPresetGroup(
            id, clean_name.c_str());
          this->WindowLevelPresetSelector->SetPresetWindow(id, w);
          this->WindowLevelPresetSelector->SetPresetLevel(id, l);
          this->WindowLevelPresetSelector->SetPresetType(
            id, med_prop->GetModality());
          const char *comment = med_prop->GetNthWindowLevelPresetComment(i);
          if (!comment || !*comment)
            {
            comment = "DICOM";
            }
          this->WindowLevelPresetSelector->SetPresetComment(id, comment);
          }
        }
      }

    // Remove the one in the preset selector that are not in the med props

    int done = 0;
    while (!done)
      {
      done = 1;
      nb_presets = 
        this->WindowLevelPresetSelector->GetNumberOfPresetsWithGroup(
          clean_name.c_str());
      for (i = 0; i < nb_presets; i++)
        {
        int id = this->WindowLevelPresetSelector->GetIdOfNthPresetWithGroup(
          i, clean_name.c_str());
        if (!med_prop->HasWindowLevelPreset(
              this->WindowLevelPresetSelector->GetPresetWindow(id),
              this->WindowLevelPresetSelector->GetPresetLevel(id)))
          {
          this->WindowLevelPresetSelector->RemovePreset(id);
          done = 0;
          break;
          }
        }
      }
    }
}

//---------------------------------------------------------------------------
void vtkVVDisplayInterface::ScheduleUpdateWindowLevelPresetThumbnails(
  int delay)
{
  // Already scheduled

  if (!this->GetApplication() ||
      this->Internals->UpdateWindowLevelPresetThumbnailsTimerId.size())
    {
    return;
    }

  this->Internals->UpdateWindowLevelPresetThumbnailsTimerId =
    this->Script(
      "after %d {catch {%s UpdateWindowLevelPresetThumbnailsCallback}}", 
      delay, this->GetTclName());
}

//---------------------------------------------------------------------------
void vtkVVDisplayInterface::UpdateWindowLevelPresetThumbnailsCallback()
{
  if (!this->WindowLevelPresetSelector || 
      !this->WindowLevelPresetSelector->GetThumbnailColumnVisibility())
    {
    return;
    }

  // Find the *first* preset that has no thumbnails, update it, and schedule
  // the callback again to update any other presets (if any), or schedule it
  // much later if it had failed.

  vtkKWIcon *thumb = NULL;
  int i, nb_presets = this->WindowLevelPresetSelector->GetNumberOfPresets();
  for (i = 0; i < nb_presets; i++)
    {
    int id = this->WindowLevelPresetSelector->GetIdOfNthPreset(i);
    if (id >= 0 && 
        this->WindowLevelPresetSelector->IsPresetFiltered(id))
      {
      thumb = this->WindowLevelPresetSelector->GetPresetThumbnail(id);
      if (!thumb)
        {
        this->UpdateWindowLevelPresetThumbnail(id);
        this->Internals->UpdateWindowLevelPresetThumbnailsTimerId = "";
        thumb = this->WindowLevelPresetSelector->GetPresetThumbnail(id);
        this->ScheduleUpdateWindowLevelPresetThumbnails(
          this->Internals->UpdatePresetThumbnailsDelay * (thumb ? 1 : 10));
        return;
        }
      }

    }
  
  this->Internals->UpdateWindowLevelPresetThumbnailsTimerId = "";
}

//---------------------------------------------------------------------------
void vtkVVDisplayInterface::UpdateWindowLevelPresetThumbnails()
{
  if (!this->WindowLevelPresetSelector || 
      !this->WindowLevelPresetSelector->GetThumbnailColumnVisibility())
    {
    return;
    }

  int i, nb_presets = this->WindowLevelPresetSelector->GetNumberOfPresets();
  for (i = 0; i < nb_presets; i++)
    {
    int id = this->WindowLevelPresetSelector->GetIdOfNthPreset(i);
    if (id >= 0 && 
        this->WindowLevelPresetSelector->IsPresetFiltered(id) && 
        !this->WindowLevelPresetSelector->GetPresetThumbnail(id))
      {
      this->UpdateWindowLevelPresetThumbnail(id);
      }
    }
}

//---------------------------------------------------------------------------
void vtkVVDisplayInterface::UpdateWindowLevelPresetThumbnail(int id)
{
  if (!this->Window || id < 0 ||
      !this->WindowLevelPresetSelector || 
      !this->WindowLevelPresetSelector->GetThumbnailColumnVisibility())
    {
    return;
    }

  int display_extent[6];

  vtkKW2DRenderWidget *rw2d = NULL; // vtkKW2DRenderWidget::SafeDownCast(this->Window->GetSelectedRenderWidget());

  // Try to find the view with the best aspect ratio to get a square thumbnail

  if (!rw2d)
    {
    int nb_rw = this->Window->GetNumberOfRenderWidgetsUsingSelectedDataItem();
    double ratio, smallest_ratio = VTK_DOUBLE_MAX;
    for (int i = 0; i < nb_rw; i++)
      {
      vtkKW2DRenderWidget *current_rw2d = vtkKW2DRenderWidget::SafeDownCast(
        this->Window->GetNthRenderWidgetUsingSelectedDataItem(i));
      if (current_rw2d)
        {
        current_rw2d->GetSliceDisplayExtent(
          current_rw2d->GetSlice(), display_extent);
        double x = (double)(display_extent[1] - display_extent[0] + 1);
        double y = (double)(display_extent[3] - display_extent[2] + 1);
        double z = (double)(display_extent[5] - display_extent[4] + 1);
        ratio = (x == 1 ? y / z : (y == 1 ? x / z : x / y));
        if (ratio < 1.0)
          {
          ratio = 1.0 / ratio;
          }
        ratio -= 1.0;
        if (ratio < smallest_ratio)
          {
          rw2d = current_rw2d;
          smallest_ratio = ratio;
          }
        }
      }
    }
  
  // Grab the view

  if (rw2d && rw2d->GetInput())
    {
    vtkKWImageMapToWindowLevelColors *map = rw2d->GetImageMapToRGBA();
    double old_w = map->GetWindow();
    double old_l= map->GetLevel();
    map->SetWindow(this->WindowLevelPresetSelector->GetPresetWindow(id));
    map->SetLevel(this->WindowLevelPresetSelector->GetPresetLevel(id));
    rw2d->GetSliceDisplayExtent(rw2d->GetSlice(), display_extent);
    vtkImageData *output = rw2d->GetImageMapToRGBA()->GetOutput();
    output->SetUpdateExtent(display_extent);
    output->Update();
    this->WindowLevelPresetSelector->
      BuildPresetThumbnailAndScreenshotFromImage(id, output);
    vtkCamera *cam = rw2d->GetRenderer()->GetActiveCamera();
    if (cam && cam->GetViewUp()[1] < 0)
      {
      this->WindowLevelPresetSelector->
        FlipPresetThumbnailAndScreenshotVertically(id);
      }
    map->SetWindow(old_w);
    map->SetLevel(old_l);
    }
}

//---------------------------------------------------------------------------
void vtkVVDisplayInterface::WindowLevelPresetApplyCallback(int id)
{
  double w, l;
  if (this->WindowLevelPresetSelector &&
      this->WindowLevelPresetSelector->HasPreset(id))
    {
    w = this->WindowLevelPresetSelector->GetPresetWindow(id);
    l = this->WindowLevelPresetSelector->GetPresetLevel(id);
    this->SetWindowLevelInInterface(w, l); // before
    this->SetWindowLevelInRenderWidgetsUsingSelectedDataItem(w, l);
    if (!this->WindowLevelPresetSelector->GetPresetThumbnail(id))
      {
      this->UpdateWindowLevelPresetThumbnail(id);
      }
    }
}

//---------------------------------------------------------------------------
int vtkVVDisplayInterface::WindowLevelPresetAddCallback()
{
  if (!this->WindowLevelPresetSelector)
    {
    return -1;
    }

  int id = this->WindowLevelPresetSelector->InsertPreset(
    this->WindowLevelPresetSelector->GetIdOfNthPreset(0));

  vtkVVDataItem *data = 
    this->Window ? this->Window->GetSelectedDataItem() : NULL;
  if (data)
    {
#if 0
    vtksys_stl::string clean_name = 
      vtksys::SystemTools::MakeCindentifier(data->GetName());
    this->WindowLevelPresetSelector->SetPresetGroup(id, clean_name.c_str());
#else
    // Force the group to be an empty (but non NULL) string to
    // match the regular expression in PopulateWindowLevelPresets
    // thus allowing the Window/Level preset selector to display
    // presets common to all files/data (empty group), and presets
    // specific to a file/data (group assigned to file/data name).
    this->WindowLevelPresetSelector->SetPresetGroup(id, "");
#endif
    }

  vtksys_stl::string filename(this->GetApplication()->GetUserDataDirectory());
  filename = filename + "/" + KWVolView_PRESETS_WLS_SUBDIR + "/";

  char buffer[256];
  time_t t = (time_t)
    (this->WindowLevelPresetSelector->GetPresetCreationTime(id) / 1000);
  strftime(buffer, sizeof(buffer), "%Y-%m-%d-%H-%M-%S.vvt", localtime(&t));
  filename += buffer;
  this->WindowLevelPresetSelector->SetPresetFileName(id, filename.c_str());

  vtksys_stl::string type(k_("User"));
#if 0
  vtkVVDataItemVolume *volume_data = vtkVVDataItemVolume::SafeDownCast(data);
  vtkMedicalImageProperties *med_prop =
    volume_data ? volume_data->GetMedicalImageProperties() : NULL;
  if (med_prop && med_prop->GetModality())
    {
    type = type + " (" + med_prop->GetModality() + ")";
    }
#endif
  this->WindowLevelPresetSelector->SetPresetType(id, type.c_str());

  this->WindowLevelPresetUpdateCallback(id);

  int old_apply = this->WindowLevelPresetSelector->GetApplyPresetOnSelection();
  this->WindowLevelPresetSelector->SetApplyPresetOnSelection(0);
  this->WindowLevelPresetSelector->SelectPreset(id); // select without apply
  this->WindowLevelPresetSelector->SetApplyPresetOnSelection(old_apply);
  
  return id;
}

//---------------------------------------------------------------------------
void vtkVVDisplayInterface::WindowLevelPresetUpdateCallback(int id)
{
  if (!this->WindowLevelPresetSelector ||
      !this->WindowEntry || !this->LevelEntry || id < 0)
    {
    return;
    }

  this->WindowLevelPresetSelector->SetPresetWindow(
    id, this->WindowEntry->GetWidget()->GetValueAsDouble());
  this->WindowLevelPresetSelector->SetPresetLevel(
    id, this->LevelEntry->GetWidget()->GetValueAsDouble());

  this->WindowLevelPresetHasChangedCallback(id);
}

//---------------------------------------------------------------------------
void vtkVVDisplayInterface::WindowLevelPresetHasChangedCallback(int id)
{
  // Add the window/level to the dataset med prop if not here already

  vtkVVDataItemVolume *volume_data = vtkVVDataItemVolume::SafeDownCast(
    this->Window ? this->Window->GetSelectedDataItem() : NULL);

  vtkMedicalImageProperties *med_prop =
    volume_data ? volume_data->GetMedicalImageProperties() : NULL;
  if (med_prop)
    {
    // Only update the presets that belong to us, not the common one, or
    // newly created ones
    const char *group = this->WindowLevelPresetSelector->GetPresetGroup(id);
    if (group && *group)
      {
      // Now add a preset for the new values
      
      double w = this->WindowLevelPresetSelector->GetPresetWindow(id);
      double l = this->WindowLevelPresetSelector->GetPresetLevel(id);
      
      int preset_idx = med_prop->GetWindowLevelPresetIndex(w, l);
      if (preset_idx < 0)
        {
        preset_idx = med_prop->AddWindowLevelPreset(w, l);
        }
      const char *comment = 
        this->WindowLevelPresetSelector->GetPresetComment(id);
      med_prop->SetNthWindowLevelPresetComment(preset_idx, comment);
      
      // We have just changed the value of a preset, so make sure we
      // remove from the medical presets all elements that are not
      // in the selector.

      int i, nb_presets = med_prop->GetNumberOfWindowLevelPresets();
      for (i = 0; i < nb_presets; i++)
        {
        if (med_prop->GetNthWindowLevelPreset(i, &w, &l) &&
         !this->WindowLevelPresetSelector->HasPresetWithGroupWithWindowLevel(
           group, w, l))
          {
          med_prop->RemoveWindowLevelPreset(w, l);
          }
        }
      }
    }

  this->UpdateWindowLevelPresetThumbnail(id);
  this->SaveWindowLevelPreset(id);
}

//---------------------------------------------------------------------------
int vtkVVDisplayInterface::WindowLevelPresetRemoveCallback(int id)
{
  if (!this->WindowLevelPresetSelector || !this->Window || id < 0)
    {
    return 0;
    }

  // Remove the window/level to the dataset med prop

  vtkVVDataItemVolume *volume_data = vtkVVDataItemVolume::SafeDownCast(
    this->Window ? this->Window->GetSelectedDataItem() : NULL);
  if (volume_data)
    {
    double w = this->WindowLevelPresetSelector->GetPresetWindow(id);
    double l = this->WindowLevelPresetSelector->GetPresetLevel(id);
  
    vtkMedicalImageProperties *med_prop = 
      volume_data->GetMedicalImageProperties();
    if (med_prop && med_prop->HasWindowLevelPreset(w, l))
      {
      // Check if we have actually removed all the presets with that value
      // If this is the case, remove the window/level from the med_prop
      // (i.e. we allow duplicate window/level presets in the UI, but not
      // in the med_prop)
      
      const char *group = this->WindowLevelPresetSelector->GetPresetGroup(id);
      if (group)
        {
        int count = 0, nb_presets = 
          this->WindowLevelPresetSelector->GetNumberOfPresetsWithGroup(group);
        for (int i = 0; i < nb_presets; i++)
          {
          int id2 = this->WindowLevelPresetSelector->GetIdOfNthPresetWithGroup(
            i, group);
          if (this->WindowLevelPresetSelector->GetPresetWindow(id2) == w &&
              this->WindowLevelPresetSelector->GetPresetLevel(id2) == l)
            {
            count++;
            }
          }
        if (count == 1)
          {
          med_prop->RemoveWindowLevelPreset(w, l);
          }
        }
      }
    }

  return 1;
}

//---------------------------------------------------------------------------
int vtkVVDisplayInterface::WindowLevelPresetLoadCallback()
{
  vtkKWLoadSaveDialog* load_dialog = vtkKWLoadSaveDialog::New();
  load_dialog->SetParent(this->GetWindow());
  load_dialog->Create();
  load_dialog->RetrieveLastPathFromRegistry("OpenPath");
  load_dialog->SetTitle("Load Window/Level Preset");
  load_dialog->SetDefaultExtension(".vvt");
  vtksys_stl::string filetypes;
  filetypes = filetypes + "{{" + k_("Window/Level Preset") + "} {*.vvt}}";
  load_dialog->SetFileTypes(filetypes.c_str()); 

  int id = -1;
  if (load_dialog->Invoke() && load_dialog->GetFileName())
    {
    load_dialog->SaveLastPathToRegistry("OpenPath");
    id = this->LoadWindowLevelPreset(load_dialog->GetFileName());
    if (id >= 0)
      {
      // Save that preset to the user data dir, unless it's there already

      vtksys_stl::string preset_filename(
        this->WindowLevelPresetSelector->GetPresetFileName(id));

      vtksys_stl::string preset_dir(
        vtksys::SystemTools::GetFilenamePath(preset_filename.c_str()));
      preset_dir = vtksys::SystemTools::CollapseFullPath(preset_dir.c_str());

      vtksys_stl::string new_preset_dir(
        this->GetApplication()->GetUserDataDirectory());
      new_preset_dir = new_preset_dir + "/" + KWVolView_PRESETS_WLS_SUBDIR;
      new_preset_dir = 
        vtksys::SystemTools::CollapseFullPath(new_preset_dir.c_str());

      if (strcmp(preset_dir.c_str(), new_preset_dir.c_str()))
        {
        vtksys_stl::string new_preset_filename(new_preset_dir);
        new_preset_filename = new_preset_filename + "/" + 
          vtksys::SystemTools::GetFilenameName(preset_filename.c_str());
        if (!vtksys::SystemTools::FileExists(new_preset_filename.c_str()) || 
            vtkKWMessageDialog::PopupYesNo(
              this->GetApplication(), 
              this->Window, 
              ks_("Overwrite Window/Level preset?"),
              k_("A Window/Level preset with the same name already "
                 "exists in your User Data directory. If you choose NOT to "
                 "overwrite it, the file you just picked will not be loaded "
                 "automatically the next time you launch this application.\n\n"
                 "Overwrite the preset in the User Data directory?"), 
              vtkKWMessageDialog::WarningIcon))
          {
          int previous_id = 
            this->WindowLevelPresetSelector->GetIdOfPresetWithFileName(
              new_preset_filename.c_str());
          this->WindowLevelPresetRemoveCallback(previous_id);
          this->WindowLevelPresetSelector->RemovePreset(previous_id);
          this->WindowLevelPresetSelector->SetPresetFileName(
            id, new_preset_filename.c_str());
          }
        }

      this->WindowLevelPresetHasChangedCallback(id);
      this->WindowLevelPresetSelector->SelectPreset(id);
      }
    }

  load_dialog->Delete();
 
  return id;
}

//----------------------------------------------------------------------------
void vtkVVDisplayInterface::WindowLevelPresetFilteringHasChangedCallback()
{
  vtkVVDataItem *data = 
    this->Window ? this->Window->GetSelectedDataItem() : NULL;
  if (data)
    {
    const char *constraint = 
      this->WindowLevelPresetSelector->GetPresetFilterUserSlotConstraint(
        this->WindowLevelPresetSelector->GetPresetTypeSlotName());
    if (constraint)
      {
      this->Internals->WindowLevelPresetSelectorFilters[data->GetName()] =
        constraint;
      }
    }

  this->ScheduleUpdateWindowLevelPresetThumbnails(
    this->Internals->UpdatePresetThumbnailsDelay);
}

//----------------------------------------------------------------------------
void vtkVVDisplayInterface::LoadVolumePropertyPresets()
{
  if (!this->VolumePropertyPresetSelector)
    {
    return;
    }

  // Try to load tfuncs from a KWVolView_PRESETS_TFUNCS_SUBDIR dir found inside:
  // - the user data dir
  // - the KWVolView_INSTALL_DATA_DIR dir one and two level up from the binary

  vtksys_stl::vector<vtksys_stl::string> dirs;

  dirs.push_back(this->GetApplication()->GetUserDataDirectory());

  vtksys_stl::string install_dir(
    this->GetApplication()->GetInstallationDirectory());
  install_dir += "/..";

  vtksys_stl::string try_dir;

  try_dir = install_dir;
  try_dir += KWVolView_INSTALL_DATA_DIR;
  dirs.push_back(try_dir);

  install_dir += "/..";

  try_dir = install_dir;
  try_dir += KWVolView_INSTALL_DATA_DIR;
  dirs.push_back(try_dir);

  dirs.push_back(KWVolView_SOURCE_DIR);
  
  vtksys_stl::map<vtksys_stl::string, int> loaded_filenames;
  
  // Iterate over all dirs

  vtksys_stl::vector<vtksys_stl::string>::iterator it = dirs.begin();
  vtksys_stl::vector<vtksys_stl::string>::iterator end = dirs.end();
  for (; it != end; it++)
    {
    vtksys_stl::string tfuncs_dir(*it);
    tfuncs_dir += "/";
    tfuncs_dir += KWVolView_PRESETS_TFUNCS_SUBDIR;
    tfuncs_dir += "/";

    vtkDirectory *dir = vtkDirectory::New();
    dir->Open(tfuncs_dir.c_str());

    int i, num_files = dir->GetNumberOfFiles();
    for (i = 0; i < num_files; ++i)
      {
      vtksys_stl::string filename(tfuncs_dir);
      filename += dir->GetFile(i);
      vtksys_stl::string ext = 
        vtksys::SystemTools::GetFilenameExtension(filename);
      if (!ext.compare(".vvt"))
        {
        filename = vtksys::SystemTools::CollapseFullPath(filename.c_str());
        loaded_filenames[filename] = 1;
        }
      }
    dir->Delete();
    }

  vtksys_stl::map<vtksys_stl::string, int>::iterator loaded_it = 
    loaded_filenames.begin();
  vtksys_stl::map<vtksys_stl::string, int>::iterator loaded_end = 
    loaded_filenames.end();
  for (; loaded_it != loaded_end; loaded_it++)
    {
    this->LoadVolumePropertyPreset(loaded_it->first.c_str());
    }
}

//----------------------------------------------------------------------------
int vtkVVDisplayInterface::LoadVolumePropertyPreset(const char *filename)
{
  int id = -1;

  if (filename && *filename && this->VolumePropertyPresetSelector)
    {
    vtkXMLDataElement *tfunc_elem = 
      vtkXMLUtilities::ReadElementFromFile(
        filename, this->GetApplication()->GetCharacterEncoding());
    if (tfunc_elem)
      {
      vtkVolumeProperty *volumeprop = vtkVolumeProperty::New();
      vtkXMLKWVolumeWidgetReader *xmlr = vtkXMLKWVolumeWidgetReader::New();
      if (xmlr->ParseTransferFunctionsElement(tfunc_elem, volumeprop))
        {
        id = this->VolumePropertyPresetSelector->AddPreset();
        if (id >= 0)
          {
          this->VolumePropertyPresetSelector->SetPresetVolumeProperty(
            id, volumeprop);
          const char *type = tfunc_elem->GetAttribute("Type");
          if (!type)
            {
            type = tfunc_elem->GetAttribute("Modality"); // backward compat
            if (!type)
              {
              type = tfunc_elem->GetAttribute("Group"); // backward compat
              }
            }
          this->VolumePropertyPresetSelector->SetPresetType(id, type);
          this->VolumePropertyPresetSelector->SetPresetComment(
            id, tfunc_elem->GetAttribute("Comment"));
          this->VolumePropertyPresetSelector->SetPresetFileName(
            id, filename);
          int blend_mode;
          if (tfunc_elem->GetScalarAttribute("BlendMode", blend_mode))
            {
            // A long string of incompatibility here
            // we used to use vtkKWVolumeWidget's constants for blending
            // but they are not known in KWWidgets, so we switch to
            // vtkVolumeMapper constants. Let's make sure we convert on the
            // fly when loading old presets; 1 is MIP in both classes, but
            // composite mode is 2 for vtkKWVolumeWidget, and 0 in 
            // vtkVolumeMapper. Note that 2 is a valid constant in 
            // vtkVolumeMapper for Minimum Intensity Blend, but not used
            // in VolView at the time this change was made.
            if (blend_mode == 2)
              {
              blend_mode = vtkVolumeMapper::COMPOSITE_BLEND;
              }
            this->VolumePropertyPresetSelector->SetPresetBlendMode(
              id, blend_mode);
            }
          // Also try to restore the zoom in the range
          double range[2];
          if (tfunc_elem->GetVectorAttribute(
                "RelativeVisibleParameterRange", 2, range) == 2)
            {
            this->VolumePropertyPresetSelector->SetPresetUserSlotAsDouble(
              id, "RelativeVisibleParameterRange0", range[0]);
            this->VolumePropertyPresetSelector->SetPresetUserSlotAsDouble(
              id, "RelativeVisibleParameterRange1", range[1]);
            }
          if (tfunc_elem->GetVectorAttribute(
                "RelativeVisibleValueRange", 2, range) == 2)
            {
            this->VolumePropertyPresetSelector->SetPresetUserSlotAsDouble(
              id, "RelativeVisibleValueRange0", range[0]);
            this->VolumePropertyPresetSelector->SetPresetUserSlotAsDouble(
              id, "RelativeVisibleValueRange1", range[1]);
            }
          }
        }

      if (xmlr->GetErrorLog())
        {
        vtkKWMessageDialog::PopupMessage(
          this->GetApplication(), 
          this->Window, 
          ks_("Load Preset Dialog|Load Error!"), 
          xmlr->GetErrorLog(),
          vtkKWMessageDialog::ErrorIcon);
        }
      xmlr->Delete();
      
      volumeprop->Delete();
      tfunc_elem->Delete();
      }
    }

  return id;
}

//----------------------------------------------------------------------------
void vtkVVDisplayInterface::SaveVolumePropertyPreset(int id)
{
  if (!this->VolumePropertyPresetSelector || id < 0)
    {
    return;
    }

  vtkVolumeProperty *volumeprop =
    this->VolumePropertyPresetSelector->GetPresetVolumeProperty(id);
  const char *filename = 
    this->VolumePropertyPresetSelector->GetPresetFileName(id);
  const char *type = 
    this->VolumePropertyPresetSelector->GetPresetType(id);
  const char *comment =
    this->VolumePropertyPresetSelector->GetPresetComment(id);

  if (!volumeprop || !filename)
    {
    return;
    }

  vtksys_stl::string filename_dir = 
    vtksys::SystemTools::GetFilenamePath(filename);
  if (!vtksys::SystemTools::FileExists(filename_dir.c_str()))
    {
    vtksys::SystemTools::MakeDirectory(filename_dir.c_str());
    }

  vtkXMLKWVolumeWidgetWriter *xmlw = vtkXMLKWVolumeWidgetWriter::New();
  vtkXMLDataElement *tfunc_elem = 
    xmlw->CreateTransferFunctionsElement(volumeprop);
  if (tfunc_elem)
    {
    tfunc_elem->SetAttribute("Type", type);
    tfunc_elem->SetAttribute("Comment", comment);
    if (this->VolumePropertyPresetSelector->HasPresetBlendMode(id))
      {
      tfunc_elem->SetIntAttribute(
        "BlendMode", 
        this->VolumePropertyPresetSelector->GetPresetBlendMode(id));
      }

    // Also try to save the zoom in the range
    if (this->VolumePropertyWidget)
      {
      double range[2];
      if (this->VolumePropertyPresetSelector->HasPresetUserSlot(
            id, "RelativeVisibleParameterRange0") &&
          this->VolumePropertyPresetSelector->HasPresetUserSlot(
            id, "RelativeVisibleParameterRange1"))
        {
        range[0] =
          this->VolumePropertyPresetSelector->GetPresetUserSlotAsDouble(
            id, "RelativeVisibleParameterRange0");
        range[1] =
          this->VolumePropertyPresetSelector->GetPresetUserSlotAsDouble(
            id, "RelativeVisibleParameterRange1");
        tfunc_elem->SetVectorAttribute(
          "RelativeVisibleParameterRange", 2, range);
        }
      if (this->VolumePropertyPresetSelector->HasPresetUserSlot(
            id, "RelativeVisibleValueRange0") &&
          this->VolumePropertyPresetSelector->HasPresetUserSlot(
            id, "RelativeVisibleValueRange1"))
        {
        range[0] =
          this->VolumePropertyPresetSelector->GetPresetUserSlotAsDouble(
            id, "RelativeVisibleValueRange0");
        range[1] =
          this->VolumePropertyPresetSelector->GetPresetUserSlotAsDouble(
            id, "RelativeVisibleValueRange1");
        tfunc_elem->SetVectorAttribute(
          "RelativeVisibleValueRange", 2, range);
        }
      }
    }
  xmlw->Delete();
  
  vtkIndent indent;
  if (!vtkXMLUtilities::WriteElementToFile(tfunc_elem, filename, &indent))
    {
    vtkKWMessageDialog::PopupMessage(
      this->GetApplication(), 
      this->Window, 
      ks_("Save Preset Dialog|Title|Write Error!"),
      k_("There was a problem writing the volume appearance preset.\n"
         "Please check the location and make sure you have write\n"
         "permissions and enough disk space."),
      vtkKWMessageDialog::ErrorIcon);
    }

  if (tfunc_elem)
    {
    tfunc_elem->Delete();
    }
}

//---------------------------------------------------------------------------
void vtkVVDisplayInterface::ScheduleUpdateVolumePropertyPresetThumbnails()
{
  // Make sure we aren't already scheduled.

  if (!this->GetApplication() ||
      this->Internals->UpdateVolumePropertyPresetThumbnailsTimerId.size())
    {
    return;
    }
  
  this->Internals->UpdateVolumePropertyPresetThumbnailsTimerId =
    this->Script(
      "after %d {catch {%s UpdateVolumePropertyPresetThumbnailsCallback}}", 
      this->Internals->UpdatePresetThumbnailsDelay, this->GetTclName());
}

//---------------------------------------------------------------------------
void vtkVVDisplayInterface::UpdateVolumePropertyPresetThumbnailsCallback()
{
  if (!this->VolumePropertyPresetSelector ||
      !this->VolumePropertyPresetSelector->GetThumbnailColumnVisibility())
    {
    return;
    }

  this->Internals->UpdateVolumePropertyPresetThumbnailsTimerId = "";

  // If we are waiting on the thumbnail creator, check to see if it is done

  if (this->WaitingOnThumbnailCreator)
    {
    // We are still process, need to reschedule ourselves to check later
    if (this->ThumbnailCreator->IsProcessing())
      {
      this->ScheduleUpdateVolumePropertyPresetThumbnails();
      return;
      }
    // We are done, update the preset that had been queued for thumbnail
    else if (this->ThumbnailCreator->IsValid())
      {
      this->VolumePropertyPresetSelector->
        BuildPresetThumbnailAndScreenshotFromImage(
          this->ThumbnailCreatorID, 
          this->ThumbnailCreator->GetRepresentativeImage());
      this->WaitingOnThumbnailCreator = 0;
      }
    else
      {
      vtkErrorMacro("Internal error: thumbnail creator failed");
      this->WaitingOnThumbnailCreator = 0;
      }
    }
  
  // Find the *first* preset that has no thumbnails, update it, and schedule
  // the callback again to update any other presets (if any), or schedule it
  // much later if it had failed.

  vtkKWIcon *thumb = NULL;
  int i, nb_presets = this->VolumePropertyPresetSelector->GetNumberOfPresets();
  for (i = 0; i < nb_presets; i++)
    {
    int id = this->VolumePropertyPresetSelector->GetIdOfNthPreset(i);
    if (id >= 0 && 
        this->VolumePropertyPresetSelector->IsPresetFiltered(id) &&
        this->VolumePropertyPresetSelector->GetPresetVolumeProperty(id))
      {
      thumb = this->VolumePropertyPresetSelector->GetPresetThumbnail(id);
      if (!thumb)
        {
        this->ScheduleUpdateVolumePropertyPresetThumbnail(id);
        // Reschedule to check if we are done creating the thumbnail
        this->ScheduleUpdateVolumePropertyPresetThumbnails();
        return;
        }
      }
    }
}

//---------------------------------------------------------------------------
void vtkVVDisplayInterface::UpdateVolumePropertyPresetThumbnails()
{
  if (!this->VolumePropertyPresetSelector ||
      !this->VolumePropertyPresetSelector->GetThumbnailColumnVisibility())
    {
    return;
    }

  // Update *all* preset which have no thumbnails.
  // This is done by deleting all thumbnails, and scheduling the
  // asynchronous update in the background

  int i, nb_presets = this->VolumePropertyPresetSelector->GetNumberOfPresets();
  for (i = 0; i < nb_presets; i++)
    {
    int id = this->VolumePropertyPresetSelector->GetIdOfNthPreset(i);
    if (id >= 0 && 
        this->VolumePropertyPresetSelector->IsPresetFiltered(id) &&
        this->VolumePropertyPresetSelector->GetPresetVolumeProperty(id))
      {
      this->VolumePropertyPresetSelector->SetPresetThumbnail(id, NULL);
      this->VolumePropertyPresetSelector->SetPresetScreenshot(id, NULL);
      }
    }

  this->ScheduleUpdateVolumePropertyPresetThumbnails();
}

//---------------------------------------------------------------------------
void vtkVVDisplayInterface::UpdateVolumePropertyPresetThumbnail(int id)
{
  if (!this->VolumePropertyPresetSelector ||
      !this->VolumePropertyPresetSelector->GetThumbnailColumnVisibility())
    {
    return;
    }

  // This is done by deleting the thumbnails, and scheduling the
  // asynchronous update in the background

  this->VolumePropertyPresetSelector->SetPresetThumbnail(id, NULL);
  this->VolumePropertyPresetSelector->SetPresetScreenshot(id, NULL);

  this->ScheduleUpdateVolumePropertyPresetThumbnails();
}

//---------------------------------------------------------------------------
void vtkVVDisplayInterface::ScheduleUpdateVolumePropertyPresetThumbnail(
  int id)
{
  if (!this->Window || id < 0 ||
      !this->VolumePropertyPresetSelector ||
      !this->VolumePropertyPresetSelector->GetThumbnailColumnVisibility())
    {
    return;
    }

  // Find the proper data

  vtkVVDataItem *data = this->Window->GetSelectedDataItem();
  vtkVVDataItemVolume *volume_data = vtkVVDataItemVolume::SafeDownCast(data);
  if (!volume_data)
    {
    return;
    }
  vtkImageData *input = volume_data->GetImageData();
  if (!input)
    {
    return;
    }

  // Use the thumbnail creator to create the thumbnail in a different 
  // thread.

  vtkVolumeProperty *volumeprop = 
    this->VolumePropertyPresetSelector->GetPresetVolumeProperty(id);
  int hist_flag = 
    this->VolumePropertyPresetSelector->GetPresetHistogramFlag(id);
  if (hist_flag)
    {
    vtkVolumeProperty *realprop = vtkVolumeProperty::New();
    vtkKWVolumePropertyHelper::DeepCopyVolumeProperty(
      realprop, volume_data->GetVolumeProperty()); // we unit scalar distance
    vtkKWVolumePropertyHelper::ConvertNormalizedRange(
      volumeprop, realprop, 
      volume_data->GetImageData(), 
      volumeprop->GetIndependentComponents(), 
      volume_data->GetHistogramSet());
    this->ThumbnailCreator->SetProperty(realprop);
    realprop->Delete();
    }
  else
    {
    this->ThumbnailCreator->SetProperty(volumeprop);
    }
  int blend_mode = 
    this->VolumePropertyPresetSelector->GetPresetBlendMode(id);
  int size = this->VolumePropertyPresetSelector->GetScreenshotSize();
  this->ThumbnailCreator->SetInput(input);
  this->ThumbnailCreator->SetBlendMode(blend_mode);
  this->ThumbnailCreator->SetRepresentativeImageSize(size, size);
  this->WaitingOnThumbnailCreator = 1;
  this->ThumbnailCreatorID = id;
  this->ThumbnailCreator->Start(); // Schedule
}

//---------------------------------------------------------------------------
void vtkVVDisplayInterface::VolumePropertyPresetApplyCallback(int id)
{
  if (this->VolumePropertyPresetSelector)
    {
    vtkVolumeProperty *volumeprop = 
      this->VolumePropertyPresetSelector->GetPresetVolumeProperty(id);
    if (volumeprop && this->Window)
      {
      vtkVVDataItem *data = this->Window->GetSelectedDataItem();
      vtkVVDataItemVolume *volume_data = 
        vtkVVDataItemVolume::SafeDownCast(data);
      vtkKWVolumeWidget *vw = 
        volume_data ? volume_data->GetVolumeWidget(this->Window) : NULL;

      if (volume_data)
        {
        int hist_flag = 
          this->VolumePropertyPresetSelector->GetPresetHistogramFlag(id);
        if (hist_flag)
          {
          vtkKWVolumePropertyHelper::ConvertNormalizedRange(
            volumeprop, volume_data->GetVolumeProperty(), 
            volume_data->GetImageData(), 
            volumeprop->GetIndependentComponents(), 
            volume_data->GetHistogramSet());
          }
        else
          {
          vtkKWVolumePropertyHelper::CopyVolumeProperty(
            volume_data->GetVolumeProperty(), 
            volumeprop, 
            vtkKWVolumePropertyHelper::CopySkipOpacityUnitDistance
            );
          }
        }
      if (vw)
        {
        if (this->VolumePropertyPresetSelector->HasPresetBlendMode(id))
          {
          int blend_mode;
          switch (this->VolumePropertyPresetSelector->GetPresetBlendMode(id))
            {
            case vtkVolumeMapper::MAXIMUM_INTENSITY_BLEND:
              blend_mode = vtkKWVolumeWidget::BLEND_MODE_MIP;
              break;
            default:
            case vtkVolumeMapper::COMPOSITE_BLEND:
              blend_mode = vtkKWVolumeWidget::BLEND_MODE_COMPOSITE;
              break;
            }
          vw->SetBlendMode(blend_mode);
          }
        }
      // Also try to restore the zoom in the range
      if (this->VolumePropertyWidget)
        {
        vtkKWPiecewiseFunctionEditor *opacity_editor = 
          this->VolumePropertyWidget->GetScalarOpacityFunctionEditor();
        if (this->VolumePropertyPresetSelector->HasPresetUserSlot(
              id, "RelativeVisibleParameterRange0") &&
            this->VolumePropertyPresetSelector->HasPresetUserSlot(
              id, "RelativeVisibleParameterRange1"))
          {
          opacity_editor->SetRelativeVisibleParameterRange(
            this->VolumePropertyPresetSelector->GetPresetUserSlotAsDouble(
              id, "RelativeVisibleParameterRange0"),
            this->VolumePropertyPresetSelector->GetPresetUserSlotAsDouble(
              id, "RelativeVisibleParameterRange1"));
          }
        else
          {
          opacity_editor->SetRelativeVisibleParameterRange(0.0, 1.0);
          }
        if (this->VolumePropertyPresetSelector->HasPresetUserSlot(
              id, "RelativeVisibleValueRange0") &&
            this->VolumePropertyPresetSelector->HasPresetUserSlot(
              id, "RelativeVisibleValueRange1"))
          {
          opacity_editor->SetRelativeVisibleValueRange(
            this->VolumePropertyPresetSelector->GetPresetUserSlotAsDouble(
              id, "RelativeVisibleValueRange0"),
            this->VolumePropertyPresetSelector->GetPresetUserSlotAsDouble(
              id, "RelativeVisibleValueRange1"));
          }
        else
          {
          opacity_editor->SetRelativeVisibleValueRange(0.0, 1.0);
          }
        }
      
      this->Window->RenderAllRenderWidgetsUsingSelectedDataItem();
        
      if (!this->VolumePropertyPresetSelector->GetPresetThumbnail(id))
        {
        this->UpdateVolumePropertyPresetThumbnail(id);
        }
        
      this->Update();
      }
    }
}

//---------------------------------------------------------------------------
int vtkVVDisplayInterface::VolumePropertyPresetAddCallback()
{
  if (!this->VolumePropertyPresetSelector)
    {
    return -1;
    }

  int id = this->VolumePropertyPresetSelector->InsertPreset(
    this->VolumePropertyPresetSelector->GetIdOfNthPreset(0));

  vtksys_stl::string filename(this->GetApplication()->GetUserDataDirectory());
  filename = filename + "/" + KWVolView_PRESETS_TFUNCS_SUBDIR + "/";

  char buffer[256];
  time_t t = (time_t)
    (this->VolumePropertyPresetSelector->GetPresetCreationTime(id) / 1000);
  strftime(buffer, sizeof(buffer), "%Y-%m-%d-%H-%M-%S.vvt", localtime(&t));
  filename += buffer;
  this->VolumePropertyPresetSelector->SetPresetFileName(id, filename.c_str());

  vtksys_stl::string type(k_("User"));
#if 0
  vtkVVDataItemVolume *volume_data = vtkVVDataItemVolume::SafeDownCast(
    this->Window->GetSelectedDataItem());
  vtkMedicalImageProperties *med_prop =
    volume_data ? volume_data->GetMedicalImageProperties() : NULL;
  if (med_prop && med_prop->GetModality())
    {
    type = type + " (" + med_prop->GetModality() + ")";
    }
#endif
  this->VolumePropertyPresetSelector->SetPresetType(id, type.c_str());

  this->VolumePropertyPresetUpdateCallback(id);

  int old_apply = 
    this->VolumePropertyPresetSelector->GetApplyPresetOnSelection();
  this->VolumePropertyPresetSelector->SetApplyPresetOnSelection(0);
  this->VolumePropertyPresetSelector->SelectPreset(id); // select without apply
  this->VolumePropertyPresetSelector->SetApplyPresetOnSelection(old_apply);
  
  return id;
}

//---------------------------------------------------------------------------
void vtkVVDisplayInterface::VolumePropertyPresetUpdateCallback(int id)
{
  if (!this->Window || !this->VolumePropertyPresetSelector || id < 0)
    {
    return;
    }

  vtkVVDataItemVolume *volume_data = vtkVVDataItemVolume::SafeDownCast(
    this->Window->GetSelectedDataItem());
  if (!volume_data)
    {
    return;
    }

  vtkKWVolumeWidget *vw = volume_data->GetVolumeWidget(this->Window);
  if (vw)
    {
    int blend_mode;
    switch (vw->GetBlendMode())
      {
      case vtkKWVolumeWidget::BLEND_MODE_MIP:
        blend_mode = vtkVolumeMapper::MAXIMUM_INTENSITY_BLEND;
        break;
      default:
      case vtkKWVolumeWidget::BLEND_MODE_COMPOSITE:
        blend_mode = vtkVolumeMapper::COMPOSITE_BLEND;
        break;
      }
    this->VolumePropertyPresetSelector->SetPresetBlendMode(id, blend_mode);
    }
  this->VolumePropertyPresetSelector->SetPresetHistogramFlag(id, 0);
  this->VolumePropertyPresetSelector->SetPresetVolumeProperty(
    id, volume_data->GetVolumeProperty());

  // Also try to save the zoom in the range

  if (this->VolumePropertyWidget)
    {
    vtkKWPiecewiseFunctionEditor *opacity_editor = 
      this->VolumePropertyWidget->GetScalarOpacityFunctionEditor();
    double range[2];
    opacity_editor->GetRelativeVisibleParameterRange(range);
    this->VolumePropertyPresetSelector->SetPresetUserSlotAsDouble(
      id, "RelativeVisibleParameterRange0", range[0]);
    this->VolumePropertyPresetSelector->SetPresetUserSlotAsDouble(
      id, "RelativeVisibleParameterRange1", range[1]);
    opacity_editor->GetRelativeVisibleValueRange(range);
    this->VolumePropertyPresetSelector->SetPresetUserSlotAsDouble(
      id, "RelativeVisibleValueRange0", range[0]);
    this->VolumePropertyPresetSelector->SetPresetUserSlotAsDouble(
      id, "RelativeVisibleValueRange1", range[1]);
    }

  this->UpdateVolumePropertyPresetThumbnail(id);
  this->SaveVolumePropertyPreset(id);
}

//---------------------------------------------------------------------------
void vtkVVDisplayInterface::VolumePropertyPresetHasChangedCallback(int id)
{
  this->SaveVolumePropertyPreset(id);
}

//---------------------------------------------------------------------------
int vtkVVDisplayInterface::VolumePropertyPresetLoadCallback()
{
  vtkKWLoadSaveDialog* load_dialog = vtkKWLoadSaveDialog::New();
  load_dialog->SetParent(this->GetWindow());
  load_dialog->Create();
  load_dialog->RetrieveLastPathFromRegistry("OpenPath");
  load_dialog->SetTitle("Load Volume Appearance Preset");
  load_dialog->SetDefaultExtension(".vvt");
  vtksys_stl::string filetypes;
  filetypes = filetypes + "{{" + k_("Volume Appearance Preset") + "} {*.vvt}}";
  load_dialog->SetFileTypes(filetypes.c_str()); 

  int id = -1;
  if (load_dialog->Invoke() && load_dialog->GetFileName())
    {
    load_dialog->SaveLastPathToRegistry("OpenPath");
    id = this->LoadVolumePropertyPreset(load_dialog->GetFileName());
    if (id >= 0)
      {
      // Save that preset to the user data dir, unless it's there already

      vtksys_stl::string preset_filename(
        this->VolumePropertyPresetSelector->GetPresetFileName(id));

      vtksys_stl::string preset_dir(
        vtksys::SystemTools::GetFilenamePath(preset_filename.c_str()));
      preset_dir = vtksys::SystemTools::CollapseFullPath(preset_dir.c_str());

      vtksys_stl::string new_preset_dir(
        this->GetApplication()->GetUserDataDirectory());
      new_preset_dir = new_preset_dir + "/" + KWVolView_PRESETS_TFUNCS_SUBDIR;
      new_preset_dir = 
        vtksys::SystemTools::CollapseFullPath(new_preset_dir.c_str());

      if (strcmp(preset_dir.c_str(), new_preset_dir.c_str()))
        {
        vtksys_stl::string new_preset_filename(new_preset_dir);
        new_preset_filename = new_preset_filename + "/" + 
          vtksys::SystemTools::GetFilenameName(preset_filename.c_str());
        if (!vtksys::SystemTools::FileExists(new_preset_filename.c_str()) || 
            vtkKWMessageDialog::PopupYesNo(
              this->GetApplication(), 
              this->Window, 
              ks_("Overwrite Volume Appearance preset?"),
              k_("A Volume Appearance preset with the same name already "
                 "exists in your User Data directory. If you choose NOT to "
                 "overwrite it, the file you just picked will not be loaded "
                 "automatically the next time you launch this application.\n\n"
                 "Overwrite the preset in the User Data directory?"), 
              vtkKWMessageDialog::WarningIcon))
          {
          int previous_id = 
            this->VolumePropertyPresetSelector->GetIdOfPresetWithFileName(
              new_preset_filename.c_str());
          this->VolumePropertyPresetSelector->RemovePreset(previous_id);
          this->VolumePropertyPresetSelector->SetPresetFileName(
            id, new_preset_filename.c_str());
          }
        }

      this->VolumePropertyPresetHasChangedCallback(id);
      this->VolumePropertyPresetSelector->SelectPreset(id);
      }
    }

  load_dialog->Delete();
 
  return id;
}

//----------------------------------------------------------------------------
void vtkVVDisplayInterface::VolumePropertyPresetFilteringHasChangedCallback()
{
  vtkVVDataItem *data = 
    this->Window ? this->Window->GetSelectedDataItem() : NULL;
  if (data)
    {
    const char *constraint = 
      this->VolumePropertyPresetSelector->GetPresetFilterUserSlotConstraint(
        this->VolumePropertyPresetSelector->GetPresetTypeSlotName());
    if (constraint)
      {
      this->Internals->VolumePropertyPresetSelectorFilters[data->GetName()] =
        constraint;
      }
    }

  this->ScheduleUpdateVolumePropertyPresetThumbnails();
}

//----------------------------------------------------------------------------
void vtkVVDisplayInterface::BlendModeCallback(int mode)
{
  vtkVVDataItemVolume *volume_data = vtkVVDataItemVolume::SafeDownCast(
    this->Window ? this->Window->GetSelectedDataItem() : NULL);
  vtkKWVolumeWidget *vw = 
    volume_data ? volume_data->GetVolumeWidget(this->Window) : NULL;
  if (vw && mode != vw->GetBlendMode())
    {
    vw->SetBlendMode(mode);
    this->Update(); // some UI elements state change depending on blend mode
    // In order not to confuse the user about which preset is selected,
    // clear the selection as soon as the appearance settings is changed.
    if (this->VolumePropertyPresetSelector)
      {
      this->VolumePropertyPresetSelector->ClearSelection();
      }
    }
}

//----------------------------------------------------------------------------
void vtkVVDisplayInterface::ProcessCallbackCommandEvents(vtkObject *caller,
                                                         unsigned long event,
                                                         void *calldata)
{
  double *dargs = reinterpret_cast<double*>(calldata);
  int *iarg = reinterpret_cast<int*>(calldata);

  vtkKWLightboxWidget *lw = vtkKWLightboxWidget::SafeDownCast(caller);
  vtkKWVolumeWidget *vw = vtkKWVolumeWidget::SafeDownCast(caller); 

  vtkVVDataItemVolume *volume_data = vtkVVDataItemVolume::SafeDownCast(
    this->Window->GetSelectedDataItem());

  int i, nb_rw = 0;
  if (this->Window)
    {
    nb_rw = this->Window->GetNumberOfRenderWidgetsUsingSelectedDataItem();
    }

  switch (event)
    {
    case vtkKWEvent::WindowLevelResetEvent:
      this->WindowLevelResetCallback();
      break;

    case vtkKWEvent::WindowLevelChangedEvent:
      this->SetWindowLevelInInterface(dargs[0], dargs[1]);
      this->SetWindowLevelInRenderWidgetsUsingSelectedDataItem(
        dargs[0], dargs[1]);
      break;

    case vtkKWEvent::WindowLevelChangingEvent:
      this->SetInteractiveWindowLevelInInterface(dargs[0], dargs[1]);
      this->SetInteractiveWindowLevelInRenderWidgetsUsingSelectedDataItem(
        dargs[0], dargs[1]);
      break;

    case vtkKWEvent::ImageInterpolateEvent:
      for (i = 0; i < nb_rw; i++)
        {
        vtkKW2DRenderWidget *rw2d = vtkKW2DRenderWidget::SafeDownCast(
          this->Window->GetNthRenderWidgetUsingSelectedDataItem(i));
        if (rw2d)
          {
          rw2d->SetInterpolate(*iarg);
          }
        }
      break;

    case vtkKWRenderWidgetPro::DisplayChannelsChangedEvent:
      for (i = 0; i < nb_rw; i++)
        {
        vtkKWRenderWidgetPro *rwp = vtkKWRenderWidgetPro::SafeDownCast(
          this->Window->GetNthRenderWidgetUsingSelectedDataItem(i));
        if (rwp)
          {
          rwp->SetDisplayChannels(*iarg);
          }
        }
      break;

    case vtkKWRenderWidgetPro::UseOpacityModulationChangedEvent:
      for (i = 0; i < nb_rw; i++)
        {
        vtkKWRenderWidgetPro *rwp = vtkKWRenderWidgetPro::SafeDownCast(
          this->Window->GetNthRenderWidgetUsingSelectedDataItem(i));
        if (rwp)
          {
          rwp->SetUseOpacityModulation(*iarg);
          }
        }
      break;

    case vtkKW2DRenderWidget::SliceOrientationChangedEvent:
      if (lw && volume_data)
        {
        volume_data->UpdateRenderWidgetsAnnotations();
        lw->Render();
        }

      break;

    case vtkKWEvent::VolumePropertyChangedEvent:
      for (i = 0; i < nb_rw; i++)
        {
        vtkKWRenderWidgetPro *rwp = vtkKWRenderWidgetPro::SafeDownCast(
          this->Window->GetNthRenderWidgetUsingSelectedDataItem(i));
        if (rwp)
          {
          rwp->VolumePropertyChanged();
          }
        }
      // In order not to confuse the user about which preset is selected,
      // clear the selection as soon as the appearance settings is changed.
      if (this->VolumePropertyPresetSelector)
        {
        this->VolumePropertyPresetSelector->ClearSelection();
        }
      break;

    case vtkKWEvent::VolumePropertyChangingEvent:
      for (i = 0; i < nb_rw; i++)
        {
        vtkKWRenderWidgetPro *rwp = vtkKWRenderWidgetPro::SafeDownCast(
          this->Window->GetNthRenderWidgetUsingSelectedDataItem(i));
        if (rwp)
          {
          rwp->VolumePropertyChanging();
          }
        }
      break;

    case vtkKWEvent::ProjectionTypeChangedEvent:
      this->Update();
      break;

    case vtkKWRenderWidget::RendererBackgroundColorChangedEvent:
      if (!vw)
        {
        for (i = 0; i < nb_rw; i++)
          {
          vtkKW2DRenderWidget *rw2d = vtkKW2DRenderWidget::SafeDownCast(
            this->Window->GetNthRenderWidgetUsingSelectedDataItem(i));
          if (rw2d)
            {
            rw2d->SetRendererBackgroundColor(dargs[0], dargs[1], dargs[2]);
            }
          }
        }
      break;

    case vtkKWRenderWidget::RendererBackgroundColor2ChangedEvent:
      if (!vw)
        {
        for (i = 0; i < nb_rw; i++)
          {
          vtkKW2DRenderWidget *rw2d = vtkKW2DRenderWidget::SafeDownCast(
            this->Window->GetNthRenderWidgetUsingSelectedDataItem(i));
          if (rw2d)
            {
            rw2d->SetRendererBackgroundColor2(dargs[0], dargs[1], dargs[2]);
            }
          }
        }
      break;

    case vtkKWRenderWidget::RendererGradientBackgroundChangedEvent:
      if (!vw)
        {
        for (i = 0; i < nb_rw; i++)
          {
          vtkKW2DRenderWidget *rw2d = vtkKW2DRenderWidget::SafeDownCast(
            this->Window->GetNthRenderWidgetUsingSelectedDataItem(i));
          if (rw2d)
            {
            rw2d->SetRendererGradientBackground(*iarg);
            }
          }
        }
      break;
    }

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

//---------------------------------------------------------------------------
void vtkVVDisplayInterface::PrintSelf(ostream& os, vtkIndent indent)
{
  this->Superclass::PrintSelf(os,indent);
}
