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

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

#include "vtkObjectFactory.h"

#include "vtkColorTransferFunction.h"
#include "vtkImageActor.h"
#include "vtkMetaImageReader.h"
#include "vtkMetaImageWriter.h"
#include "vtkPiecewiseFunction.h"
#include "vtkPointData.h"
#include "vtkVolumeProperty.h"

#include "vtkKWApplication.h"
#include "vtkKWBalloonHelpManager.h"
#include "vtkKWFrameWithLabel.h"
#include "vtkKWIcon.h"
#include "vtkKWImageWidget.h"
#include "vtkKWInternationalization.h"
#include "vtkKWLabel.h"
#include "vtkKWLabelWithLabel.h"
#include "vtkKWLoadSaveDialog.h"
#include "vtkKWMessageDialog.h"
#include "vtkKWMenu.h"
#include "vtkKWMultiColumnList.h"
#include "vtkKWMultiColumnListWithScrollbars.h"
#include "vtkKWTkUtilities.h"
#include "vtkKWVolumeWidget.h"

#include "vtkVVPaintbrushOptionsFrame.h"
#include "vtkVVPaintbrushWidgetToolbar.h"
#include "vtkVVWindowBase.h"
#include "vtkVVDataItemVolume.h"
#include "vtkVVDataItemPool.h"

#include "vtkKWEPaintbrushWidget.h"
#include "vtkKWEPaintbrushRepresentation2D.h"
#include "vtkKWEPaintbrushShape.h"
#include "vtkKWEPaintbrushOperation.h"
#include "vtkKWEPaintbrushDrawing.h"
#include "vtkKWEPaintbrushSketch.h"
#include "vtkKWEPaintbrushProperty.h"
#include "vtkKWEPaintbrushLabelData.h"
#include "vtkKWEWidgetGroup.h"
#include "vtkKWEPaintbrushShapeEllipsoid.h"
#include "vtkKWEPaintbrushDrawingStatistics.h"

#include <vtkstd/set>
#include <vtkstd/string>
#include <vtksys/ios/sstream>
#include <vtksys/SystemTools.hxx>

#include <time.h>
#include <stdio.h>

const char *vtkVVPaintbrushWidgetEditor::IndexColumnName      = "Index";
const char *vtkVVPaintbrushWidgetEditor::LabelColumnName      = "Label";
const char *vtkVVPaintbrushWidgetEditor::VisibilityColumnName = "Visibility";
const char *vtkVVPaintbrushWidgetEditor::ColorColumnName      = "Color";
const char *vtkVVPaintbrushWidgetEditor::MutableColumnName = "Mutable";
const char *vtkVVPaintbrushWidgetEditor::IdentifierColumnName = "Identifier";
const char *vtkVVPaintbrushWidgetEditor::VolumeColumnName = "Volume";

const char *vtkVVPaintbrushWidgetEditor::LabelMapPathRegKey = "LabelMapPath";
const char *vtkVVPaintbrushWidgetEditor::LastSavedLabelMapRegKey = "LastSavedLabelMap";

//----------------------------------------------------------------------------
vtkCxxRevisionMacro(vtkVVPaintbrushWidgetEditor, "1.42");
vtkStandardNewMacro(vtkVVPaintbrushWidgetEditor);

//----------------------------------------------------------------------------
vtkVVPaintbrushWidgetEditor::vtkVVPaintbrushWidgetEditor()
{
  this->PaintbrushWidget = NULL;
  this->ImageWidget      = NULL;

  // GUI

  this->EditorFrame  = NULL;
  this->SketchList   = NULL;
  this->ControlFrame = NULL;
  this->Toolbar      = NULL;
  this->HelpLabel    = NULL;
  this->ContextMenu  = NULL;

  this->PaintbrushOptions = NULL;
  this->DrawingStatistics = vtkKWEPaintbrushDrawingStatistics::New();
}

//----------------------------------------------------------------------------
vtkVVPaintbrushWidgetEditor::~vtkVVPaintbrushWidgetEditor()
{
  // GUI

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

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

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

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

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

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

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

  this->SetPaintbrushWidget(NULL);
  this->SetImageWidget(NULL);
  this->DrawingStatistics->Delete();
}

//----------------------------------------------------------------------------
void vtkVVPaintbrushWidgetEditor::CreateWidget()
{
  // Check if already created

  if (this->IsCreated())
    {
    vtkErrorMacro(<< this->GetClassName() << " already created");
    return;
    }

  // Call the superclass to create the whole widget

  this->Superclass::CreateWidget();

  // --------------------------------------------------------------
  // Frame

  if (!this->EditorFrame)
    {
    this->EditorFrame = vtkKWFrameWithLabel::New();
    }
  this->EditorFrame->SetParent(this);
  this->EditorFrame->Create();
  this->EditorFrame->SetLabelText("Label Map Editor");

  this->Script(
    "pack %s -side top -fill both -expand y -pady 0 -padx 0 -ipady 0 -ipadx 0",
    this->EditorFrame->GetWidgetName());

  vtkKWFrame *frame = this->EditorFrame->GetFrame();

  // --------------------------------------------------------------
  // Sketch list

  if (!this->SketchList)
    {
    this->SketchList = vtkKWMultiColumnListWithScrollbars::New();
    }

  this->SketchList->SetParent(frame);
  this->SketchList->Create();
  //this->SketchList->HorizontalScrollbarVisibilityOff();

  this->Script(
    "pack %s -side top -anchor nw -fill both -expand t -padx 2 -pady 2",
    this->SketchList->GetWidgetName());

  vtkKWMultiColumnList *list = this->SketchList->GetWidget();
  list->SetHeight(7);
  list->SetSelectionModeToExtended();
  list->SetSelectionChangedCommand(this, "SketchSelectionChangedCallback");
  list->SetPotentialCellColorsChangedCommand(
    list, "ScheduleRefreshColorsOfAllCellsWithWindowCommand");
  list->SetColumnSortedCommand(
    list, "ScheduleRefreshColorsOfAllCellsWithWindowCommand");
  list->ColumnSeparatorsVisibilityOn();
  list->ResizableColumnsOn();
  list->StretchableColumnsOn();
  list->SetCellUpdatedCommand(this, "SketchCellUpdatedCallback");

  this->CreateSketchListColumns();

  // --------------------------------------------------------------
  // Control frame

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

  this->ControlFrame->SetParent(frame);
  this->ControlFrame->Create();

  this->Script("pack %s -side top -anchor nw -fill both -expand f",
               this->ControlFrame->GetWidgetName());

  // --------------------------------------------------------------
  // Toolbar

  if (!this->Toolbar)
    {
    this->Toolbar = vtkVVPaintbrushWidgetToolbar::New();
    }
  
  this->Toolbar->SetParent(this->ControlFrame);
  this->Toolbar->Create();
  this->Toolbar->SetWidgetsFlatAdditionalPadX(
    this->Toolbar->GetWidgetsFlatAdditionalPadX() + 1);
  this->Toolbar->SetWidgetsFlatAdditionalInternalPadX(
    this->Toolbar->GetWidgetsFlatAdditionalInternalPadX() + 1);
  this->Toolbar->SetWidgetsFlatAdditionalInternalPadY(
    this->Toolbar->GetWidgetsFlatAdditionalInternalPadY() + 1);
  this->Toolbar->SetAddSketchCommand(
    this, "AddSketchCallback");
  this->Toolbar->SetDeleteSketchCommand(
    this, "DeleteSketchCallback");
  this->Toolbar->SetLoadDrawingCommand(
    this, "LoadDrawingCallback");
  this->Toolbar->SetSaveDrawingCommand(
    this, "SaveDrawingCallback");
  this->Toolbar->SetUndoStrokeCommand(
    this, "UndoStrokeCallback");
  this->Toolbar->SetRedoStrokeCommand(
    this, "RedoStrokeCallback");
  this->Toolbar->SetMergeSketchesCommand(
    this, "MergeSketchCallback");
  this->Toolbar->SetCopyToPreviousSliceCommand(
    this, "CopySketchToPreviousSliceCallback");
  this->Toolbar->SetCopyToNextSliceCommand(
    this, "CopySketchToNextSliceCallback");
  this->Toolbar->SetPromoteDrawingToVolumeCommand(
    this, "PromoteDrawingToVolumeCallback");
  this->Toolbar->SetConvertVolumeToDrawingCommand(
    this, "ConvertVolumeToDrawingCallback");

  this->Script("pack %s -side top -anchor nw -fill none -expand t",
               this->Toolbar->GetWidgetName());

  // --------------------------------------------------------------
  // Paintbrush options

  if (!this->PaintbrushOptions)
    {
    this->PaintbrushOptions = vtkVVPaintbrushOptionsFrame::New();
    }

  this->PaintbrushOptions->SetParent(this->ControlFrame);
  this->PaintbrushOptions->Create();
  this->PaintbrushOptions->SetOpacityChangedCommand(
    this, "OpacityChangedCallback");
  this->PaintbrushOptions->SetShapeSizeChangedCommand(
    this, "ShapeSizeChangedCallback");
  this->PaintbrushOptions->SetSingleSliceBrushChangedCommand(
    this, "SingleSliceBrushChangedCallback");

  this->Script("pack %s -side top -anchor nw -fill x -expand n",
               this->PaintbrushOptions->GetWidgetName());

  // --------------------------------------------------------------
  // Help message

  if (!this->HelpLabel)
    {
    this->HelpLabel = vtkKWLabelWithLabel::New();
    }

  this->HelpLabel->SetParent(this->ControlFrame);
  this->HelpLabel->Create();
  this->HelpLabel->ExpandWidgetOn();
  this->HelpLabel->GetLabel()->SetImageToPredefinedIcon(
    vtkKWIcon::IconSilkHelp);

  vtkKWLabel *msg = this->HelpLabel->GetWidget();
  msg->SetJustificationToLeft();
  msg->SetAnchorToNorthWest();
  msg->AdjustWrapLengthToWidthOn();
  msg->SetText("Left Click to draw a stroke. Ctrl + Left Click to erase. Default behavior affects the selected label. Add Shift to affect all labels.");

  this->Script("pack %s -side top -anchor nw -fill x -expand n",
               this->HelpLabel->GetWidgetName());

  // Update

  this->Update();
}

//----------------------------------------------------------------------------
void vtkVVPaintbrushWidgetEditor::CreateSketchListColumns()
{
  vtkKWMultiColumnList *list = this->SketchList->GetWidget();

  int col;

  // We need that column to store the index of the sketch in its drawing

  col = list->AddColumn("Index");
  list->SetColumnName(col, vtkVVPaintbrushWidgetEditor::IndexColumnName);
  list->ColumnVisibilityOff(col);

  // Label

  col = list->AddColumn("Label");
  list->SetColumnName(col, vtkVVPaintbrushWidgetEditor::LabelColumnName);
  list->SetColumnWidth(col, 7);
  list->SetColumnResizable(col, 1);
  list->SetColumnStretchable(col, 0);
  list->SetColumnEditable(col, 0);

  // Visibility

  col = list->AddColumn(NULL);
  list->SetColumnName(col, vtkVVPaintbrushWidgetEditor::VisibilityColumnName);
  list->SetColumnLabelImageToPredefinedIcon(col, vtkKWIcon::IconSilkEye);
  list->SetColumnResizable(col, 0);
  list->SetColumnStretchable(col, 0);
  list->SetColumnFormatCommandToEmptyOutput(col);
  list->SetColumnWidth(col, -20);

  // Color

  col = list->AddColumn(NULL);
  list->SetColumnName(col, vtkVVPaintbrushWidgetEditor::ColorColumnName);
  list->SetColumnLabelImageToPredefinedIcon(
    col, vtkKWIcon::IconSilkColorSwatch);
  list->SetColumnResizable(col, 0);
  list->SetColumnStretchable(col, 0);
  list->SetColumnEditable(col, 1);
  list->SetColumnFormatCommandToEmptyOutput(col);

  // Mutable

  col = list->AddColumn(NULL);
  list->SetColumnName(col, vtkVVPaintbrushWidgetEditor::MutableColumnName);
  list->SetColumnLabelImageToPredefinedIcon(col, vtkKWIcon::IconSilkLock);
  list->SetColumnResizable(col, 0);
  list->SetColumnStretchable(col, 0);
  list->SetColumnFormatCommandToEmptyOutput(col);
  list->SetColumnWidth(col, -20);

  // Identifier

  col = list->AddColumn("Identifier");
  list->SetColumnName(col, vtkVVPaintbrushWidgetEditor::IdentifierColumnName);
  list->SetColumnResizable(col, 1);
  list->SetColumnStretchable(col, 1);
  list->SetColumnEditable(col, 1);
  list->SetColumnWidth(col, 17);

  // Volume

  col = list->AddColumn("Volume");
  list->SetColumnName(col, vtkVVPaintbrushWidgetEditor::VolumeColumnName);
  list->SetColumnResizable(col, 1);
  list->SetColumnStretchable(col, 1);
  list->SetColumnEditable(col, 0);
}

//----------------------------------------------------------------------------
int vtkVVPaintbrushWidgetEditor::GetIndexColumnIndex()
{
  return this->SketchList ? 
    this->SketchList->GetWidget()->GetColumnIndexWithName(
      vtkVVPaintbrushWidgetEditor::IndexColumnName) : -1;
}

//----------------------------------------------------------------------------
int vtkVVPaintbrushWidgetEditor::GetLabelColumnIndex()
{
  return this->SketchList ? 
    this->SketchList->GetWidget()->GetColumnIndexWithName(
      vtkVVPaintbrushWidgetEditor::LabelColumnName) : -1;
}

//----------------------------------------------------------------------------
int vtkVVPaintbrushWidgetEditor::GetVisibilityColumnIndex()
{
  return this->SketchList ? 
    this->SketchList->GetWidget()->GetColumnIndexWithName(
      vtkVVPaintbrushWidgetEditor::VisibilityColumnName) : -1;
}

//----------------------------------------------------------------------------
int vtkVVPaintbrushWidgetEditor::GetColorColumnIndex()
{
  return this->SketchList ? 
    this->SketchList->GetWidget()->GetColumnIndexWithName(
      vtkVVPaintbrushWidgetEditor::ColorColumnName) : -1;
}

//----------------------------------------------------------------------------
int vtkVVPaintbrushWidgetEditor::GetMutableColumnIndex()
{
  return this->SketchList ? 
    this->SketchList->GetWidget()->GetColumnIndexWithName(
      vtkVVPaintbrushWidgetEditor::MutableColumnName) : -1;
}

//----------------------------------------------------------------------------
int vtkVVPaintbrushWidgetEditor::GetIdentifierColumnIndex()
{
  return this->SketchList ? 
    this->SketchList->GetWidget()->GetColumnIndexWithName(
      vtkVVPaintbrushWidgetEditor::IdentifierColumnName) : -1;
}

//----------------------------------------------------------------------------
int vtkVVPaintbrushWidgetEditor::GetVolumeColumnIndex()
{
  return this->SketchList ? 
    this->SketchList->GetWidget()->GetColumnIndexWithName(
      vtkVVPaintbrushWidgetEditor::VolumeColumnName) : -1;
}

//----------------------------------------------------------------------------
void vtkVVPaintbrushWidgetEditor::SetPaintbrushWidget(
  vtkKWEPaintbrushWidget *arg)
{
  if (this->PaintbrushWidget == arg)
    {
    return;
    }

  if (this->PaintbrushWidget)
    {
    this->RemoveCallbackCommandObserver(
      this->PaintbrushWidget, vtkKWEPaintbrushWidget::IncrementSketchEvent);
    this->RemoveCallbackCommandObserver(
      this->PaintbrushWidget, vtkKWEPaintbrushWidget::DecrementSketchEvent);
    this->RemoveCallbackCommandObserver(
      this->PaintbrushWidget, vtkKWEPaintbrushWidget::BeginNewSketchEvent);
    this->RemoveCallbackCommandObserver(
      this->PaintbrushWidget, vtkKWEPaintbrushWidget::IncreaseOpacityEvent);
    this->RemoveCallbackCommandObserver(
      this->PaintbrushWidget, vtkKWEPaintbrushWidget::DecreaseOpacityEvent);
    this->RemoveCallbackCommandObserver(
      this->PaintbrushWidget, vtkKWEPaintbrushWidget::ResizeEvent);
    this->PaintbrushWidget->UnRegister(this);
    }
    
  this->PaintbrushWidget = arg;

  if (this->PaintbrushWidget)
    {
    this->PaintbrushWidget->Register(this);
    this->AddCallbackCommandObserver(
      this->PaintbrushWidget, vtkKWEPaintbrushWidget::IncrementSketchEvent);
    this->AddCallbackCommandObserver(
      this->PaintbrushWidget, vtkKWEPaintbrushWidget::DecrementSketchEvent);
    this->AddCallbackCommandObserver(
      this->PaintbrushWidget, vtkKWEPaintbrushWidget::BeginNewSketchEvent);
    this->AddCallbackCommandObserver(
      this->PaintbrushWidget, vtkKWEPaintbrushWidget::EndStrokeEvent);
    this->AddCallbackCommandObserver(
      this->PaintbrushWidget, vtkKWEPaintbrushWidget::UndoStrokeEvent);
    this->AddCallbackCommandObserver(
      this->PaintbrushWidget, vtkKWEPaintbrushWidget::RedoStrokeEvent);    
    this->AddCallbackCommandObserver(
      this->PaintbrushWidget, vtkKWEPaintbrushWidget::IncreaseOpacityEvent);
    this->AddCallbackCommandObserver(
      this->PaintbrushWidget, vtkKWEPaintbrushWidget::DecreaseOpacityEvent);
    this->AddCallbackCommandObserver(
      this->PaintbrushWidget, vtkKWEPaintbrushWidget::ResizeEvent);
    }

  this->Modified();

  this->PopulateSketchList();
  this->UpdatePaintbrushPropertyWidgets();
}

//----------------------------------------------------------------------------
void vtkVVPaintbrushWidgetEditor::SetImageWidget(vtkKWImageWidget *arg)
{
  if (this->ImageWidget == arg)
    {
    return;
    }

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

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

  this->Modified();
}

//----------------------------------------------------------------------------
void vtkVVPaintbrushWidgetEditor::PopulateSketchList()
{
  if (!this->SketchList || !this->PaintbrushWidget)
    {
    return;
    }

  vtkKWEPaintbrushRepresentation2D *rep2d = 
    vtkKWEPaintbrushRepresentation2D::SafeDownCast(
      this->PaintbrushWidget->GetRepresentation());
  vtkKWEPaintbrushDrawing *drawing = rep2d->GetPaintbrushDrawing();

  vtkKWMultiColumnList *list = this->SketchList->GetWidget();
  list->DeleteAllRows();
  
  // For computing statistics on the drawing.
  this->DrawingStatistics->SetInput(drawing);
  this->DrawingStatistics->Update();

  int index_col_index = this->GetIndexColumnIndex();
  int label_col_index = this->GetLabelColumnIndex();
  int vis_col_index = this->GetVisibilityColumnIndex();
  int color_col_index = this->GetColorColumnIndex();
  int mut_col_index = this->GetMutableColumnIndex();
  int identifier_col_index = this->GetIdentifierColumnIndex();
  int volume_col_index = this->GetVolumeColumnIndex();
  char rgb[256];

  int i, row = 0, nb_sketches = drawing->GetNumberOfItems();
  for (i = 0; i < nb_sketches; i++, row++)
    {
    vtkKWEPaintbrushSketch *sketch = drawing->GetItem(i);
    vtkKWEPaintbrushProperty *property = sketch->GetPaintbrushProperty();
    double *color = property->GetColor();

    list->InsertCellTextAsInt(row, index_col_index, i);

    list->SetCellTextAsInt(row, label_col_index, sketch->GetLabel());

    list->SetCellTextAsInt(row, vis_col_index, property->GetVisibility());
    list->SetCellWindowCommandToCheckButton(row, vis_col_index);

    sprintf(rgb, "%g %g %g", color[0], color[1], color[2]);
    list->SetCellText(row, color_col_index, rgb);
    list->SetCellWindowCommandToColorButton(row, color_col_index);

    list->SetCellTextAsInt(row, mut_col_index, property->GetMutable());
    list->SetCellWindowCommandToCheckButton(row, mut_col_index);

    list->SetCellText(row, identifier_col_index, property->GetIdentifier());

    list->SetCellTextAsFormattedDouble(row, volume_col_index, 
          this->DrawingStatistics->GetVolume(sketch), 5 /*precision */);
    }
  
  this->UpdateSketchListSelection();
}

//----------------------------------------------------------------------------
void vtkVVPaintbrushWidgetEditor::UpdateVolumesOfSketches()
{
  if (!this->SketchList || !this->PaintbrushWidget)
    {
    return;
    }

  vtkKWEPaintbrushRepresentation2D *rep2d = 
    vtkKWEPaintbrushRepresentation2D::SafeDownCast(
      this->PaintbrushWidget->GetRepresentation());
  vtkKWEPaintbrushDrawing *drawing = rep2d->GetPaintbrushDrawing();

  const int nb_drawings = drawing->GetNumberOfItems();
  const int volume_col_index = this->GetVolumeColumnIndex();
  vtkKWMultiColumnList *list = this->SketchList->GetWidget();
  for (int i = 0; i < nb_drawings; i++)
    {
    vtkKWEPaintbrushSketch *sketch = drawing->GetItem(i);
    const int row = list->FindCellTextAsIntInColumn(
                           this->GetIndexColumnIndex(), i);
    list->SetCellTextAsFormattedDouble(row, volume_col_index, 
          this->DrawingStatistics->GetVolume(sketch), 5 /*precision */);    
    }  
}

//----------------------------------------------------------------------------
void vtkVVPaintbrushWidgetEditor::UpdateSketchListSelection()
{
  if (!this->SketchList || !this->PaintbrushWidget)
    {
    return;
    }

  vtkKWEPaintbrushRepresentation2D *rep2d = 
    vtkKWEPaintbrushRepresentation2D::SafeDownCast(
      this->PaintbrushWidget->GetRepresentation());
  vtkKWEPaintbrushDrawing *drawing = rep2d->GetPaintbrushDrawing();
  if (drawing->GetNumberOfItems())
    {
    vtkKWMultiColumnList *list = this->SketchList->GetWidget();
    int row_index = list->FindCellTextAsIntInColumn(
      this->GetIndexColumnIndex(), rep2d->GetSketchIndex());
    list->SelectSingleRow(row_index);
    }
}

//----------------------------------------------------------------------------
void vtkVVPaintbrushWidgetEditor::UpdatePaintbrushPropertyWidgets()
{
  if (!this->PaintbrushWidget)
    {
    return;
    }

  vtkKWEPaintbrushRepresentation2D *rep2d = 
    vtkKWEPaintbrushRepresentation2D::SafeDownCast(
      this->PaintbrushWidget->GetRepresentation());

  if (this->PaintbrushOptions)
    {
    // This should be computed from the data
    this->PaintbrushOptions->SetShapeSizeRange(0.0, 50.0);
    this->PaintbrushOptions->SetShapeSizeResolution(0.05);
    this->PaintbrushOptions->SetShapeSize(
      *(rep2d->GetPaintbrushOperation()->GetPaintbrushShape()->GetWidth()));
    this->PaintbrushOptions->SetShapeSize(
      *(rep2d->GetPaintbrushOperation()->GetPaintbrushShape()->GetWidth()));
    this->PaintbrushOptions->SetSingleSliceBrush(
      rep2d->GetSingleSliceThickBrush());
    }

  vtkKWEPaintbrushDrawing *drawing = rep2d->GetPaintbrushDrawing();
  if (!drawing->GetNumberOfItems())
    {
    return;
    }
  
  if (this->PaintbrushOptions)
    {
    this->PaintbrushOptions->SetOpacity(
        drawing->GetItem(0)->GetPaintbrushProperty()->GetOpacity());
    }

}

//----------------------------------------------------------------------------
void vtkVVPaintbrushWidgetEditor::Update()
{
  // Update enable state

  this->UpdateEnableState();
}

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

  this->PropagateEnableState(this->EditorFrame);
  this->PropagateEnableState(this->SketchList);
  this->PropagateEnableState(this->ControlFrame);
  this->PropagateEnableState(this->Toolbar);
  this->PropagateEnableState(this->HelpLabel);
  this->PropagateEnableState(this->PaintbrushOptions);
}

//----------------------------------------------------------------------------
vtkKWEPaintbrushSketch* 
vtkVVPaintbrushWidgetEditor::GetSelectedPaintbrushSketchInList()
{
  vtkstd::vector< vtkKWEPaintbrushSketch* > sketches = 
    this->GetSelectedPaintbrushSketchesInList();
  return sketches.size() ? sketches[0] : NULL;
}

//----------------------------------------------------------------------------
vtkstd::vector< vtkKWEPaintbrushSketch* > vtkVVPaintbrushWidgetEditor
::GetSelectedPaintbrushSketchesInList()
{
  SketchContainerType sketches;
  if (this->SketchList && this->PaintbrushWidget)
    {
    vtkKWMultiColumnList *list = this->SketchList->GetWidget();
    const int nSelectedSketches = list->GetNumberOfSelectedRows();

    int *selectedRowIndices = new int[nSelectedSketches];
    list->GetSelectedRows(selectedRowIndices);

    vtkKWEPaintbrushRepresentation2D *rep2d = 
      vtkKWEPaintbrushRepresentation2D::SafeDownCast(
        this->PaintbrushWidget->GetRepresentation());
    vtkKWEPaintbrushDrawing *drawing = rep2d->GetPaintbrushDrawing();

    int targetSketchIndex = list->GetCellTextAsInt(
        selectedRowIndices[0], this->GetIndexColumnIndex());
    vtkKWEPaintbrushSketch *targetSketch = drawing->GetItem(targetSketchIndex);

    for (int i = 0; i < nSelectedSketches; i++)
      {
      int sketch_index = list->GetCellTextAsInt(
        selectedRowIndices[i], this->GetIndexColumnIndex());
      sketches.push_back(drawing->GetItem(sketch_index));
      }

    delete [] selectedRowIndices;
    }

  return sketches;
}

//----------------------------------------------------------------------------
void vtkVVPaintbrushWidgetEditor::SketchSelectionChangedCallback()
{
  if (!this->PaintbrushWidget)
    {
    return;
    }

  if (this->Toolbar)
    {
    vtkKWMultiColumnList *list = 
      this->SketchList ? this->SketchList->GetWidget() : NULL;
    this->Toolbar->SetSupportMultiSketchOperations(
      list && list->GetNumberOfSelectedRows() > 1 ? 1 : 0);
    }

  vtkKWEPaintbrushSketch *sketch = this->GetSelectedPaintbrushSketchInList();
  if (sketch)
    {
    vtkKWEPaintbrushRepresentation2D *rep2d = 
      vtkKWEPaintbrushRepresentation2D::SafeDownCast(
        this->PaintbrushWidget->GetRepresentation());
    vtkKWEPaintbrushDrawing *drawing = rep2d->GetPaintbrushDrawing();
    if (sketch != drawing->GetItem(rep2d->GetSketchIndex()))
      {
      this->PaintbrushWidget->GoToSketch(drawing->GetIndexOfItem(sketch));
      }
    }
}

//----------------------------------------------------------------------------
void vtkVVPaintbrushWidgetEditor::MergeSketchCallback()
{
  SketchContainerType sketches = this->GetSelectedPaintbrushSketchesInList();

  if (sketches.size() >= 2) // at least 2 selections required for a merge
    {
    vtkKWEPaintbrushRepresentation *rep =
      vtkKWEPaintbrushRepresentation::SafeDownCast(
        this->PaintbrushWidget->GetRepresentation());
    vtkKWEPaintbrushDrawing *drawing = rep->GetPaintbrushDrawing();
    
    // Each of the sketches below will be merged into the first selected sketch
    // and then removed from the drawing.
    for (size_t i = 1; i < sketches.size(); i++)
      {
      sketches[0]->Add(sketches[i]);
      drawing->RemoveItem(sketches[i]);
      }

    // Repopulate list and render.
    this->PopulateSketchList();
    this->PaintbrushWidget->GetWidgetGroup()->Render();
    }
}

//---------------------------------------------------------------------------
void vtkVVPaintbrushWidgetEditor::SketchCellUpdatedCallback(
  int row, int col, const char *text)
{
  if (!this->SketchList)
    {
    return;
    }

  vtkKWMultiColumnList *list = this->SketchList->GetWidget();
  int sketch_index = list->GetCellTextAsInt(row, this->GetIndexColumnIndex());
  vtkKWEPaintbrushRepresentation2D *rep2d = 
    vtkKWEPaintbrushRepresentation2D::SafeDownCast(
      this->PaintbrushWidget->GetRepresentation());
  vtkKWEPaintbrushSketch *sketch =   
    rep2d->GetPaintbrushDrawing()->GetItem(sketch_index);
  if (!sketch)
    {
    return;
    }

  vtkKWEPaintbrushProperty *property = sketch->GetPaintbrushProperty();

  // Visibility

  if (col == this->GetVisibilityColumnIndex())
    {
    property->SetVisibility(list->GetCellTextAsInt(row, col));
    this->PaintbrushWidget->GetWidgetGroup()->Render();
    return;
    }

  // Color

  if (col == this->GetColorColumnIndex())
    {
    double rgb[3];
    if (sscanf(list->GetCellText(row, col), 
               "%lg %lg %lg", rgb, rgb + 1, rgb + 2) == 3)
      {
      property->SetColor(rgb);
      this->PaintbrushWidget->GetWidgetGroup()->Render();
      }
    return;
    }

  // Mutable

  if (col == this->GetMutableColumnIndex())
    {
    property->SetMutable(list->GetCellTextAsInt(row, col));
    return;
    }

  // Identifier

  if (col == this->GetIdentifierColumnIndex())
    {
    property->SetIdentifier(list->GetCellText(row, col));
    this->PaintbrushWidget->GetWidgetGroup()->Render();
    return;
    }
}

//----------------------------------------------------------------------------
void vtkVVPaintbrushWidgetEditor::AddSketchCallback()
{
  if (!this->PaintbrushWidget)
    {
    return;
    }

  vtkKWEPaintbrushRepresentation2D *rep2d = 
    vtkKWEPaintbrushRepresentation2D::SafeDownCast(
      this->PaintbrushWidget->GetRepresentation());
  vtkKWEPaintbrushDrawing *drawing = rep2d->GetPaintbrushDrawing();
  vtkKWEPaintbrushLabelData *ldata = 
    vtkKWEPaintbrushLabelData::SafeDownCast(drawing->GetPaintbrushData());

  // Not using vtkKWEPaintbrushData::GetLabels because it's iterating
  // over the whole label data.
  typedef vtkstd::set< vtkKWEPaintbrushEnums::LabelType > LabelsContainer;
  LabelsContainer labels;
  int i, nb_sketches = drawing->GetNumberOfItems();
  for (i = 0; i < nb_sketches; i++)
    {
    labels.insert(drawing->GetItem(i)->GetLabel());
    }
  int new_label = 1;
  if (labels.size() && *labels.begin() == new_label)
    {
    LabelsContainer::iterator it = labels.begin();
    LabelsContainer::iterator end = labels.end();
    new_label = *it; ++it;
    for (; it != end; ++it)
      {
      if (*it - new_label > 1) break;
      new_label = *it;
      }
    ++new_label;
    }

  vtkKWEPaintbrushSketch *sketch = vtkKWEPaintbrushSketch::New();
  drawing->AddItem(sketch);
  sketch->SetLabel(new_label);
  sketch->GetPaintbrushProperty()->SetColor(
    vtkVVPaintbrushWidgetEditor::GetBasicColor(sketch->GetLabel() - 1));
  sketch->Initialize(ldata);
  sketch->Delete();

  this->PopulateSketchList();

  this->PaintbrushWidget->GoToSketch(drawing->GetIndexOfItem(sketch));
  this->UpdateSketchListSelection();
}

//----------------------------------------------------------------------------
void vtkVVPaintbrushWidgetEditor::DeleteSketchCallback()
{
  if (!vtkKWMessageDialog::PopupYesNo( 
        this->GetApplication(), 
        this->GetParentTopLevel(), 
        k_("Delete selected label map(s)?"),
        k_("Are you sure you want to delete the selected label map(s)?"), 
        vtkKWMessageDialog::WarningIcon | 
        vtkKWMessageDialog::InvokeAtPointer))
    {
    return;
    }

  if (!this->SketchList || !this->PaintbrushWidget)
    {
    return;
    }

  vtkKWEPaintbrushRepresentation2D *rep2d = 
    vtkKWEPaintbrushRepresentation2D::SafeDownCast(
      this->PaintbrushWidget->GetRepresentation());

  SketchContainerType sketches = this->GetSelectedPaintbrushSketchesInList();
  for (SketchIteratorType it = sketches.begin(); it != sketches.end(); ++it)
    {
    rep2d->RemoveSketch(*it);
    }

  // This Render() call should really have been done by the paintbrush 
  // itself, just like adding a sketch would do.
  this->PaintbrushWidget->GetWidgetGroup()->Render();
  this->PopulateSketchList();
}

//----------------------------------------------------------------------------
void vtkVVPaintbrushWidgetEditor::LoadDrawingCallback()
{
  if (!this->PaintbrushWidget)
    {
    return;
    }

  vtkKWLoadSaveDialog *load_dialog = vtkKWLoadSaveDialog::New();
  load_dialog->SetParent(this->GetParentTopLevel());
  load_dialog->Create();
  load_dialog->RetrieveLastPathFromRegistry(
    vtkVVPaintbrushWidgetEditor::LabelMapPathRegKey);
  load_dialog->SetTitle("Load Paintbrush Data");
  load_dialog->SetFileTypes("{{MetaImage (compressed)} {.mha}} "
                            "{{MetaImage (un-compressed)} {.mhd}} ");
  load_dialog->SetDefaultExtension(".mha");

  int res = load_dialog->Invoke();
  if (res)
    {
    load_dialog->SaveLastPathToRegistry(
      vtkVVPaintbrushWidgetEditor::LabelMapPathRegKey);
    vtkMetaImageReader *reader = vtkMetaImageReader::New();
    reader->SetFileName(load_dialog->GetFileName());
    reader->Update();
    vtkKWEPaintbrushRepresentation2D *rep2d =
      vtkKWEPaintbrushRepresentation2D::SafeDownCast(
        this->PaintbrushWidget->GetRepresentation());
    vtkKWEPaintbrushDrawing *drawing = rep2d->GetPaintbrushDrawing();
    drawing->RemoveAllItems();
    vtkKWEPaintbrushLabelData *final_label_map =
      vtkKWEPaintbrushLabelData::SafeDownCast(drawing->GetPaintbrushData());
    final_label_map->SetLabelMap(reader->GetOutput());
    reader->Delete();

    // Relabel the data to contiguous labels.
    //final_label_map->RelabelDataToContiguousLabels();

    // Create sketches in the drawing based on labels in the label map
    drawing->CreateSketches();
    for (int i = 0; i < drawing->GetNumberOfItems(); i++)
      {
      drawing->GetItem(i)->GetPaintbrushProperty()->SetColor(
          vtkVVPaintbrushWidgetEditor::GetBasicColor(i));
      }

    this->PopulateSketchList();
    this->PaintbrushWidget->GetWidgetGroup()->Render();
    }

 load_dialog->Delete();
}

//----------------------------------------------------------------------------
void vtkVVPaintbrushWidgetEditor::SaveDrawingCallback()
{
  if (!this->PaintbrushWidget)
    {
    return;
    }

  vtkKWLoadSaveDialog *save_dialog = vtkKWLoadSaveDialog::New();
  save_dialog->SetParent(this->GetParentTopLevel());
  save_dialog->Create();
  save_dialog->RetrieveLastPathFromRegistry(
    vtkVVPaintbrushWidgetEditor::LabelMapPathRegKey);
  save_dialog->SetTitle("Save Paintbrush Data");
  save_dialog->SaveDialogOn();
  save_dialog->SetFileTypes("{{MetaImage (compressed)} {.mha}} "
                            "{{MetaImage (un-compressed)} {.mhd}} ");
  save_dialog->SetDefaultExtension(".mha");

  int res = save_dialog->Invoke();
  if (res)
    {
    save_dialog->SaveLastPathToRegistry(
      vtkVVPaintbrushWidgetEditor::LabelMapPathRegKey);
    vtkKWEPaintbrushRepresentation2D *rep2d = 
      vtkKWEPaintbrushRepresentation2D::SafeDownCast(
        this->PaintbrushWidget->GetRepresentation());
    vtkKWEPaintbrushDrawing *drawing = rep2d->GetPaintbrushDrawing();
    vtkKWEPaintbrushLabelData *final_label_map = 
      vtkKWEPaintbrushLabelData::SafeDownCast(drawing->GetPaintbrushData());
    vtkMetaImageWriter *writer = vtkMetaImageWriter::New();
    writer->SetFileName(save_dialog->GetFileName());
    writer->SetInput(final_label_map->GetLabelMap());

    // MHD uses no compression. MHA uses compression.
    vtksys_stl::string filename(save_dialog->GetFileName());
    vtksys_stl::string ext = 
        vtksys::SystemTools::GetFilenameExtension(filename);
    writer->SetCompression(ext.compare(".mha") ? 0 : 1);

    writer->Write();
    writer->Delete();
    if (vtksys::SystemTools::FileExists(save_dialog->GetFileName()))
      {
      this->GetApplication()->SetRegistryValue(
        2, "RunTime", vtkVVPaintbrushWidgetEditor::LastSavedLabelMapRegKey, 
        save_dialog->GetFileName());
      }
    }

  save_dialog->Delete();
}

//----------------------------------------------------------------------------
void vtkVVPaintbrushWidgetEditor::PromoteDrawingToVolumeCallback()
{
  // We are cheating here, but no time for better solution
  vtkVVWindowBase *win = 
    vtkVVWindowBase::SafeDownCast(this->GetParentTopLevel());
  if (!win)
    {
    return;
    }
  
  vtkVVDataItemVolume *selected_vol = vtkVVDataItemVolume::SafeDownCast(
    win->GetSelectedDataItem());
  if (!selected_vol)
    {
    return;
    }

  win->RemoveCallbackCommandObservers();

  vtkVVDataItemVolume *new_vol = vtkVVDataItemVolume::New();
  vtkstd::string new_vol_name;
  int i = 0;
  do
    {
    i++;
    vtksys_ios::ostringstream temp;
    temp << selected_vol->GetName() << " (Label Map " << i << ")";
    new_vol_name.assign(temp.str());
    } while (win->GetDataItemPool()->HasDataItemWithName(new_vol_name.c_str()));
  new_vol->SetName(new_vol_name.c_str());
  new_vol->SetMedicalImageProperties(selected_vol->GetMedicalImageProperties());

  new_vol->SetDistanceUnits(selected_vol->GetDistanceUnits());
  new_vol->SetScope(selected_vol->GetScope());
  new_vol->SetDisplayMode(selected_vol->GetDisplayMode());

  vtkKWEPaintbrushRepresentation2D *rep2d = 
    vtkKWEPaintbrushRepresentation2D::SafeDownCast(
      this->PaintbrushWidget->GetRepresentation());
  vtkKWEPaintbrushDrawing *drawing = rep2d->GetPaintbrushDrawing();
  vtkImageData *image_data = vtkImageData::New();
  drawing->GetPaintbrushData()->GetPaintbrushDataAsImageData(image_data);
  new_vol->SetImageData(image_data);
  image_data->Delete();

  win->GetDataItemPool()->AddDataItem(new_vol);
  new_vol->AddDefaultRenderWidgets(win);
  new_vol->Delete();

  vtkKWVolumeWidget *vol_widget = new_vol->GetVolumeWidget(win);
  int nb_sketches = drawing->GetNumberOfItems();
  if (vol_widget && nb_sketches)
    {
    vtkVolumeProperty *vol_prop = vol_widget->GetVolumeProperty();
    vtkColorTransferFunction *color_tfunc = vol_prop->GetRGBTransferFunction();
    color_tfunc->RemoveAllPoints();
    int max_label = drawing->GetItem(0)->GetLabel();
    int min_label = drawing->GetItem(nb_sketches - 1)->GetLabel();
    for (int i = 0; i < nb_sketches; i++)
      {
      vtkKWEPaintbrushSketch *sketch = drawing->GetItem(i);
      vtkKWEPaintbrushProperty *property = sketch->GetPaintbrushProperty();
      double *rgb = property->GetColor();
      int label = sketch->GetLabel();
      color_tfunc->AddRGBPoint(label, rgb[0], rgb[1], rgb[2], 0.5, 1.0);
      if (label < min_label)
        {
        min_label = label;
        }
      if (label > max_label)
        {
        max_label = label;
        }
      }
    vtkPiecewiseFunction *opacity_tfunc =  vol_prop->GetScalarOpacity();
    opacity_tfunc->RemoveAllPoints();
    opacity_tfunc->AddPoint((double)min_label - 0.5, 0.0);
    opacity_tfunc->AddPoint((double)max_label + 0.5, 1.0);
    vol_widget->VolumePropertyChanged();
    }

  win->AddCallbackCommandObservers();
  win->Update();
}

//----------------------------------------------------------------------------
void vtkVVPaintbrushWidgetEditor::ConvertVolumeToDrawingCallback()
{
  // We are cheating here, but no time for better solution
  vtkVVWindowBase *win = 
    vtkVVWindowBase::SafeDownCast(this->GetParentTopLevel());
  if (!win)
    {
    return;
    }

  DataItemContainerType data_items = 
    this->GetEligibleDataItemsForConversionToDrawing();

  // Create the context menu if needed

  if (!this->ContextMenu)
    {
    this->ContextMenu = vtkKWMenu::New();
    }
  if (!this->ContextMenu->IsCreated())
    {
    this->ContextMenu->SetParent(this);
    this->ContextMenu->Create();
    }
  this->ContextMenu->DeleteAllItems();

  if (!data_items.size())
    {
    int index = this->ContextMenu->AddCommand("No Eligible Volumes");
    this->ContextMenu->SetItemStateToDisabled(index);
    }
  else
    {
    int index = this->ContextMenu->AddCommand("Select a Volume:");
    this->ContextMenu->SetItemStateToDisabled(index);
    this->ContextMenu->AddSeparator();

    DataItemIteratorType it = data_items.begin();
    DataItemIteratorType end = data_items.end();
    for (; it != end; ++it)
      {
      vtkVVDataItemVolume *data_item = *it;
      vtksys_stl::string cmd("ConvertVolumeToDrawing {");
      cmd += data_item->GetTclName();
      cmd += "}";
      index = this->ContextMenu->AddCommand(
        data_item->GetDescriptiveName(), this, cmd.c_str());
      }
    }

  int x, y;
  vtkKWTkUtilities::GetMousePointerCoordinates(
    this->GetApplication()->GetMainInterp(), ".", &x, &y);
  this->ContextMenu->PopUp(x, y);
}

//----------------------------------------------------------------------------
void vtkVVPaintbrushWidgetEditor::ConvertVolumeToDrawing(
  vtkVVDataItemVolume *vol)
{
  vtkKWEPaintbrushRepresentation2D *rep2d =
    vtkKWEPaintbrushRepresentation2D::SafeDownCast(
      this->PaintbrushWidget->GetRepresentation());
  vtkKWEPaintbrushDrawing *drawing = rep2d->GetPaintbrushDrawing();
  drawing->RemoveAllItems();
  vtkKWEPaintbrushLabelData *final_label_map =
    vtkKWEPaintbrushLabelData::SafeDownCast(drawing->GetPaintbrushData());
  final_label_map->SetLabelMap(vol->GetImageData());

  // Create sketches in the drawing based on labels in the label map
  drawing->CreateSketches();
  for (int i = 0; i < drawing->GetNumberOfItems(); i++)
    {
    drawing->GetItem(i)->GetPaintbrushProperty()->SetColor(
      vtkVVPaintbrushWidgetEditor::GetBasicColor(i));
    }

  this->PopulateSketchList();
  this->PaintbrushWidget->GetWidgetGroup()->Render();
}

//----------------------------------------------------------------------------
vtkstd::vector< vtkVVDataItemVolume* > 
vtkVVPaintbrushWidgetEditor::GetEligibleDataItemsForConversionToDrawing()
{
  // Find the data items that can be converting to a drawing for the
  // paintbrush widget. They have to be the same size.
  DataItemContainerType data_items;
  vtkVVWindowBase *win = 
    vtkVVWindowBase::SafeDownCast(this->GetParentTopLevel());
  if (win)
    {
    vtkKWEPaintbrushRepresentation2D *rep2d = 
      vtkKWEPaintbrushRepresentation2D::SafeDownCast(
        this->PaintbrushWidget->GetRepresentation());
    vtkKWEPaintbrushDrawing *drawing = rep2d->GetPaintbrushDrawing();
    vtkImageData *image_data = drawing->GetImageData();
    if (image_data)
      {
      int *image_data_dims = image_data->GetDimensions();
      vtkVVDataItemPool *pool = win->GetDataItemPool();
      for (int i = 0; i < pool->GetNumberOfDataItems(); i++)
        {
        vtkVVDataItemVolume *data_item = vtkVVDataItemVolume::SafeDownCast(
          pool->GetNthDataItem(i));
        if (data_item)
          {
          vtkImageData *candidate_data = data_item->GetImageData();
          if (candidate_data && candidate_data != image_data)
            {
            int *candidate_data_dims = candidate_data->GetDimensions();
            if (image_data_dims[0] == candidate_data_dims[0] && 
                image_data_dims[1] == candidate_data_dims[1] &&
                image_data_dims[2] == candidate_data_dims[2])
              {
              data_items.push_back(data_item);
              }
            }
          }
        }
      }
    }

  return data_items;
}

//----------------------------------------------------------------------------
void vtkVVPaintbrushWidgetEditor::OpacityChangedCallback(double value)
{
  if (!this->PaintbrushWidget)
    {
    return;
    }

  vtkKWEPaintbrushRepresentation2D *rep2d = 
    vtkKWEPaintbrushRepresentation2D::SafeDownCast(
      this->PaintbrushWidget->GetRepresentation());
  vtkKWEPaintbrushDrawing *drawing = rep2d->GetPaintbrushDrawing();
  int i, nb_sketches = drawing->GetNumberOfItems();
  for (i = 0; i < nb_sketches; i++)
    {
    drawing->GetItem(i)->GetPaintbrushProperty()->SetOpacity(value);
    }
  this->PaintbrushWidget->GetWidgetGroup()->Render();
}

//----------------------------------------------------------------------------
void vtkVVPaintbrushWidgetEditor::ShapeSizeChangedCallback(double value)
{
  if (!this->PaintbrushWidget)
    {
    return;
    }

  // Set the shape size for all widgets in this group.
  vtkKWEWidgetGroup *group = this->PaintbrushWidget->GetWidgetGroup();
  const unsigned int nWidgets = group->GetNumberOfWidgets();
  for (unsigned int i = 0; i < nWidgets; i++)
    {
    vtkKWEPaintbrushRepresentation2D *rep2d = 
      vtkKWEPaintbrushRepresentation2D::SafeDownCast(
        group->GetNthWidget(i)->GetRepresentation());
    if (vtkKWEPaintbrushShapeEllipsoid *shape =
          vtkKWEPaintbrushShapeEllipsoid::SafeDownCast(
            rep2d->GetPaintbrushOperation()->GetPaintbrushShape()))
      {
      shape->SetWidth(value, value, value);
      }
    }
}

//----------------------------------------------------------------------------
void vtkVVPaintbrushWidgetEditor::SingleSliceBrushChangedCallback(int e)
{
  if (!this->PaintbrushWidget)
    {
    return;
    }

  // Set the shape size for all widgets in this group.
  vtkKWEWidgetGroup *group = this->PaintbrushWidget->GetWidgetGroup();
  const unsigned int nWidgets = group->GetNumberOfWidgets();
  for (unsigned int i = 0; i < nWidgets; i++)
    {
    vtkKWEPaintbrushRepresentation2D *rep2d = 
      vtkKWEPaintbrushRepresentation2D::SafeDownCast(
        group->GetNthWidget(i)->GetRepresentation());
    rep2d->SetSingleSliceThickBrush(e);
    }
}

//----------------------------------------------------------------------------
void vtkVVPaintbrushWidgetEditor::UndoStrokeCallback()
{
  if (!this->PaintbrushWidget)
    {
    return;
    }

  vtkKWEPaintbrushRepresentation2D *rep2d = 
    vtkKWEPaintbrushRepresentation2D::SafeDownCast(
      this->PaintbrushWidget->GetRepresentation());
  rep2d->UndoStroke();
  this->PaintbrushWidget->GetWidgetGroup()->Render();
}

//----------------------------------------------------------------------------
void vtkVVPaintbrushWidgetEditor::RedoStrokeCallback()
{
  if (!this->PaintbrushWidget)
    {
    return;
    }

  vtkKWEPaintbrushRepresentation2D *rep2d = 
    vtkKWEPaintbrushRepresentation2D::SafeDownCast(
      this->PaintbrushWidget->GetRepresentation());
  rep2d->RedoStroke();
  this->PaintbrushWidget->GetWidgetGroup()->Render();
}

//----------------------------------------------------------------------------
void vtkVVPaintbrushWidgetEditor::CopySketchToNextSliceCallback()
{
  if (!this->PaintbrushWidget)
    {
    return;
    }

  int success = 0;
  vtkKWEPaintbrushRepresentation2D *rep2d = 
        vtkKWEPaintbrushRepresentation2D::SafeDownCast(
            this->PaintbrushWidget->GetRepresentation());
  
  if (rep2d)
    {
    SketchContainerType sketches = this->GetSelectedPaintbrushSketchesInList();
    for (SketchIteratorType it = sketches.begin(); it != sketches.end(); ++it)
      {
      success += rep2d->CopySketchToNextSlice(*it);
      }
    }

  if (success) // Render in response to changes
    {
    // Reinitializes the history information and wipes any undo/redo stuff
    rep2d->GetPaintbrushDrawing()->CreateSketches();
    rep2d->GetPaintbrushDrawing()->CollapseHistory();

    this->PaintbrushWidget->GetWidgetGroup()->Render();
    if (this->ImageWidget)
      {
      this->ImageWidget->IncrementSlice();
      }
    }
}

//----------------------------------------------------------------------------
void vtkVVPaintbrushWidgetEditor::CopySketchToPreviousSliceCallback()
{
  if (!this->PaintbrushWidget)
    {
    return;
    }

  int success = 0;
  vtkKWEPaintbrushRepresentation2D *rep2d = 
        vtkKWEPaintbrushRepresentation2D::SafeDownCast(
            this->PaintbrushWidget->GetRepresentation());
  
  if (rep2d)
    {
    SketchContainerType sketches = this->GetSelectedPaintbrushSketchesInList();
    for (SketchIteratorType it = sketches.begin(); it != sketches.end(); ++it)
      {
      success += rep2d->CopySketchToPreviousSlice(*it);
      }
    }

  if (success) // Render in response to changes
    {
    // Reinitializes the history information and wipes any undo/redo stuff
    rep2d->GetPaintbrushDrawing()->CreateSketches();
    rep2d->GetPaintbrushDrawing()->CollapseHistory();

    this->PaintbrushWidget->GetWidgetGroup()->Render();
    if (this->ImageWidget)
      {
      this->ImageWidget->DecrementSlice();
      }
    }
}

//----------------------------------------------------------------------------
void vtkVVPaintbrushWidgetEditor::ProcessCallbackCommandEvents(
  vtkObject *caller,
  unsigned long event,
  void *calldata)
{
  if (caller == this->GetPaintbrushWidget())
    {
    switch (event)
      {
      case vtkKWEPaintbrushWidget::IncrementSketchEvent:
      case vtkKWEPaintbrushWidget::DecrementSketchEvent:
        this->UpdateSketchListSelection();
        break;
      case vtkKWEPaintbrushWidget::BeginNewSketchEvent:
        this->PopulateSketchList();
        break;
      case vtkKWEPaintbrushWidget::EndStrokeEvent:
      case vtkKWEPaintbrushWidget::UndoStrokeEvent:
      case vtkKWEPaintbrushWidget::RedoStrokeEvent:
        this->UpdateVolumesOfSketches();
        break;
      case vtkKWEPaintbrushWidget::IncreaseOpacityEvent:
      case vtkKWEPaintbrushWidget::DecreaseOpacityEvent:
      case vtkKWEPaintbrushWidget::ResizeEvent:
        this->UpdatePaintbrushPropertyWidgets();
        break;
      }
    }

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

//----------------------------------------------------------------------------
double* vtkVVPaintbrushWidgetEditor::GetBasicColor(int index)
{
  static double basic_colors[][3] =
  {
    {1.00, 0.50, 0.50}, 
    {1.00, 1.00, 0.50}, 
    {0.50, 1.00, 0.50}, 
    //    {0.00, 1.00, 0.50}, 
    {0.50, 1.00, 1.00}, 
    {0.00, 0.50, 1.00}, 
    {1.00, 0.50, 0.75}, 
    {1.00, 0.50, 1.00}, 
    {1.00, 0.00, 0.00}, 
    {1.00, 1.00, 0.00}, 
    {0.50, 1.00, 0.00}, 
    {0.00, 1.00, 0.25}, 
    {0.00, 1.00, 1.00}, 
    {0.00, 0.50, 0.75}, 
    {0.50, 0.50, 0.75}, 
    {1.00, 0.00, 1.00}, 
    {0.50, 0.25, 0.25}, 
    {1.00, 0.50, 0.25}, 
    {0.00, 1.00, 0.00}, 
    {0.00, 0.50, 0.50}, 
    {0.00, 0.25, 0.50}, 
    {0.50, 0.50, 1.00}, 
    {0.50, 0.00, 0.25}, 
    {1.00, 0.00, 0.50}, 
    {0.50, 0.00, 0.00}, 
    {1.00, 0.50, 0.00}, 
    {0.00, 0.50, 0.00}, 
    {0.00, 0.50, 0.25}, 
    {0.00, 0.00, 1.00}, 
    {0.00, 0.00, 0.63}, 
    {0.50, 0.00, 0.50}, 
    {0.50, 0.00, 1.00}, 
    {0.25, 0.00, 0.00}, 
    {0.50, 0.25, 0.00}, 
    {0.00, 0.25, 0.00}, 
    {0.00, 0.25, 0.25}, 
    {0.00, 0.00, 0.50}, 
    {0.00, 0.00, 0.25}, 
    {0.25, 0.00, 0.25}, 
    {0.25, 0.00, 0.50}, 
    {0.00, 0.00, 0.00}, 
    {0.50, 0.50, 0.00}, 
    {0.50, 0.50, 0.25}, 
    {0.50, 0.50, 0.50}, 
    {0.25, 0.50, 0.50}, 
    {0.75, 0.75, 0.75}, 
    {0.25, 0.00, 0.25}, 
    {1.00, 1.00, 1.00}, 
  };

  size_t nb_colors = sizeof(basic_colors) / sizeof(basic_colors[0]);
  return basic_colors[index % nb_colors];
}

//----------------------------------------------------------------------------
void vtkVVPaintbrushWidgetEditor::PrintSelf(ostream& os, vtkIndent indent)
{
  this->Superclass::PrintSelf(os, indent);
  
  os << indent << "PaintbrushWidget: ";
  if (this->PaintbrushWidget)
    {
    os << endl;
    this->PaintbrushWidget->PrintSelf(os, indent.GetNextIndent());
    }
  else
    {
    os << "None" << endl;
    }
}
