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

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

#include "vtkDirectory.h"
#include "vtkImageData.h"
#include "vtkAlgorithm.h"
#include "vtkKWApplicationPro.h"
#include "vtkKWWindow.h"
#include "vtkObjectFactory.h"
#include "vtkStructuredPoints.h"
#include "vtkStreamingDemandDrivenPipeline.h"
#include "vtkDataSetAttributes.h"
#include "vtkInformation.h"
#include "vtkMedicalImageProperties.h"
#include "vtkImageReslice.h"
#include "vtkStringArray.h"
#include "vtkPointData.h"
#include "vtkCornerAnnotation.h"
#include "vtkUnstructuredGrid.h"

// Some widgets we use

#include "vtkKWCheckButton.h"
#include "vtkKWColorImageConversionFilter.h"
#include "vtkKWEntry.h"
#include "vtkKWFrame.h"
#include "vtkKWIcon.h"
#include "vtkKWImageWidget.h"
#include "vtkKWLabel.h"
#include "vtkKWEntryWithLabel.h"
#include "vtkKWInternationalization.h"
#include "vtkKWLabelWithLabel.h"
#include "vtkKWMenu.h"
#include "vtkKWMenuButtonWithLabel.h"
#include "vtkKWLoadSaveDialog.h"
#include "vtkKWMessageDialog.h"
#include "vtkKWMenuButton.h"
#include "vtkKWOpenFileHelper.h"
#include "vtkKWOpenFileProperties.h"
#include "vtkKWOrientationFilter.h"
#include "vtkKWProgressCommand.h"
#include "vtkKWPushButton.h"
#include "vtkKWRadioButtonSet.h"
#include "vtkKWRadioButton.h"
#include "vtkKWSpinBox.h"
#include "vtkKWSpinBoxWithLabel.h"

#include "vtkDICOMCollectorOptions.h"

// The readers we support

#include "vtkAnalyzeReader.h"
#include "vtkBMPReader.h"
#include "vtkDICOMCollector.h"
#include "vtkDICOMReader.h"
#include "vtkGESignaReader.h"
#include "vtkGESignaReader3D.h"
#include "vtkJPEGReader.h"
#include "vtkLSMReader.h"
#include "vtkPICReader.h"
#include "vtkPNGReader.h"
#include "vtkPNMReader.h"
#include "vtkSLCReader.h"
#include "vtkSTKReader.h"
#include "vtkStructuredPointsReader.h"
#include "vtkTIFFReader.h"
#include "vtkMetaImageReader.h"
#include "vtkXMLImageDataReader.h"

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

#include <sys/stat.h>
#include <time.h>

#define VTK_VV_OW_MULTIPLICITY_SERIES_BUTTON_ID     0
#define VTK_VV_OW_MULTIPLICITY_SINGLE_BUTTON_ID     1

#define VTK_VV_OW_DEFAULT_POSTTEXT_LABEL     "\n"

#define VTK_VV_OW_DATA_ORIGIN_UNKNOWN -0.125
#define VTK_VV_OW_DATA_SPACING_UNKNOWN -0.125

#ifdef KWCommonPro_USE_XML_RW
#include "XML/vtkXMLKWOpenFilePropertiesReader.h"
#include "XML/vtkXMLKWOpenFilePropertiesWriter.h"
#endif

//----------------------------------------------------------------------------
vtkStandardNewMacro( vtkKWOpenWizard );
vtkCxxRevisionMacro(vtkKWOpenWizard, "$Revision: 1.96 $");

vtkCxxSetObjectMacro(vtkKWOpenWizard, PreviousReader, vtkAlgorithm);

//----------------------------------------------------------------------------
class vtkKWOpenWizardInternals
{
public:
  vtksys_stl::string ScheduleSetupRawPreviewTimerId;

};

//----------------------------------------------------------------------------
vtkKWOpenWizard::vtkKWOpenWizard()
{
  this->Internals = new vtkKWOpenWizardInternals;

  this->OpenFileHelper = vtkKWOpenFileHelper::New();
  this->OpenFileHelper->SetOpenWizard(this);

  this->LoadDialog        = NULL;
  this->ReadyToLoad       = vtkKWOpenWizard::DATA_IS_UNAVAILABLE;

  this->PreviousReader             = NULL;
  this->PreviousOpenFileProperties = NULL;

  this->Invoked           = 0;

  this->OrientationFilter          = NULL;
  this->ColorImageConversionFilter = NULL;
  
  this->SeriesFrame = NULL;
  this->PatternEntry = NULL;
  this->KMinEntry    = NULL;
  this->KMaxEntry    = NULL;

  this->SpatialAttributesFrame = NULL;
  this->OriginLabel    = NULL;
  this->SpacingLabel   = NULL;
  this->UnitsFrame     = NULL;

  this->DistanceUnitsEntry = NULL;

  int i;
  
  for (i = 0; i < VTK_MAX_VRCOMP; i++)
    {
    this->ScalarUnitsEntry[i] = 0;
    }
  
  for (i = 0; i < 3; i++)
    {
    this->OriginEntry[i]  = 0;
    this->SpacingEntry[i] = 0;
    }

  this->OrientationFrame = NULL;
  this->SliceAxisMenu    = NULL;
  this->RowAxisMenu      = NULL;
  this->ColumnAxisMenu   = NULL;

  this->MultiplicityFrame  = NULL;
  this->MultiplicityChoice = NULL;

  this->RawInfoFrame     = NULL;
  this->Preview          = NULL;
  this->IDimEntry        = NULL;
  this->JDimEntry        = NULL;
  this->KDimEntry        = NULL;
  this->ScalarTypeMenu     = NULL;
  this->ByteOrderMenu    = NULL;
  this->ScalarComponentsMenu = NULL;

  this->ComponentsFrame   = NULL;
  this->IndependentComponentsButton = NULL;

  this->ScopeFrame   = NULL;
  this->ScopeChoice = NULL;

  this->PreviewReader = NULL;

  this->OpenWithCurrentOpenFileProperties = 0;
  this->IgnoreVVIOnRead = 0;
  this->IgnoreVVIOnWrite = 0;

  this->FileNames = vtkStringArray::New();
}

//----------------------------------------------------------------------------
vtkKWOpenWizard::~vtkKWOpenWizard()
{
  delete this->Internals;
  this->Internals = NULL;

  int i;

  if (this->LoadDialog)
    {
    this->LoadDialog->Delete();
    this->LoadDialog = 0;
    }

  this->SetPreviousReader(0);

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

  if (this->OrientationFilter)
    {
    this->OrientationFilter->Delete();
    this->OrientationFilter = 0;
    }

  if (this->ColorImageConversionFilter)
    {
    this->ColorImageConversionFilter->Delete();
    this->ColorImageConversionFilter = 0;
    }

  if (this->PreviousReader)
    {
    this->PreviousReader->Delete();
    }
  
  if (this->PreviousOpenFileProperties)
    {
    this->PreviousOpenFileProperties->Delete();
    }

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

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

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

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

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

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

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

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

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

  for (i = 0; i < 3; i++)
    {
    if (this->OriginEntry[i])
      {
      this->OriginEntry[i]->Delete();
      }
    if (this->SpacingEntry[i])
      {
      this->SpacingEntry[i]->Delete();
      }
    }
  for (i = 0; i < VTK_MAX_VRCOMP; i++)
    {
    if (this->ScalarUnitsEntry[i])
      {
      this->ScalarUnitsEntry[i]->Delete();      
      }
    }
  
  if (this->OrientationFrame)
    {
    this->OrientationFrame->Delete();
    }
  
  if (this->SliceAxisMenu)
    {
    this->SliceAxisMenu->Delete();
    }
  
  if (this->RowAxisMenu)
    {
    this->RowAxisMenu->Delete();
    }
  
  if (this->ColumnAxisMenu)
    {
    this->ColumnAxisMenu->Delete();
    }
  
  if (this->MultiplicityFrame)
    {
    this->MultiplicityFrame->Delete();
    }
  
  if (this->MultiplicityChoice)
    {
    this->MultiplicityChoice->Delete();
    }
  
  if (this->Preview)
    {
    this->Preview->Close();
    this->Preview->SetParent(0);
    this->Preview->Delete();
    }
  
  if (this->PreviewReader)
    {
    this->PreviewReader->Delete();
    }
  
  if (this->RawInfoFrame)
    {
    this->RawInfoFrame->Delete();
    }
  
  if (this->IDimEntry)
    {
    this->IDimEntry->Delete();
    }
  
  if (this->JDimEntry)
    {
    this->JDimEntry->Delete();
    }
  
  if (this->KDimEntry)
    {
    this->KDimEntry->Delete();
    }
  
  if (this->ScalarTypeMenu)
    {
    this->ScalarTypeMenu->Delete();
    }
  
  if (this->ByteOrderMenu)
    {
    this->ByteOrderMenu->Delete();
    }
  
  if (this->ScalarComponentsMenu)
    {
    this->ScalarComponentsMenu->Delete();
    }
  
  if (this->ComponentsFrame)
    {
    this->ComponentsFrame->Delete();
    }
  
  if (this->IndependentComponentsButton)
    {
    this->IndependentComponentsButton->Delete();
    }

  if (this->ScopeFrame)
    {
    this->ScopeFrame->Delete();
    }
  
  if (this->ScopeChoice)
    {
    this->ScopeChoice->Delete();
    }
  
  if (this->OpenFileHelper)
    {
    this->OpenFileHelper->Delete();
    this->OpenFileHelper = 0;
    }
}

//----------------------------------------------------------------------------
void vtkKWOpenWizard::SetFileName(const char *filename)
{ 
  this->FileNames->Reset();
  if (filename && *filename)
    {
    this->FileNames->InsertNextValue(filename);
    }
}
 
//----------------------------------------------------------------------------
const char* vtkKWOpenWizard::GetFileName()
{ 
  if (this->FileNames->GetNumberOfValues())
    {
    const char *filename = this->FileNames->GetValue(0).c_str();
    return *filename ? filename : NULL;
    }
  return NULL;
}
 
//----------------------------------------------------------------------------
void vtkKWOpenWizard::CreateWidget()
{
  if (this->IsCreated())
    {
    vtkErrorMacro("widget already created " << this->GetClassName());
    return;
    }

  this->Superclass::CreateWidget();

  this->TitleLabel->SetText(ks_("Open Wizard|Title|Open File Wizard"));
  
  // Create the dialog

  if (!this->LoadDialog)
    {
    this->LoadDialog = vtkKWLoadSaveDialog::New();
    this->LoadDialog->SetApplication(this->GetApplication());
    this->LoadDialog->SetMasterWindow(this->MasterWindow);
    }

  if (!this->LoadDialog->IsCreated())
    {
    this->LoadDialog->Create();
    }

  this->PostTextLabel->SetForegroundColor(0.8, 0, 0);
}

//----------------------------------------------------------------------------
int vtkKWOpenWizard::PromptRaw()
{
  // Always clear first

  this->ForgetClientArea();

  // let the user know what is up

  this->SetPreText(
    k_("This application was unable to open your data file. If you wish, it "
       "will try to load your data file as a raw data file."));
  this->SetPostText(VTK_VV_OW_DEFAULT_POSTTEXT_LABEL);
  this->SubTitleLabel->SetText(ks_("Open Wizard|Raw File?"));

  this->NextButton->EnabledOn();
  this->NextButton->SetCommand(this, "ValidateRaw");
  if (!this->Invoked)
    {
    this->Invoked = 1;
    return this->vtkKWWizard::Invoke();
    }
  return 1;
}

//----------------------------------------------------------------------------
int vtkKWOpenWizard::ValidateRaw()
{
  // Set the back button 

  this->AddBackButtonCommand("PromptRaw");

  // Create a raw reader

  vtkImageReader2 *rdr = vtkImageReader2::SafeDownCast(this->GetLastReader());
  if (!rdr)
    {
    rdr = vtkImageReader2::New();
    this->SetLastReader(rdr);
    rdr->Delete();
    }

  // And move to the next page

  return this->PromptMultiplicity();
}

//----------------------------------------------------------------------------
void vtkKWOpenWizard::CreateMultiplicityFrame()
{
  if (!this->IsCreated())
    {
    return;
    }

  // Create the raw frame

  this->MultiplicityFrame = vtkKWFrame::New();
  this->MultiplicityFrame->SetParent(this->ClientArea);
  this->MultiplicityFrame->Create();

  this->MultiplicityChoice = vtkKWRadioButtonSet::New();
  this->MultiplicityChoice->SetParent(this->MultiplicityFrame);
  this->MultiplicityChoice->Create();
  
  vtkKWRadioButton *rb;

  rb = this->MultiplicityChoice->AddWidget(
    VTK_VV_OW_MULTIPLICITY_SINGLE_BUTTON_ID);
  rb->SetValueAsInt(VTK_VV_OW_MULTIPLICITY_SINGLE_BUTTON_ID);
  rb->SetText(k_("My data is stored in a single 2D or 3D file."));

  rb = this->MultiplicityChoice->AddWidget(
    VTK_VV_OW_MULTIPLICITY_SERIES_BUTTON_ID);
  rb->SetValueAsInt(VTK_VV_OW_MULTIPLICITY_SERIES_BUTTON_ID);
  rb->SetText(k_("My data is stored in a series of 2D files."));

  this->Script("grid %s -row 0 -column 0 -sticky nsew -padx 4 -pady 4",
               this->MultiplicityChoice->GetWidgetName());
}

//----------------------------------------------------------------------------
int vtkKWOpenWizard::PromptMultiplicity()
{
  // Always clear first

  this->ForgetClientArea();

  if (!this->MultiplicityFrame)
    {
    this->CreateMultiplicityFrame();
    }

  // let the user know what is up

  this->SetPreText(
    k_("This application can load your data as a single 2D or 3D file "
       "or as a series of 2D files. Please select how you would like to "
       "proceed.")); 
  this->SetPostText(VTK_VV_OW_DEFAULT_POSTTEXT_LABEL);
  this->SubTitleLabel->SetText(ks_("Open Wizard|Series?"));

  vtkKWRadioButton *series_button = this->MultiplicityChoice->GetWidget(
    VTK_VV_OW_MULTIPLICITY_SERIES_BUTTON_ID);
  vtkKWRadioButton *single_button = this->MultiplicityChoice->GetWidget(
    VTK_VV_OW_MULTIPLICITY_SINGLE_BUTTON_ID);

  // If we have a pattern, it's likely we are dealing with a series,
  // though the test below will refine this

  if (this->GetOpenFileProperties()->GetFilePattern())
    {
    series_button->SelectedStateOn();
    }

  // If the output is a single 2D slice, but the whole extent as set
  // in the VVI file is 3D, then we had a series

  vtkImageReader2 *rdr = vtkImageReader2::SafeDownCast(this->GetLastReader());
  if (rdr)
    {
    int *output_wext = rdr->GetOutput()->GetWholeExtent();
    int *open_prop_wext = this->GetOpenFileProperties()->GetWholeExtent();
    if (output_wext[4] == output_wext[5])
      {
      if (open_prop_wext[4] != open_prop_wext[5])
        {
        series_button->SelectedStateOn();
        }
      }
    else
      {
      // The DICOM reader manages to get the proper 3D extent even from
      // a single slice (by collecting the others)
      // (that's provided that we don't deal with 3D DICOM files stored
      // as a single file, which we haven't dealt with so far)
      if (vtkDICOMReader::SafeDownCast(rdr) && 
          open_prop_wext[4] != open_prop_wext[5])
        {
        series_button->SelectedStateOn();
        }
      else
        {
        single_button->SelectedStateOn();
        }
      }
    }

  // Nothing selected? Go for one.

  if (!series_button->GetSelectedState() && 
      !single_button->GetSelectedState())
    {
    series_button->SelectedStateOn();
    }

  this->Script("pack %s",this->MultiplicityFrame->GetWidgetName());

  this->NextButton->EnabledOn();
  this->NextButton->SetCommand(this, "ValidateMultiplicity");
  if (!this->Invoked)
    {
    this->Invoked = 1;
    return this->vtkKWWizard::Invoke();
    }
  return 1;
}

//----------------------------------------------------------------------------
int vtkKWOpenWizard::ValidateMultiplicity()
{
  // Set the back button 

  this->AddBackButtonCommand("PromptMultiplicity");

  vtkImageReader2 *rdr = vtkImageReader2::SafeDownCast(this->GetLastReader());
  if (rdr)
    {
    vtkDICOMReader *dicom_rdr = vtkDICOMReader::SafeDownCast(rdr);
    vtkDICOMCollectorOptions *options = 
      dicom_rdr ? dicom_rdr->GetDICOMCollector()->GetOptions() : NULL;
    vtkImageData *output = rdr->GetOutput();
    int output_wext[6];
    output->GetWholeExtent(output_wext);
    int is_raw = 
      (output_wext[1] <= output_wext[0]) || (output_wext[3] <= output_wext[2]);

    if (this->MultiplicityChoice->GetWidget(
          VTK_VV_OW_MULTIPLICITY_SINGLE_BUTTON_ID)->GetSelectedState())
      {
      rdr->SetFileDimensionality(is_raw ? 3 : 2);
      rdr->SetFileName(this->GetFileName());
      rdr->SetFilePattern(NULL);
      if (dicom_rdr)
        {
        int old_explore = options->GetExploreDirectory();
        options->ExploreDirectoryOff();
        if (old_explore != options->GetExploreDirectory())
          {
          dicom_rdr->GetDICOMCollector()->ClearCollectedSlices();
          }
        }
      this->GetOpenFileProperties()->SetFileDimensionality(
        rdr->GetFileDimensionality());
      this->GetOpenFileProperties()->SetFilePattern(
        rdr->GetFilePattern());
      if (is_raw)
        {
        return this->PromptRawInfo();
        }
      else
        {
        this->GetOpenFileProperties()->SetWholeExtent(
          output_wext[0], output_wext[1], 
          output_wext[2], output_wext[3], 
          0, 0);
        return this->PromptScope();
        }
      }
    else // It's a series
      {
      if (dicom_rdr)
        {
        int old_explore = options->GetExploreDirectory();
        options->ExploreDirectoryOn();
        if (old_explore != options->GetExploreDirectory())
          {
          dicom_rdr->GetDICOMCollector()->ClearCollectedSlices();
          }
        }
      rdr->SetFileDimensionality(2);
      this->GetOpenFileProperties()->SetFileDimensionality(
        rdr->GetFileDimensionality());
      if (is_raw)
        {
        return this->PromptRawInfo();
        }
      else
        {
        // If it's a series, then the output whole extent for sure is
        // either in 2D at [0,0], or in 3D for readers like the DICOM one.
        // The open prop's whole extent may have the *right* 3D extent
        // loaded from the VVI though (i.e. the extent that was picked 
        // for a RAW series for example, valuable for the next step), so
        // let's overwrite it only if the output extent is 3D.
        int *open_prop_wext = this->GetOpenFileProperties()->GetWholeExtent();
        int output_is_3d = (output_wext[4] != output_wext[5]);
        this->GetOpenFileProperties()->SetWholeExtent(
          output_wext[0], output_wext[1], 
          output_wext[2], output_wext[3], 
          output_is_3d ? output_wext[4] : open_prop_wext[4], 
          output_is_3d ? output_wext[5] : open_prop_wext[5]);
        return this->PromptSeries();
        }
      }
    }

  // And move to the next page

  return this->PromptRawInfo();
}

//----------------------------------------------------------------------------
void vtkKWOpenWizard::CreateRawInfoFrame()
{
  if (!this->IsCreated())
    {
    return;
    }

  // Create the pattern frame

  this->RawInfoFrame = vtkKWFrame::New();
  this->RawInfoFrame->SetParent(this->ClientArea);
  this->RawInfoFrame->Create();

  int label_width = 8;
  int entry_width = 4;
  int menu_label_width = 11;
  int menu_width = 15;

  int min_range = 1;
  int max_range = 1000;

  this->IDimEntry = vtkKWSpinBoxWithLabel::New();
  this->IDimEntry->SetParent(this->RawInfoFrame);
  this->IDimEntry->Create();
  this->IDimEntry->GetLabel()->SetText(ks_("Open Wizard|Columns:"));
  this->IDimEntry->SetLabelWidth(label_width);
  this->IDimEntry->GetWidget()->SetWidth(entry_width);
  this->IDimEntry->GetWidget()->SetCommand(
    this, "RawDimensionCallback");
  this->IDimEntry->GetWidget()->SetRange(min_range, max_range);
  this->IDimEntry->GetWidget()->SetRestrictValueToInteger();
  this->IDimEntry->GetWidget()->SetCommandTriggerToAnyChange();

  this->JDimEntry = vtkKWSpinBoxWithLabel::New();
  this->JDimEntry->SetParent(this->RawInfoFrame);
  this->JDimEntry->Create();
  this->JDimEntry->GetLabel()->SetText(ks_("Open Wizard|Rows:"));
  this->JDimEntry->SetLabelWidth(label_width);
  this->JDimEntry->GetWidget()->SetWidth(entry_width);
  this->JDimEntry->GetWidget()->SetCommand(
    this, "RawDimensionCallback");
  this->JDimEntry->GetWidget()->SetRange(min_range, max_range);
  this->JDimEntry->GetWidget()->SetRestrictValue(
    this->IDimEntry->GetWidget()->GetRestrictValue());
  this->JDimEntry->GetWidget()->SetCommandTrigger(
    this->IDimEntry->GetWidget()->GetCommandTrigger());

  this->KDimEntry = vtkKWSpinBoxWithLabel::New();
  this->KDimEntry->SetParent(this->RawInfoFrame);
  this->KDimEntry->Create();
  this->KDimEntry->GetLabel()->SetText(ks_("Open Wizard|Slice(s):"));
  this->KDimEntry->SetLabelWidth(label_width);
  this->KDimEntry->GetWidget()->SetWidth(entry_width);
  this->KDimEntry->GetWidget()->SetCommand(
    this, "RawDimensionCallback");
  this->KDimEntry->GetWidget()->SetRange(min_range, max_range);
  this->KDimEntry->GetWidget()->SetRestrictValue(
    this->IDimEntry->GetWidget()->GetRestrictValue());
  this->KDimEntry->GetWidget()->SetCommandTrigger(
    this->IDimEntry->GetWidget()->GetCommandTrigger());

  this->ScalarTypeMenu = vtkKWMenuButtonWithLabel::New();
  this->ScalarTypeMenu->SetParent(this->RawInfoFrame);
  this->ScalarTypeMenu->Create();
  this->ScalarTypeMenu->ExpandWidgetOn();
  this->ScalarTypeMenu->SetLabelWidth(menu_label_width);
  this->ScalarTypeMenu->GetLabel()->SetText(ks_("Open Wizard|Data Type:"));

  vtkKWMenuButton *menubutton = this->ScalarTypeMenu->GetWidget();
  menubutton->SetWidth(menu_width);
  menubutton->SetAnchorToWest();
  vtkKWMenu *menu = menubutton->GetMenu();

  menu->SetItemSelectedValueAsInt(
    menu->AddRadioButton(
      ks_("Data Type|Unsigned 8 bit"), this, "ScalarTypeCallback"),
    VTK_UNSIGNED_CHAR);

  menu->SetItemSelectedValueAsInt(
    menu->AddRadioButton(
      ks_("Data Type|Signed 8 bit"), this, "ScalarTypeCallback"),
    VTK_CHAR);

  menu->SetItemSelectedValueAsInt(
    menu->AddRadioButton(
      ks_("Data Type|Unsigned 16 bit"), this, "ScalarTypeCallback"),
    VTK_UNSIGNED_SHORT);

  menu->SetItemSelectedValueAsInt(
    menu->AddRadioButton(
      ks_("Data Type|Signed 16 bit"), this, "ScalarTypeCallback"),
    VTK_SHORT);

  menu->SetItemSelectedValueAsInt(
    menu->AddRadioButton(
      ks_("Data Type|Unsigned 32 bit"), this, "ScalarTypeCallback"),
    VTK_UNSIGNED_LONG);

  menu->SetItemSelectedValueAsInt(
    menu->AddRadioButton(
      ks_("Data Type|Signed 32 bit"), this, "ScalarTypeCallback"),
    VTK_LONG);

  menu->SetItemSelectedValueAsInt(
    menu->AddRadioButton(
      ks_("Data Type|Float"), this, "ScalarTypeCallback"),
    VTK_FLOAT);

  menu->SetItemSelectedValueAsInt(
    menu->AddRadioButton(
      ks_("Data Type|Double"), this, "ScalarTypeCallback"),
    VTK_DOUBLE);

  this->ScalarComponentsMenu = vtkKWMenuButtonWithLabel::New();
  this->ScalarComponentsMenu->SetParent(this->RawInfoFrame);
  this->ScalarComponentsMenu->Create();
  this->ScalarComponentsMenu->ExpandWidgetOn();
  this->ScalarComponentsMenu->SetLabelWidth(menu_label_width);
  this->ScalarComponentsMenu->GetLabel()->SetText(ks_("Open Wizard|Channels:"));

  menubutton = this->ScalarComponentsMenu->GetWidget();
  menubutton->SetWidth(menu_width);
  menubutton->SetAnchorToWest();
  menu = menubutton->GetMenu();

  menu->SetItemSelectedValueAsInt(
    menu->AddRadioButton(
      ks_("Open Wizard|Color Channels|1 (greyscale)"), 
      this, "ScalarComponentsCallback"),
    1);

  menu->SetItemSelectedValueAsInt(
    menu->AddRadioButton(
      ks_("Open Wizard|Color Channels|2 (greyscale)"), 
      this, "ScalarComponentsCallback"),
    2);

  menu->SetItemSelectedValueAsInt(
    menu->AddRadioButton(
      ks_("Open Wizard|Color Channels|3 (color RGB)"), 
      this, "ScalarComponentsCallback"),
    3);

  menu->SetItemSelectedValueAsInt(
    menu->AddRadioButton(
      ks_("Open Wizard|Color Channels|4 (color RGBA)"), 
      this, "ScalarComponentsCallback"),
    4);

  this->ByteOrderMenu = vtkKWMenuButtonWithLabel::New();
  this->ByteOrderMenu->SetParent(this->RawInfoFrame);
  this->ByteOrderMenu->Create();
  this->ByteOrderMenu->ExpandWidgetOn();
  this->ByteOrderMenu->SetLabelWidth(menu_label_width);
  this->ByteOrderMenu->GetLabel()->SetText(ks_("Open Wizard|Byte Order:"));

  menubutton = this->ByteOrderMenu->GetWidget();
  menubutton->SetWidth(menu_width);
  menubutton->SetAnchorToWest();
  menu = menubutton->GetMenu();

  menu->SetItemSelectedValueAsInt(
    menu->AddRadioButton(
      ks_("Byte Order|Little Endian (PC)"), this, "ByteOrderCallback"),
    vtkKWOpenFileProperties::DataByteOrderLittleEndian);
    
  menu->SetItemSelectedValueAsInt(
    menu->AddRadioButton(
      ks_("Byte Order|Big Endian (Unix)"), this, "ByteOrderCallback"),
    vtkKWOpenFileProperties::DataByteOrderBigEndian);

  this->Script("grid %s -row 0 -column 0 -sticky nwe -padx 2 -pady 2",
               this->IDimEntry->GetWidgetName());
  this->Script("grid %s -row 1 -column 0 -sticky nwe -padx 2 -pady 2",
               this->JDimEntry->GetWidgetName());

  this->Script("grid %s -row 0 -column 1 -sticky nwe -padx 2 -pady 2",
               this->ScalarComponentsMenu->GetWidgetName());
  this->Script("grid %s -row 1 -column 1 -sticky nwe -padx 2 -pady 2",
               this->ScalarTypeMenu->GetWidgetName());
  this->Script("grid %s -row 2 -column 1 -sticky nwe -padx 2 -pady 2",
               this->ByteOrderMenu->GetWidgetName());

  this->Script("grid rowconfigure %s 0 -weight 1", 
               this->RawInfoFrame->GetWidgetName());
  this->Script("grid rowconfigure %s 1 -weight 1", 
               this->RawInfoFrame->GetWidgetName());
  this->Script("grid rowconfigure %s 2 -weight 1", 
               this->RawInfoFrame->GetWidgetName());
  this->Script("grid rowconfigure %s 3 -weight 50", 
               this->RawInfoFrame->GetWidgetName());

  // Create the image preview

  this->PreviewReader = vtkImageReader2::New();

  this->Script("grid columnconfigure %s 2 -weight 1", 
               this->RawInfoFrame->GetWidgetName());
}

//----------------------------------------------------------------------------
int vtkKWOpenWizard::PromptRawInfo()
{
  // Always clear first

  this->ForgetClientArea();

  if (!this->RawInfoFrame)
    {
    this->CreateRawInfoFrame();
    }

  // Let the user know what is up

  this->SetPreText(
    k_("This application has analyzed your data and has tried to make some "
       "estimates as to the nature of your data file. Please verify the "
       "following parameters. An active preview of one slice loaded using the "
       "current parameters is displayed to aid in this process."));
  this->SetPostText(VTK_VV_OW_DEFAULT_POSTTEXT_LABEL);
  this->SubTitleLabel->SetText(ks_("Open Wizard|Raw File Information"));

  vtkImageReader2 *rdr = 
    vtkImageReader2::SafeDownCast(this->GetLastReader());
  

  // if the valid information already has reasonable values then keep them
  // Make sure the computed size is not larger than the file size

  struct stat statbuf;
  stat(this->GetFileName(), &statbuf);
  vtkKWOpenFileProperties *open_prop = this->GetOpenFileProperties();
  int *open_prop_wext = open_prop->GetWholeExtent();
  int zdim = open_prop->GetFileDimensionality() == 2 
    ? 1 : (open_prop_wext[5] - open_prop_wext[4] + 1);
  unsigned long fsize = 
    open_prop->GetNumberOfScalarComponents() *
    (open_prop_wext[1] - open_prop_wext[0] + 1) *
    (open_prop_wext[3] - open_prop_wext[2] + 1) *
    zdim *
    open_prop->GetScalarSize();
  
  if ((unsigned int)statbuf.st_size < fsize || fsize == 0)
    {
    // Analyze the data to make a best guess at the properties
    this->GetOpenFileHelper()->AnalyzeRawFile(this->GetFileName());
    }

  this->IDimEntry->GetWidget()->SetValue(
    open_prop_wext[1] - open_prop_wext[0] + 1);
  this->JDimEntry->GetWidget()->SetValue(
    open_prop_wext[3] - open_prop_wext[2] + 1);

  this->ScalarTypeMenu->GetWidget()->GetMenu()->SelectItemWithSelectedValueAsInt(
    open_prop->GetScalarType());

  this->ByteOrderMenu->SetEnabled(open_prop->GetScalarSize() > 1 ? 1 : 0);

  this->ByteOrderMenu->GetWidget()->GetMenu()->SelectItemWithSelectedValueAsInt(
    open_prop->GetDataByteOrder());

  this->ScalarComponentsMenu->GetWidget()->GetMenu()->SelectItemWithSelectedValueAsInt(
    open_prop->GetNumberOfScalarComponents());

  if (open_prop->GetFileDimensionality() == 3)
    {
    this->KDimEntry->GetWidget()->SetValue(
      open_prop_wext[5] - open_prop_wext[4] + 1);
    this->Script("grid %s -row 2 -column 0 -sticky nwe -padx 2 -pady 2",
                 this->KDimEntry->GetWidgetName());
    }
  else if (open_prop->GetFileDimensionality() == 2)
    {
    this->Script("grid forget %s", this->KDimEntry->GetWidgetName());
    }

  this->Script("pack %s -expand 1 -fill both", 
               this->RawInfoFrame->GetWidgetName());

  this->NextButton->SetCommand(this, "ValidateRawInfo");

  // Set up the preview

  this->PreviewReader->SetDataExtent(open_prop->GetWholeExtent());
  this->PreviewReader->SetDataSpacing(open_prop->GetSpacing());
  this->PreviewReader->SetDataOrigin(open_prop->GetOrigin());
  this->PreviewReader->SetDataScalarType(open_prop->GetScalarType());
  this->PreviewReader->SetNumberOfScalarComponents(
    open_prop->GetNumberOfScalarComponents());
  if (open_prop->GetDataByteOrder() != 
      vtkKWOpenFileProperties::DataByteOrderUnknown)
    {
    this->PreviewReader->SetDataByteOrder(
      open_prop->GetDataByteOrder());
    }
  this->PreviewReader->SetFileDimensionality(
    open_prop->GetFileDimensionality());
  this->PreviewReader->SetFileName(this->GetFileName());

  this->AreRawFileValuesReasonable();
  
  if (!this->Invoked)
    {
    this->Invoked = 1;
    return this->vtkKWWizard::Invoke();
    }

  return 1;
}

//----------------------------------------------------------------------------
void vtkKWOpenWizard::SetupRawPreview()
{
  // I wish we could keep the preview around, but apparently there is
  // a bug that prevents it from recovering from a bad estimate in the
  // structure of a RAW file. Can't find where despite lots of
  // debugging. In the meantime, re-create it from scratch each time
  // the user tries a new RAW structure. Do it slightly asynchronously so
  // that the user can still get interactive rates.

  if (this->Preview)
    {
    this->Preview->Close();
    this->Preview->SetParent(NULL);
    this->Preview->Delete();
    this->Preview = NULL;
    }

  int preview_was_reallocated = 0;
  if (!this->Preview)
    {
    preview_was_reallocated = 1;
    this->Preview = vtkKWImageWidget::New();
    this->Preview->ResetWindowLevelForSelectedSliceOnlyOn();
    this->Preview->SetSupportSideAnnotation(0);
    this->Preview->HasSliceControlOff();
    }

  int preview_was_created = 0;
  if (!this->Preview->IsCreated())
    {
    preview_was_created = 1;
    this->Preview->SetParent(this->RawInfoFrame);
    this->Preview->Create();
    this->Preview->SetConfigurationOptionAsInt("-width", 120);
    this->Preview->SetConfigurationOptionAsInt("-height", 120);
    }

  int reasonable = !this->AreRawFileValuesLargerThanFileSize();
  if (reasonable)
    {
    this->Preview->SetInput(this->PreviewReader->GetOutput());
    int kdim = (int)this->KDimEntry->GetWidget()->GetValue();
    int slice = (kdim < 1) ? 0 : ((kdim - 1) / 2);
    if (this->Preview->GetSlice() != slice)
      {
      this->Preview->SetSlice(slice);
      }
    if (!preview_was_reallocated)
      {
      this->Preview->ResetWindowLevel();
      this->Preview->Reset(); // calls ResetCamera and Render()
      }
    } 
  else 
    {
    this->Preview->SetInput(NULL);
    this->Preview->Render();
    }
  
  if (preview_was_created)
    {
    this->Script(
      "grid %s -row 0 -column 2 -rowspan 4 -sticky nsew -padx 2 -pady 0",
      this->Preview->GetWidgetName());
    this->Preview->AddBindings();
    }

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

//----------------------------------------------------------------------------
void vtkKWOpenWizard::ScheduleSetupRawPreview()
{
  // Already scheduled?

  if (this->Internals->ScheduleSetupRawPreviewTimerId.size() ||
      !this->IsCreated())
    {
    return;
    }

  this->Internals->ScheduleSetupRawPreviewTimerId =
    this->Script(
      "after 200 {catch {%s SetupRawPreviewCallback}}", 
      this->GetTclName());
}

//----------------------------------------------------------------------------
void vtkKWOpenWizard::SetupRawPreviewCallback()
{
  if (!this->GetApplication() || this->GetApplication()->GetInExit() ||
      !this->IsAlive())
    {
    return;
    }

  this->SetupRawPreview();
  this->Internals->ScheduleSetupRawPreviewTimerId = "";
}

//----------------------------------------------------------------------------
int vtkKWOpenWizard::AreRawFileValuesLargerThanFileSize()
{
  // Make sure the computed size is not larger than the file size

  struct stat statbuf;
  stat(this->PreviewReader->GetFileName(), &statbuf);
  
  // The call to get header size makes sure the increments are computed

  this->PreviewReader->GetHeaderSize();
  unsigned long *di = this->PreviewReader->GetDataIncrements();
  return ((unsigned int)statbuf.st_size < 
          di[this->PreviewReader->GetFileDimensionality()]);
}

//----------------------------------------------------------------------------
int vtkKWOpenWizard::AreRawFileValuesReasonable()
{
  if (!this->PreviewReader->GetFileName())
    {
    return 1;
    }

  this->ScheduleSetupRawPreview();

  int reasonable = !this->AreRawFileValuesLargerThanFileSize();
  if (reasonable)
    {
    this->SetPostText(VTK_VV_OW_DEFAULT_POSTTEXT_LABEL);
    }
  else
    {
    this->SetPostText(
      k_("Error. The current parameter values result in a file size larger "
         "than the file selected. Please correct the values."));
    }

  this->NextButton->SetEnabled(reasonable);

  return reasonable;
}

//----------------------------------------------------------------------------
void vtkKWOpenWizard::RawDimensionCallback(int)
{
  int idim = (int)this->IDimEntry->GetWidget()->GetValue();
  if (idim < 1)
    {
    idim = 1;
    }
  int jdim = (int)this->JDimEntry->GetWidget()->GetValue();
  if (jdim < 1)
    {
    jdim= 1;
    }
  int kdim = (int)this->KDimEntry->GetWidget()->GetValue();
  if (kdim < 1)
    {
    kdim = 1;
    }

  if (this->GetOpenFileProperties()->GetFileDimensionality() == 3)
    {
    this->PreviewReader->SetDataExtent(
      0, idim - 1, 0, jdim - 1, 0, kdim - 1);
    }
  else if (this->GetOpenFileProperties()->GetFileDimensionality() == 2)
    {
    this->PreviewReader->SetDataExtent(
      0, idim - 1, 0, jdim - 1, 0, 0);
    }

  this->AreRawFileValuesReasonable();
}

//----------------------------------------------------------------------------
void vtkKWOpenWizard::ScalarComponentsCallback()
{
  vtkKWMenu *menu = this->ScalarComponentsMenu->GetWidget()->GetMenu();
  int nb_comp = 
    menu->GetItemSelectedValueAsInt(menu->GetIndexOfSelectedItem());

  this->PreviewReader->SetNumberOfScalarComponents(nb_comp);

  this->AreRawFileValuesReasonable();
}

//----------------------------------------------------------------------------
void vtkKWOpenWizard::ByteOrderCallback()
{
  vtkKWMenu *menu = this->ByteOrderMenu->GetWidget()->GetMenu();
  int byte_order = 
    menu->GetItemSelectedValueAsInt(menu->GetIndexOfSelectedItem());

  this->PreviewReader->SetDataByteOrder(byte_order);

  this->AreRawFileValuesReasonable();
}

//----------------------------------------------------------------------------
void vtkKWOpenWizard::ScalarTypeCallback()
{
  vtkKWMenu *menu = this->ScalarTypeMenu->GetWidget()->GetMenu();
  int scalar_type = 
    menu->GetItemSelectedValueAsInt(menu->GetIndexOfSelectedItem());

  this->ByteOrderMenu->SetEnabled(
    vtkDataArray::GetDataTypeSize(scalar_type) > 1 ? 1 : 0);

  // If no byte order is selected yet, default to LittleEndian, otherwise we
  // crash at the next step. 

  vtkKWMenu * byteOrderMenu = this->ByteOrderMenu->GetWidget()->GetMenu();

  if (this->ByteOrderMenu->GetEnabled())
    {
    if (byteOrderMenu->GetIndexOfSelectedItem() == -1)
      {
      byteOrderMenu->SelectItemWithSelectedValueAsInt(
        vtkKWOpenFileProperties::DataByteOrderLittleEndian);

      // Invoke the callback to update the preview reader in response to changes.
      this->ByteOrderCallback();
      }
    }

  this->PreviewReader->SetDataScalarType(scalar_type);

  this->AreRawFileValuesReasonable();
}

//----------------------------------------------------------------------------
int vtkKWOpenWizard::ValidateRawInfo()
{
  // Set the back button 

  this->AddBackButtonCommand("PromptRawInfo");

  vtkKWMenu *menu = this->ScalarTypeMenu->GetWidget()->GetMenu();
  this->GetOpenFileProperties()->SetScalarType(
    menu->GetItemSelectedValueAsInt(menu->GetIndexOfSelectedItem()));

  menu = this->ByteOrderMenu->GetWidget()->GetMenu();
  this->GetOpenFileProperties()->SetDataByteOrder(
    menu->GetItemSelectedValueAsInt(menu->GetIndexOfSelectedItem()));

  menu = this->ScalarComponentsMenu->GetWidget()->GetMenu();
  this->GetOpenFileProperties()->SetNumberOfScalarComponents(
    menu->GetItemSelectedValueAsInt(menu->GetIndexOfSelectedItem()));
  
  int idim = (int)this->IDimEntry->GetWidget()->GetValue();
  if (idim < 1)
    {
    idim = 1;
    }
  int jdim = (int)this->JDimEntry->GetWidget()->GetValue();
  if (jdim < 1)
    {
    jdim= 1;
    }
  int kdim = (int)this->KDimEntry->GetWidget()->GetValue();
  if (kdim < 1)
    {
    kdim = 1;
    }

  if (this->GetOpenFileProperties()->GetFileDimensionality() == 3)
    {
    this->GetOpenFileProperties()->SetWholeExtent(
      0, idim - 1, 0, jdim - 1, 0, kdim - 1);
    return this->PromptScope();
    }

  if (this->GetOpenFileProperties()->GetFileDimensionality() == 2)
    {
    this->GetOpenFileProperties()->SetWholeExtent(
      0, idim - 1, 0, jdim - 1, 0, 0);
    }
  
  // And move to the next page

  return this->PromptSeries();
}

//----------------------------------------------------------------------------
void vtkKWOpenWizard::CreateSeriesFrame()
{
  if (!this->IsCreated())
    {
    return;
    }

  // Create the pattern frame

  this->SeriesFrame = vtkKWFrame::New();
  this->SeriesFrame->SetParent(this->ClientArea);
  this->SeriesFrame->Create();

  int label_width = 16;
  int max_range = 1000;

  this->PatternEntry = vtkKWEntryWithLabel::New();
  this->PatternEntry->SetParent(this->SeriesFrame);
  this->PatternEntry->Create();
  this->PatternEntry->GetLabel()->SetText(
    ks_("Open Wizard|Filename Pattern:"));
  this->PatternEntry->SetLabelWidth(label_width);
  this->PatternEntry->GetWidget()->SetWidth(60);
  this->PatternEntry->GetWidget()->SetCommand(this, "SeriesPatternCallback");
  this->PatternEntry->GetWidget()->SetCommandTriggerToAnyChange();

  this->KMinEntry = vtkKWSpinBoxWithLabel::New();
  this->KMinEntry->SetParent(this->SeriesFrame);
  this->KMinEntry->Create();
  this->KMinEntry->GetLabel()->SetText(
    ks_("Open Wizard|Starting Slice:"));
  this->KMinEntry->SetLabelWidth(label_width);
  this->KMinEntry->GetWidget()->SetWidth(6);
  this->KMinEntry->GetWidget()->SetRange(0, max_range);
  this->KMinEntry->GetWidget()->SetRestrictValueToInteger();
  this->KMinEntry->GetWidget()->SetCommand(this, "SeriesExtentCallback");
  this->KMinEntry->GetWidget()->SetCommandTriggerToAnyChange();

  this->KMaxEntry = vtkKWSpinBoxWithLabel::New();
  this->KMaxEntry->SetParent(this->SeriesFrame); 
  this->KMaxEntry->Create();
  this->KMaxEntry->GetLabel()->SetText(
    ks_("Open Wizard|Ending Slice:"));
  this->KMaxEntry->SetLabelWidth(label_width);
  this->KMaxEntry->GetWidget()->SetWidth(
    this->KMinEntry->GetWidget()->GetWidth());
  this->KMaxEntry->GetWidget()->SetRange(0, max_range);
  this->KMaxEntry->GetWidget()->SetRestrictValue(
    this->KMinEntry->GetWidget()->GetRestrictValue());
  this->KMaxEntry->GetWidget()->SetCommand(this, "SeriesExtentCallback");
  this->KMaxEntry->GetWidget()->SetCommandTriggerToAnyChange();
  
  this->Script("grid %s -row 0 -column 0 -sticky nsew -padx 4 -pady 4",
               this->PatternEntry->GetWidgetName());
  this->Script("grid %s -row 1 -column 0 -sticky nws -padx 4 -pady 4",
               this->KMinEntry->GetWidgetName());
  this->Script("grid %s -row 2 -column 0 -sticky nws -padx 4 -pady 4",
               this->KMaxEntry->GetWidgetName());
}

//----------------------------------------------------------------------------
int vtkKWOpenWizard::PromptSeries()
{
  // if it is not a vtkImageReader2 subclass or if it is but is set
  // to read a whole 3D file, then it cannot be handled as a series.
  // If it is DICOM, don't waste your time here, the series should have
  // been collected by the DICOM Collector already.

  vtkImageReader2 *rdr = 
    vtkImageReader2::SafeDownCast(this->GetLastReader());
  if (!rdr || 
      this->GetOpenFileProperties()->GetFileDimensionality() == 3 ||
      vtkDICOMReader::SafeDownCast(rdr))
    {
    return this->PromptScope();
    }

  // Otherwise it can be handled as a series

  // Always clear first

  this->ForgetClientArea();

  if (!this->SeriesFrame)
    {
    this->CreateSeriesFrame();
    }

  // Promt the user if this is a good series range & pattern
  
  this->SetPreText(
    k_("The file pattern is used to compute a unique filename for each slice. "
       "In the pattern the %i (or %03i) will be replaced with the slice "
       "number. Please check that this is the correct pattern and modify it "
       "if neccessary. All slices between and including the starting and "
       "ending slices will be loaded.")); 
  this->SetPostText(VTK_VV_OW_DEFAULT_POSTTEXT_LABEL);
  this->SubTitleLabel->SetText(ks_("Open Wizard|Series Information"));

  // If the extent is all inclusive (2D), and/or the pattern is NULL, let's
  // try to guess.

  int zmin = this->GetOpenFileProperties()->GetWholeExtent()[4];
  int zmax = this->GetOpenFileProperties()->GetWholeExtent()[5];
  const char *pattern = 
    this->GetOpenFileProperties()->GetAbsoluteFilePatternForFile(
      this->GetFileName());
  
  char *guess_pattern = NULL;
  if (zmin == zmax || !pattern || !*pattern)
    {
    int guess_zmin = 0, guess_zmax = 0;
    guess_pattern = new char[strlen(this->GetFileName()) * 2];
    if (vtkKWOpenFileHelper::FindSeriesPattern(
          this->GetFileName(), guess_pattern, &guess_zmin, &guess_zmax))
      {
      if (!pattern || !*pattern)
        {
        pattern = guess_pattern;
        }
      if (zmin == zmax)
        {
        if (guess_zmin >= 0)
          {
          zmin = guess_zmin;
          }
        if (guess_zmax >= 0)
          {
          zmax = guess_zmax;
          }
        }
      }
    }

  this->KMinEntry->GetWidget()->SetValue(zmin);
  this->KMaxEntry->GetWidget()->SetValue(zmax);
  this->PatternEntry->GetWidget()->SetValue(pattern);

  delete [] guess_pattern;

  this->Script("pack %s",this->SeriesFrame->GetWidgetName());

  this->AreSeriesValuesReasonable();

  this->NextButton->SetCommand(this, "ValidateSeries");
  if (!this->Invoked)
    {
    this->Invoked = 1;
    return this->vtkKWWizard::Invoke();
    }
  return 1;
}

//----------------------------------------------------------------------------
int vtkKWOpenWizard::AreSeriesValuesReasonable()
{
  int kmin = (int)this->KMinEntry->GetWidget()->GetValue();
  int kmax = (int)this->KMaxEntry->GetWidget()->GetValue();
  if (kmax < kmin)
    {
    int tmp = kmax;
    kmax = kmin;
    kmin = tmp;
    }

  // Verify that the range of files exists

  int i, valid = 1;
  vtksys_stl::string pattern(this->PatternEntry->GetWidget()->GetValue());
  char *testFileName = new char [strlen(pattern.c_str()) + 50];
  for (i = kmin; i <= kmax; ++i)
    {
    sprintf(testFileName, pattern.c_str(), i);
    if(!vtksys::SystemTools::FileExists(testFileName))
      {
      valid = 0;
      break;
      }
    }
  delete [] testFileName;
  
  if (valid)
    {
    this->SetPostText(VTK_VV_OW_DEFAULT_POSTTEXT_LABEL);
    this->NextButton->EnabledOn();
    return 1;
    }

  this->SetPostText(
    k_("Error. Files cannot be found for the series range specified. "
       "Please verify that the series range is specified correctly "
       "and that there are no missing slices."));

  this->NextButton->EnabledOff();

  return 0;
}

//----------------------------------------------------------------------------
void vtkKWOpenWizard::SeriesPatternCallback(const char*)
{
  this->AreSeriesValuesReasonable();
}

//----------------------------------------------------------------------------
void vtkKWOpenWizard::SeriesExtentCallback(int)
{
  this->AreSeriesValuesReasonable();
}

//----------------------------------------------------------------------------
const char *vtkKWOpenWizard::GetSeriesPattern()
{ 
  return this->PatternEntry ? this->PatternEntry->GetWidget()->GetValue() : NULL;
}
  
//----------------------------------------------------------------------------
int vtkKWOpenWizard::GetSeriesMinimum()
{ 
  return this->KMinEntry ? (int)this->KMinEntry->GetWidget()->GetValue() : 0;
}

//----------------------------------------------------------------------------
int vtkKWOpenWizard::GetSeriesMaximum()
{ 
  return this->KMaxEntry ? (int)this->KMaxEntry->GetWidget()->GetValue() : 0;
}
 
//----------------------------------------------------------------------------
void vtkKWOpenWizard::SetSeriesPattern(const char * pattern)
{ 
  if( this->PatternEntry )
    {
    this->PatternEntry->GetWidget()->SetValue(pattern);
    }
}  

//----------------------------------------------------------------------------
void vtkKWOpenWizard::SetSeriesMinimum(int minimum)
{ 
  if( this->KMinEntry )
    {
    this->KMinEntry->GetWidget()->SetValue(minimum);
    }
}  

//----------------------------------------------------------------------------
void vtkKWOpenWizard::SetSeriesMaximum(int maximum)
{ 
  if( this->KMaxEntry )
    {
    this->KMaxEntry->GetWidget()->SetValue(maximum);
    }
}  

//----------------------------------------------------------------------------
int vtkKWOpenWizard::ValidateSeries()
{
  // Set the back button 

  this->AddBackButtonCommand("PromptSeries");

  // At this point the values are valid otherwise we would not have been
  // able to press the "Next" button

  int kmin = (int)this->KMinEntry->GetWidget()->GetValue();
  int kmax = (int)this->KMaxEntry->GetWidget()->GetValue();
  if (kmax < kmin)
    {
    int tmp = kmax;
    kmax = kmin;
    kmin = tmp;
    }

  vtksys_stl::string pattern(this->PatternEntry->GetWidget()->GetValue());
  
  // Modify the file name to be the first slice of this series
  // NO, this should not be done (and why was it??)
  //sprintf(testFileName,pattern,kmin);
  //this->SetFileName(testFileName);
  //delete [] testFileName;

  int *open_prop_wext = this->GetOpenFileProperties()->GetWholeExtent();
  this->GetOpenFileProperties()->SetWholeExtent(
    open_prop_wext[0], open_prop_wext[1], 
    open_prop_wext[2], open_prop_wext[3], 
    kmin, kmax);
  this->GetOpenFileProperties()->SetFilePattern(pattern.c_str());

  // And move to the next page

  return this->PromptScope();
}

//----------------------------------------------------------------------------
void vtkKWOpenWizard::CreateComponentsFrame()
{
  if (!this->IsCreated())
    {
    return;
    }

  // Create the component frame

  this->ComponentsFrame = vtkKWFrame::New();
  this->ComponentsFrame->SetParent(this->ClientArea);
  this->ComponentsFrame->Create();

  this->IndependentComponentsButton = vtkKWCheckButton::New();
  this->IndependentComponentsButton->SetParent(this->ComponentsFrame);
  this->IndependentComponentsButton->Create();
  this->IndependentComponentsButton->SetText(
    ks_("Open Wizard|Components are independent"));

  this->Script("grid %s -row 0 -column 0 -sticky nsew -padx 4 -pady 4",
               this->IndependentComponentsButton->GetWidgetName());
}

//----------------------------------------------------------------------------
void vtkKWOpenWizard::CreateScopeFrame()
{
  if (!this->IsCreated())
    {
    return;
    }

  // Create the component frame

  this->ScopeFrame = vtkKWFrame::New();
  this->ScopeFrame->SetParent(this->ClientArea);
  this->ScopeFrame->Create();

  this->ScopeChoice = vtkKWRadioButtonSet::New();
  this->ScopeChoice->SetParent(this->ScopeFrame);
  this->ScopeChoice->Create();
  
  vtkKWRadioButton *rb;

  rb = this->ScopeChoice->AddWidget(vtkKWOpenFileProperties::ScopeMedical);
  rb->SetValueAsInt(vtkKWOpenFileProperties::ScopeMedical);
  rb->SetText(k_("Medical data"));

  rb = this->ScopeChoice->AddWidget(vtkKWOpenFileProperties::ScopeScientific);
  rb->SetValueAsInt(vtkKWOpenFileProperties::ScopeScientific);
  rb->SetText(k_("Scientific/Generic data"));

  this->Script("grid %s -row 0 -column 0 -sticky nsew -padx 4 -pady 4",
               this->ScopeChoice->GetWidgetName());
}

//----------------------------------------------------------------------------
int vtkKWOpenWizard::PromptScope()
{
  // Always clear first

  this->ForgetClientArea();

  if (!this->ScopeFrame)
    {
    this->CreateScopeFrame();
    }

  // let the user know what is up

  this->SetPreText(
    k_("This application can set up default parameters specific to the "
       "scope of your data. For example, the annotations will be set to an "
       "XYZ coordinate system for scientific data, as opposed to LPS "
       " (Left, Posterior, Superior) for medical data.")); 
  this->SetPostText(VTK_VV_OW_DEFAULT_POSTTEXT_LABEL);
  this->SubTitleLabel->SetText(ks_("Open Wizard|Scope"));

  vtkKWRadioButton *medical_button = this->ScopeChoice->GetWidget(
    vtkKWOpenFileProperties::ScopeMedical);
  vtkKWRadioButton *scientific_button = this->ScopeChoice->GetWidget(
    vtkKWOpenFileProperties::ScopeScientific);

  if (this->GetOpenFileProperties()->GetScope() == 
      vtkKWOpenFileProperties::ScopeMedical)
    {
    medical_button->SelectedStateOn();
    }
  if (this->GetOpenFileProperties()->GetScope() == 
      vtkKWOpenFileProperties::ScopeScientific)
    {
    scientific_button->SelectedStateOn();
    }

  // Nothing selected? Go for one.

  if (!medical_button->GetSelectedState() && 
      !scientific_button->GetSelectedState())
    {
    scientific_button->SelectedStateOn();
    }

  this->Script("pack %s", this->ScopeFrame->GetWidgetName());

  this->NextButton->EnabledOn();
  this->NextButton->SetCommand(this, "ValidateScope");
  if (!this->Invoked)
    {
    this->Invoked = 1;
    return this->vtkKWWizard::Invoke();
    }
  return 1;
}

//----------------------------------------------------------------------------
int vtkKWOpenWizard::ValidateScope()
{
  // Set the back button 

  this->AddBackButtonCommand("PromptScope");

  if (this->ScopeChoice->GetWidget(
        vtkKWOpenFileProperties::ScopeMedical)->GetSelectedState())
    {
    this->GetOpenFileProperties()->SetScope(
      vtkKWOpenFileProperties::ScopeMedical);
    }
  if (this->ScopeChoice->GetWidget(
        vtkKWOpenFileProperties::ScopeScientific)->GetSelectedState())
    {
    this->GetOpenFileProperties()->SetScope(
      vtkKWOpenFileProperties::ScopeScientific);
    }

  // And move to the next page

  return this->PromptSpatialAttributes();
}

//----------------------------------------------------------------------------
void vtkKWOpenWizard::CreateSpatialAttributesFrame()
{
  if (!this->IsCreated())
    {
    return;
    }

  int i;
  
  // Create the attribute frame

  this->SpatialAttributesFrame = vtkKWFrame::New();
  this->SpatialAttributesFrame->SetParent(this->ClientArea);
  this->SpatialAttributesFrame->Create();

  this->OriginLabel = vtkKWLabel::New();
  this->OriginLabel->SetParent(this->SpatialAttributesFrame);
  this->OriginLabel->Create();
  this->OriginLabel->SetText(ks_("Open Wizard|Origin:"));

  this->SpacingLabel = vtkKWLabel::New();
  this->SpacingLabel->SetParent(this->SpatialAttributesFrame);
  this->SpacingLabel->Create();
  this->SpacingLabel->SetText(ks_("Open Wizard|Spacing:"));

  this->Script("grid %s -row 0 -column 0 -sticky nsew -padx 4 -pady 4",
               this->OriginLabel->GetWidgetName());

  this->Script("grid %s -row 1 -column 0 -sticky nsew -padx 4 -pady 4",
               this->SpacingLabel->GetWidgetName());

  for (i = 0; i < 3; i++)
    {
    this->OriginEntry[i] = vtkKWEntry::New();
    this->OriginEntry[i]->SetParent(this->SpatialAttributesFrame);
    this->OriginEntry[i]->Create();
    this->OriginEntry[i]->SetWidth(16);

    this->SpacingEntry[i] = vtkKWEntry::New();
    this->SpacingEntry[i]->SetParent(this->SpatialAttributesFrame);
    this->SpacingEntry[i]->Create();
    this->SpacingEntry[i]->SetWidth(this->OriginEntry[i]->GetWidth());

    this->Script("grid %s -row 0 -column %i -sticky nsew -padx 2 -pady 4",
                 this->OriginEntry[i]->GetWidgetName(), i + 1);
    this->Script("grid %s -row 1 -column %i -sticky nsew -padx 2 -pady 4",
                 this->SpacingEntry[i]->GetWidgetName(), i + 1);
    this->Script("grid columnconfigure %s %i -weight 1", 
                 this->SpatialAttributesFrame->GetWidgetName(), i + 1);
    }

  this->Script("grid rowconfigure %s 0 -weight 1", 
               this->SpatialAttributesFrame->GetWidgetName());
  this->Script("grid rowconfigure %s 1 -weight 1", 
               this->SpatialAttributesFrame->GetWidgetName());
}

//----------------------------------------------------------------------------
int vtkKWOpenWizard::PromptSpatialAttributes()
{
  // For certain file types, skip this page

  if (vtkDICOMReader::SafeDownCast(this->GetLastReader()) ||
      vtkGESignaReader::SafeDownCast(this->GetLastReader()) ||
      vtkGESignaReader3D::SafeDownCast(this->GetLastReader()) ||
      vtkStructuredPointsReader::SafeDownCast(this->GetLastReader()) ||
      vtkMetaImageReader::SafeDownCast(this->GetLastReader())  ||
      vtkXMLImageDataReader::SafeDownCast(this->GetLastReader()))
    {
    return this->PromptComponents();
    }

  int i;
  
  // Always clear first

  this->ForgetClientArea();

  if (!this->SpatialAttributesFrame)
    {
    this->CreateSpatialAttributesFrame();
    }

  this->SetPreText(
    k_("Please verify that the data's origin, and pixel spacing are correct. "
       "If this application was unable to determine the origin or spacing then "
       "it will list 'Unknown' as the value. If you leave the origin as "
       "'Unknown' it will be assigned a default value of 0.0. If you leave "
       "the spacing as 'Unknown' it will be assigned a default value of 1.0.")
    ); 
  this->SetPostText(VTK_VV_OW_DEFAULT_POSTTEXT_LABEL);
  this->SubTitleLabel->SetText(ks_("Open Wizard|Spatial Attributes"));
  
  for (i = 0; i < 3; i++)
    {
    if (this->GetOpenFileProperties()->GetOrigin()[i] == 
        VTK_VV_OW_DATA_ORIGIN_UNKNOWN)
      {
      this->OriginEntry[i]->SetValue(ks_("Open Wizard|Unknown")); 
      }
    else
      {
      this->OriginEntry[i]->SetValueAsDouble(
        this->GetOpenFileProperties()->GetOrigin()[i]); 
      }
    if (this->GetOpenFileProperties()->GetSpacing()[i] == 
        VTK_VV_OW_DATA_SPACING_UNKNOWN)
      {
      this->SpacingEntry[i]->SetValue(ks_("Open Wizard|Unknown")); 
      }
    else
      {
      this->SpacingEntry[i]->SetValueAsDouble(
        this->GetOpenFileProperties()->GetSpacing()[i]); 
      }
    }
  
  this->Script("pack %s",this->SpatialAttributesFrame->GetWidgetName());

  this->NextButton->SetCommand(this, "ValidateSpatialAttributes");
  if (!this->Invoked)
    {
    this->Invoked = 1;
    return this->vtkKWWizard::Invoke();
    }
  return 1;
}

//----------------------------------------------------------------------------
int vtkKWOpenWizard::ValidateSpatialAttributes()
{
  // Set the back button 

  this->AddBackButtonCommand("PromptSpatialAttributes");

  // Assign values to OpenFileProperties
  // really should check that they entered some useful values here  

  double origin[3];
  double spacing[3];
  const char *value;
  int i;
  
  for (i = 0; i < 3; ++i)
    {
    value = this->OriginEntry[i]->GetValue();
    if (!strcmp(ks_("Open Wizard|Unknown"), value))
      {
      origin[i] = 0;
      }
    else
      {
      origin[i] = atof(value);
      }

    value = this->SpacingEntry[i]->GetValue();
    if (!strcmp(ks_("Open Wizard|Unknown"), value))
      {
      spacing[i] = 1;
      }
    else
      {
      spacing[i] = atof(value);
      }
    }
  
  this->GetOpenFileProperties()->SetSpacing(spacing);
  this->GetOpenFileProperties()->SetOrigin(origin);
    
  // And move to the next page

  return this->PromptComponents();
}


//----------------------------------------------------------------------------
int vtkKWOpenWizard::PromptComponents()
{
  // Only display this page if there is more than one componment

  if (this->GetOpenFileProperties()->GetNumberOfScalarComponents() <= 1)
    {
    return this->PromptUnits();
    }
  
  // Always clear first

  this->ForgetClientArea();

  if (!this->ComponentsFrame)
    {
    this->CreateComponentsFrame();
    }

  vtksys_stl::string msg;
  switch (this->GetOpenFileProperties()->GetNumberOfScalarComponents())
    {
    case 2:
      msg = k_(
"It appears that you are loading a volume with two scalar components "
"per voxel. This application can treat these components in two different ways. "
"If you treat the components as independent then each component will have its "
"own color and opacity transfer functions. An example of such a volume would "
"be where the first component is temperature and the second component is "
"density.\n\nIf the components are not independent then they will be considered "
"to represent intensity and opacity respectively. Please verify that the "
"setting of Independent Components is correct.");
      break;
      
    case 3:
      msg = k_(
"It appears that you are loading a volume with three scalar components per"
"voxel. This application can treat these components in two ways. If you treat "
"the components as independent then each component will have its own color "
"and opacity transfer functions. An example of such a volume would be where "
"the first component is temperature, the second density, and the third "
"pressure.\n\nIf the components are not independent then they will be assumed "
"to represent color as Red, Green, Blue as is typical for physical slices or "
"color photographs. Please verify that the setting of Independent Components "
"is correct.");
   break;

    case 4:
      msg = k_(
"It appears that you are loading a volume with four scalar components per "
"voxel. This application can treat these components in two different ways. "
"If you treat the components as independent then each component will have its "
"own color and opacity transfer functions.\n\nIf the components are not "
"independent then they will be considered to represent color and opacity as "
"Red, Green, Blue, Opacity respectively. Please verify that the setting of "
"Independent Components is corrent.");
      break;

    }

  this->SetPreText(msg.c_str());
  this->SetPostText(VTK_VV_OW_DEFAULT_POSTTEXT_LABEL);
  this->SubTitleLabel->SetText(ks_("Open Wizard|Components"));
  
  this->Script("pack %s",this->ComponentsFrame->GetWidgetName());

  this->IndependentComponentsButton->SetSelectedState(
    this->GetOpenFileProperties()->GetIndependentComponents());

  this->NextButton->SetCommand(this, "ValidateComponents");
  if (!this->Invoked)
    {
    this->Invoked = 1;
    return this->vtkKWWizard::Invoke();
    }
  return 1;
}

//----------------------------------------------------------------------------
int vtkKWOpenWizard::ValidateComponents()
{
  // Set the back button 

  this->AddBackButtonCommand("PromptComponents");

  this->GetOpenFileProperties()->SetIndependentComponents(
    this->IndependentComponentsButton->GetSelectedState());
    
  // And move to the next page

  return this->PromptUnits();
}

//----------------------------------------------------------------------------
void vtkKWOpenWizard::CreateUnitsFrame()
{
  if (!this->IsCreated())
    {
    return;
    }

  int i;
  
  // Create the units frame

  this->UnitsFrame = vtkKWFrame::New();
  this->UnitsFrame->SetParent(this->ClientArea);
  this->UnitsFrame->Create();

  this->DistanceUnitsEntry = vtkKWEntryWithLabel::New();
  this->DistanceUnitsEntry->SetParent(this->UnitsFrame);
  this->DistanceUnitsEntry->Create();
  this->DistanceUnitsEntry->GetLabel()->SetText(
    ks_("Open Wizard|Distance Units:"));

  this->Script("grid %s -row 0 -column 0 -sticky nsew -padx 4 -pady 4",
               this->DistanceUnitsEntry->GetWidgetName());

  char buffer[256];
  for (i = 0; i < VTK_MAX_VRCOMP; i++)
    {
    this->ScalarUnitsEntry[i] = vtkKWEntryWithLabel::New();
    this->ScalarUnitsEntry[i]->SetParent(this->UnitsFrame);
    this->ScalarUnitsEntry[i]->Create();
    sprintf(buffer, ks_("Open Wizard|Units of Component %d:"), i + 1);
    this->ScalarUnitsEntry[i]->GetLabel()->SetText(buffer);
    }

  this->Script("grid %s -row 0 -column 1 -sticky nsew -padx 4 -pady 4",
               this->ScalarUnitsEntry[0]->GetWidgetName());
}

//----------------------------------------------------------------------------
int vtkKWOpenWizard::PromptUnits()
{
  // Fill some defaults

  if (vtkLSMReader::SafeDownCast(this->GetLastReader()))
    {
    this->GetOpenFileProperties()->SetDistanceUnits("µm");
    }

  vtkDICOMReader *dicom_reader = 
    vtkDICOMReader::SafeDownCast(this->GetLastReader());
  if (dicom_reader)
    {
    this->GetOpenFileProperties()->SetDistanceUnits("mm");
    }

  // STK files may have units in them
  if (vtkSTKReader::SafeDownCast(this->GetLastReader()))
    {
    const char *units = 
      vtkSTKReader::SafeDownCast(this->GetLastReader())->GetUnits();
    if (units && units[0] != '\0')
      {
      this->GetOpenFileProperties()->SetDistanceUnits(units);
      }
    }

  int nb_components = 
    this->GetOpenFileProperties()->GetNumberOfScalarComponents();

  if (!this->GetOpenFileProperties()->GetIndependentComponents() && 
      nb_components >= 3)
    {
    if (!this->GetOpenFileProperties()->GetScalarUnits(0) && 
        !this->GetOpenFileProperties()->GetScalarUnits(1) && 
        !this->GetOpenFileProperties()->GetScalarUnits(2))
      {
      this->GetOpenFileProperties()->SetScalarUnits(
        0, ks_("Open Wizard|Units|red"));
      this->GetOpenFileProperties()->SetScalarUnits(
        1, ks_("Open Wizard|Units|green"));
      this->GetOpenFileProperties()->SetScalarUnits(
        2, ks_("Open Wizard|Units|blue"));
      }
    if (!this->GetOpenFileProperties()->GetScalarUnits(3) && 
        nb_components >= 4)
      {
      this->GetOpenFileProperties()->SetScalarUnits(
        3, ks_("Open Wizard|Units|average"));
      }
    }

  int i;
  if (dicom_reader)
    {
    vtkDICOMCollector *collector = dicom_reader->GetDICOMCollector();
    if (collector)
      {
      vtkMedicalImageProperties *hinfo = 
        collector->GetCurrentImageMedicalProperties();
      if (hinfo && hinfo->GetModality())
        {
        if (!strcmp(hinfo->GetModality(), "CT"))
          {
          for (i = 0; i < nb_components; i++)
            {
            this->GetOpenFileProperties()->SetScalarUnits(i, "HU");
            }
          }
        }
      }
    }

  // If DICOM, we already know enough

  if (dicom_reader)
    {
    return this->ValidateUnits();
    }

  // Always clear first

  if (!this->IsCreated())
    {
    return 1;
    }

  this->ForgetClientArea();

  if (!this->UnitsFrame)
    {
    this->CreateUnitsFrame();
    }

  this->SetPreText(
    k_("Please verify the unit labels for this data file. "
       "If the application was unable to determine the units then it will "
       "list an 'Unknown' value.")); 
  this->SetPostText(VTK_VV_OW_DEFAULT_POSTTEXT_LABEL);
  this->SubTitleLabel->SetText(ks_("Open Wizard|Units"));
  
  this->DistanceUnitsEntry->GetWidget()->SetValue(
    this->GetOpenFileProperties()->GetDistanceUnits() ? 
    this->GetOpenFileProperties()->GetDistanceUnits() : 
    ks_("Open Wizard|Unknown"));

  for (i = 0; i < VTK_MAX_VRCOMP; i++)
    {
    if (i < nb_components)
      {
      this->ScalarUnitsEntry[i]->GetWidget()->SetValue(
        this->GetOpenFileProperties()->GetScalarUnits(i) ? 
        this->GetOpenFileProperties()->GetScalarUnits(i) : 
        ks_("Open Wizard|Unknown"));
      this->Script("grid %s -row %d -column 1 -sticky nsew -padx 4 -pady 4",
                   this->ScalarUnitsEntry[i]->GetWidgetName(), i);
      }
    else
      {
      this->Script("grid forget %s", 
                   this->ScalarUnitsEntry[i]->GetWidgetName());
      }
    }

  this->Script("pack %s", this->UnitsFrame->GetWidgetName());

  this->NextButton->SetCommand(this, "ValidateUnits");
  if (!this->Invoked)
    {
    this->Invoked = 1;
    return this->vtkKWWizard::Invoke();
    }

  return 1;
}

//----------------------------------------------------------------------------
int vtkKWOpenWizard::ValidateUnits()
{
  // Set the back button 

  this->AddBackButtonCommand("PromptUnits");

  if (this->DistanceUnitsEntry &&
      *this->DistanceUnitsEntry->GetWidget()->GetValue() &&
      strcmp(this->DistanceUnitsEntry->GetWidget()->GetValue(), 
             ks_("Open Wizard|Unknown")))
    {
    this->GetOpenFileProperties()->SetDistanceUnits(
      this->DistanceUnitsEntry->GetWidget()->GetValue());
    }

  int i;
  for (i = 0; 
       i < this->GetOpenFileProperties()->GetNumberOfScalarComponents(); i++)
    {
    if (this->ScalarUnitsEntry && 
        this->ScalarUnitsEntry[i] &&
        *this->ScalarUnitsEntry[i]->GetWidget()->GetValue() &&
        strcmp(this->ScalarUnitsEntry[i]->GetWidget()->GetValue(), 
               ks_("Open Wizard|Unknown")))
      {
      this->GetOpenFileProperties()->SetScalarUnits(
        i, this->ScalarUnitsEntry[i]->GetWidget()->GetValue());
      }
    }
    
  // And move to the next page

  return this->PromptOrientation();
}

//----------------------------------------------------------------------------
void vtkKWOpenWizard::CreateOrientationFrame()
{
  if (!this->IsCreated())
    {
    return;
    }

  // Create the orientation frame

  this->OrientationFrame = vtkKWFrame::New();
  this->OrientationFrame->SetParent(this->ClientArea);
  this->OrientationFrame->Create();

  int label_width = 150; // in pixels, now that we have an icon
  int menu_width = 22;

  /* 
   * Resource generated for file:
   *    KWScanIncreasingColumns.png (zlib, base64) (image file)
   */
  static const unsigned int  image_KWScanIncreasingColumns_width          = 25;
  static const unsigned int  image_KWScanIncreasingColumns_height         = 25;
  static const unsigned int  image_KWScanIncreasingColumns_pixel_size     = 4;
  static const unsigned long image_KWScanIncreasingColumns_length         = 172;
  static const unsigned long image_KWScanIncreasingColumns_decoded_length = 2500;

  static const unsigned char image_KWScanIncreasingColumns[] = 
    "eNr7//8/w/9hhL28vP5TG2OzY8OGDXA8ZcoUiviD3Y5jx/b9v3r1xP+NG1eRZQdIHSF87N"
    "jO//MYGMA0yB5c6vD5A+ZOkBkgGoaR+SA7QBgivo/ksEI2gxgMUk+qHbT2B73ig9bparjk"
    "QWLig1g8Wl6Nxsdws4PW9flQxwDJ7dZM";

  /* 
   * Resource generated for file:
   *    KWScanIncreasingRows.png (zlib, base64) (image file)
   */
  static const unsigned int  image_KWScanIncreasingRows_width          = 25;
  static const unsigned int  image_KWScanIncreasingRows_height         = 25;
  static const unsigned int  image_KWScanIncreasingRows_pixel_size     = 4;
  static const unsigned long image_KWScanIncreasingRows_length         = 188;
  static const unsigned long image_KWScanIncreasingRows_decoded_length = 2500;

  static const unsigned char image_KWScanIncreasingRows[] = 
    "eNrdVTEKwCAM9H2+z7kP6KRjl46hH/A9KRewSOnWHLQZjpAguXhHVFWTBkLOWb3xxFFrvV"
    "BKeZVH58A5L3xJK5Fdez8sMjhaW63/kpJF5J5+oJ/IZv0HkKPu5ceY/w7UvbSC/ph71go5"
    "6t6ezxys/RieILL2g3GPKBxR/WC/V6x3l/2f/x0nE3/WTA==";

  /* 
   * Resource generated for file:
   *    KWScanIncreasingSlices.png (zlib, base64) (image file)
   */
  static const unsigned int  image_KWScanIncreasingSlices_width          = 25;
  static const unsigned int  image_KWScanIncreasingSlices_height         = 25;
  static const unsigned int  image_KWScanIncreasingSlices_pixel_size     = 4;
  static const unsigned long image_KWScanIncreasingSlices_length         = 420;
  static const unsigned long image_KWScanIncreasingSlices_decoded_length = 2500;

  static const unsigned char image_KWScanIncreasingSlices[] = 
    "eNrdljGLwkAQhfP7UgbyT+zOgwPBwvIIhGsu1SKIIRGxOAuLK8wWaVUEf0YIpBr3hUyMcr"
    "fxTPYKi8dmyDLfvnlLCBFZ9ESaTN5kFAUHrLZtLx9RGwP9Py2LTqf0kKbf0vO8ZRzHtdrq"
    "vzCg6fSjGI1ectd1M5bjOJmuVgzoCOlmBQ/M2e2SIkk2ue/7mTpnFoZhuf5WCyGwahnwul"
    "5HUgi/YE4QvNNwOCD17iH9xMBcx+PXfL+XxcWPpO12Q4vFrH5W+0jlUa6s21rHwGxXq3kO"
    "D9ecr/q5DwbmixmhH3OavC4M3EfcF+SJPZgPn5+FGv3adI8P7DHhgxm4j9iDfNGT8+gzc/"
    "ah69FXHvfMvWseJnzc5mGSYdJHpfL7ZiKPButYfT9799FkVDLGaLD+hdFV9GT/PGdKGpRE";

  vtkKWMenuButton *menubutton;
  vtkKWMenu *menu;
  vtkKWLabel *label;

  this->ColumnAxisMenu = vtkKWMenuButtonWithLabel::New();
  this->ColumnAxisMenu->SetParent(this->OrientationFrame);
  this->ColumnAxisMenu->Create();
  this->ColumnAxisMenu->ExpandWidgetOn();
 
  label = this->ColumnAxisMenu->GetLabel();
  label->SetText(ks_("Open Wizard| Increasing Columns are:"));
  label->SetWidth(label_width);
  label->SetCompoundModeToLeft();
  label->SetImageToPixels(image_KWScanIncreasingColumns,
                          image_KWScanIncreasingColumns_width, 
                          image_KWScanIncreasingColumns_height, 
                          image_KWScanIncreasingColumns_pixel_size, 
                          image_KWScanIncreasingColumns_length);

  menubutton = this->ColumnAxisMenu->GetWidget();
  menubutton->SetWidth(menu_width);
  menubutton->SetAnchorToWest();
  menu = menubutton->GetMenu();

  menu->SetItemSelectedValueAsInt(
    menu->AddRadioButton(
      ks_("Orientation|Left / +X axis (default)"),
      this, "OrientationCallback"),
    vtkKWOpenFileProperties::AxisOrientationPlusX);

  menu->SetItemSelectedValueAsInt(
    menu->AddRadioButton(
      ks_("Orientation|Right / -X axis"),
      this, "OrientationCallback"),
    vtkKWOpenFileProperties::AxisOrientationMinusX);

  menu->SetItemSelectedValueAsInt(
    menu->AddRadioButton(
      ks_("Orientation|Posterior / +Y axis"),
      this, "OrientationCallback"),
    vtkKWOpenFileProperties::AxisOrientationPlusY);

  menu->SetItemSelectedValueAsInt(
    menu->AddRadioButton(
      ks_("Orientation|Anterior / -Y axis"),
      this, "OrientationCallback"),
    vtkKWOpenFileProperties::AxisOrientationMinusY);

  menu->SetItemSelectedValueAsInt(
    menu->AddRadioButton(
      ks_("Orientation|Superior / +Z axis"),
      this, "OrientationCallback"),
    vtkKWOpenFileProperties::AxisOrientationPlusZ);

  menu->SetItemSelectedValueAsInt(
    menu->AddRadioButton(
      ks_("Orientation|Inferior / -Z axis"),
      this, "OrientationCallback"),
    vtkKWOpenFileProperties::AxisOrientationMinusZ);
  
  this->RowAxisMenu = vtkKWMenuButtonWithLabel::New();
  this->RowAxisMenu->SetParent(this->OrientationFrame);
  this->RowAxisMenu->Create();
  this->RowAxisMenu->ExpandWidgetOn();

  label = this->RowAxisMenu->GetLabel();
  label->SetText(ks_("Open Wizard| Increasing Rows are:"));
  label->SetWidth(label_width);
  label->SetCompoundModeToLeft();
  label->SetImageToPixels(image_KWScanIncreasingRows,
                          image_KWScanIncreasingRows_width, 
                          image_KWScanIncreasingRows_height, 
                          image_KWScanIncreasingRows_pixel_size, 
                          image_KWScanIncreasingRows_length);

  menubutton = this->RowAxisMenu->GetWidget();
  menubutton->SetWidth(menu_width);
  menubutton->SetAnchorToWest();
  menu = menubutton->GetMenu();

  menu->SetItemSelectedValueAsInt(
    menu->AddRadioButton(
      ks_("Orientation|Left / +X axis"),
      this, "OrientationCallback"),
    vtkKWOpenFileProperties::AxisOrientationPlusX);

  menu->SetItemSelectedValueAsInt(
    menu->AddRadioButton(
      ks_("Orientation|Right / -X axis"),
      this, "OrientationCallback"),
    vtkKWOpenFileProperties::AxisOrientationMinusX);

  menu->SetItemSelectedValueAsInt(
    menu->AddRadioButton(
      ks_("Orientation|Posterior / +Y axis (default)"),
      this, "OrientationCallback"),
    vtkKWOpenFileProperties::AxisOrientationPlusY);

  menu->SetItemSelectedValueAsInt(
    menu->AddRadioButton(
      ks_("Orientation|Anterior / -Y axis"),
      this, "OrientationCallback"),
    vtkKWOpenFileProperties::AxisOrientationMinusY);

  menu->SetItemSelectedValueAsInt(
    menu->AddRadioButton(
      ks_("Orientation|Superior / +Z axis"),
      this, "OrientationCallback"),
    vtkKWOpenFileProperties::AxisOrientationPlusZ);

  menu->SetItemSelectedValueAsInt(
    menu->AddRadioButton(
      ks_("Orientation|Inferior / -Z axis"),
      this, "OrientationCallback"),
    vtkKWOpenFileProperties::AxisOrientationMinusZ);

  this->SliceAxisMenu = vtkKWMenuButtonWithLabel::New();
  this->SliceAxisMenu->SetParent(this->OrientationFrame);
  this->SliceAxisMenu->Create();
  this->SliceAxisMenu->ExpandWidgetOn();

  label = this->SliceAxisMenu->GetLabel();
  label->SetText(ks_("Open Wizard| Increasing Slices are:"));
  label->SetWidth(label_width);
  label->SetCompoundModeToLeft();
  label->SetImageToPixels(image_KWScanIncreasingSlices,
                          image_KWScanIncreasingSlices_width, 
                          image_KWScanIncreasingSlices_height, 
                          image_KWScanIncreasingSlices_pixel_size, 
                          image_KWScanIncreasingSlices_length);

  menubutton = this->SliceAxisMenu->GetWidget();
  menubutton->SetWidth(menu_width);
  menubutton->SetAnchorToWest();
  menu = menubutton->GetMenu();

  menu->SetItemSelectedValueAsInt(
    menu->AddRadioButton(
      ks_("Orientation|Left / +X axis"),
      this, "OrientationCallback"),
    vtkKWOpenFileProperties::AxisOrientationPlusX);

  menu->SetItemSelectedValueAsInt(
    menu->AddRadioButton(
      ks_("Orientation|Right / -X axis"),
      this, "OrientationCallback"),
    vtkKWOpenFileProperties::AxisOrientationMinusX);

  menu->SetItemSelectedValueAsInt(
    menu->AddRadioButton(
      ks_("Orientation|Posterior / +Y axis"),
      this, "OrientationCallback"),
    vtkKWOpenFileProperties::AxisOrientationPlusY);

  menu->SetItemSelectedValueAsInt(
    menu->AddRadioButton(
      ks_("Orientation|Anterior / -Y axis"),
      this, "OrientationCallback"),
    vtkKWOpenFileProperties::AxisOrientationMinusY);

  menu->SetItemSelectedValueAsInt(
    menu->AddRadioButton(
      ks_("Orientation|Superior / +Z axis (default)"),
      this, "OrientationCallback"),
    vtkKWOpenFileProperties::AxisOrientationPlusZ);

  menu->SetItemSelectedValueAsInt(
    menu->AddRadioButton(
      ks_("Orientation|Inferior / -Z axis"),
      this, "OrientationCallback"),
    vtkKWOpenFileProperties::AxisOrientationMinusZ);

  this->Script("grid %s -row 0 -column 0 -sticky nsew -padx 4 -pady 4",
               this->ColumnAxisMenu->GetWidgetName());

  this->Script("grid %s -row 1 -column 0 -sticky nsew -padx 4 -pady 4",
               this->RowAxisMenu->GetWidgetName());

  this->Script("grid %s -row 2 -column 0 -sticky nsew -padx 4 -pady 4",
               this->SliceAxisMenu->GetWidgetName());
}

//----------------------------------------------------------------------------
int vtkKWOpenWizard::PromptOrientation()
{
  // For certain file types, skip this page
  // The DICOM reader already puts LPS medical image into
  // VTK's RAS, so we don't need to ask for more, the orientation filter
  // which is set to RAS by default, will be a no-op, the data will fly
  // through it.

  if (vtkDICOMReader::SafeDownCast(this->GetLastReader()) ||
      vtkGESignaReader::SafeDownCast(this->GetLastReader()) ||
      vtkGESignaReader3D::SafeDownCast(this->GetLastReader()))
    {
    this->OK();
    return 1;
    }

  // Always clear first

  this->ForgetClientArea();

  if (!this->OrientationFrame)
    {
    this->CreateOrientationFrame();
    }

  this->SetPreText(
    k_("Please verify that the data's orientation is correct. The menus "
       "below allow you to define the mapping between the pixels in your "
       "image and the major axes in this application.")); 
  this->SetPostText(VTK_VV_OW_DEFAULT_POSTTEXT_LABEL);
  this->SubTitleLabel->SetText(ks_("Open Wizard|Orientation"));
  
  this->Script("pack %s",this->OrientationFrame->GetWidgetName());

  this->SliceAxisMenu->GetWidget()->GetMenu()->SelectItemWithSelectedValueAsInt(
    this->GetOpenFileProperties()->GetSliceAxis());

  this->RowAxisMenu->GetWidget()->GetMenu()->SelectItemWithSelectedValueAsInt(
    this->GetOpenFileProperties()->GetRowAxis());
  
  this->ColumnAxisMenu->GetWidget()->GetMenu()->SelectItemWithSelectedValueAsInt(
    this->GetOpenFileProperties()->GetColumnAxis());
  
  this->AreOrientationValuesReasonable();

  this->NextButton->SetCommand(this, "ValidateOrientation");
  this->FinishButton->SetCommand(this,"ValidateOrientation");
  if (!this->Invoked)
    {
    this->Invoked = 1;
    return this->vtkKWWizard::Invoke();
    }
  return 1;
}

//----------------------------------------------------------------------------
int vtkKWOpenWizard::AreOrientationValuesReasonable()
{
  vtkKWMenu *menu = this->SliceAxisMenu->GetWidget()->GetMenu();
  int slice_axis = menu->GetItemSelectedValueAsInt(
    menu->GetIndexOfSelectedItem());

  menu = this->RowAxisMenu->GetWidget()->GetMenu();
  int row_axis = menu->GetItemSelectedValueAsInt(
    menu->GetIndexOfSelectedItem());

  menu = this->ColumnAxisMenu->GetWidget()->GetMenu();
  int column_axis = menu->GetItemSelectedValueAsInt(
    menu->GetIndexOfSelectedItem());
  
  if (vtkKWOpenFileProperties::IsOrientationValid(
        column_axis, row_axis, slice_axis))
    {
    this->SetPostText(VTK_VV_OW_DEFAULT_POSTTEXT_LABEL);
    this->NextButton->EnabledOn();
    this->FinishButton->EnabledOn();
    return 1;
    }

  this->SetPostText(
    k_("Error. The orientation you have specified is not a valid orientation: "
       "the same axis is being used for more than one direction. Only one "
       "entry from each of the following three pairs may be used: "
       "(Left, Right) (Anterior, Posterior) (Superior, Inferior)"));

  this->NextButton->EnabledOff();
  this->FinishButton->EnabledOff();

  return 0;
}

//----------------------------------------------------------------------------
void vtkKWOpenWizard::OrientationCallback()
{
  this->AreOrientationValuesReasonable();
}

//----------------------------------------------------------------------------
int vtkKWOpenWizard::ValidateOrientation()
{
  // Set the back button 

  this->AddBackButtonCommand("PromptOrientation");

  // At this point the orientation *is* valid, otherwise we could not have
  // clicked on the "next" button

  vtkKWMenu *menu = this->SliceAxisMenu->GetWidget()->GetMenu();
  this->GetOpenFileProperties()->SetSliceAxis(
    menu->GetItemSelectedValueAsInt(menu->GetIndexOfSelectedItem()));

  menu = this->RowAxisMenu->GetWidget()->GetMenu();
  this->GetOpenFileProperties()->SetRowAxis(
    menu->GetItemSelectedValueAsInt(menu->GetIndexOfSelectedItem()));

  menu = this->ColumnAxisMenu->GetWidget()->GetMenu();
  this->GetOpenFileProperties()->SetColumnAxis(
    menu->GetItemSelectedValueAsInt(menu->GetIndexOfSelectedItem()));

  // And move to the next page

  this->OK();
  return 1;
}

//----------------------------------------------------------------------------
void vtkKWOpenWizard::Cancel()
{
  this->Superclass::Cancel();
  
  // Restore the Previous reader and properties

  this->SetLastReader(this->PreviousReader);
  this->SetPreviousReader(NULL);

  if (this->PreviousOpenFileProperties)
    {
    this->GetOpenFileProperties()->DeepCopy(this->PreviousOpenFileProperties);
    }
}

//----------------------------------------------------------------------------
int vtkKWOpenWizard::Invoke()
{
  // First invokes a FileIO dialog to get the file name

  if (!this->QueryForFileName())
    {
    return 0;
    }

  return this->Invoke(vtkKWOpenWizard::INVOKE_VERBOSE);
}

//----------------------------------------------------------------------------
int vtkKWOpenWizard::InvokeQuiet()
{
  // First invokes a FileIO dialog to get the file name

  if (!this->QueryForFileName())
    {
    return 0;
    }

  return this->Invoke(vtkKWOpenWizard::INVOKE_QUIET);
}

//----------------------------------------------------------------------------
int vtkKWOpenWizard::Invoke(const char *fname, int verbosity)
{
  this->SetFileName(fname);
  return this->Invoke(verbosity);
}

//----------------------------------------------------------------------------
int vtkKWOpenWizard::Invoke(vtkStringArray *fileNames, int verbosity)
{
  this->FileNames->DeepCopy(fileNames);
  return this->Invoke(verbosity);
}

//----------------------------------------------------------------------------
int vtkKWOpenWizard::Invoke(int verbosity)
{
  const char *fname = this->GetFileName();
  if (!fname || !vtksys::SystemTools::FileExists(fname))
    {
    return 0;
    }

  // As a convenience, if somebody tries to open a .vvi, open the 
  // corresponding file automatically.

  vtksys_stl::string fname_last_ext(
    vtksys::SystemTools::GetFilenameLastExtension(fname));
  if (!strcmp(".vvi", fname_last_ext.c_str()))
    {
    vtksys_stl::string fname_path(
      vtksys::SystemTools::GetFilenamePath(fname));
    fname_path += '/';
    fname_path += 
      vtksys::SystemTools::GetFilenameWithoutLastExtension(fname);
    return this->Invoke(fname_path.c_str(), verbosity);
    }

  // The filename should not be in the future (i.e., somebody tempered
  // with the clock to bypass expiration time

  long int c_time = vtksys::SystemTools::CreationTime(fname);
  long int m_time = vtksys::SystemTools::ModifiedTime(fname);
  time_t t;
  time(&t);
  time_t margin = (time_t)(25 * 60 * 60); // a little more than a day
  if ((c_time > 0 && c_time > (t + margin)) || 
      (m_time > 0 && m_time > (t + margin)))
    {
    vtkKWMessageDialog::PopupMessage(
      this->GetApplication(), 0, 
      ks_("Open Wizard|Dialog|Open File Error!"), 
      k_("Please check your system clock: it seems the timestamp of the "
         "file you are trying to open is not consistent with the current "
         "time of your system."),
      vtkKWMessageDialog::ErrorIcon);
    return 0;
    }

  // No master window, use one

  if (!this->GetMasterWindow() && this->GetApplication())
    {
    this->SetMasterWindow(this->GetApplication()->GetNthWindow(0));
    }

  // Special case: if the title has been set to NULL, set the dialog title
  // to use the current application name

  if (this->IsCreated() && !this->GetTitle())
    {
    vtksys_ios::ostringstream title;
    title << this->GetApplication()->GetPrettyName() 
          << " : " << ks_("Open Wizard|Title|Open File Wizard");
    this->SetTitle(title.str().c_str());
    }

  this->Invoked = 0;
  this->ReadyToLoad = vtkKWOpenWizard::DATA_IS_UNAVAILABLE;

  // Save the current reader and properties in case the user selects cancel

  this->SetPreviousReader(this->GetLastReader());
  if (!this->PreviousOpenFileProperties)
    {
    this->PreviousOpenFileProperties = vtkKWOpenFileProperties::New();
    }
  this->PreviousOpenFileProperties->DeepCopy(this->GetOpenFileProperties());
  
  // Clear a few values and set some defaults

  vtkKWOpenFileProperties *open_prop = this->GetOpenFileProperties();
  open_prop->Reset();

  /* The LPS co-ordinate system is shown below. The values that correspond
     to the X,Y,Z axes are also shown. Know what you are doing if you mess
     with this.
    
             HEAD/SUPERIOR                     +Z(S)[10/4]
                     POSTERIOR                 |  / +Y(P)[2]
                ()                             | /
        RIGHT  -/\-  LEFT                      |/
                ||              -X(R)[1] ------+------ +X(L)[0]
       ANTERIOR                               /|
           FEET/INFERIOR                     / |
                                            /  |
                                     -Y(A)[3]  -Z(I)[5]
    
    
     NOTE:
     Switching Volview to use RAS instead of LPS is very simple. This was the
     behaviour as of 20 Apr 06. 
    
      1. Replace the values 10,2,0 in the following 3 lines with 10,3,1. 
      2. In vtkVVDataItemVolume, locate the line
           vw->GetOrientationWidget()->SetAnnotationTypeToMedical();
         and add the following line below it.
           vw->GetOrientationWidget()->SetCoordinateSystemMedicalToRAS();
      3. In the constructor of vtkKW2DRenderWidget, replace the default
            this->CoordinateSystemMedical 
               = vtkKW2DRenderWidget::COORDINATE_SYSTEM_MEDICAL_LPS;
         with 
            this->CoordinateSystemMedical 
               = vtkKW2DRenderWidget::COORDINATE_SYSTEM_MEDICAL_RAS;

     NOTE: the orientation filter below defaults to +X, +Y, +Z, meaning
     it does nothing and keeps the data in VTK's "favorite" coordinate 
     system LPS. For DICOM data, we never show the orientation GUI, data is
     loaded by the DICOM reader and re-oriented while loading it, and we are
     *looking* at it in a LPS fashion
     (see vtkKW2DRenderWidget, double-checked by Seb and Karthik Aug 4 2008).
  */           
 
  open_prop->SetColumnAxis(
    vtkKWOpenFileProperties::AxisOrientationPlusX); // Left      (+X)
  open_prop->SetRowAxis(
    vtkKWOpenFileProperties::AxisOrientationPlusY); // Posterior (+Y)
  open_prop->SetSliceAxis(
    vtkKWOpenFileProperties::AxisOrientationPlusZ); // Superior  (+Z)
  
  // Is the file valid ?

  vtkKWOpenFileHelper *open_helper = this->GetOpenFileHelper();

  int valid = open_helper->IsFileValid(this->FileNames);
  if (valid == vtkKWOpenFileHelper::FILE_IS_INVALID ||
      valid == vtkKWOpenFileHelper::FILE_IS_VALID_NOT_SUPPORTED)
    {
    if (verbosity == vtkKWOpenWizard::INVOKE_VERBOSE && this->IsCreated())
      {
      vtksys_stl::string msg;

      // Try to print an informative message for DICOM readers.

      vtkDICOMReader *dicom_reader =
        vtkDICOMReader::SafeDownCast(this->GetLastReader());
      if (dicom_reader)
        {
        msg = k_("Sorry. The file specified cannot be read.");
        msg += "\n\n";

        vtkDICOMCollector *collector = dicom_reader->GetDICOMCollector();
        if (collector && 
            collector->GetFailureStatus() != vtkDICOMCollector::FailureNone)
          {
          vtkDICOMCollector::FailureStatusTypes ft =
          (vtkDICOMCollector::FailureStatusTypes)collector->GetFailureStatus();
          if (ft & vtkDICOMCollector::FailureCannotGetPixelDataSize)
            {
            msg += k_("The pixel data size could not be retrieved.");
            }
          if (ft & vtkDICOMCollector::FailureTooLittlePixelData)
            {
            msg += k_("Too little pixel data was supplied.");
            }
          if (ft & vtkDICOMCollector::FailureImagesNotFacingTheSameDirection)
            {
            msg += k_("Images in this series are not all facing the same direction and cannot be used.");
            }
          if (ft & vtkDICOMCollector::FailureGantryTilt)
            {
            msg += k_("Some DICOM features like gantry tilt are not supported for the moment.");
            }
          if (ft & vtkDICOMCollector::FailureMoreThanOneSamplePerPixel)
            {
            msg += k_("DICOM files with more than 1 sample per pixel are not supported at the moment.");
            }
          if (ft & vtkDICOMCollector::FailureMoreThanOneNumberOfFrames)
            {
            msg += k_("DICOM files with more than 1 number of frames are not supported at the moment.");
            }
          }
        }
      else
        {
        msg = 
          k_("Sorry. The file specified is either not supported, corrupt or "
             "invalid and cannot be read.");
        }

      vtkKWMessageDialog::PopupMessage(
        this->GetApplication(), 0, 
        ks_("Open Wizard|Dialog|Open File Error!"), 
        msg.c_str(),
        vtkKWMessageDialog::ErrorIcon);
      }
    return 0;
    }
  
  // So we know that at least the file specified can be read in by volview,
  // now lets figure out what additional information we need
  
  // If the file is not a data file then we are done

  if (valid == vtkKWOpenFileHelper::FILE_IS_VALID_NOT_DATA)
    {
    return 1;
    }
  
  // Otherwise this is a data file
  // Check to see if there is a .vvi file
  // If nothing works, go to verbose move (ask the user) unless:
  // - we were called in light-mode (i.e. we were not created), 
  // - it's a DICOM file (in that case for backward compat reason we will
  //   try to load the whole stack).
  // - we are in testing mode (i.e. the we are testing the app and should
  //   not bring any UI in order not to block the tests)

  vtkKWApplicationPro *proapp = 
    vtkKWApplicationPro::SafeDownCast(this->GetApplication());
  
  int success = 1;
  int vvi_was_read = 0; 

  if (!this->IgnoreVVIOnRead &&
      (!open_helper->GetDICOMOptions() || 
       open_helper->GetDICOMOptions()->GetExploreDirectory()))
    {
    vvi_was_read = this->ReadVVIForFile(fname);
    }
  
  if (!vvi_was_read && 
      !this->OpenWithCurrentOpenFileProperties &&
      this->IsCreated() &&
      !vtkDICOMReader::SafeDownCast(this->GetLastReader()) &&
      (!proapp || !proapp->GetTestingMode()))
    {
    verbosity = vtkKWOpenWizard::INVOKE_VERBOSE;
    success = 0;
    }

  // If we requested to use the current open file properties, instead of
  // the ones either set by calling IsFileValid or ReadVVIFile, then
  // bring them back from PreviousOpenFileProperties, since it is a copy
  // of the open properties before either methods were called.

  if (this->OpenWithCurrentOpenFileProperties)
    {
    this->GetOpenFileProperties()->DeepCopy(
      this->PreviousOpenFileProperties);
    }

  // Start prompting the GUI, if needed

  if (this->IsCreated())
    {
    this->SetPostText(VTK_VV_OW_DEFAULT_POSTTEXT_LABEL);
    }

  if (valid == vtkKWOpenFileHelper::FILE_IS_VALID && 
      verbosity == vtkKWOpenWizard::INVOKE_VERBOSE)
    {
    // Skip this if it is 3D for sure

    vtkImageReader2 *rdr = vtkImageReader2::SafeDownCast(this->GetLastReader());
    vtkImageData *output = rdr ? rdr->GetOutput() : NULL;
    // The test here is done on the output's whole extent, not the open
    // properties's whole extent, which may have been read from the VVI
    // file, so that picking a single file from a series that was previously
    // opened as a whole series will still allow the user to choose if
    // he wants to open it as a series or a single slice.
    int output_is_3d = output ? 
      (output->GetWholeExtent()[5] - output->GetWholeExtent()[4] > 0) : 0;
    vtkDICOMReader *dicom_rdr = vtkDICOMReader::SafeDownCast(rdr);
    if (!rdr || (output_is_3d && !dicom_rdr) || (!output_is_3d && dicom_rdr))
      {
      success = this->PromptScope();
      }
    else
      {
      success = this->PromptMultiplicity();
      }
    }
  else if (valid == vtkKWOpenFileHelper::FILE_IS_PROBABLY_VALID && 
           verbosity == vtkKWOpenWizard::INVOKE_VERBOSE)
    {
    success = this->PromptRaw();
    }
  
  // If the invocation was successful save the info out as a 
  // vvi file so that the user will have the correct defaults next time

  if (success)
    {
    // Free the Previous Reader
    this->SetPreviousReader(NULL);

    // copy parameters over to the reader
    vtkImageReader2 *rdr = 
      vtkImageReader2::SafeDownCast(this->GetLastReader());
    if (rdr)
      {
      rdr->SetDataExtent(this->GetOpenFileProperties()->GetWholeExtent());
      rdr->SetDataSpacing(this->GetOpenFileProperties()->GetSpacing());
      rdr->SetDataOrigin(this->GetOpenFileProperties()->GetOrigin());
      rdr->SetDataScalarType(this->GetOpenFileProperties()->GetScalarType());
      rdr->SetNumberOfScalarComponents(
        this->GetOpenFileProperties()->GetNumberOfScalarComponents());
      if (this->GetOpenFileProperties()->GetDataByteOrder() != 
          vtkKWOpenFileProperties::DataByteOrderUnknown)
        {
        rdr->SetDataByteOrder(
          this->GetOpenFileProperties()->GetDataByteOrder());
        }
      rdr->SetFileDimensionality(
        this->GetOpenFileProperties()->GetFileDimensionality());
      rdr->SetFilePattern(
        this->GetOpenFileProperties()->GetAbsoluteFilePatternForFile(fname));
      }
    this->ReadyToLoad = vtkKWOpenWizard::DATA_IS_READY_TO_LOAD;
    if (!this->IgnoreVVIOnWrite)
      {
      this->WriteVVIForFile(fname);
      }
    }

  return success;
}

//----------------------------------------------------------------------------
void vtkKWOpenWizard::Release(int output_port)
{
  if (this->GetOutput(output_port) && 
      this->ReadyToLoad == vtkKWOpenWizard::DATA_IS_LOADED)
    {
    this->GetOutput(output_port)->ReleaseData();
    // fixme
    this->ReadyToLoad = vtkKWOpenWizard::DATA_IS_READY_TO_LOAD;
    }
}

//----------------------------------------------------------------------------
void vtkKWOpenWizard::Load(int output_port)
{
  if (this->ReadyToLoad != vtkKWOpenWizard::DATA_IS_READY_TO_LOAD)
    {
    return;
    }

  // Release old data if not done already

  if (this->GetOutput(0))
    {
    this->GetOutput(0)->ReleaseData();
    }

  // Check the new data

  // If it is a valid unstructured grid, then we are done.
  vtkUnstructuredGrid *unstructuredRes = this->GetLastReader() ? vtkUnstructuredGrid::SafeDownCast(
    this->GetLastReader()->GetOutputDataObject(output_port)) : NULL;
  if (unstructuredRes)
    {
    this->ReadyToLoad = vtkKWOpenWizard::DATA_IS_LOADED;
    return;
    }

  vtkImageData *res = this->GetLastReader() ? vtkImageData::SafeDownCast(
    this->GetLastReader()->GetOutputDataObject(output_port)) : NULL;
  if (!res)
    {
    vtkKWMessageDialog::PopupMessage(
      this->GetApplication(), 0, 
      ks_("Open Wizard|Dialog|Open File Error!"),
      k_("The data could not be loaded!"), 
      vtkKWMessageDialog::ErrorIcon );
    return;
    }

  // Connect color filter

  if (!this->ColorImageConversionFilter)
    {
    this->ColorImageConversionFilter = vtkKWColorImageConversionFilter::New();
    }

  this->ColorImageConversionFilter->SetInput(res);
  this->ColorImageConversionFilter->SetAlphaFloor(1.0);
  this->ColorImageConversionFilter->SetIndependentComponents(
    this->GetOpenFileProperties()->GetIndependentComponents());
  this->ColorImageConversionFilter->GetOutput()->ReleaseDataFlagOn();

  // UpdateInfo has to be called here so that we can query GetConversions()
  // below (done in vtkKWColorImageConversionFilter::RequestInfo).
  // Bear in mind that this propagate up to the reader, meaning the
  // reader will likely resets its DataSpacing, DataOrigin, etc, if those
  // infos are indeed extracted and supported by the reader.

  this->ColorImageConversionFilter->UpdateInformation();

  res->ReleaseDataFlagOn();

  // execute the reader, with progress
  vtkKWWindow *firstw = vtkKWWindow::SafeDownCast(this->MasterWindow);
  if (firstw)
    {
    vtkKWProgressCommand *cb = vtkKWProgressCommand::New();
    vtkKWProgressCommand *cb2 = vtkKWProgressCommand::New();
    cb2->SetWindow(firstw);
    cb2->SetStartMessage(ks_("Progress|Converting color data"));
    if (this->ColorImageConversionFilter->GetConversions()
        & vtkKWColorImageConversionFilter::ConvertedToFloat ||
        this->ColorImageConversionFilter->GetConversions()
        & vtkKWColorImageConversionFilter::ConvertedToColor)
      {
      cb2->SetStartMessage(ks_("Progress|Reading and Converting color data"));
      }
    else
      {
      cb->SetWindow(firstw);
      cb->SetStartMessage(ks_("Progress|Reading data from disk"));
      this->GetLastReader()->AddObserver(vtkCommand::StartEvent, cb);
      this->GetLastReader()->AddObserver(vtkCommand::ProgressEvent, cb);
      this->GetLastReader()->AddObserver(vtkCommand::EndEvent, cb);
      }
    this->ColorImageConversionFilter->AddObserver(
      vtkCommand::StartEvent, cb2);
    this->ColorImageConversionFilter->AddObserver(
      vtkCommand::ProgressEvent, cb2);
    this->ColorImageConversionFilter->AddObserver(vtkCommand::EndEvent, cb2);
    this->ColorImageConversionFilter->UpdateWholeExtent();
    // remove the progress because it creates a circular loop
    if (res->GetNumberOfScalarComponents() != 3 ||
        this->GetOpenFileProperties()->GetIndependentComponents())
      {
      this->GetLastReader()->RemoveObserver(cb); 
      }
    this->ColorImageConversionFilter->RemoveObserver(cb2);
    cb->Delete();
    cb2->Delete();
    }
  else
    {
    this->ColorImageConversionFilter->UpdateWholeExtent();
    }

  // No data

  vtkImageData *color_output = this->ColorImageConversionFilter->GetOutput();
  if (!color_output->GetPointData() || 
      !color_output->GetPointData()->GetScalars())
    {
    vtkKWMessageDialog::PopupMessage(
      this->GetApplication(), 0, 
      ks_("Open Wizard|Dialog|Open File Error!"),
      k_("This file does not contain any usable data (structured points, "
         "pixels, etc.). Most likely this file format can store different "
         "kind of data, but this specific instance does not hold anything "
         "this application can use."), 
      vtkKWMessageDialog::ErrorIcon);
    return;
    }

  // if some funky conversion had to be done then report this to the user
  unsigned long conv = this->ColorImageConversionFilter->GetConversions();
  if (conv & vtkKWColorImageConversionFilter::CompressedSpacing ||
      conv & vtkKWColorImageConversionFilter::CompressedAspectRatio ||
      conv & vtkKWColorImageConversionFilter::ShiftedOrigin)
    {
    vtkKWMessageDialog::PopupMessage(
      this->GetApplication(), 0, 
      ks_("Open Wizard|Dialog|Open File Warning!"),
      k_("The volume you are loading has an origin, spacing, "
         "or aspect ratio that exceeds the application's limits. "
         "These values have been automatically adjusted. As a result "
         "physical properties such as position, surface area, "
         "may not accurately reflect your original data."), 
      vtkKWMessageDialog::WarningIcon );
    }
  
  if (conv & vtkKWColorImageConversionFilter::CompressedScalarRange)
    {
    vtkKWMessageDialog::PopupMessage(
      this->GetApplication(), 0, 
      ks_("Open Wizard|Dialog|Open File Warning!"),
      k_("The volume you are loading has a scalar range that "
         "exceeds the application's limits. These values have been "
         "automatically adjusted. As a result voxel intensities may not "
         "accurately reflect your original data."), 
      vtkKWMessageDialog::WarningIcon );
    }
  
  // deal with orientation
  if (!this->OrientationFilter)
    {
    this->OrientationFilter = vtkKWOrientationFilter::New();
    }
  else
    {
#if 0
    // TODO: plugins detach from the reader (vtkVVPlugin)
    // this is a no-op in the new executive, so it has to be
    // taken care of somehow and see how the below code should
    // be modified

    // reattach the source in case a plugin removed it
    if (!this->GetOutput()->GetSource())
      {
      this->GetOutput()->SetSource(this->OrientationFilter);
      }
#endif
    }

  if (firstw)
    {
    vtkKWProgressCommand *cb = vtkKWProgressCommand::New();
    cb->SetWindow(firstw);
    cb->SetStartMessage(ks_("Progress|Orienting data"));
    this->OrientationFilter->AddObserver(vtkCommand::StartEvent, cb);
    this->OrientationFilter->AddObserver(vtkCommand::ProgressEvent, cb);
    this->OrientationFilter->AddObserver(vtkCommand::EndEvent, cb);
    this->AdjustOrientationFilter();

    // remove the progress because it creates a circular loop
    this->OrientationFilter->RemoveObserver(cb);
    cb->Delete();
    }
  else
    {
    this->AdjustOrientationFilter();
    }

  this->ReadyToLoad = vtkKWOpenWizard::DATA_IS_LOADED;
}

//----------------------------------------------------------------------------
void vtkKWOpenWizard::AdjustOrientationFilter()
{
  if (!this->GetLastReader())
    {
    return;
    }
  
  // Assume all readers are vtkAlgorithms and output is the first output

  this->OrientationFilter->SetInput(
    this->ColorImageConversionFilter->GetOutput());
  
  // if you are wonder how the heck the numbers below were picked it
  // is pretty easy. The positive and negative i,j,k axes are numbered
  // 0,1,2,3,4,5 (so negative I axis is 1 for example) if the number
  // is greater that 5 then the axis it refers to is num%6. This
  // consistency is used by algoritms and should not be changed.
  // Please don't do anything that would give the impression that
  // these values could be changed. (such as making them defines or an
  // enum) Think of these values as the dimensionality of a vtkImageData 
  // (2 or 3) Not a good place for #define because so many algorithms 
  // require these values to always be 2 or 3

  int iaxis = this->GetOpenFileProperties()->GetColumnAxis() % 6;
  int jaxis = this->GetOpenFileProperties()->GetRowAxis() % 6;
  int kaxis = this->GetOpenFileProperties()->GetSliceAxis() % 6;
  
  int outAxes[3];
  outAxes[iaxis/2] = 0;
  if (iaxis%2)
    {
    outAxes[iaxis/2] = 3;
    }
  outAxes[jaxis/2] = 1;
  if (jaxis%2)
    {
    outAxes[jaxis/2] = 4;
    }
  outAxes[kaxis/2] = 2;
  if (kaxis%2)
    {
    outAxes[kaxis/2] = 5;
    }

  // In most cases outAxes should be (0,1,2). The DICOMReader tries to read 
  // data and place it in the right order to make things fast. If you were to 
  // switch from LPS to RAS as detailed in the comments on the top of the file,
  // clearly outAxes would generally read be (3,4,2) 
   
  this->OrientationFilter->SetOutputAxes(outAxes);
  this->OrientationFilter->UpdateWholeExtent();
}

//----------------------------------------------------------------------------
vtkImageData *vtkKWOpenWizard::GetSeriesOutput(int inputIndex, int output_port)
{
  int index = inputIndex + this->GetSeriesMinimum();
  
  if( index < this->GetSeriesMinimum() ||
      index > this->GetSeriesMaximum() )
    {
    return NULL;
    }

  if( this->GetLastReader() )
    {
    const char * pattern = this->GetSeriesPattern();
    char *fileName = new char [strlen(pattern) + 50];
    sprintf(fileName,pattern,index);
    vtkImageReader2 *rdr = 
      vtkImageReader2::SafeDownCast(this->GetLastReader());
    rdr->SetFileName(fileName);
    // fixme
    this->Release(output_port);
    this->Load(output_port);
    delete [] fileName;
    return this->GetOutput(output_port);
    }

  return NULL;
}

//----------------------------------------------------------------------------
vtkUnstructuredGrid *vtkKWOpenWizard::GetUnstructuredGridOutput(int output_port)
{
  // why is output_port ignored?
  return this->GetLastReader() ? 
    vtkUnstructuredGrid::SafeDownCast(this->GetLastReader()->GetOutputDataObject(0))
    : NULL;
}

//----------------------------------------------------------------------------
vtkImageData *vtkKWOpenWizard::GetOutput(int output_port)
{
  // why is output_port ignored?
  return this->OrientationFilter ? 
    vtkImageData::SafeDownCast(this->OrientationFilter->GetOutputDataObject(0))
    : NULL;
}

//----------------------------------------------------------------------------
void vtkKWOpenWizard::WriteVVIForFile(const char *fname)
{
  // if it is a vtkImageReader2, then look to see if FilePattern is set
  // if it is set then use the FilePattern and WholeExtent to determine
  // the right name for the vvi file

  char *vvi_fname = NULL;
  vtkImageReader2 *rdr = vtkImageReader2::SafeDownCast(this->GetLastReader());
  if (rdr && !rdr->GetFileName())
    {
    // Remove the VVI file that would eventually be associated to 
    // the file when it was open as a single slice. Otherwise it will
    // be picked over and over again from "Open Recent File" for example
    // instead of picking the VVI file associated to the whole series,
    // which name is created below.

    if (this->GetFileName())
      {
      vtksys_stl::string prev_vvi_fname(this->GetFileName());
      prev_vvi_fname += ".vvi";
      vtksys::SystemTools::RemoveFile(prev_vvi_fname.c_str());
      }

    vvi_fname = new char[strlen(rdr->GetFilePattern()) + 10];

    // Actually instead of using the first slice of the whole extent,
    // we will always use slice 0 *even* if it doesn't exist. The rational
    // here is that if the user request slices 20 to 80 our of a 0 to 100
    // stack, we would have ended creating a foo-20.raw.vvi file for example.
    // Now the next time we request slice 0 to 100, we end up writing
    // foo-00.raw.vvi, etc etc. So we end up with potentially lots of
    // vvi files in the directory, all of them good candidates to be used
    // the next time we open a file part of these series (since both files
    // above could be valid while loading slice 25 for example).
    // To avoid that, *always* write to foo-00.raw.vvi, so that only one
    // single VVI file describes a series of file (all other would describe
    // loading a *single* slice). Sounds like a good compromise.
    // and this could conflict with a 

    sprintf(vvi_fname, rdr->GetFilePattern(), 0); // rdr->GetDataExtent()[4]);
    }
  else
    {
    // If DICOM reader, use the first slice filename

    vtkDICOMReader *dicom_reader = 
      vtkDICOMReader::SafeDownCast(this->GetLastReader());
    if (dicom_reader)
      {
      vtkDICOMCollector *col = dicom_reader->GetDICOMCollector();
      if (col && col->CollectAllSlices() > 1)
        {
        const char *first_slice_fname = col->GetSliceFileName(0);
        if (first_slice_fname)
          {
          // Same as above, remove the VVI file for a single slice
          if (this->GetFileName())
            {
            vtksys_stl::string prev_vvi_fname(this->GetFileName());
            prev_vvi_fname += ".vvi";
            vtksys::SystemTools::RemoveFile(prev_vvi_fname.c_str());
            }
          fname = first_slice_fname;
          }
        }
      }

    vvi_fname = new char[strlen(fname) + 10];
    strcpy(vvi_fname, fname);
    }

  strcat(vvi_fname, ".vvi");

  vtkKWOpenFileProperties *open_prop = this->GetOpenFileProperties();
  vtkXMLKWOpenFilePropertiesWriter *xmlw = 
    vtkXMLKWOpenFilePropertiesWriter::SafeDownCast(
      open_prop->GetNewXMLWriter());
  xmlw->DiscardFilePatternDirectoryOn();
  xmlw->WriteIndentedOn();
  xmlw->WriteToFile(vvi_fname);
  xmlw->Delete();

  delete [] vvi_fname;
}

//----------------------------------------------------------------------------
int vtkKWOpenWizard::IsVVIValid(const char *vvi_fname)
{
  int res = vtkKWOpenWizard::VVI_IS_VALID;

  // Use a new open prop, do not overwrite ours

  vtkKWOpenFileProperties *open_prop = vtkKWOpenFileProperties::New();
  vtkXMLKWOpenFilePropertiesReader *xmlr = 
    vtkXMLKWOpenFilePropertiesReader::SafeDownCast(
      open_prop->GetNewXMLReader());
  xmlr->SetObject(open_prop);
  if (!xmlr->ParseFile(vvi_fname))
    {
    vtkErrorMacro("Failed reading VVI file!");
    res = vtkKWOpenWizard::VVI_IS_INVALID;
    }
  else if (!xmlr->IsValid())
    {
    res = vtkKWOpenWizard::VVI_IS_INVALID;
    }
  xmlr->Delete();
  open_prop->Delete();

  return res;
}

//----------------------------------------------------------------------------
int vtkKWOpenWizard::DoesVVIIncludeFile(const char *vvi_fname, 
                                        const char *fname)
{
  int res = vtkKWOpenWizard::VVI_IS_VALID;

  // Check if the information included in the VVI file describe a series
  // that fname is part of

  // Use a new open prop, do not overwrite ours

  vtkKWOpenFileProperties *open_prop = vtkKWOpenFileProperties::New();
  vtkXMLKWOpenFilePropertiesReader *xmlr = 
    vtkXMLKWOpenFilePropertiesReader::SafeDownCast(
      open_prop->GetNewXMLReader());
  if (!xmlr->ParseFile(vvi_fname))
    {
    vtkErrorMacro("Failed reading VVI file!");
    res = vtkKWOpenWizard::VVI_IS_INVALID;
    }
  
  // First check it is valid
  
  if (!xmlr->IsValid())
    {
    res = vtkKWOpenWizard::VVI_IS_INVALID;
    }
  else
    {
    // We need a VVI file that describes a pattern based series

    if (xmlr->IsDescribingPatternSeries())
      {
      res = vtkKWOpenWizard::VVI_HAS_REQUIRED_TOKEN;
      int *wext = open_prop->GetWholeExtent();
      char tmp[3000];
      
      // Try checking if the file matches for some reasonable slice numbers

      const char *pattern = open_prop->GetAbsoluteFilePatternForFile(fname);
      for (int i = wext[4]; i <= wext[5]; ++i)
        {
        sprintf(tmp, pattern, i);
        if (!strcmp(fname, tmp))
          {
          res = vtkKWOpenWizard::VVI_IS_COMPLIANT;
          break;
          }
        }
      }
    }

  xmlr->Delete();
  open_prop->Delete();

  // If not, i.e. the file is just "valid", not "compliant", then
  // maybe fname is a DICOM file and the contents of the VVI file was not
  // enough to detect if it is part of the same series (there is no 
  // FilePattern or FileName in a DICOM VVI file). Try to see if
  // this is a DICOM file, and if the VVI file that was stored for the
  // first slice of this series matches a filename part of the same series.

  // Note that there is a hack involved here: creating a whole new
  // DICOMReader *does* cost resources, as the whole stack of slices
  // will be collected again, and multiple slices may potentially be
  // read to make sure the scalar type is right (especially for CT).
  // To avoid that, we check if 'fname' is actually the filename we 
  // are processing at the very moment in LastReader. If it is the case,
  // then use LastReader and its DICOM collector, as they already have cached
  // most of the information we need.

  if (res == vtkKWOpenWizard::VVI_IS_VALID)
    {
    vtkKWOpenWizard *dummy = NULL;
    vtkDICOMReader *rdr = vtkDICOMReader::SafeDownCast(this->GetLastReader());
    if (!rdr || strcmp(fname, rdr->GetFileName()))
      {
      dummy = vtkKWOpenWizard::New();
      dummy->GetOpenFileHelper()->SetDICOMOptions(
        this->GetOpenFileHelper()->GetDICOMOptions());
      if (dummy->GetOpenFileHelper()->IsFileValid(fname) == 
          vtkKWOpenFileHelper::FILE_IS_VALID)
        {
        rdr = vtkDICOMReader::SafeDownCast(dummy->GetLastReader());
        //rdr->SetFileName(fname);
        // TODO: there is still a problem here as a we are not taking into
        // account a list of filenames set explicitly (this method's API
        // should be modified to include all extra filenames).
        // Try this:
        if (rdr)
          {
          rdr->SetFileNames(this->FileNames);
          }
        }
      }
    if (rdr)
      {
      vtksys_stl::string slice_name = 
        vtksys::SystemTools::GetFilenamePath(vvi_fname) + "/" +
        vtksys::SystemTools::GetFilenameWithoutLastExtension(vvi_fname);
      if (rdr->GetDICOMCollector()->DoesIncludeFile(slice_name.c_str()))
        {
        res = vtkKWOpenWizard::VVI_IS_COMPLIANT;
        }
      }
    if (dummy)
      {
      dummy->Delete();
      }
    }
  
  return res;
}

//----------------------------------------------------------------------------
int vtkKWOpenWizard::ReadVVIForFile(const char *fname)
{
  int success = 0;

  vtkKWOpenFileProperties *open_prop = this->GetOpenFileProperties();
  vtksys_stl::string fname_dir = vtksys::SystemTools::GetFilenamePath(fname);
  vtkXMLKWOpenFilePropertiesReader *xmlr = 
    vtkXMLKWOpenFilePropertiesReader::SafeDownCast(
      open_prop->GetNewXMLReader());

  vtksys_stl::string vvi_fname(fname);
  vvi_fname += ".vvi";

  // Maybe it is a series ... is there a vvi file in the same directory?
  // that has this slice as part of it?

  int has_vvi_file = vtksys::SystemTools::FileExists(vvi_fname.c_str());

  if(!has_vvi_file)
    {
    vtkDirectory *dir = vtkDirectory::New();
    if (dir->Open(fname_dir.c_str()))
      {
      int num_files = dir->GetNumberOfFiles();
      for (int i = 0; i < num_files; ++i)
        {
        vtksys_stl::string fname_try(dir->GetFile(i));
        vtksys_stl::string fname_try_ext(
          vtksys::SystemTools::GetFilenameLastExtension(fname_try));
        if (!strcmp(".vvi", fname_try_ext.c_str()))
          {
          vtksys_stl::string fname_try_full(fname_dir);
          fname_try_full = fname_try_full + "/" + fname_try;
          if (this->DoesVVIIncludeFile(fname_try_full.c_str(), fname) == 
              vtkKWOpenWizard::VVI_IS_COMPLIANT)
            {
            vvi_fname = fname_try_full;
            has_vvi_file = 1;
            break;
            }
          }
        }
      }
    dir->Delete();
    }

  // Has a VVI file, let's try to read it

  if (has_vvi_file &&
      this->IsVVIValid(vvi_fname.c_str()) == vtkKWOpenWizard::VVI_IS_VALID)
    {
    success = xmlr->ParseFile(vvi_fname.c_str()) ? 1 : 0;
    if (!success)
      {
      vtkErrorMacro("Failed reading VVI file! " << vvi_fname);
      }
    }

  xmlr->Delete();

  if (success)
    {
    // If it's a 2D slice, prevent the collector from getting any more slices
    // Shouldn't that be at the end of the big ::Invoke(...) 
    vtkDICOMReader *dicom_reader = 
      vtkDICOMReader::SafeDownCast(this->GetLastReader());
    if (dicom_reader)
      {
      vtkDICOMCollectorOptions *options = 
        dicom_reader->GetDICOMCollector()->GetOptions();
      int old_explore = options->GetExploreDirectory();
      int *wext = open_prop->GetWholeExtent();
      options->SetExploreDirectory(wext[5] <= wext[4] ? 0 : 1);
      if (old_explore != options->GetExploreDirectory())
        {
        dicom_reader->GetDICOMCollector()->ClearCollectedSlices();
        }
      }
    }
  
  return success;
}

//----------------------------------------------------------------------------
int vtkKWOpenWizard::QueryForFileName(const char *title)
{
  this->LoadDialog->SetTitle(
    title ? title : ks_("Open Wizard|Title|Open File"));

  //this->LoadDialog->SetDefaultExtension(".vtk");
  
  char fileTypes[2048];
  fileTypes[0] = 0;
  strcat(fileTypes, "{{DICOM} {*}} ");

  if (!this->GetOpenFileHelper()->GetSupportDICOMFormatOnly())
    {
    strcat(
      fileTypes, 
      "{{3D} {.vtk .vti .pic .lsm .slc .stk .hdr}} ");
    strcat(
      fileTypes, 
      "{{2D} {.bmp .jpg .jpeg .png .pgm .ppm .pnm .tif .tiff}} ");

    vtksys_stl::string helperFileTypes =
      this->GetOpenFileHelper()->GetFileTypesTclString();
    if (helperFileTypes != "")
      {
      strcat(fileTypes, helperFileTypes.c_str());
      }

    strcat(fileTypes, "{{VTK} {.vtk .vti}} ");
    strcat(fileTypes, "{{Analyze} {.hdr}} ");
    strcat(fileTypes, "{{BMP} {.bmp}} ");
    strcat(fileTypes, "{{GE Signa} {.MR .CT}} ");
    strcat(fileTypes, "{{JPEG} {.jpg .jpeg}} ");
    strcat(fileTypes, "{{Zeiss LSM} {.lsm}} ");
    strcat(fileTypes, "{{Metamorph Stack} {.stk}} ");
    strcat(fileTypes, "{{PIC} {.pic}} ");
    strcat(fileTypes, "{{PNG} {.png}} ");
    strcat(fileTypes, "{{PNM} {.pgm .ppm .pnm}} ");
    strcat(fileTypes, "{{SLC} {.slc}} ");
    strcat(fileTypes, "{{TIFF} {.tif .tiff}} ");
    strcat(fileTypes, "{{MetaImage} {.mhd .mha}} ");
    strcat(fileTypes, "{{Raw} {*}} ");
    }

  this->LoadDialog->SetFileTypes(fileTypes);
  
  if (!this->LoadDialog->Invoke())
    {
    return 0;
    }

  if(this->LoadDialog->GetNumberOfFileNames())
    {
    this->FileNames->DeepCopy(this->LoadDialog->GetFileNames());
    return 1;
    }
  return 0;
}

//----------------------------------------------------------------------------
vtkKWOpenFileProperties* vtkKWOpenWizard::GetOpenFileProperties()
{
  return this->GetOpenFileHelper()->GetOpenFileProperties();
}

//----------------------------------------------------------------------------
vtkAlgorithm* vtkKWOpenWizard::GetLastReader()
{
  return this->GetOpenFileHelper()->GetLastReader();
}

//----------------------------------------------------------------------------
void vtkKWOpenWizard::SetLastReader(vtkAlgorithm *algorithm)
{
  this->GetOpenFileHelper()->SetLastReader(algorithm);
}

//----------------------------------------------------------------------------
void vtkKWOpenWizard::AddValidFileExtension(
  const char *description, const char *extension)
{
  this->GetOpenFileHelper()->AddValidFileExtension(description, extension);
}

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

  os << indent << "LoadDialog: " << this->LoadDialog << endl;
  os << indent << "ReadyToLoad: " << this->ReadyToLoad << endl;

  os << indent << "IgnoreVVIOnRead: " << this->IgnoreVVIOnRead << endl;
  os << indent << "IgnoreVVIOnWrite: " << this->IgnoreVVIOnWrite << endl;
  os << indent << "OpenWithCurrentOpenFileProperties: " << this->OpenWithCurrentOpenFileProperties << endl;

  os << indent << "FileNames:";
  if (this->FileNames)
    {
    os << endl;
    this->FileNames->PrintSelf(os,indent.GetNextIndent());
    }
  else
    {
    os << " (none)" << endl;
    }

  os << indent << "OpenFileHelper:";
  if (this->OpenFileHelper)
    {
    os << endl;
    this->OpenFileHelper->PrintSelf(os,indent.GetNextIndent());
    }
  else
    {
    os << " (none)" << endl;
    }
}
