home *** CD-ROM | disk | FTP | other *** search
/ Hackers Magazine 57 / CdHackersMagazineNr57.iso / Software / Multimedia / k3d-setup-0.7.11.0.exe / lib / site-packages / cgkit / undo.py < prev    next >
Encoding:
Python Source  |  2007-01-11  |  8.0 KB  |  264 lines

  1. # ***** BEGIN LICENSE BLOCK *****
  2. # Version: MPL 1.1/GPL 2.0/LGPL 2.1
  3. #
  4. # The contents of this file are subject to the Mozilla Public License Version
  5. # 1.1 (the "License"); you may not use this file except in compliance with
  6. # the License. You may obtain a copy of the License at
  7. # http://www.mozilla.org/MPL/
  8. #
  9. # Software distributed under the License is distributed on an "AS IS" basis,
  10. # WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  11. # for the specific language governing rights and limitations under the
  12. # License.
  13. #
  14. # The Original Code is the Python Computer Graphics Kit.
  15. #
  16. # The Initial Developer of the Original Code is Matthias Baas.
  17. # Portions created by the Initial Developer are Copyright (C) 2004
  18. # the Initial Developer. All Rights Reserved.
  19. #
  20. # Contributor(s):
  21. #
  22. # Alternatively, the contents of this file may be used under the terms of
  23. # either the GNU General Public License Version 2 or later (the "GPL"), or
  24. # the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  25. # in which case the provisions of the GPL or the LGPL are applicable instead
  26. # of those above. If you wish to allow use of your version of this file only
  27. # under the terms of either the GPL or the LGPL, and not to allow others to
  28. # use your version of this file under the terms of the MPL, indicate your
  29. # decision by deleting the provisions above and replace them with the notice
  30. # and other provisions required by the GPL or the LGPL. If you do not delete
  31. # the provisions above, a recipient may use your version of this file under
  32. # the terms of any one of the MPL, the GPL or the LGPL.
  33. #
  34. # ***** END LICENSE BLOCK *****
  35. # $Id: undo.py,v 1.1.1.1 2004/12/12 14:31:30 mbaas Exp $
  36.  
  37. ## \file undo.py
  38. ## \brief Provides the undo/redo framework.
  39.  
  40. class UndoError(Exception):
  41.     pass
  42.  
  43. # _ReverseIterator
  44. class _ReverseIterator:
  45.     """Iterator that iterates over a list in reverse order.
  46.     """
  47.     def __init__(self, list):
  48.         self._idx = len(list)-1
  49.         self._list = list
  50.  
  51.     def __iter__(self):
  52.         return self
  53.  
  54.     def next(self):
  55.         if self._idx==-1:
  56.             raise StopIteration
  57.         res = self._list[self._idx]
  58.         self._idx -= 1
  59.         return res
  60.  
  61.  
  62. # UndoManager
  63. class UndoManager:
  64.     """This class manages undo objects.
  65.  
  66.     This class can be used to undo/redo operations. Each operation is
  67.     represented by an undo object (UndoObject) that knows how to undo
  68.     and redo this operation. Whenever an operation is invoked for the
  69.     first time it has to create an undo object and push() it onto the
  70.     undo stack in the manager. The undo() method then pops the undo
  71.     objects from the stack performing their undo action and pushes
  72.     them on the redo stack. The redo() method just does the opposite.
  73.     Whenever push() is called the redo stack is emptied.
  74.  
  75.     You can iterate over all undo objects in the undo stack by using
  76.     the manager as a sequence or by the iterUndo() method. The iterRedo()
  77.     method iterates over all the undo objects in the redo stack.
  78.     """
  79.     
  80.     def __init__(self, maxundoops=None):
  81.         """Constructor.
  82.  
  83.         \param maxundoops (\c int) Maximum number of undo operations to maintain or None for an unlimited number.
  84.         """
  85.         # The last element is the top element
  86.         self._undo_stack = []
  87.         self._redo_stack = []
  88.         # Maximum number of undo objects or None (=unlimited)
  89.         self._max_undo_ops = maxundoops
  90.  
  91.     def __len__(self):
  92.         return len(self._undo_stack)
  93.  
  94.     def __iter__(self):
  95.         return _ReverseIterator(self._undo_stack)
  96.  
  97.     # undoCount
  98.     def undoCount(self):
  99.         """Return the number of operations on the undo stack.
  100.  
  101.         \return Number of operation son the undo stack (\c int).
  102.         """
  103.         return len(self._undo_stack)
  104.     
  105.     # iterUndo
  106.     def iterUndo(self):
  107.         """Iterate over all undo objects in the undo stack (from top to bottom)."""
  108.         return _ReverseIterator(self._undo_stack)
  109.  
  110.     # redoCount
  111.     def redoCount(self):
  112.         """Return the number of operations on the redo stack.
  113.  
  114.         \return Number of operation son the redo stack (\c int).
  115.         """
  116.         return len(self._redo_stack)
  117.  
  118.     # iterRedo
  119.     def iterRedo(self):
  120.         """Iterate over all undo objects in the redo stack (from top to bottom)."""
  121.         return _ReverseIterator(self._redo_stack)
  122.  
  123.     # clear
  124.     def clear(self):
  125.         """Clear the undo/redo stack.
  126.  
  127.         All undo objects are removed from both stacks.
  128.         """
  129.         del self._undo_stack[:]
  130.         del self._redo_stack[:]
  131.  
  132.     def undoBegin(self, desc):
  133.         """Start an undo block.
  134.  
  135.         All following undo operations are combined into one single
  136.         undo operation.
  137.  
  138.         \param desc (\c str) Description text.
  139.         """
  140.         pass
  141.  
  142.     def undoEnd(self):
  143.         pass
  144.  
  145.     # undo
  146.     def undo(self):
  147.         """Performs an undo operation.
  148.  
  149.         The last operation is undone and pushed on the redo stack.
  150.         If the undo stack is empty an UndoError exception is thrown.
  151.  
  152.         If the undo operation throws an exception, then both stacks
  153.         are discarded and the exception is propagated to the caller.
  154.         """
  155.         if len(self._undo_stack)==0:
  156.             raise UndoError, "There is no operation to undo."
  157.         u = self._undo_stack.pop()
  158.         try:
  159.             u.undo()
  160.         except:
  161.             self.clear()
  162.             raise
  163.         self._redo_stack.append(u)
  164.  
  165.     # redo
  166.     def redo(self):
  167.         """Performs a redo operation.
  168.  
  169.         The last undo operation is redone. If the redo stack is empty
  170.         an UndoError exception is thrown.
  171.  
  172.         If the redo operation throws an exception, then both stacks
  173.         are discarded and the exception is propagated to the caller.
  174.         """
  175.         if len(self._redo_stack)==0:
  176.             raise UndoError, "There is no operation to redo."
  177.         u = self._redo_stack.pop()
  178.         try:
  179.             u.redo()
  180.         except:
  181.             self.clear()
  182.             raise
  183.         self._undo_stack.append(u)
  184.  
  185.     # push
  186.     def push(self, undoobj):
  187.         """Push an undo object on the stack.
  188.  
  189.         \param undoobj (\c UndoObject) Undo object
  190.         """
  191.         self._undo_stack.append(undoobj)
  192.         del self._redo_stack[:]
  193.         # Make sure there are no more items on the stack than the
  194.         # specified maximum number of operations...
  195.         if self._max_undo_ops!=None:
  196.             if len(self._undo_stack)>self._max_undo_ops:
  197.                 del self._undo_stack[0]
  198.  
  199.  
  200. # UndoObject
  201. class UndoObject:
  202.     """Base undo object which represents an undoable operation.
  203.  
  204.     This class has the following attributes:
  205.  
  206.     - desc (\c str): A short description describing the operation.
  207.          This description might be shown in the undo menu.
  208.     """
  209.     
  210.     def __init__(self, desc):
  211.         self.description = desc
  212.  
  213.     def __str__(self):
  214.         return "<Undo object '%s'>"%self.desc
  215.  
  216.     # undo
  217.     def undo(self):
  218.         """Performs an undo operation."""
  219.         raise UndoError, "No undo operation implemented."
  220.  
  221.     # redo
  222.     def redo(self):
  223.         """Performs a redo operation.
  224.  
  225.         This method may only be called if undo() was called previously.
  226.         """
  227.         raise UndoError, "No redo operation implemented."
  228.  
  229. ######################################################################
  230.  
  231. _undo_manager = {None:UndoManager()}
  232.  
  233. def undoCount(stackid=None):
  234.     global _undo_manager
  235.     return _undo_manager[stackid].undoCount()
  236.  
  237. def redoCount(stackid=None):
  238.     global _undo_manager
  239.     return _undo_manager[stackid].redoCount()
  240.  
  241. def iterUndo(stackid=None):
  242.     global _undo_manager
  243.     return _undo_manager[stackid].iterUndo()
  244.  
  245. def iterRedo(stackid=None):
  246.     global _undo_manager
  247.     return _undo_manager[stackid].iterRedo()
  248.  
  249. def clear(stackid=None):
  250.     global _undo_manager
  251.     _undo_manager[stackid].clear()
  252.  
  253. def undo(stackid=None):
  254.     global _undo_manager
  255.     _undo_manager[stackid].undo()
  256.  
  257. def redo(stackid=None):
  258.     global _undo_manager
  259.     _undo_manager[stackid].redo()
  260.  
  261. def push(undoobj, stackid=None):
  262.     global _undo_manager
  263.     _undo_manager[stackid].push(undoobj)
  264.