Browse Source

undo for CC tracks. Still some problems, but better than before

master
Nils 6 years ago
parent
commit
8ac4a7ec77
  1. 92
      engine/api.py
  2. 12
      engine/ccsubtrack.py

92
engine/api.py

@ -140,27 +140,21 @@ class ClientCallbacks(Callbacks): #inherits from the templates api callbacks
def _updateGraphTrackCC(self, trId, cc):
"""No cursor, so it always needs a graphTrack Id.
Also handles a GraphBlock overview callback,
for extra block view or background colors."""
#For simplistic reasons we just update all of this CC in all tracks.
#Makes things much more simple with content links across tracks.
#We will see how that turns out performance wise.
#TODO: This reeks of O[n^n]!!!
#We keep the trId parameter but don't use it.
#for track in session.data.tracks:
track = session.data.trackById(trId)
if cc in track.ccGraphTracks:
graphTrackCC = track.ccGraphTracks[cc]
trId = id(track)
ex = graphTrackCC.staticRepresentation()
for func in self.updateGraphTrackCC:
func(trId, cc, ex)
for extra block view or background colors."""
track = session.data.trackById(trId)
if cc in track.ccGraphTracks:
graphTracksThatNeedUpdate = track.ccGraphTracks[cc].otherTracksWithOurLinkedContent()
for gTr in graphTracksThatNeedUpdate:
trId = id(gTr.parentTrack)
ex = gTr.staticRepresentation()
for func in self.updateGraphTrackCC:
func(trId, cc, ex)
ex = graphTrackCC.staticGraphBlocksRepresentation()
for func in self.updateGraphBlockTrack:
func(trId, cc, ex)
ex = gTr.staticGraphBlocksRepresentation()
for func in self.updateGraphBlockTrack:
func(trId, cc, ex)
def _updateSingleTrackAllCC(self, trId):
"""Used on CC-Channels change. No own callback list."""
@ -170,7 +164,9 @@ class ClientCallbacks(Callbacks): #inherits from the templates api callbacks
func(trId, cc, ex)
def _graphCCTracksChanged(self, trId):
"""CC graphs of a track deleted or added"""
"""CC graphs of a track deleted or added.
Does not need to react to block or item changes, even (not) block duration changes
which change the tracks overall duration!"""
#TODO: moved?
if self.graphCCTracksChanged:
ex = list(session.data.trackById(trId).ccGraphTracks.keys()) # list of ints from 0 to 127
@ -548,9 +544,10 @@ def deleteTrack(trId):
insertTrack(trackIndex, trackObject)
session.history.register(registeredUndoFunction, descriptionString = "delete track")
callbacks._tracksChanged()
callbacks._setCursor()
callbacks._metronomeChanged()
callbacks._setCursor()
if trackObject is session.data.currentMetronomeTrack:
useCurrentTrackAsMetronome() #we already have a new current one
def deleteCurrentTrack():
deleteTrack(id(session.data.currentTrack()))
@ -1825,7 +1822,7 @@ def _lazyCCUndoRedo(trId:int, cc:int, data, description:str): #For items and bl
track.ccGraphTracks[cc] = GraphTrackCC.instanceFromSerializedData(data, parentTrack=track)
session.history.register(lambda: _lazyCCUndoRedo(trId, cc, oldData, description), descriptionString=description)
callbacks._historyChanged()
callbacks._updateGraphTrackCC(trId, cc)
callbacks._updateGraphTrackCC(trId, cc)
def _CCUndoCreater(trId:int, cc:int, description:str=""): #For items and blocks, not for tracks.
"""Can be used whenever saving the old state and actually registering undo does not depend
@ -1903,19 +1900,19 @@ def moveCCBlockToOtherTrack(graphBlockId, newTrackId, listOfBlockIdsForNewTrack)
graphBlock.parentGraphTrack = newGraphTrack
callbacks._updateGraphTrackCC(trId, cc)
callbacks._updateGraphTrackCC(newTrackId, cc)
callbacks._updateGraphTrackCC(newTrackId, cc) #in case of a linked block this is redundant, but for a normal move it is not redundant.
def changeCCBlock(graphBlockId, newParametersDict):
"""Mostly Duration changes. But includes the name as well"""
trId, cc, graphBlock = session.data.graphBlockById(graphBlockId)
session.history.clear()
_CCUndoCreater(trId, cc, "Change CC Block")
graphBlock.putDataFromDict(newParametersDict)
callbacks._updateGraphTrackCC(trId, cc)
def duplicateCCBlock(graphBlockId, times = 1):
trId, cc, graphBlock = session.data.graphBlockById(graphBlockId)
ccTrack = session.data.trackById(trId).ccGraphTracks[cc]
session.history.clear()
_CCUndoCreater(trId, cc, "Duplicate CC Block")
for i in range(times):
ccTrack.duplicateBlock(graphBlock)
callbacks._updateGraphTrackCC(trId, cc)
@ -1923,42 +1920,36 @@ def duplicateCCBlock(graphBlockId, times = 1):
def duplicateContentLinkCCBlock(graphBlockId, times = 1):
trId, cc, graphBlock = session.data.graphBlockById(graphBlockId)
ccTrack = session.data.trackById(trId).ccGraphTracks[cc]
session.history.clear()
_CCUndoCreater(trId, cc, "Content Link CC Block")
for i in range(times):
ccTrack.duplicateContentLinkBlock(graphBlock)
callbacks._updateGraphTrackCC(trId, cc)
def unlinkCCBlock(graphBlockId):
trId, cc, graphBlock = session.data.graphBlockById(graphBlockId)
newData, newDuration = graphBlock.getUnlinkedData()
_CCUndoCreater(trId, cc, "Unlink CC Block")
newData, newLinkedContentBlocks, newDuration = graphBlock.getUnlinkedData()
assert newData, newData
assert newDuration, newDuration
_setCCBlockData(graphBlock, newData, newDuration) #handles undo and callbacks
def _setCCBlockData(graphBlock, newData, newDuration):
"""For undo and redo.
newDuration is the original list with a single integer that may be content linked.
Thats why we get and set the block._duration parameter directly instead of using the setter
.duration."""
session.history.register(lambda bl=graphBlock, old=graphBlock.data, dur=graphBlock._duration: _setCCBlockData(bl, old, dur), descriptionString = "set CC block data")
graphBlock.data = newData
graphBlock._duration = newDuration
#no callbacks needed.
graphBlock.linkedContentBlocks = newLinkedContentBlocks
graphBlock._duration = newDuration
def deleteCCBlock(graphBlockId):
trId, cc, graphBlock = session.data.graphBlockById(graphBlockId)
ccTrack = session.data.trackById(trId).ccGraphTracks[cc]
oldLayout = ccTrack.asListOfBlockIds()
oldData = ccTrack.serialize()
deletedBlock = ccTrack.deleteBlock(graphBlock)
if deletedBlock: #not the last block
#Blocks are never truly deleted but a stored in the GraphBlock.allBlocks dict. This keeps the reference to this deleted block alive and it can be added through rearrange, which gets its blocks from this dict.
session.history.clear()
description = "Delete CC Block"
session.history.register(lambda: _lazyCCUndoRedo(trId, cc, oldData, description), descriptionString=description)
callbacks._updateGraphTrackCC(trId, cc)
def extendLastCCBlockToTrackLength(trId, cc):
ccTrack = session.data.trackById(trId).ccGraphTracks[cc]
lastCCBlock = ccTrack.blocks[-1]
session.history.clear()
_CCUndoCreater(trId, cc, "Exten last CC Block to Track length")
lastCCBlock.extendToTrackLength(ccTrack)
callbacks._updateGraphTrackCC(trId, cc)
@ -1966,18 +1957,23 @@ def splitCCBlock(graphBlockId, positionInTicksRelativeToBlock):
"""tick position is relative to block start"""
trId, cc, graphBlock = session.data.graphBlockById(graphBlockId)
ccTrack = session.data.trackById(trId).ccGraphTracks[cc]
oldData = ccTrack.serialize()
success = ccTrack.splitGraphBlock(graphBlock, positionInTicksRelativeToBlock)
session.history.clear()
if success:
description = "Split CC Block"
session.history.register(lambda: _lazyCCUndoRedo(trId, cc, oldData, description), descriptionString=description)
callbacks._updateGraphTrackCC(trId, cc)
def mergeWithNextGraphBlock(graphBlockId):
trId, cc, graphBlock = session.data.graphBlockById(graphBlockId)
ccTrack = session.data.trackById(trId).ccGraphTracks[cc]
oldData = ccTrack.serialize()
positionForSplit = ccTrack.mergeWithNextGraphBlock(graphBlock)
session.history.clear() #TODO: merging deletes hidden items in the first block without remembering them for undo
if positionForSplit:
description = "Split CC Block"
session.history.register(lambda: _lazyCCUndoRedo(trId, cc, oldData, description), descriptionString=description)
callbacks._updateGraphTrackCC(trId, cc)
##CC Blocks and User Points
def addGraphItem(blockId, positionInTicksRelativeToBlock, newCCValue):
@ -1988,7 +1984,7 @@ def addGraphItem(blockId, positionInTicksRelativeToBlock, newCCValue):
def _addExistingGraphItem(blockId, positionInTicksRelativeToBlock, graphItem):
"""blockId includes the track as well as the CC"""
trId, cc, graphBlock = session.data.graphBlockById(blockId)
trId, cc, graphBlock = session.data.graphBlockById(blockId)
_CCUndoCreater(trId, cc, "Add CC Point")
graphBlock.insert(graphItem, positionInTicksRelativeToBlock)
callbacks._updateGraphTrackCC(trId, cc)

12
engine/ccsubtrack.py

@ -165,7 +165,7 @@ class GraphBlock(object):
if newValue <= 0:
raise ValueError("duration must be > 1")
listId = id(self._duration)
self._duration.pop()
self._duration.pop() #don't replace the data structure, keep the mutable list alive.
self._duration.append(newValue)
assert len(self._duration) == 1
assert listId == id(self._duration)
@ -678,3 +678,13 @@ class GraphTrackCC(object):
if block.getMaxContentPosition() == block.duration:
block.remove(block.duration)
def otherTracksWithOurLinkedContent(self)->set:
"""returns all tracks that need to be updated we change"""
result = set()
for block in self.blocks:
for linkedBlock in block.linkedContentBlocks:
result.add(linkedBlock.parentGraphTrack)
assert result #at least this very track
assert len(result) <= len(self.parentTrack.parentData.tracks) #For now we assume that blocks cannot be linked across CCs. This can be removed after we tested it for a while and the time comes for cross-CC links
return result

Loading…
Cancel
Save