text: /*=========================================================================
  
    Program:   ParaView
    Module:    vtkSMUndoStack.cxx
  
    Copyright (c) Kitware, Inc.
    All rights reserved.
    See Copyright.txt or http://www.paraview.org/HTML/Copyright.html 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 "vtkSMUndoStack.h"
  
  #include "vtkCollection.h"
  #include "vtkCommand.h"
  #include "vtkObjectFactory.h"
  #include "vtkProcessModule.h"
  #include "vtkPVXMLElement.h"
  #include "vtkSMMessage.h"
  #include "vtkSMProxyManager.h"
  #include "vtkSMSession.h"
  #include "vtkSMUndoElement.h"
  #include "vtkUndoSet.h"
  #include "vtkUndoStackInternal.h"
  #include "vtkSMStateLocator.h"
  #include "vtkSMProxyLocator.h"
  #include "vtkSMProxyManager.h"
  #include "vtkSMDeserializerProtobuf.h"
  #include "vtkSMRemoteObjectUpdateUndoElement.h"
  
  #include <vtksys/RegularExpression.hxx>
  #include <set>
  #include "vtkNew.h"
  
  //*****************************************************************************
  class vtkSMUndoStack::vtkInternal
  {
  public:
    typedef  std::set<vtkSmartPointer<vtkSMSession> >   SessionSetType;
    SessionSetType  Sessions;
    vtkNew<vtkSMProxyLocator>         UndoSetProxyLocator;
    vtkNew<vtkSMDeserializerProtobuf> UndoSetProxyDeserializer;
    vtkNew<vtkSMStateLocator>         UndoSetStateLocator;
  
    vtkInternal()
      {
      this->UndoSetProxyDeserializer->SetStateLocator(
          this->UndoSetStateLocator.GetPointer());
      this->UndoSetProxyLocator->SetDeserializer(
          this->UndoSetProxyDeserializer.GetPointer());
      this->UndoSetProxyLocator->UseSessionToLocateProxy(true);
      }
  
    void FillLocatorWithUndoStates(vtkUndoSet* undoSet, bool useBeforeState)
      {
      this->UndoSetStateLocator->UnRegisterAllStates(false);
      int max = undoSet->GetNumberOfElements();
      for (int cc=0; cc < max; ++cc)
        {
        vtkSMRemoteObjectUpdateUndoElement* elem =
            vtkSMRemoteObjectUpdateUndoElement::SafeDownCast(
                undoSet->GetElement(cc));
  
        if(elem)
          {
          elem->SetProxyLocator(this->UndoSetProxyLocator.GetPointer());
          if(useBeforeState)
            {
            this->UndoSetStateLocator->RegisterState(elem->BeforeState);
            }
          else
            {
            this->UndoSetStateLocator->RegisterState(elem->AfterState);
            }
          }
        }
      }
  
    void UpdateSessions(vtkUndoSet* undoSet)
      {
      int max = undoSet->GetNumberOfElements();
      this->Sessions.clear();
      for (int cc=0; cc < max; ++cc)
        {
        vtkSMUndoElement* elem =
            vtkSMUndoElement::SafeDownCast(undoSet->GetElement(cc));
        if(elem->GetSession())
          {
          this->Sessions.insert(elem->GetSession());
          }
        }
  
      assert("Undo element should not involve more than one session" && this->Sessions.size() < 2);
      if(this->Sessions.size() == 1)
        {
        this->UndoSetStateLocator->SetParentLocator(this->Sessions.begin()->GetPointer()->GetStateLocator());
        }
      }
  
    void FillSessionsRemoteObjects(vtkCollection* collection)
      {
      SessionSetType::iterator iter = this->Sessions.begin();
      while(iter != this->Sessions.end())
        {
        iter->GetPointer()->GetAllRemoteObjects(collection);
        iter++;
        }
      }
  
    void Clear()
      {
      this->ClearProxyLocators();
      this->Sessions.clear();
      }
  
    void ClearProxyLocators()
      {
      this->UndoSetProxyLocator->Clear();
      SessionSetType::iterator iter = this->Sessions.begin();
      while(iter != this->Sessions.end())
        {
        iter->GetPointer()->GetProxyLocator()->Clear();
        iter++;
        }
      }
  };
  //*****************************************************************************
  vtkStandardNewMacro(vtkSMUndoStack);
  //-----------------------------------------------------------------------------
  vtkSMUndoStack::vtkSMUndoStack()
  {
    this->Internal = new vtkInternal();
  }
  
  //-----------------------------------------------------------------------------
  vtkSMUndoStack::~vtkSMUndoStack()
  {
    delete this->Internal; this->Internal = NULL;
  }
  
  //-----------------------------------------------------------------------------
  void vtkSMUndoStack::Push(const char* label, vtkUndoSet* changeSet)
  {
    this->Superclass::Push(label, changeSet);
    this->InvokeEvent(PushUndoSetEvent, changeSet);
  }
  
  //-----------------------------------------------------------------------------
  int vtkSMUndoStack::Undo()
  {
    if (!this->CanUndo())
      {
      vtkErrorMacro("Cannot undo. Nothing on undo stack.");
      return 0;
      }
  
    // Hold remote objects refs while the UndoSet is processing
    vtkNew<vtkCollection> remoteObjectsCollection;
    this->FillWithRemoteObjects(this->GetNextUndoSet(), remoteObjectsCollection.GetPointer());
    this->Internal->FillLocatorWithUndoStates(this->GetNextUndoSet(), true);
  
    int retValue = this->Superclass::Undo();
    this->Internal->Clear();
  
    return retValue;
  }
  
  //-----------------------------------------------------------------------------
  int vtkSMUndoStack::Redo()
  {
    if (!this->CanRedo())
      {
      vtkErrorMacro("Cannot redo. Nothing on redo stack.");
      return 0;
      }
  
    // Hold remote objects refs while the UndoSet is processing
    vtkNew<vtkCollection> remoteObjectsCollection;
    this->FillWithRemoteObjects(this->GetNextRedoSet(), remoteObjectsCollection.GetPointer());
    this->Internal->FillLocatorWithUndoStates(this->GetNextRedoSet(), false);
  
    int retValue = this->Superclass::Redo();
    this->Internal->Clear();
  
    return retValue;
  }
  
  //-----------------------------------------------------------------------------
  void vtkSMUndoStack::FillWithRemoteObjects( vtkUndoSet *undoSet,
                                              vtkCollection *collection)
  {
    if(!undoSet || !collection)
      return;
  
    this->Internal->UpdateSessions(undoSet);
    this->Internal->FillSessionsRemoteObjects(collection);
  }
  
  //-----------------------------------------------------------------------------
  void vtkSMUndoStack::PrintSelf(ostream& os, vtkIndent indent)
  {
    this->Superclass::PrintSelf(os, indent);
  }
  