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

  Program:   Visualization Toolkit
  Module:    vtkPVAnimationInterface.cxx
  Language:  C++
  Date:      $Date$
  Version:   $Revision$

Copyright (c) 1998-2000 Kitware Inc. 469 Clifton Corporate Parkway,
Clifton Park, NY, 12065, USA.

All rights reserved. No part of this software may be reproduced, distributed,
or modified, in any form or by any means, without permission in writing from
Kitware Inc.

IN NO EVENT SHALL THE AUTHORS OR DISTRIBUTORS BE LIABLE TO ANY PARTY FOR
DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT
OF THE USE OF THIS SOFTWARE, ITS DOCUMENTATION, OR ANY DERIVATIVES THEREOF,
EVEN IF THE AUTHORS HAVE BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

THE AUTHORS AND DISTRIBUTORS SPECIFICALLY DISCLAIM ANY WARRANTIES, INCLUDING,
BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
PARTICULAR PURPOSE, AND NON-INFRINGEMENT.  THIS SOFTWARE IS PROVIDED ON AN
"AS IS" BASIS, AND THE AUTHORS AND DISTRIBUTORS HAVE NO OBLIGATION TO PROVIDE
MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.

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

#include "vtkKWCheckButton.h"
#include "vtkKWEntry.h"
#include "vtkKWLabel.h"
#include "vtkKWLabeledEntry.h"
#include "vtkKWLabeledFrame.h"
#include "vtkKWMenu.h"
#include "vtkKWMenuButton.h"
#include "vtkKWPushButton.h"
#include "vtkKWScale.h"
#include "vtkKWText.h"
#include "vtkKWTkUtilities.h"
#include "vtkKWView.h"
#include "vtkObjectFactory.h"
#include "vtkPVApplication.h"
#include "vtkPVRenderView.h"
#include "vtkPVSource.h"
#include "vtkPVSourceCollection.h"
#include "vtkPVWidget.h"
#include "vtkPVWidgetCollection.h"
#include "vtkPVWindow.h"

// We need to:
// Format min/max/resolution entries better.
// Add callbacks to take the place of accept button.
// Handle methods with multiple entries.
// Handle special sources (contour, probe, threshold).

/* 
 * This part was generated by ImageConvert from image:
 *    play.png (zlib, base64)
 */
#define image_play_width         16
#define image_play_height        16
#define image_play_pixel_size    4
#define image_play_buffer_length 108

static unsigned char image_play[] = 
  "eNr7//8/w/9BhoFACYgFGbAAIvW7APEZbGaQoP8/NjNI1I9hBhn6UcwgUz/cDAr0g3AoBf"
  "rTKHB/GgXhl0ZB/KVRkH7SyE2/9MQARXskxQ==";

/* 
 * This part was generated by ImageConvert from image:
 *    stop.png (zlib, base64)
 */
#define image_stop_width         16
#define image_stop_height        16
#define image_stop_pixel_size    4
#define image_stop_buffer_length 64

static unsigned char image_stop[] = 
  "eNr7//8/w/9hgoFACYhdCGAlPPrLgfg/AVw+qn/Y6k8D4t0EcNr/YZRnAMXDIoM=";

/* 
 * This part was generated by ImageConvert from image:
 *    loop.png (zlib, base64)
 */
#define image_loop_width         16
#define image_loop_height        16
#define image_loop_pixel_size    4
#define image_loop_buffer_length 212

static unsigned char image_loop[] = 
  "eNrNk1ENwCAMRLGAhVnAAhZmYRZqYRawMAtYwMIszEJHk9tCSMuWfe2S+6H3CLTAzI5/4K"
  "pQTY2jG0jhRUlKMH3g5wd+QUbjffUBLhk8IeMVXmp7kw3G0SVDCp+rV/csyWSDpxc8Gbzc"
  "eXvBSyYp/NX7acBOyMzG/OQOBbPo5VHLg/lfmQN9ivCKtXtvg297VJq3VPre8k/+3QkrPX"
  "VE";

/* 
 * This part was generated by ImageConvert from image:
 *    goto_beginning.png (zlib, base64)
 */
#define image_goto_beginning_width         16
#define image_goto_beginning_height        16
#define image_goto_beginning_pixel_size    4
#define image_goto_beginning_buffer_length 188

static unsigned char image_goto_beginning[] = 
  "eNrN00ENgDAMBdBZmAUsYAELWJiFWcBCPaFlFkq30KQpLesNlpTDz94nwEDEhB8PrX7Z0n"
  "MVJ19p6sQXJ++2TTxbnbPFFy+tzKX1vLaca2v5w7B4dzYj136nOZ37Q8D3fdno4OeHgE9G"
  "h3z/EPC6Q39/CHjZYZ0/UL7SLMa+7OTjPOAP/r0LX11aGw==";

/* 
 * This part was generated by ImageConvert from image:
 *    goto_end.png (zlib, base64)
 */
#define image_goto_end_width         16
#define image_goto_end_height        16
#define image_goto_end_pixel_size    4
#define image_goto_end_buffer_length 184

static unsigned char image_goto_end[] = 
  "eNrNk1sNgDAMRWcBC1jAAhawMAuzgIV6QgsWykbSpCn3bvxBk/vT7Jy9VTXpx6lVapb0rL"
  "Umk74G/gSOexxwIF6Bw/joYHx0eN47erx3RN4cI94cGfRb9he8kPmPmm3AC9l/Y6fB+oWc"
  "v7G98xNy/55lvJB3FlnEo7fbagas9Yv+4O9dh3laGw==";


//----------------------------------------------------------------------------
vtkStandardNewMacro(vtkPVAnimationInterface);
vtkCxxRevisionMacro(vtkPVAnimationInterface, "1.28.2.8");

vtkCxxSetObjectMacro(vtkPVAnimationInterface,ControlledWidget, vtkPVWidget);

int vtkPVAnimationInterfaceCommand(ClientData cd, Tcl_Interp *interp,
                           int argc, char *argv[]);

//----------------------------------------------------------------------------
vtkPVAnimationInterface::vtkPVAnimationInterface()
{
  this->CommandFunction = vtkPVAnimationInterfaceCommand;

  this->TimeStart = 0;
  this->TimeStep = 1;
  this->TimeEnd = 100;

  this->StopFlag = 0;
  this->InPlay = 0;
  this->Loop = 0;

  this->PVSource = NULL;

  this->View = NULL;
  this->Window = NULL;

  // Animation control

  this->ControlFrame = vtkKWLabeledFrame::New();

  this->ControlButtonFrame = vtkKWWidget::New();

  this->PlayButton = vtkKWPushButton::New();

  this->StopButton = vtkKWPushButton::New();

  this->GoToBeginningButton = vtkKWPushButton::New();

  this->GoToEndButton = vtkKWPushButton::New();

  this->LoopCheckButton = vtkKWCheckButton::New();

  this->TimeFrame = vtkKWWidget::New();

  this->TimeStartEntry = vtkKWLabeledEntry::New();

  this->TimeEndEntry = vtkKWLabeledEntry::New();

  this->TimeStepEntry = vtkKWLabeledEntry::New();

  this->TimeScale = vtkKWScale::New();

  // Action and script editing

  this->ActionFrame = vtkKWLabeledFrame::New();

  this->ScriptCheckButtonFrame = vtkKWWidget::New();

  this->ScriptCheckButton = vtkKWCheckButton::New();

  this->ScriptEditor = vtkKWText::New();

  this->SourceMethodFrame = vtkKWWidget::New();

  this->SourceLabel = vtkKWLabel::New();

  this->SourceMenuButton = vtkKWMenuButton::New();

  this->MethodLabel = vtkKWLabel::New();

  this->MethodMenuButton = vtkKWMenuButton::New();

  this->ControlledWidget = NULL;
}

//----------------------------------------------------------------------------
vtkPVAnimationInterface::~vtkPVAnimationInterface()
{
  this->CommandFunction = vtkPVAnimationInterfaceCommand;

  if (this->ControlFrame)
    {
    this->ControlFrame->Delete();
    this->ControlFrame = NULL;
    }
  if (this->ControlButtonFrame)
    {
    this->ControlButtonFrame->Delete();
    this->ControlButtonFrame = NULL;
    }
  if (this->PlayButton)
    {
    this->PlayButton->Delete();
    this->PlayButton = NULL;
    }
  if (this->StopButton)
    {
    this->StopButton->Delete();
    this->StopButton = NULL;
    }
  if (this->GoToBeginningButton)
    {
    this->GoToBeginningButton->Delete();
    this->GoToBeginningButton = NULL;
    }
  if (this->GoToEndButton)
    {
    this->GoToEndButton->Delete();
    this->GoToEndButton = NULL;
    }
  if (this->LoopCheckButton)
    {
    this->LoopCheckButton->Delete();
    this->LoopCheckButton = NULL;
    }
  if (this->TimeScale)
    {
    this->TimeScale->Delete();
    this->TimeScale = NULL;
    }
  if (this->TimeFrame)
    {
    this->TimeFrame->Delete();
    this->TimeFrame = NULL;
    }
  if (this->TimeStartEntry)
    {
    this->TimeStartEntry->Delete();
    this->TimeStartEntry = NULL;
    }
  if (this->TimeEndEntry)
    {
    this->TimeEndEntry->Delete();
    this->TimeEndEntry = NULL;
    }
  if (this->TimeStepEntry)
    {
    this->TimeStepEntry->Delete();
    this->TimeStepEntry = NULL;
    }

  if (this->ScriptEditor)
    {
    this->ScriptEditor->Delete();
    this->ScriptEditor = NULL;
    }
  
  if (this->ActionFrame)
    {
    this->ActionFrame->Delete();
    this->ActionFrame = NULL;
    }
  if (this->ScriptCheckButtonFrame)
    {
    this->ScriptCheckButtonFrame->Delete();
    this->ScriptCheckButtonFrame = NULL;
    }
  if (this->ScriptCheckButton)
    {
    this->ScriptCheckButton->Delete();
    this->ScriptCheckButton = NULL;
    }
  if (this->SourceMethodFrame)
    {
    this->SourceMethodFrame->Delete();
    this->SourceMethodFrame = NULL;
    }
 if (this->SourceLabel)
    {
    this->SourceLabel->Delete();
    this->SourceLabel = NULL;
    }
  if (this->SourceMenuButton)
    {
    this->SourceMenuButton->Delete();
    this->SourceMenuButton = NULL;
    }

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

  this->SetPVSource(NULL);
  this->SetControlledWidget(NULL);
  this->SetView(NULL);
  this->SetWindow(NULL);
}

//----------------------------------------------------------------------------
void vtkPVAnimationInterface::Create(vtkKWApplication *app, char *frameArgs)
{  
  vtkPVApplication *pvApp = vtkPVApplication::SafeDownCast(app);

  if (this->Application)
    {
    vtkErrorMacro("Widget has already been created.");
    return;
    }

  if (pvApp == NULL)
    {
    vtkErrorMacro("Need the subclass vtkPVApplication to create this object.");
    return;
    }

  this->SetApplication(app);

  // We are going to 'grid' most of it, so let's define some const

  int col_1_padx = 2;
  int button_pady = 1;

  // Create the frame for this widget.

  this->Script("frame %s %s", this->GetWidgetName(), frameArgs);

  // Animation Control

  this->ControlFrame->SetParent(this);
  this->ControlFrame->ShowHideFrameOn();
  this->ControlFrame->Create(this->Application);
  this->ControlFrame->SetLabel("Animation Control");

  this->ControlButtonFrame->SetParent(this->ControlFrame->GetFrame());
  this->ControlButtonFrame->Create(this->Application, "frame", "");

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

  // Animation Control: Play button to start the animation.

  this->PlayButton->SetParent(this->ControlButtonFrame);
  this->PlayButton->Create(this->Application, "");
  this->PlayButton->SetCommand(this, "Play");

  ostrstream play;
  play << this->PlayButton->GetWidgetName() << ".playimg" << ends;
  this->Script("image create photo %s", play.str());
  if (!vtkKWTkUtilities::UpdatePhoto(this->Application->GetMainInterp(),
                                     play.str(), 
                                     image_play, 
                                     image_play_width, 
                                     image_play_height, 
                                     image_play_pixel_size,
                                     image_play_buffer_length,
                                     this->PlayButton->GetWidgetName()))
    {
    vtkWarningMacro(<< "Error creating photo (play)");
    this->PlayButton->SetLabel("Play");
    }
  else
    {
    this->Script("%s configure -image %s", 
                 this->PlayButton->GetWidgetName(), play.str());
    }
  play.rdbuf()->freeze(0);
  this->PlayButton->SetBalloonHelpString("Play animation");

  // Animation Control: Stop button to stop the animation.

  this->StopButton->SetParent(this->ControlButtonFrame);
  this->StopButton->Create(this->Application, "");
  this->StopButton->SetCommand(this, "Stop");

  ostrstream stop;
  stop << this->StopButton->GetWidgetName() << ".stopimg" << ends;
  this->Script("image create photo %s", stop.str());
  if (!vtkKWTkUtilities::UpdatePhoto(this->Application->GetMainInterp(),
                                     stop.str(), 
                                     image_stop, 
                                     image_stop_width, 
                                     image_stop_height, 
                                     image_stop_pixel_size,
                                     image_stop_buffer_length,
                                     this->StopButton->GetWidgetName()))
    {
    vtkWarningMacro(<< "Error creating photo (stop)");
    this->StopButton->SetLabel("Stop");
    }
  else
    {
    this->Script("%s configure -image %s", 
                 this->StopButton->GetWidgetName(), stop.str());
    }
  stop.rdbuf()->freeze(0);
  this->StopButton->SetBalloonHelpString("Stop animation");

  // Animation Control: "go to beginning" button.

  this->GoToBeginningButton->SetParent(this->ControlButtonFrame);
  this->GoToBeginningButton->Create(this->Application, "");
  this->GoToBeginningButton->SetCommand(this, "GoToBeginning");

  ostrstream goto_beginning;
  goto_beginning << this->GoToBeginningButton->GetWidgetName() 
                 << ".goto_beginningimg" << ends;
  this->Script("image create photo %s", goto_beginning.str());
  if (!vtkKWTkUtilities::UpdatePhoto(this->Application->GetMainInterp(),
                                     goto_beginning.str(), 
                                     image_goto_beginning, 
                                     image_goto_beginning_width, 
                                     image_goto_beginning_height, 
                                     image_goto_beginning_pixel_size,
                                     image_goto_beginning_buffer_length,
                                     this->GoToBeginningButton->GetWidgetName()))
    {
    vtkWarningMacro(<< "Error creating photo (goto_beginning)");
    this->GoToBeginningButton->SetLabel("Go to beginning");
    }
  else
    {
    this->Script("%s configure -image %s", 
                 this->GoToBeginningButton->GetWidgetName(), goto_beginning.str());
    }
  goto_beginning.rdbuf()->freeze(0);
  this->GoToBeginningButton->SetBalloonHelpString(
    "Go to the beginning of the animation");

  // Animation Control: "go to beginning" button.

  this->GoToEndButton->SetParent(this->ControlButtonFrame);
  this->GoToEndButton->Create(this->Application, "");
  this->GoToEndButton->SetCommand(this, "GoToEnd");

  ostrstream goto_end;
  goto_end << this->GoToEndButton->GetWidgetName() 
                 << ".goto_endimg" << ends;
  this->Script("image create photo %s", goto_end.str());
  if (!vtkKWTkUtilities::UpdatePhoto(this->Application->GetMainInterp(),
                                     goto_end.str(), 
                                     image_goto_end, 
                                     image_goto_end_width, 
                                     image_goto_end_height, 
                                     image_goto_end_pixel_size,
                                     image_goto_end_buffer_length,
                                     this->GoToEndButton->GetWidgetName()))
    {
    vtkWarningMacro(<< "Error creating photo (goto_end)");
    this->GoToEndButton->SetLabel("Go to end");
    }
  else
    {
    this->Script("%s configure -image %s", 
                 this->GoToEndButton->GetWidgetName(), goto_end.str());
    }
  goto_end.rdbuf()->freeze(0);
  this->GoToEndButton->SetBalloonHelpString(
    "Go to the end of the animation");

  //  Animation Control: loop button to loop the animation.

  this->LoopCheckButton->SetParent(this->ControlButtonFrame);
  this->LoopCheckButton->Create(this->Application, "");
  this->LoopCheckButton->SetCommand(this, "LoopCheckButtonCallback");
  this->LoopCheckButton->SetState(this->Loop ? 1 : 0);

  ostrstream loop;
  loop << this->LoopCheckButton->GetWidgetName() << ".loopimg" << ends;
  this->Script("image create photo %s", loop.str());
  if (!vtkKWTkUtilities::UpdatePhoto(this->Application->GetMainInterp(),
                                     loop.str(), 
                                     image_loop, 
                                     image_loop_width, 
                                     image_loop_height, 
                                     image_loop_pixel_size,
                                     image_loop_buffer_length,
                                     this->LoopCheckButton->GetWidgetName()))
    {
    vtkWarningMacro(<< "Error creating photo (loop)");
    this->LoopCheckButton->SetText("Loop");
    }
  else
    {
    this->Script("%s configure -image %s -highlightthickness 0", 
                 this->LoopCheckButton->GetWidgetName(), loop.str());
    this->LoopCheckButton->SetIndicator(0);
    }
  loop.rdbuf()->freeze(0);
  this->LoopCheckButton->SetBalloonHelpString("Enable/Disable animation loop.");

  //  Animation Control: pack the transport buttons

  this->Script("pack %s %s %s %s %s -side left -fill both -padx 1", 
               this->GoToBeginningButton->GetWidgetName(), 
               this->PlayButton->GetWidgetName(), 
               this->StopButton->GetWidgetName(),
               this->GoToEndButton->GetWidgetName(), 
               this->LoopCheckButton->GetWidgetName());

  // Animation Control: Time scale

  this->TimeScale->SetParent(this->ControlFrame->GetFrame());
  this->TimeScale->Create(this->Application, "");
  this->TimeScale->DisplayEntry();
  this->TimeScale->DisplayEntryAndLabelOnTopOff();
  this->TimeScale->DisplayLabel("Time:");
  this->TimeScale->SetEndCommand(this, "TimeScaleCallback");
  this->TimeScale->SetEntryCommand(this, "TimeScaleCallback");

  this->Script("pack %s -side top -expand t -fill x", 
               this->TimeScale->GetWidgetName());

  // Animation Control: Start/Step/End

  this->TimeFrame->SetParent(this->ControlFrame->GetFrame());
  this->TimeFrame->Create(this->Application, "frame", "");

  this->Script("pack %s -side top -expand t -fill x", 
               this->TimeFrame->GetWidgetName());

  this->TimeStartEntry->SetParent(this->TimeFrame);
  this->TimeStartEntry->Create(this->Application);
  this->TimeStartEntry->GetEntry()->SetWidth(6);
  this->TimeStartEntry->SetLabel("Start:");
  this->TimeStartEntry->SetValue(this->TimeStart, 2);

  this->Script("bind %s <KeyPress-Return> {%s TimeStartEntryCallback}",
               this->TimeStartEntry->GetEntry()->GetWidgetName(),
               this->GetTclName());

  this->Script("bind %s <FocusOut> {%s TimeStartEntryCallback}",
               this->TimeStartEntry->GetEntry()->GetWidgetName(),
               this->GetTclName());

  this->TimeStepEntry->SetParent(this->TimeFrame);
  this->TimeStepEntry->Create(this->Application);
  this->TimeStepEntry->GetEntry()->SetWidth(6);
  this->TimeStepEntry->SetLabel("Step:");
  this->TimeStepEntry->SetValue(this->TimeStep, 2);

  this->Script("bind %s <KeyPress-Return> {%s TimeStepEntryCallback}",
               this->TimeStepEntry->GetEntry()->GetWidgetName(),
               this->GetTclName());

  this->Script("bind %s <FocusOut> {%s TimeStepEntryCallback}",
               this->TimeStepEntry->GetEntry()->GetWidgetName(),
               this->GetTclName());

  this->TimeEndEntry->SetParent(this->TimeFrame);
  this->TimeEndEntry->Create(this->Application);
  this->TimeEndEntry->GetEntry()->SetWidth(6);
  this->TimeEndEntry->SetLabel("End:");
  this->TimeEndEntry->SetValue(this->TimeEnd, 2);

  this->Script("bind %s <KeyPress-Return> {%s TimeEndEntryCallback}",
               this->TimeEndEntry->GetEntry()->GetWidgetName(),
               this->GetTclName());

  this->Script("bind %s <FocusOut> {%s TimeEndEntryCallback}",
               this->TimeEndEntry->GetEntry()->GetWidgetName(),
               this->GetTclName());

  this->Script("pack %s %s %s -side left -expand t -fill x", 
               this->TimeStartEntry->GetWidgetName(),
               this->TimeStepEntry->GetWidgetName(),
               this->TimeEndEntry->GetWidgetName());

  // Action frame

  this->ActionFrame->SetParent(this);
  this->ActionFrame->ShowHideFrameOn();
  this->ActionFrame->Create(this->Application);
  this->ActionFrame->SetLabel("Action");

  // Action frame: Source/Method

  this->SourceMethodFrame->SetParent(this->ActionFrame->GetFrame());
  this->SourceMethodFrame->Create(this->Application, "frame", "");

  this->Script("pack %s -side top -expand t -fill both", 
               this->SourceMethodFrame->GetWidgetName());

  // Action frame: Source menu's label.

  this->SourceLabel->SetParent(this->SourceMethodFrame);
  this->SourceLabel->Create(this->Application, ""); // -justify right -width 15 
  this->SourceLabel->SetLabel("Filter/Source:");

  // Action frame: Source menu

  this->SourceMenuButton->SetParent(this->SourceMethodFrame);
  this->SourceMenuButton->GetMenu()->SetTearOff(0);
  this->SourceMenuButton->Create(app, "");
  this->SourceMenuButton->SetBalloonHelpString(
    "Select the filter/source whose instance varible will change with time.");
  if (this->PVSource)
    {
    this->SourceMenuButton->SetButtonText(this->PVSource->GetName());
    }
  else
    {
    this->SourceMenuButton->SetButtonText("None");
    }

  // Action frame: Method menu's label.

  this->MethodLabel->SetParent(this->SourceMethodFrame);
  this->MethodLabel->Create(this->Application, ""); // -width 15 -justify right
  this->MethodLabel->SetLabel("Variable:");

  // Action frame: Method menu

  this->MethodMenuButton->SetParent(this->SourceMethodFrame);
  this->MethodMenuButton->Create(app, "");
  this->MethodMenuButton->GetMenu()->SetTearOff(0);
  this->MethodMenuButton->SetButtonText("Method");
  this->MethodMenuButton->SetBalloonHelpString(
    "Select the method that will be called.");

  this->Script("grid %s -column 0 -row 0 -sticky nws", 
               this->SourceLabel->GetWidgetName());
  this->Script("grid %s -column 1 -row 0 -sticky news -padx %d -pady %d", 
               this->SourceMenuButton->GetWidgetName(), 
               col_1_padx, button_pady);

  this->Script("grid %s -column 0 -row 1 -sticky nws", 
               this->MethodLabel->GetWidgetName());
  this->Script("grid %s -column 1 -row 1 -sticky news -padx %d -pady %d", 
               this->MethodMenuButton->GetWidgetName(),
               col_1_padx, button_pady);

  this->Script("grid columnconfigure %s 0 -weight 1", 
               this->SourceMenuButton->GetParent()->GetWidgetName());

  this->Script("grid columnconfigure %s 1 -weight 5", 
               this->SourceMenuButton->GetParent()->GetWidgetName());

  // Action frame: Script check button and script editor

  this->ScriptCheckButtonFrame->SetParent(this->ActionFrame->GetFrame());
  this->ScriptCheckButtonFrame->Create(this->Application, "frame", "");

  this->Script("pack %s -side top -expand t -fill x", 
               this->ScriptCheckButtonFrame->GetWidgetName());

  this->ScriptCheckButton->SetParent(this->ScriptCheckButtonFrame);
  this->ScriptCheckButton->Create(this->Application, "-text {Script Editor}");
  this->ScriptCheckButton->SetCommand(this, "ScriptCheckButtonCallback");

  this->Script("pack %s -side left -expand f", 
               this->ScriptCheckButton->GetWidgetName());

  this->ScriptEditor->SetParent(this->ActionFrame->GetFrame());
  this->ScriptEditor->Create(this->Application, "-relief sunken -bd 2");

  this->Script("bind %s <KeyPress> {%s ScriptEditorCallback}",
               this->ScriptEditor->GetWidgetName(), this->GetTclName());

  // Pack frames

  this->Script("pack %s %s -side top -expand t -fill x -padx 2 -pady 2", 
               this->ControlFrame->GetWidgetName(),
               this->ActionFrame->GetWidgetName());

  this->UpdateSourceMenu();
  this->UpdateMethodMenu();
  this->UpdateInterface();
}

//----------------------------------------------------------------------------
void vtkPVAnimationInterface::UpdateInterface()
{
  if (this->PlayButton && this->PlayButton->IsCreated())
    {
    if (this->InPlay)
      {
      this->PlayButton->Disable();
      }
    else
      {
      this->PlayButton->Enable();
      }
    }

  if (this->StopButton && this->StopButton->IsCreated())
    {
    if (this->InPlay)
      {
      this->StopButton->Enable();
      }
    else
      {
      this->StopButton->Disable();
      }
    }

  if (this->GoToBeginningButton && this->GoToBeginningButton->IsCreated())
    {
    if (this->InPlay)
      {
      this->GoToBeginningButton->Disable();
      }
    else
      {
      this->GoToBeginningButton->Enable();
      }
    }

  if (this->GoToEndButton && this->GoToEndButton->IsCreated())
    {
    if (this->InPlay)
      {
      this->GoToEndButton->Disable();
      }
    else
      {
      this->GoToEndButton->Enable();
      }
    }

  if (this->TimeStartEntry && this->TimeStartEntry->IsCreated())
    {
    this->TimeStartEntry->SetValue(this->TimeStart, 2);
    if (this->InPlay)
      {
      this->TimeStartEntry->EnabledOff();
      }
    else
      {
      this->TimeStartEntry->EnabledOn();
      }
    }

  if (this->TimeEndEntry && this->TimeEndEntry->IsCreated())
    {
    this->TimeEndEntry->SetValue(this->TimeEnd, 2);
    if (this->InPlay)
      {
      this->TimeEndEntry->EnabledOff();
      }
    else
      {
      this->TimeEndEntry->EnabledOn();
      }
    }

  if (this->TimeStepEntry && this->TimeStepEntry->IsCreated())
    {
    this->TimeStepEntry->SetValue(this->TimeStep, 2);
    if (this->InPlay)
      {
      this->TimeStepEntry->EnabledOff();
      }
    else
      {
      this->TimeStepEntry->EnabledOn();
      }
    }

  if (this->TimeScale && this->TimeScale->IsCreated())
    {
    this->TimeScale->SetRange(this->TimeStart, this->TimeEnd);
    this->TimeScale->SetResolution(this->TimeStep);
    if (this->InPlay)
      {
      // We do not disable it so that the slider will still be updated
      // during "Play"
      this->TimeScale->UnBind();
      }
    else
      {
      this->TimeScale->Bind();
      }
    }

  if (this->LoopCheckButton && this->LoopCheckButton->IsCreated())
    {
    this->LoopCheckButton->SetState(this->Loop);
    }
}

//----------------------------------------------------------------------------
void vtkPVAnimationInterface::SetTimeStart(float t)
{
  if (this->TimeStart == t)
    {
    return;
    }
  this->TimeStart = t;
  this->Modified();

  if (this->TimeStartEntry->IsCreated())
    {
    this->TimeStartEntry->SetValue(this->TimeStart, 2);
    }

  if (this->TimeScale->IsCreated())
    {
    this->TimeScale->SetRange(this->TimeStart, this->TimeEnd);
    }

  this->AddTraceEntry("$kw(%s) SetTimeStart {%f}", 
                      this->GetTclName(), this->TimeStart);
}

//----------------------------------------------------------------------------
void vtkPVAnimationInterface::TimeStartEntryCallback()
{
  this->SetTimeStart(this->TimeStartEntry->GetValueAsFloat());
}

//----------------------------------------------------------------------------
void vtkPVAnimationInterface::SetTimeEnd(float t)
{
  if (this->TimeEnd == t)
    {
    return;
    }
  this->TimeEnd = t;
  this->Modified();

  if (this->TimeEndEntry->IsCreated())
    {
    this->TimeEndEntry->SetValue(this->TimeEnd, 2);
    }

  if (this->TimeScale->IsCreated())
    {
    this->TimeScale->SetRange(this->TimeStart, this->TimeEnd);
    }

  this->AddTraceEntry("$kw(%s) SetTimeEnd {%f}", 
                      this->GetTclName(), this->TimeEnd);
}

//----------------------------------------------------------------------------
void vtkPVAnimationInterface::TimeEndEntryCallback()
{
  this->SetTimeEnd(this->TimeEndEntry->GetValueAsFloat());
}

//----------------------------------------------------------------------------
void vtkPVAnimationInterface::SetTimeStep(float t)
{
  if (this->TimeStep == t)
    {
    return;
    }
  this->TimeStep = t;
  this->Modified();

  if (this->TimeStepEntry->IsCreated())
    {
    this->TimeStepEntry->SetValue(this->TimeStep, 2);
    }

  if (this->TimeScale->IsCreated())
    {
    this->TimeScale->SetResolution(this->TimeStep);
    }

  this->AddTraceEntry("$kw(%s) SetTimeStep {%f}", 
                      this->GetTclName(), this->TimeStep);
}

//----------------------------------------------------------------------------
void vtkPVAnimationInterface::TimeStepEntryCallback()
{
  this->SetTimeStep(this->TimeStepEntry->GetValueAsFloat());
}

//----------------------------------------------------------------------------
void vtkPVAnimationInterface::EntryCallback()
{
  this->TimeStartEntryCallback();
  this->TimeEndEntryCallback();
  this->TimeStepEntryCallback();
}

//----------------------------------------------------------------------------
void vtkPVAnimationInterface::SetLoop(int v)
{
  if (this->Loop == v)
    {
    return;
    }
  this->Loop = v;
  this->Modified();

  this->AddTraceEntry("$kw(%s) SetLoop %d", \
                      this->GetTclName(), this->GetLoop());
}

//----------------------------------------------------------------------------
void vtkPVAnimationInterface::LoopCheckButtonCallback()
{
  this->SetLoop(this->LoopCheckButton->GetState());
}

//----------------------------------------------------------------------------
void vtkPVAnimationInterface::ScriptCheckButtonCallback()
{
  if (this->ScriptCheckButton->GetState())
    {
    this->AddTraceEntry("$kw(%s) SetScriptCheckButtonState 1", 
                        this->GetTclName());
    this->Script("pack %s -side top -expand yes -fill x -padx 2",
                 this->ScriptEditor->GetWidgetName());
    }
  else
    {
    this->AddTraceEntry("$kw(%s) SetScriptCheckButtonState 0", 
                        this->GetTclName());
    this->Script("pack forget %s", 
                 this->ScriptEditor->GetWidgetName());
    }
}

//----------------------------------------------------------------------------
void vtkPVAnimationInterface::SetScriptCheckButtonState(int val)
{
  if (this->ScriptCheckButton->GetState() == val)
    {
    return;
    }

  this->ScriptCheckButton->SetState(val);
  this->ScriptCheckButtonCallback();
}

//----------------------------------------------------------------------------
void vtkPVAnimationInterface::SetPVSource(vtkPVSource *source)
{
  if (source == this->PVSource)
    {
    return;
    }

  if (this->PVSource)
    {
    //this->PVSource->UnRegister(this);
    this->PVSource = NULL;
    }

  // Special case during destruction.
  if (this->SourceMenuButton == NULL)
    {
    return;
    }

  if (source)
    {
    //source->Register(this);
    this->PVSource = source;
    this->SourceMenuButton->SetButtonText(this->PVSource->GetName());
    this->AddTraceEntry("$kw(%s) SetPVSource $kw(%s)", this->GetTclName(), 
                        source->GetTclName());
    }
  else
    {
    this->SourceMenuButton->SetButtonText("None");
    this->AddTraceEntry("$kw(%s) SetPVSource {}", this->GetTclName());
    }

  this->UpdateMethodMenu();
}

//----------------------------------------------------------------------------
float vtkPVAnimationInterface::GetCurrentTime()
{
  return this->TimeScale->GetValue();
}

//----------------------------------------------------------------------------
void vtkPVAnimationInterface::SetCurrentTime(float time)
{  
  this->TimeScale->SetValue(time);

  vtkPVApplication *pvApp = vtkPVApplication::SafeDownCast(this->Application);
  if (pvApp)
    {
    pvApp->BroadcastScript("set pvTime %f", this->GetCurrentTime());
    pvApp->BroadcastScript("catch {%s}", this->ScriptEditor->GetValue());

    if (this->ControlledWidget)
      {
      this->ControlledWidget->ModifiedCallback();
      this->ControlledWidget->Reset();
      }

    if (this->View)
      {
      this->View->EventuallyRender();
      }

    this->AddTraceEntry("$kw(%s) SetCurrentTime %f", 
                        this->GetTclName(), this->GetCurrentTime());

    // Allow the application GUI to be refreshed (ex: in a trace file)

    this->Script("update");
    }
}

//----------------------------------------------------------------------------
void vtkPVAnimationInterface::TimeScaleCallback()
{
  this->SetCurrentTime(this->TimeScale->GetValue());
}

//----------------------------------------------------------------------------
void vtkPVAnimationInterface::Play()
{  
  // If playing, do not do anything

  if (this->InPlay)
    {
    return;
    }
  this->InPlay = 1;

  // Make sure we have the up to date entries for end and step.

  this->EntryCallback();

  // Update the buttons according to the play status

  this->UpdateInterface();

  // We need a different end test if the step is negative.

  float t, sgn;
  sgn = 1.0;
  if (this->TimeStep < 0)
    {
    sgn = -1.0;
    }

  // NOTE: the object registers itself for the following reason. The call
  // to "update" in the loop below (through SetCurrentTime()) enables the
  // user to exit the application. 
  // In that case all objects, including this one, are deleted, and this 
  // object's ivars will be trashed, resulting in a crash.
  // To prevent this object from being deleted, it registers itself before
  // the loop, and unregisters itself after the loop. If objects deletion
  // were scheduled, they will happen When UnRegister() is called.
  // Of course, the loop has to end, so the parent of this object should
  // make a call to Stop() before deleting it.

  this->Register(this);

  this->StopFlag = 0;
  do
    {
    t = this->GetCurrentTime();
    if (t >= this->GetTimeEnd())
      {
      this->SetCurrentTime(this->GetTimeStart());
      t = this->GetCurrentTime();
      }
    while ((sgn*t) < (sgn*this->TimeEnd) && !this->StopFlag)
      {
      t = t + this->TimeStep;
      if ((sgn*t) > (sgn*this->TimeEnd))
        {
        t = this->TimeEnd;
        }
      this->SetCurrentTime(t);
      // The stop button can be used here because SetCurrentTime()
      // makes a call to "update"
      }
    } while (this->Loop && !this->StopFlag);

  this->InPlay = 0;
  this->StopFlag = 0;
  this->UpdateInterface();

  this->UnRegister(this);
}

//----------------------------------------------------------------------------
void vtkPVAnimationInterface::Stop()
{  
  this->StopFlag = 1;
}

//----------------------------------------------------------------------------
void vtkPVAnimationInterface::GoToBeginning()
{  
  this->SetCurrentTime(this->GetTimeStart());
}

//----------------------------------------------------------------------------
void vtkPVAnimationInterface::GoToEnd()
{  
  this->SetCurrentTime(this->GetTimeEnd());
}

//----------------------------------------------------------------------------
void vtkPVAnimationInterface::UpdateSourceMenu()
{
  char methodAndArgString[1024];
  int sourceValid = 0;
  
  // Remove all previous items form the menu.
  this->SourceMenuButton->GetMenu()->DeleteAllMenuItems();

  if (this->Window == NULL)
    {
    return;
    }

  // Update the selection menu.
  vtkPVSourceCollection* col = this->Window->GetSourceList("Sources");
  if (col)
    {
    vtkPVSource *source;
    col->InitTraversal();
    while ( (source = col->GetNextPVSource()) )
      {
      sprintf(methodAndArgString, "SetPVSource %s", source->GetTclName());
      this->SourceMenuButton->GetMenu()->AddCommand(source->GetName(), this,
                                                    methodAndArgString);
      if (this->PVSource == source)
        {
        sourceValid = 1;
        }
      }
    }

  // The source may have been deleted. If so, then set our source to NULL.
  if ( ! sourceValid)
    {
    this->SetPVSource(NULL);
    }
}

//----------------------------------------------------------------------------
void vtkPVAnimationInterface::ScriptEditorCallback()
{
  // If some one is typing in the script editor, then the method
  // selection is no longer valid.
  this->MethodMenuButton->SetButtonText("None");
}

//----------------------------------------------------------------------------
void vtkPVAnimationInterface::SetScript(const char* script)
{
  this->ScriptEditor->SetValue(script);
  this->MethodMenuButton->SetButtonText("None");
}

//----------------------------------------------------------------------------
void vtkPVAnimationInterface::SetLabelAndScript(const char* label,
                                                const char* script)
{
  this->SetScript(script);
  this->MethodMenuButton->SetButtonText(label);
  if (this->Application)
    {
    this->AddTraceEntry("$kw(%s) SetLabelAndScript {%s} {%s}", 
                        this->GetTclName(), label, script);
    }
}

//----------------------------------------------------------------------------
const char* vtkPVAnimationInterface::GetScript()
{
  return this->ScriptEditor->GetValue();
}

//----------------------------------------------------------------------------
void vtkPVAnimationInterface::UpdateMethodMenu()
{
  vtkPVWidgetCollection *pvWidgets;
  vtkPVWidget *pvw;

  // Remove all previous items form the menu.
  this->MethodMenuButton->GetMenu()->DeleteAllMenuItems();

  this->MethodMenuButton->SetButtonText("None");
  if (this->PVSource == NULL)
    {
    return;
    }
  
  pvWidgets = this->PVSource->GetWidgets();
  pvWidgets->InitTraversal();
  while ( (pvw = pvWidgets->GetNextPVWidget()) )
    {
    pvw->AddAnimationScriptsToMenu(this->MethodMenuButton->GetMenu(), this);
    }
}

//----------------------------------------------------------------------------
void vtkPVAnimationInterface::SetWindow(vtkPVWindow *window)
{
  this->Window = window;
}

//----------------------------------------------------------------------------
void vtkPVAnimationInterface::SetView(vtkPVRenderView *renderView)
{
  this->View = renderView;
}

//----------------------------------------------------------------------------
void vtkPVAnimationInterface::SaveInTclScript(ofstream *file, 
                                              const char* fileRoot,
                                              const char* extension,
                                              const char* writerName)
{
  float t;
  float sgn;
  char countStr[100];

  // We need a different end test if the step is negative.
  sgn = 1.0;
  if (this->TimeStep < 0)
    {
    sgn = -1.0;
    }

  t = this->GetTimeStart();
  *file << "set pvTime " << t << "\n";
  *file << this->GetScript() << endl;
  *file << "if {$myProcId} {treeComp RenderRMI} else {\n\t";  
  sprintf(countStr, "%05d", (int)(t));
  if ( extension && writerName )
    {
    *file << "# This update is necessary to resolve some exposure event\n\t";
    *file << "# problems which occur with certain cards on Windows.\n\t";
    *file << "update\n\t";
    *file << "WinToImage Modified\n\t";
    *file << "Writer SetFileName {" << fileRoot << countStr << extension
          << "}\n\t";
    *file << "Writer Write\n";
    }
  else
    {
    *file << "RenWin1 Render\n";
    }
  *file << "}\n\n"; 

  while ((sgn*t) < (sgn*this->TimeEnd))
    {
    t = t + this->TimeStep;
    if ((sgn*t) > (sgn*this->TimeEnd))
      {
      t = this->TimeEnd;
      }
    *file << "set pvTime " << t << "\n";
    *file << this->GetScript() << endl;
    *file << "if {$myProcId != 0} {treeComp RenderRMI} else {\n\t";  
    sprintf(countStr, "%05d", (int)(t));
    // Not necessary because WinToImage causes a render.
    //*file << "RenWin1 Render\n\t";
    *file << "# This update is necessary to resolve some exposure event\n\t";
    *file << "# problems which occur with certain cards on Windows.\n\t";
    *file << "update\n\t";
    *file << "WinToImage Modified\n\t";
    *file << "Writer SetFileName {" << fileRoot << countStr << extension
          << "}\n\t";
    *file << "Writer Write\n"; 
    *file << "}\n\n";
    }
}

//----------------------------------------------------------------------------
void vtkPVAnimationInterface::PrintSelf(ostream& os, vtkIndent indent)
{
  this->Superclass::PrintSelf(os,indent);
  os << indent << "ControlledWidget: " << this->GetControlledWidget();
  os << indent << "PVSource: " << this->GetPVSource();
  os << indent << "TimeEnd: " << this->GetTimeEnd();
  os << indent << "TimeStart: " << this->GetTimeStart();
  os << indent << "TimeStep: " << this->GetTimeStep();
  os << indent << "Loop: " << this->GetLoop();
  os << indent << "View: " << this->GetView();
  os << indent << "Window: " << this->GetWindow();
}
