Source code for editortools.operation
#-# Modified by D.C.-G. for translation purpose
import atexit
import os
import shutil
import tempfile
import albow
from albow.translate import _
from pymclevel import BoundingBox
import numpy
from albow.root import Cancel
import pymclevel
from albow import showProgress
from pymclevel.mclevelbase import exhaust
undo_folder = os.path.join(tempfile.gettempdir(), "mcedit_undo", str(os.getpid()))
[docs]def mkundotemp():
if not os.path.exists(undo_folder):
os.makedirs(undo_folder)
return tempfile.mkdtemp("mceditundo", dir=undo_folder)
atexit.register(shutil.rmtree, undo_folder, True)
[docs]class Operation(object):
changedLevel = True
undoLevel = None
redoLevel = None
def __init__(self, editor, level):
self.editor = editor
self.level = level
[docs] def extractUndo(self, level, box):
if isinstance(level, pymclevel.MCInfdevOldLevel):
return self.extractUndoChunks(level, box.chunkPositions, box.chunkCount)
else:
return self.extractUndoSchematic(level, box)
[docs] def extractUndoChunks(self, level, chunks, chunkCount=None):
if not isinstance(level, pymclevel.MCInfdevOldLevel):
chunks = numpy.array(list(chunks))
mincx, mincz = numpy.min(chunks, 0)
maxcx, maxcz = numpy.max(chunks, 0)
box = BoundingBox((mincx << 4, 0, mincz << 4), (maxcx << 4, level.Height, maxcz << 4))
return self.extractUndoSchematic(level, box)
undoLevel = pymclevel.MCInfdevOldLevel(mkundotemp(), create=True)
if not chunkCount:
try:
chunkCount = len(chunks)
except TypeError:
chunkCount = -1
def _extractUndo():
yield 0, 0, "Recording undo..."
for i, (cx, cz) in enumerate(chunks):
undoLevel.copyChunkFrom(level, cx, cz)
yield i, chunkCount, _("Copying chunk %s...") % ((cx, cz),)
undoLevel.saveInPlace()
if chunkCount > 25 or chunkCount < 1:
if "Canceled" == showProgress("Recording undo...", _extractUndo(), cancel=True):
if albow.ask("Continue with undo disabled?", ["Continue", "Cancel"]) == "Cancel":
raise Cancel
else:
return None
else:
exhaust(_extractUndo())
return undoLevel
@staticmethod
[docs] def extractUndoSchematic(level, box):
if box.volume > 131072:
sch = showProgress("Recording undo...", level.extractZipSchematicIter(box), cancel=True)
else:
sch = level.extractZipSchematic(box)
if sch == "Cancel":
raise Cancel
if sch:
sch.sourcePoint = box.origin
return sch
# represents a single undoable operation
[docs] def perform(self, recordUndo=True):
" Perform the operation. Record undo information if recordUndo"
[docs] def undo(self):
""" Undo the operation. Ought to leave the Operation in a state where it can be performed again.
Default implementation copies all chunks in undoLevel back into level. Non-chunk-based operations
should override this."""
if self.undoLevel:
self.redoLevel = self.extractUndo(self.level, self.dirtyBox())
def _undo():
yield 0, 0, "Undoing..."
if hasattr(self.level, 'copyChunkFrom'):
for i, (cx, cz) in enumerate(self.undoLevel.allChunks):
self.level.copyChunkFrom(self.undoLevel, cx, cz)
yield i, self.undoLevel.chunkCount, "Copying chunk %s..." % ((cx, cz),)
else:
for i in self.level.copyBlocksFromIter(self.undoLevel, self.undoLevel.bounds,
self.undoLevel.sourcePoint, biomes=True):
yield i, self.undoLevel.chunkCount, "Copying..."
if self.undoLevel.chunkCount > 25:
showProgress("Undoing...", _undo())
else:
exhaust(_undo())
self.editor.invalidateChunks(self.undoLevel.allChunks)
[docs] def redo(self):
if self.redoLevel:
def _redo():
yield 0, 0, "Redoing..."
if hasattr(self.level, 'copyChunkFrom'):
for i, (cx, cz) in enumerate(self.redoLevel.allChunks):
self.level.copyChunkFrom(self.redoLevel, cx, cz)
yield i, self.redoLevel.chunkCount, "Copying chunk %s..." % ((cx, cz),)
else:
for i in self.level.copyBlocksFromIter(self.redoLevel, self.redoLevel.bounds,
self.redoLevel.sourcePoint, biomes=True):
yield i, self.undoLevel.chunkCount, "Copying..."
if self.redoLevel.chunkCount > 25:
showProgress("Redoing...", _redo())
else:
exhaust(_redo())
[docs] def dirtyBox(self):
""" The region modified by the operation.
Return None to indicate no blocks were changed.
"""
return None