@ -208,6 +208,7 @@ class ClientCallbacks(Callbacks): #inherits from the templates api callbacks
for func in self . patternLengthMultiplicatorChanged :
func ( export )
self . _patternChanged ( track ) #includes dataChanged
callbacks . _dataChanged ( )
def _swingChanged ( self ) :
export = session . data . swing
@ -216,6 +217,7 @@ class ClientCallbacks(Callbacks): #inherits from the templates api callbacks
for func in self . swingPercentChanged :
func ( _swingToPercent_Table [ export ] )
callbacks . _dataChanged ( )
#Inject our derived Callbacks into the parent module
template . engine . api . callbacks = ClientCallbacks ( )
@ -322,6 +324,17 @@ def seek(value):
##Score
def set_quarterNotesPerMinute ( value ) :
""" Transport Master is set implicitly. If value == None Patroneo will switch into
JackTransport Slave mode """
if session . data . tempoMap . isTransportMaster :
oldValue = session . data . tempoMap . getQuarterNotesPerMinute ( )
else :
oldValue = None
if oldValue == value : return # no change
session . history . register ( lambda v = oldValue : set_quarterNotesPerMinute ( v ) , descriptionString = " Tempo " )
if value is None :
session . data . tempoMap . isTransportMaster = False #triggers rebuild
elif value == " on " :
@ -338,8 +351,10 @@ def set_quarterNotesPerMinute(value):
def set_whatTypeOfUnit ( ticks ) :
""" Denominator of Time Signature """
if session . data . whatTypeOfUnit == ticks : return
""" Denominator of Time Signature = Each Group produces a Quarter """
if session . data . whatTypeOfUnit == ticks : return #no change
session . history . register ( lambda v = session . data . whatTypeOfUnit : set_whatTypeOfUnit ( v ) , descriptionString = " Group Duration " )
session . data . whatTypeOfUnit = ticks
session . data . buildAllTracks ( )
if session . inLoopMode :
@ -348,8 +363,10 @@ def set_whatTypeOfUnit(ticks):
callbacks . _timeSignatureChanged ( )
def set_howManyUnits ( value ) :
""" Numerator of Time Signature """
if session . data . howManyUnits == value : return
""" Numerator of Time Signature = Steps per Pattern """
if session . data . howManyUnits == value : return #no change
session . history . register ( lambda v = session . data . howManyUnits : set_howManyUnits ( v ) , descriptionString = " Steps per Pattern " )
session . data . howManyUnits = value
session . data . buildAllTracks ( )
if session . inLoopMode :
@ -359,7 +376,9 @@ def set_howManyUnits(value):
def set_subdivisions ( value ) :
if session . data . subdivisions == value : return
""" In groups of 1, 2, 4 """
if session . data . subdivisions == value : return #no change
session . history . register ( lambda v = session . data . subdivisions : set_subdivisions ( v ) , descriptionString = " Group Size " )
session . data . subdivisions = value
session . data . buildAllTracks ( )
if session . inLoopMode :
@ -369,9 +388,10 @@ def set_subdivisions(value):
def convert_subdivisions ( value , errorHandling ) :
""" " errorHandling can be fail, delete or merge """
if session . data . subdivisions == value : return
oldValue = session . data . subdivisions
result = session . data . convertSubdivisions ( value , errorHandling )
if result :
if result : #bool for success
session . history . register ( lambda v = oldValue : convert_subdivisions ( v , " delete " ) , descriptionString = " Convert Grouping " ) #the error handling = delete should not matter at all. We are always in a position where this is possible because we just converted to the current state.
session . data . buildAllTracks ( )
updatePlayback ( )
callbacks . _timeSignatureChanged ( ) #includes subdivisions
@ -392,6 +412,7 @@ def set_swing(value:float):
logger . warning ( f " Swing can only be between -0.5 and 0.5, not { value } " )
return
session . history . register ( lambda v = session . data . swing : set_swing ( v ) , descriptionString = " Swing " )
session . data . swing = value
session . data . buildAllTracks ( )
updatePlayback ( )
@ -409,12 +430,14 @@ def setSwingPercent(value:int):
if value < - 100 or value > 100 :
logger . warning ( f " Swing in percent can only be between -100 and +100, not { value } " )
return
set_swing ( _percentToSwing_Table [ value ] )
set_swing ( _percentToSwing_Table [ value ] ) #handles undo and callbacks
def set_numberOfMeasures ( value ) :
if session . data . numberOfMeasures == value :
return
session . history . register ( lambda v = session . data . numberOfMeasures : set_numberOfMeasures ( v ) , descriptionString = " Measures per Track " )
session . data . numberOfMeasures = value
session . data . buildSongDuration ( )
updatePlayback ( )
@ -424,12 +447,14 @@ def set_numberOfMeasures(value):
def set_measuresPerGroup ( value ) :
if session . data . measuresPerGroup == value :
return
session . history . register ( lambda v = session . data . measuresPerGroup : set_measuresPerGroup ( v ) , descriptionString = " Measures per Group " )
session . data . measuresPerGroup = value
#No playback change
callbacks . _scoreChanged ( )
def changeTrackName ( trackId , name ) :
track = session . data . trackById ( trackId )
session . history . register ( lambda trId = trackId , v = track . sequencerInterface . name : changeTrackName ( trId , v ) , descriptionString = " Track Name " )
track . sequencerInterface . name = " " . join ( name . split ( ) )
callbacks . _trackMetaDataChanged ( track )
@ -437,6 +462,7 @@ def changeTrackColor(trackId, colorInHex):
""" Expects " #rrggbb """
track = session . data . trackById ( trackId )
assert len ( colorInHex ) == 7 , colorInHex
session . history . register ( lambda trId = trackId , v = track . color : changeTrackColor ( trId , v ) , descriptionString = " Track Color " )
track . color = colorInHex
callbacks . _trackMetaDataChanged ( track )
@ -447,6 +473,7 @@ def changeTrackMidiChannel(trackId, newChannel:int):
logger . warning ( f " Midi Channel must be between 1-16 for this function, was: { newChannel } . Doing nothing. " )
return
track = session . data . trackById ( trackId )
session . history . register ( lambda trId = trackId , v = track . midiChannel : changeTrackMidiChannel ( trId , v ) , descriptionString = " Track Midi Channel " )
track . midiChannel = newChannel - 1
track . pattern . buildExportCache ( )
track . buildTrack ( )
@ -456,7 +483,10 @@ def changeTrackMidiChannel(trackId, newChannel:int):
def addTrack ( scale = None ) :
if scale :
assert type ( scale ) == tuple
session . data . addTrack ( scale = scale )
track = session . data . addTrack ( scale = scale )
assert track
trackId = id ( track )
session . history . register ( lambda trId = trackId : deleteTrack ( trId ) , descriptionString = " Add Track " )
callbacks . _numberOfTracksChanged ( )
def createSiblingTrack ( trackId ) :
@ -478,14 +508,30 @@ def createSiblingTrack(trackId):
newTrackAgain = session . data . tracks . pop ( newIndex )
assert newTrackAgain is newTrack
session . data . tracks . insert ( oldIndex + 1 , newTrackAgain )
session . history . register ( lambda trId = id ( newTrackAgain ) : deleteTrack ( trId ) , descriptionString = " Clone Track " )
callbacks . _numberOfTracksChanged ( )
return newTrack . export ( )
def _reinsertDeletedTrack ( track , trackIndex ) :
""" For undo """
track . sequencerInterface . recreateThroughUndo ( )
session . data . tracks . insert ( trackIndex , track )
session . history . register ( lambda trId = id ( track ) : deleteTrack ( trId ) , descriptionString = " Add deleted Track again " )
updatePlayback ( )
callbacks . _numberOfTracksChanged ( )
def deleteTrack ( trackId ) :
track = session . data . trackById ( trackId )
session . data . deleteTrack ( track )
oldIndex = session . data . tracks . index ( track )
deletedTrack = session . data . deleteTrack ( track )
if not session . data . tracks : #always keep at least one track
session . data . addTrack ( )
with session . history . sequence ( " Delete Track and autocreated Track " ) :
addTrack ( ) #has it's own undo
session . history . register ( lambda tr = deletedTrack , pos = oldIndex : _reinsertDeletedTrack ( tr , pos ) , descriptionString = " Delete Track " )
else :
session . history . register ( lambda tr = deletedTrack , pos = oldIndex : _reinsertDeletedTrack ( tr , pos ) , descriptionString = " Delete Track " )
updatePlayback ( )
callbacks . _numberOfTracksChanged ( )
@ -494,6 +540,7 @@ def moveTrack(trackId, newIndex):
track = session . data . trackById ( trackId )
oldIndex = session . data . tracks . index ( track )
if not oldIndex == newIndex :
session . history . register ( lambda tr = trackId , pos = oldIndex : moveTrack ( trackId , pos ) , descriptionString = " Move Track " )
session . data . tracks . pop ( oldIndex )
session . data . tracks . insert ( newIndex , track )
callbacks . _numberOfTracksChanged ( )
@ -503,6 +550,7 @@ def setTrackPatternLengthMultiplicator(trackId, newMultiplicator:int):
return #Invalid input
track = session . data . trackById ( trackId )
session . history . register ( lambda tr = trackId , v = track . patternLengthMultiplicator : setTrackPatternLengthMultiplicator ( trackId , v ) , descriptionString = " Pattern Multiplier " )
track . patternLengthMultiplicator = newMultiplicator
track . pattern . buildExportCache ( )
track . buildTrack ( )
@ -513,22 +561,37 @@ def setTrackPatternLengthMultiplicator(trackId, newMultiplicator:int):
callbacks . _patternChanged ( track )
#Track Switches
#Aka measures
def _setTrackStructure ( trackId , structure ) :
""" For undo. Used by all functions as entry point, then calls itself for undo/redo.
structure is a set of integers which we can copy with . copy ( ) """
track = session . data . trackById ( trackId )
session . history . register ( lambda tr = trackId , v = track . structure . copy ( ) : _setTrackStructure ( trackId , v ) , descriptionString = " Set Measures " )
track . structure = structure #restore data
track . buildTrack ( )
updatePlayback ( )
callbacks . _trackStructureChanged ( track )
#Track Switches
def setSwitches ( trackId , setOfPositions , newBool ) :
""" Used in the GUI to select multiple switches in a row by dragging the mouse """
track = session . data . trackById ( trackId )
session . history . register ( lambda tr = trackId , v = track . structure . copy ( ) : _setTrackStructure ( trackId , v ) , descriptionString = " Set Measures " )
if newBool :
track . structure = track . structure . union ( setOfPositions ) #add setOfPositions to the existing one
track . structure = track . structure . union ( setOfPositions ) #merge: add setOfPositions to the existing one
else :
track . structure = track . structure . difference ( setOfPositions ) #remove everything from setOfPositions that is in the existing one, ignore entries from setOfPositions not in existing.
track . structure = track . structure . difference ( setOfPositions ) #replace: re move everything from setOfPositions that is in the existing one, ignore entries from setOfPositions not in existing.
track . buildTrack ( )
updatePlayback ( )
callbacks . _trackStructureChanged ( track )
def setSwitch ( trackId , position , newBool ) :
track = session . data . trackById ( trackId )
session . history . register ( lambda trId = trackId , v = track . structure . copy ( ) : _setTrackStructure ( trId , v ) , descriptionString = " Set Measures " )
if newBool :
if position in track . structure : return
track . structure . add ( position )
@ -542,6 +605,7 @@ def setSwitch(trackId, position, newBool):
def trackInvertSwitches ( trackId ) :
track = session . data . trackById ( trackId )
session . history . register ( lambda trId = trackId , v = track . structure . copy ( ) : _setTrackStructure ( trId , v ) , descriptionString = " Set Measures " )
"""
if track . structure :
new = set ( i for i in range ( max ( track . structure ) ) )
@ -557,6 +621,7 @@ def trackInvertSwitches(trackId):
def trackOffAllSwitches ( trackId ) :
track = session . data . trackById ( trackId )
session . history . register ( lambda trId = trackId , v = track . structure . copy ( ) : _setTrackStructure ( trId , v ) , descriptionString = " Set Measures " )
track . structure = set ( )
track . buildTrack ( )
updatePlayback ( )
@ -564,6 +629,7 @@ def trackOffAllSwitches(trackId):
def trackOnAllSwitches ( trackId ) :
track = session . data . trackById ( trackId )
session . history . register ( lambda trId = trackId , v = track . structure . copy ( ) : _setTrackStructure ( trId , v ) , descriptionString = " Set Measures " )
track . structure = set ( i for i in range ( session . data . numberOfMeasures ) )
track . buildTrack ( )
updatePlayback ( )
@ -573,6 +639,7 @@ def trackMergeCopyFrom(sourceTrackId, targetTrackId):
if not sourceTrackId == targetTrackId :
sourceTrack = session . data . trackById ( sourceTrackId )
targetTrack = session . data . trackById ( targetTrackId )
session . history . register ( lambda trId = id ( targetTrack ) , v = targetTrack . structure . copy ( ) : _setTrackStructure ( trId , v ) , descriptionString = " Set Measures " )
targetTrack . structure = targetTrack . structure . union ( sourceTrack . structure )
targetTrack . whichPatternsAreScaleTransposed . update ( sourceTrack . whichPatternsAreScaleTransposed )
targetTrack . whichPatternsAreHalftoneTransposed . update ( sourceTrack . whichPatternsAreHalftoneTransposed )
@ -584,6 +651,7 @@ def trackPatternReplaceFrom(sourceTrackId, targetTrackId):
if not sourceTrackId == targetTrackId :
sourceTrack = session . data . trackById ( sourceTrackId )
targetTrack = session . data . trackById ( targetTrackId )
session . history . register ( lambda trId = id ( targetTrack ) , v = targetTrack . structure . copy ( ) : _setTrackStructure ( trId , v ) , descriptionString = " Set Measures " )
copyPattern = sourceTrack . pattern . copy ( newParentTrack = targetTrack )
targetTrack . pattern = copyPattern
@ -592,24 +660,66 @@ def trackPatternReplaceFrom(sourceTrackId, targetTrackId):
updatePlayback ( )
callbacks . _patternChanged ( targetTrack )
#Transpositions and Modal Shifts
def _setSwitchesScaleTranspose ( trackId , whichPatternsAreScaleTransposed ) :
""" For undo. Used by all functions as entry point, then calls itself for undo/redo.
whichPatternsAreScaleTransposed is a dicts of int : int which we can copy with . copy ( ) """
track = session . data . trackById ( trackId )
session . history . register ( lambda tr = trackId , v = track . whichPatternsAreScaleTransposed . copy ( ) : _setSwitchesScaleTranspose ( trackId , v ) , descriptionString = " Set Modal Shift " )
track . whichPatternsAreScaleTransposed = whichPatternsAreScaleTransposed #restore data
track . buildTrack ( )
updatePlayback ( )
callbacks . _trackStructureChanged ( track )
def setSwitchScaleTranspose ( trackId , position , transpose ) :
""" Scale transposition is flipped. lower value means higher pitch """
track = session . data . trackById ( trackId )
session . history . register ( lambda tr = trackId , v = track . whichPatternsAreScaleTransposed . copy ( ) : _setSwitchesScaleTranspose ( trackId , v ) , descriptionString = " Set Modal Shift " )
track . whichPatternsAreScaleTransposed [ position ] = transpose
track . buildTrack ( )
updatePlayback ( )
callbacks . _trackStructureChanged ( track )
return True
def _setSwitchHalftoneTranspose ( trackId , whichPatternsAreHalftoneTransposed ) :
""" For undo. Used by all functions as entry point, then calls itself for undo/redo.
whichPatternsAreScaleTransposed is a dicts of int : int which we can copy with . copy ( ) """
track = session . data . trackById ( trackId )
session . history . register ( lambda tr = trackId , v = track . whichPatternsAreHalftoneTransposed . copy ( ) : _setSwitchHalftoneTranspose ( trackId , v ) , descriptionString = " Set Half Tone Shift " )
track . whichPatternsAreHalftoneTransposed = whichPatternsAreHalftoneTransposed #restore data
track . buildTrack ( )
updatePlayback ( )
callbacks . _trackStructureChanged ( track )
def setSwitchHalftoneTranspose ( trackId , position , transpose ) :
""" Halftone transposition is not flipped. Higher value means higher pitch """
track = session . data . trackById ( trackId )
session . history . register ( lambda tr = trackId , v = track . whichPatternsAreHalftoneTransposed . copy ( ) : _setSwitchHalftoneTranspose ( trackId , v ) , descriptionString = " Set Half Tone Shift " )
track . whichPatternsAreHalftoneTransposed [ position ] = transpose
track . buildTrack ( )
updatePlayback ( )
callbacks . _trackStructureChanged ( track )
return True
def _registerHistoryWholeTrackSwitches ( track ) :
""" This is used by insertSilence, clearSwitchGroupTranspositions etc.
It assumes to run inside the context :
with session . history . sequence ( " " ) :
"""
trackId = id ( track )
session . history . register ( lambda trId = trackId , v = track . patternLengthMultiplicator : setTrackPatternLengthMultiplicator ( trId , v ) , descriptionString = " Pattern Multiplier " )
session . history . register ( lambda trId = trackId , v = track . structure . copy ( ) : _setTrackStructure ( trId , v ) , descriptionString = " Set Measures " )
session . history . register ( lambda trId = trackId , v = track . whichPatternsAreScaleTransposed . copy ( ) : _setSwitchesScaleTranspose ( trId , v ) , descriptionString = " Set Modal Shift " )
session . history . register ( lambda trId = trackId , v = track . whichPatternsAreHalftoneTransposed . copy ( ) : _setSwitchHalftoneTranspose ( trId , v ) , descriptionString = " Set Half Tone Shift " )
def insertSilence ( howMany : int , beforeMeasureNumber : int ) :
""" Insert empty measures into all tracks.
Parameters are un - multiplied . """
@ -617,13 +727,18 @@ def insertSilence(howMany:int, beforeMeasureNumber:int):
#In each track shift every switch to the right if it is before the dividing measure number
#Keep the concept of a "group" even if there are multiplicators in the track.
#If you insert 4 normal measures then a multiplicator-track of two gets only 2 new ones.
for track in session . data . tracks :
thisTrackWhere = beforeMeasureNumber / / track . patternLengthMultiplicator #integer division
thisTrackHowMany = howMany / / track . patternLengthMultiplicator #integer division
track . structure = set ( ( switch + thisTrackHowMany if switch > = thisTrackWhere else switch ) for switch in track . structure )
track . whichPatternsAreScaleTransposed = { ( k + thisTrackHowMany if k > = thisTrackWhere else k ) : v for k , v in track . whichPatternsAreScaleTransposed . items ( ) }
track . whichPatternsAreHalftoneTransposed = { ( k + thisTrackHowMany if k > = thisTrackWhere else k ) : v for k , v in track . whichPatternsAreHalftoneTransposed . items ( ) }
callbacks . _trackStructureChanged ( track )
with session . history . sequence ( " Insert/Duplicate Group " ) : #this actually handles duplicateSwitchGroup undo as well!!!
for track in session . data . tracks :
_registerHistoryWholeTrackSwitches ( track )
thisTrackWhere = beforeMeasureNumber / / track . patternLengthMultiplicator #integer division
thisTrackHowMany = howMany / / track . patternLengthMultiplicator #integer division
track . structure = set ( ( switch + thisTrackHowMany if switch > = thisTrackWhere else switch ) for switch in track . structure )
track . whichPatternsAreScaleTransposed = { ( k + thisTrackHowMany if k > = thisTrackWhere else k ) : v for k , v in track . whichPatternsAreScaleTransposed . items ( ) }
track . whichPatternsAreHalftoneTransposed = { ( k + thisTrackHowMany if k > = thisTrackWhere else k ) : v for k , v in track . whichPatternsAreHalftoneTransposed . items ( ) }
callbacks . _trackStructureChanged ( track )
callbacks . _dataChanged ( ) #register undo
session . data . buildAllTracks ( )
updatePlayback ( )
@ -632,8 +747,9 @@ def duplicateSwitchGroup(startMeasureForGroup:int, endMeasureExclusive:int):
format . """
groupSize = endMeasureExclusive - startMeasureForGroup
insertSilence ( groupSize , endMeasureExclusive ) #insert silence handles multiplicator-tracks on its own
#Undo: InsertSilence has a complete undo already registered. We chose a neutral undo description so it handles both duplicate and insert silence.
insertSilence ( groupSize , endMeasureExclusive ) #insert silence handles multiplicator-tracks on its own
for track in session . data . tracks :
thisTrackStartMeasure = startMeasureForGroup / / track . patternLengthMultiplicator #integer division
thisTrackEndMeasure = endMeasureExclusive / / track . patternLengthMultiplicator
@ -647,59 +763,69 @@ def duplicateSwitchGroup(startMeasureForGroup:int, endMeasureExclusive:int):
if switch - thisGroupSize in track . whichPatternsAreHalftoneTransposed :
track . whichPatternsAreHalftoneTransposed [ switch ] = track . whichPatternsAreHalftoneTransposed [ switch - thisGroupSize ]
callbacks . _trackStructureChanged ( track )
session . data . buildAllTracks ( )
updatePlayback ( )
def clearSwitchGroupTranspositions ( startMeasureForGroup : int , endMeasureExclusive : int ) :
""" startMeasureForGroup and endMeasureExclusive are in the global, un-multiplied measure counting
format . """
for track in session . data . tracks :
thisTrackStartMeasure = startMeasureForGroup / / track . patternLengthMultiplicator #integer division
thisTrackEndMeasure = endMeasureExclusive / / track . patternLengthMultiplicator
for switch in range ( thisTrackStartMeasure , thisTrackEndMeasure ) :
if switch in track . whichPatternsAreScaleTransposed :
del track . whichPatternsAreScaleTransposed [ switch ]
if switch in track . whichPatternsAreHalftoneTransposed :
del track . whichPatternsAreHalftoneTransposed [ switch ]
callbacks . _trackStructureChanged ( track )
with session . history . sequence ( " Clear all Group Transpositions " ) :
for track in session . data . tracks :
_registerHistoryWholeTrackSwitches ( track )
thisTrackStartMeasure = startMeasureForGroup / / track . patternLengthMultiplicator #integer division
thisTrackEndMeasure = endMeasureExclusive / / track . patternLengthMultiplicator
for switch in range ( thisTrackStartMeasure , thisTrackEndMeasure ) :
if switch in track . whichPatternsAreScaleTransposed :
del track . whichPatternsAreScaleTransposed [ switch ]
if switch in track . whichPatternsAreHalftoneTransposed :
del track . whichPatternsAreHalftoneTransposed [ switch ]
callbacks . _trackStructureChanged ( track )
callbacks . _dataChanged ( ) #register undo
session . data . buildAllTracks ( )
updatePlayback ( )
def deleteSwitches ( howMany , fromMeasureNumber ) :
""" Parameters are un-multiplied measures. """
for track in session . data . tracks :
thisTrackHowMany = howMany / / track . patternLengthMultiplicator #integer division
thisTrackWhere = fromMeasureNumber / / track . patternLengthMultiplicator #integer division
new_structure = set ( )
for switch in track . structure :
if switch < thisTrackWhere :
new_structure . add ( switch )
elif switch > = thisTrackWhere + thisTrackHowMany : #like a text editor let gravitate left into the hole left by the deleted range
new_structure . add ( switch - thisTrackHowMany )
#else: #discard all in range to delete
track . structure = new_structure
new_scaleTransposed = dict ( )
for k , v in track . whichPatternsAreScaleTransposed . items ( ) :
if k < thisTrackWhere :
new_scaleTransposed [ k ] = v
elif k > = thisTrackWhere + thisTrackHowMany : #like a text editor let gravitate left into the hole left by the deleted range
new_scaleTransposed [ k - thisTrackHowMany ] = v
#else: #discard all in range to delete
track . whichPatternsAreScaleTransposed = new_scaleTransposed
new_halftoneTransposed = dict ( )
for k , v in track . whichPatternsAreHalftoneTransposed . items ( ) :
if k < thisTrackWhere :
new_halftoneTransposed [ k ] = v
elif k > = thisTrackWhere + thisTrackHowMany : #like a text editor let gravitate left into the hole left by the deleted range
new_halftoneTransposed [ k - thisTrackHowMany ] = v
#else: #discard all in range to delete
track . whichPatternsAreHalftoneTransposed = new_halftoneTransposed
callbacks . _trackStructureChanged ( track )
with session . history . sequence ( " Delete whole Group " ) :
for track in session . data . tracks :
_registerHistoryWholeTrackSwitches ( track )
thisTrackHowMany = howMany / / track . patternLengthMultiplicator #integer division
thisTrackWhere = fromMeasureNumber / / track . patternLengthMultiplicator #integer division
new_structure = set ( )
for switch in track . structure :
if switch < thisTrackWhere :
new_structure . add ( switch )
elif switch > = thisTrackWhere + thisTrackHowMany : #like a text editor let gravitate left into the hole left by the deleted range
new_structure . add ( switch - thisTrackHowMany )
#else: #discard all in range to delete
track . structure = new_structure
new_scaleTransposed = dict ( )
for k , v in track . whichPatternsAreScaleTransposed . items ( ) :
if k < thisTrackWhere :
new_scaleTransposed [ k ] = v
elif k > = thisTrackWhere + thisTrackHowMany : #like a text editor let gravitate left into the hole left by the deleted range
new_scaleTransposed [ k - thisTrackHowMany ] = v
#else: #discard all in range to delete
track . whichPatternsAreScaleTransposed = new_scaleTransposed
new_halftoneTransposed = dict ( )
for k , v in track . whichPatternsAreHalftoneTransposed . items ( ) :
if k < thisTrackWhere :
new_halftoneTransposed [ k ] = v
elif k > = thisTrackWhere + thisTrackHowMany : #like a text editor let gravitate left into the hole left by the deleted range
new_halftoneTransposed [ k - thisTrackHowMany ] = v
#else: #discard all in range to delete
track . whichPatternsAreHalftoneTransposed = new_halftoneTransposed
callbacks . _trackStructureChanged ( track )
callbacks . _dataChanged ( ) #register undo
session . data . buildAllTracks ( )
updatePlayback ( )