Salome HOME
On the road of homogeneous containers.
[modules/yacs.git] / src / hmi / commands.cxx
index c87ed0d0aa72586eb9ae64a9f2feb32ac5900662..0b10ec9a7b8b4c0e6d69b67669ff8d8478e4719a 100644 (file)
-//  Copyright (C) 2006-2008  CEA/DEN, EDF R&D
+// Copyright (C) 2006-2014  CEA/DEN, EDF R&D
 //
-//  This library is free software; you can redistribute it and/or
-//  modify it under the terms of the GNU Lesser General Public
-//  License as published by the Free Software Foundation; either
-//  version 2.1 of the License.
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Lesser General Public
+// License as published by the Free Software Foundation; either
+// version 2.1 of the License, or (at your option) any later version.
 //
-//  This library is distributed in the hope that it will be useful,
-//  but WITHOUT ANY WARRANTY; without even the implied warranty of
-//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-//  Lesser General Public License for more details.
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// Lesser General Public License for more details.
 //
-//  You should have received a copy of the GNU Lesser General Public
-//  License along with this library; if not, write to the Free Software
-//  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+// You should have received a copy of the GNU Lesser General Public
+// License along with this library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
 //
-//  See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
+// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
 //
+
 #include "commands.hxx"
+#include "guiContext.hxx"
+#include "Proc.hxx"
+#include "Catalog.hxx"
+#include "RuntimeSALOME.hxx"
 
 //#define _DEVDEBUG_
 #include "YacsTrace.hxx"
+#include <sstream>
 
 using namespace std;
 
 using namespace YACS;
 using namespace YACS::HMI;
 
+int Invocator::_ctr = 0;
+
 Command::Command()
 {
   _subCommands.clear();
+  _normalReverse = true;
 }
 
+//! the command is executed a first time after its registration for undo redo, then on redo
+/*!
+ *  Execute may induce subcommands to be registered during its execution.
+ *  Subcommands must be deleted before redo, to be recreated by redo.
+ *  CommandDestroy is a special case, where subcommands are registered before Execute, and
+ *  undo action calls executeSubOnly instead of reverse. Subcommands pf CommandDestroy must 
+ *  not be deleted.
+ */
 bool Command::execute()
 {
+  if ( !GuiContext::getCurrent() || !GuiContext::getCurrent()->getInvoc() )
+    return false;
+
+  bool commandInProgress = !GuiContext::getCurrent()->getInvoc()->isSpecialReverse();
+  if (commandInProgress)
+    GuiContext::getCurrent()->getInvoc()->_commandsInProgress.push_back(this);
+
   bool ret=true;
+  if (GuiContext::getCurrent()->getInvoc()->_isRedo && _normalReverse) 
+    {
+      for (int i=0; i<_subCommands.size(); i++)
+        delete _subCommands[i];
+      _subCommands.clear();
+    }
   ret = localExecute();
+  if (ret) GuiContext::getCurrent()->setNotSaved(true);
+
+  if (commandInProgress)
+    GuiContext::getCurrent()->getInvoc()->_commandsInProgress.pop_back();
+  return ret;
+}
+
+//! Reverse action: undo
+/*! Recursive reverse of sub commands then local action
+ *  @param isNormal: true for ordinary command, false for command Destroy
+ */
+bool Command::reverse(bool isNormal)
+{
+  bool ret=true;
   if (! _subCommands.empty())
     {
-      list<Command*>::iterator it;
-      for (it=_subCommands.begin(); it!=_subCommands.end(); it++)
+      for (int i=0; i<_subCommands.size(); i++)
         {
+          ret =_subCommands[i]->reverse(isNormal);
           if (!ret) break;
-          ret = (*it)->execute();
         }
     }
+  if (ret)
+    {
+      if (isNormal) // true for all commands but commandDestroy
+        ret = localReverse();
+      else
+        ret = localExecute(); // subCommand of command Destroy 
+    }
   return ret;
 }
 
-bool Command::reverse()
+//! execute only sub commands in a reverse action
+/*!
+ * for undo of commandDestroy, only sub commands of commandDestroy must be executed,
+ * via subcommand->reverse(false)
+ */
+bool Command::executeSubOnly()
 {
   bool ret=true;
   if (! _subCommands.empty())
     {
-      list<Command*>::reverse_iterator it;
-      for (it=_subCommands.rbegin(); it!=_subCommands.rend(); it++)
+      for (int i=0; i<_subCommands.size(); i++)
         {
-          ret =(*it)->execute();
+          ret =_subCommands[i]->reverse(false);
           if (!ret) break;
         }
     }
-  if (ret) ret = localReverse();
+  return ret;
+}
+
+std::string Command::dump()
+{
+  return "=== generic command dump ===";
+}
+
+std::string Command::recursiveDump(int level)
+{
+  string prefix = "";
+  for (int i=0; i<level; i++) prefix += "  ";
+  string ret = prefix + dump() + "\n";
+  for (int i=0; i<_subCommands.size(); i++)
+    ret += _subCommands[i]->recursiveDump(level+1);
   return ret;
 }
 
@@ -68,36 +136,151 @@ void Command::addSubCommand(Command* command)
   _subCommands.push_back(command); 
 }
 
+
+
+
+
 Invocator::Invocator()
 {
+  _commandsDone.clear();
+  _commandsUndone.clear();
+  _commandsInProgress.clear();
+  _isRedo = false;
+  _isUndo = false;
+  _specialReverse = false;
+  _undoCata = new YACS::ENGINE::Catalog("undoCata");
+  YACS::ENGINE::RuntimeSALOME* runTime = YACS::ENGINE::getSALOMERuntime();
+  _undoProc= runTime->createProc("undoProc");
 }
 
 void Invocator::add(Command* command)
 {
-  _commandsDone.push(command); // to do after execute ok
+  DEBTRACE("Invocator::add");
+  if(_isUndo)
+    return;
+  if (GuiContext::getCurrent()->getInvoc()->isSpecialReverse())
+    {
+      delete command;
+      return;
+    }
+  if (_commandsInProgress.empty())
+    {
+      _commandsDone.push_back(command); // --- to do after execute ok
+      _commandsUndone.clear();           // --- undone stack is no more relevant
+    }
+  else
+    {
+      DEBTRACE("addSubCommand");
+      _commandsInProgress.back()->addSubCommand(command);
+    }
 }
 
 bool Invocator::undo()
 {
+  DEBTRACE("Invocator::undo");
   bool ret = true;
+
+  {
+    stringstream ret1;
+    ret1 << "_commandsDone" << endl;
+    for (int i=0; i<_commandsDone.size(); i++)
+      ret1 << i << _commandsDone[i]->recursiveDump(2);
+    DEBTRACE(ret1.str());
+    stringstream ret2;
+    ret2 << "_commandsUndone" << endl;
+    for (int i=0; i<_commandsUndone.size(); i++)
+      ret2 << i << _commandsUndone[i]->recursiveDump(2);
+    DEBTRACE(ret2.str());
+  }
+
   if (! _commandsDone.empty())
     {
-      _commandsDone.pop();
-      ret = _commandsDone.top()->reverse();
-      _commandsUndone.push(_commandsDone.top());
+      bool isNormal = _commandsDone.back()->isNormalReverse();
+      _specialReverse = !isNormal;
+      _isUndo=true;
+      if (isNormal)
+        ret = _commandsDone.back()->reverse(isNormal);
+      else
+        ret = _commandsDone.back()->executeSubOnly();
+      _isUndo=false;
+      _commandsUndone.push_back(_commandsDone.back());
+      _commandsDone.pop_back();
+      _specialReverse = false;
     }
+
+  {
+    stringstream ret1;
+    ret1 << "_commandsDone" << endl;
+    for (int i=0; i<_commandsDone.size(); i++)
+      ret1 << i << _commandsDone[i]->recursiveDump(2);
+    DEBTRACE(ret1.str());
+    stringstream ret2;
+    ret2 << "_commandsUndone" << endl;
+    for (int i=0; i<_commandsUndone.size(); i++)
+      ret2 << i << _commandsUndone[i]->recursiveDump(2);
+    DEBTRACE(ret2.str());
+  }
+
   return ret;
 }
 
 bool Invocator::redo()
 {
+  DEBTRACE("Invocator::redo");
   bool ret = true;
+
+  {
+    stringstream ret1;
+    ret1 << "_commandsDone" << endl;
+    for (int i=0; i<_commandsDone.size(); i++)
+      ret1 << i << _commandsDone[i]->recursiveDump(2);
+    DEBTRACE(ret1.str());
+    stringstream ret2;
+    ret2 << "_commandsUndone" << endl;
+    for (int i=0; i<_commandsUndone.size(); i++)
+      ret2 << i << _commandsUndone[i]->recursiveDump(2);
+    DEBTRACE(ret2.str());
+  }
+
   if (! _commandsUndone.empty())
     {
-      _commandsUndone.pop();
-      ret = _commandsUndone.top()->execute();
-      _commandsDone.push(_commandsUndone.top());
+      _isRedo=true;
+      ret = _commandsUndone.back()->execute();
+      _isRedo=false;
+      _commandsDone.push_back(_commandsUndone.back());
+      _commandsUndone.pop_back();
     }
+
+  {
+    stringstream ret1;
+    ret1 << "_commandsDone" << endl;
+    for (int i=0; i<_commandsDone.size(); i++)
+      ret1 << i << _commandsDone[i]->recursiveDump(2);
+    DEBTRACE(ret1.str());
+    stringstream ret2;
+    ret2 << "_commandsUndone" << endl;
+    for (int i=0; i<_commandsUndone.size(); i++)
+      ret2 << i << _commandsUndone[i]->recursiveDump(2);
+    DEBTRACE(ret2.str());
+  }
+
   return ret;
 }
 
+std::list<std::string> Invocator::getDone()
+{
+  list<string> listDone;
+  listDone.clear();
+  for (int i=0; i<_commandsDone.size(); i++)
+    listDone.push_back(_commandsDone[i]->recursiveDump());
+  return listDone;
+}
+
+std::list<std::string> Invocator::getUndone()
+{
+  list<string> listUndone;
+  listUndone.clear();
+  for (int i=0; i<_commandsUndone.size(); i++)
+    listUndone.push_back(_commandsUndone[i]->recursiveDump());
+  return listUndone;
+}