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)
 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.
editortools/operation.py