from.itemsimport*#loading from file needs all items.
classBlock(object):
#allBlocks = WeakValueDictionary() #key is the blockId, value is the weak reference to the Block
allBlocks={}#key is the blockId, value is the Block. This is a one way dict. It gets never deleted so undo can recover old blocks. Since blocks are unique this is no problem.
#NEVER!! iteratre over allBlocks if you don't know what you are doing. This contains deleted blocks as well.
firstBlockWithNewContentDuringDeserializeToObject=dict()#this is not resetted anywhere since each load is a program start.
self._name=[str(id(self))]#list of len==1 so it is mutable for linked blocks. See @property below
self._minimumInTicks=[0]# if this is bigger than the actual duration-sum of the content this will be used instead. Advice: best used in the form of x*210 (multiple of real base-durations) #is content linked, thats why it is a mutable list of len==1. See @property below
ifserializedObject["data"]isNone:#Found a content linked block which already has one member of its group in the score
firstBlock=Block.firstBlockWithNewContentDuringDeserializeToObject[serializedObject["contentLinkGroup"]]#block with the same contentGroup. This is the one with the real data.
else:#content linked, but not the first. Block already serialized.
result["data"]=None#we don't need to do anything more. The rest is handled by load and instanceFromSerializedData
break
#else:
#loop ran through. This never happens.
returnresult
defrememberBlock(self):
oid=id(self)
Block.allBlocks[oid]=self#This is on the score level, or a global level. That means we don't need to change this, even if the track gets moved to a new track by the api.
new._minimumInTicks=self._minimumInTicks[:]#it is a mutable value, we make a copy of the list (which has an int inside, which is immutable and copied automatically)
"""The first block might have an upbeat. We check that here and not in the track."""
actualBlockDuration=0
foriteminself.data:
actualBlockDuration+=item.logicalDuration()
ifactualBlockDuration>=self.minimumInTicks:
returnactualBlockDuration
else:
returnself.minimumInTicks
defstaticExportEndMarkerDuration(self):
actualBlockDuration=0
foriteminself.data:
actualBlockDuration+=item.logicalDuration()
ifactualBlockDuration>=self.minimumInTicks:#this also guarantees that the substraction below is > 0.
return0
else:
returnself.minimumInTicks-actualBlockDuration#this is the difference to self.duration()
defposition(self):
"""the position of the subcursor in this block"""
returnself.localCursorIndex
defleft(self):
ifself.localCursorIndex>0:
self.localCursorIndex-=1
returnTrue
else:
returnFalse#Already at the start.
defright(self):
ifnotself.isAppending():
self.localCursorIndex+=1
returnTrue
else:
returnFalse#Already at the end.
defhead(self):
self.localCursorIndex=0
deftail(self):
self.localCursorIndex=len(self.data)#eventhough len counts from 1 and the cursorIndex from 0 we want exactly to be after the last item in data.
defgoToItem(self,itemInstance):
ifitemInstance:
self.head()
whileself.right():
item=self.currentItem()
ifitemisitemInstance:
returnTrue
else:
raiseValueError("Item not in this block. ",self,itemInstance)
elifitemInstanceisNone:
self.tail()
else:
raiseValueError("You must go to an item or None, not to: ",itemInstance)
defcurrentItem(self):
"""Can be used with goToItem"""
ifnotself.isAppending():
returnself.data[self.localCursorIndex]
else:
returnNone
defpreviousItem(self):
"""Can be used with goToItem"""
ifself.localCursorIndex>0:
returnself.data[self.localCursorIndex-1]
else:
returnNone
defnextItem(self):
"""Can be used with goToItem"""
ifself.localCursorIndex+1>=len(self.data):#one before appending or appending itself
returnNone
else:
returnself.data[self.localCursorIndex+1]
definsert(self,item):
self.data.insert(self.localCursorIndex,item)#we do not need to check if appending or not. list.insert appends if the index is higher then len()
#self.localCursorIndex += 1 #we don't need to go right here because track.insert() is calling its own right() directly after insert, which triggers block.right()
#we don't need to delete self from item.parentBlocks. It is automatically deleted in all contentLinked blocks, of course including its parentBlocks WeakSet()
returnself.localCursorIndex==len(self.data)#len counts from 1 and the cursorIndex from 0. So if they are the same we are one cursor position right of last item