Can Add Marker on editing partition TUODetailDialog '+' botton & reconstruct Template
This commit is contained in:
parent
bf797762ea
commit
80ef86527b
7 changed files with 579 additions and 113 deletions
|
|
@ -319,10 +319,15 @@ class Solfa(val sharedScreenModel: SharedScreenModel, private val fileRepository
|
|||
val t0Idx = lines.indexOfFirst { it.startsWith("T0:") }
|
||||
val u0Idx = lines.indexOfFirst { it.startsWith("U0:") }
|
||||
|
||||
val noteS = lines[lines.indexOfFirst { it.startsWith("N1") }]
|
||||
val noteA = lines[lines.indexOfFirst { it.startsWith("N2") }]
|
||||
val noteT = lines[lines.indexOfFirst { it.startsWith("N3") }]
|
||||
val noteB = lines[lines.indexOfFirst { it.startsWith("N4") }]
|
||||
val idxS = lines.indexOfFirst { it.startsWith("N1") }
|
||||
val idxA = lines.indexOfFirst { it.startsWith("N2") }
|
||||
val idxT = lines.indexOfFirst { it.startsWith("N3") }
|
||||
val idxB = lines.indexOfFirst { it.startsWith("N4") }
|
||||
|
||||
val noteS = if (idxS != -1) lines[idxS] else ""
|
||||
val noteA = if (idxA != -1) lines[idxA] else ""
|
||||
val noteT = if (idxT != -1) lines[idxT] else ""
|
||||
val noteB = if (idxB != -1) lines[idxB] else ""
|
||||
|
||||
val currentStanza = sharedScreenModel.stanza.value
|
||||
|
||||
|
|
@ -331,7 +336,6 @@ class Solfa(val sharedScreenModel: SharedScreenModel, private val fileRepository
|
|||
}
|
||||
|
||||
val lyrics = lines[lyrIdx]
|
||||
println("Parole sur $currentStanza: $lyrics")
|
||||
|
||||
val content = smartYLyrics(lyrics).substringAfter(":")
|
||||
|
||||
|
|
@ -370,33 +374,7 @@ class Solfa(val sharedScreenModel: SharedScreenModel, private val fileRepository
|
|||
|
||||
when {
|
||||
t0Idx != -1 -> {
|
||||
val line = lines[t0Idx]
|
||||
val prefix = line.substringBefore('{') + "{"
|
||||
val suffix = "}"
|
||||
val body = line.substringAfter('{').substringBeforeLast('}')
|
||||
|
||||
val regex = Regex("([^:|/!]*[:|/!])|([^:|/!]+)")
|
||||
val fragments = regex.findAll(body).map { it.value }.toMutableList()
|
||||
// println("Nombre de blocs détectés : ${fragments.size}")
|
||||
|
||||
val markerRegex = Regex("""\$\{.*?\}|\$.""")
|
||||
|
||||
if (targetIdx in fragments.indices) {
|
||||
val oldFrag = fragments[targetIdx]
|
||||
|
||||
val delimiter = if (oldFrag.isNotEmpty() && oldFrag.last() in ":|/!") {
|
||||
oldFrag.last().toString()
|
||||
} else ""
|
||||
|
||||
val cleanBody = fragments.joinToString("") { it.replace(markerRegex, "") }
|
||||
lines[t0Idx] = prefix + cleanBody + suffix
|
||||
|
||||
println("AVANT: $line")
|
||||
println("APRÈS: ${lines[t0Idx]}")
|
||||
} else {
|
||||
println("Index $targetIdx hors limites pour T0 (taille=${fragments.size})")
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
u0Idx != -1 -> {
|
||||
|
|
@ -405,7 +383,7 @@ class Solfa(val sharedScreenModel: SharedScreenModel, private val fileRepository
|
|||
// println("fullLine = '$fullLine'")
|
||||
|
||||
val afterU0 = fullLine.substringAfter("U0:")
|
||||
val blankPrefixRegex = Regex("^(z[048CEKO]:)")
|
||||
val blankPrefixRegex = Regex("^(.*?z[0-9A-Z]:)")
|
||||
val match = blankPrefixRegex.find(afterU0)
|
||||
val blankPrefix = match?.value ?: ""
|
||||
val body = if (match != null) {
|
||||
|
|
@ -420,7 +398,7 @@ class Solfa(val sharedScreenModel: SharedScreenModel, private val fileRepository
|
|||
val symboles = parser.parsed()
|
||||
|
||||
val newLigneT = "U0:"+blankPrefix+symboles
|
||||
println("Ligne de U0 ==> $newLigneT")
|
||||
// println("Ligne de U0 ==> $newLigneT")
|
||||
lines[u0Idx] = newLigneT
|
||||
|
||||
}
|
||||
|
|
@ -438,20 +416,39 @@ class Solfa(val sharedScreenModel: SharedScreenModel, private val fileRepository
|
|||
}*/
|
||||
|
||||
|
||||
val markerClean = editState.marker
|
||||
val modulaRegex = Regex("""^\s*D(ô|o)?\s*dia\s*(C|Db|D|Eb|E|F|Gb|G|Ab|A|Bb|B)\s*$""", RegexOption.IGNORE_CASE)
|
||||
val match = modulaRegex.find(markerClean)
|
||||
|
||||
val newMarker = if (match != null) {
|
||||
val lettre = match.groupValues[2]
|
||||
"\${c:$lettre}"
|
||||
} else {
|
||||
markerClean.split(Regex("\\s+")).filter { it.isNotEmpty() }.joinToString(" ") { part ->
|
||||
when (part) {
|
||||
"𝄐" -> "\$Q"
|
||||
else -> "\${$part}"
|
||||
}
|
||||
}
|
||||
}
|
||||
/* MODIF TEMPLATE */
|
||||
val markerUpdated = updateMarkerSource(if(u0Idx != -1) lines[u0Idx] else lines[t0Idx], targetIdx, newMarker)
|
||||
|
||||
/* MODIF SOURCE Note&Lyrics */
|
||||
val updatedSource = updateSourceLines(lines, templatArray, targetIdx, editState)
|
||||
|
||||
|
||||
/*
|
||||
MODIF NOTES N1 N2 N3 N4
|
||||
*/
|
||||
// println("taille tA: ${templatArray.size} et le trgt $targetIdx")
|
||||
val updatedNotes = updateSourceLines(lines, templatArray, targetIdx, editState)
|
||||
|
||||
val templateUpd = reconstructIt(markerUpdated, measureString[0].digitToInt())
|
||||
|
||||
println("Re-formater:: $templateUpd")
|
||||
|
||||
// RESTAURATION DES TEMPLATES
|
||||
if (templateString != "") {
|
||||
updatedNotes[templateIndices] = templateString
|
||||
updatedSource[templateIndices] = templateUpd
|
||||
}
|
||||
|
||||
val finalString = updatedNotes.joinToString("\n")
|
||||
val finalString = updatedSource.joinToString("\n")
|
||||
|
||||
val tempDir = System.getProperty("java.io.tmpdir")
|
||||
val fileName = filePath.substringAfterLast('/')
|
||||
|
|
@ -481,27 +478,61 @@ class Solfa(val sharedScreenModel: SharedScreenModel, private val fileRepository
|
|||
val result = StringBuilder()
|
||||
val lastSlash = filePath.lastIndexOf('/')
|
||||
val directory = if (lastSlash != -1) filePath.substring(0, lastSlash + 1) else ""
|
||||
val myLines = fileRepository.readFileLines(filePath)
|
||||
// Si inclusion multiples
|
||||
val myLines = try {
|
||||
fileRepository.readFileLines(filePath)
|
||||
} catch (e: Exception) {
|
||||
return "// Erreur lecture: $filePath"
|
||||
}
|
||||
|
||||
val inclusionRegex = Regex("""I\d+:[^ \n\r]+""")
|
||||
|
||||
myLines.forEach { line ->
|
||||
if (line.startsWith("I0:")) {
|
||||
var lastIndex = 0
|
||||
val matches = inclusionRegex.findAll(line).toList()
|
||||
|
||||
if (matches.isNotEmpty()) {
|
||||
val lineBuilder = StringBuilder()
|
||||
for (match in matches) {
|
||||
lineBuilder.append(line.substring(lastIndex, match.range.first))
|
||||
|
||||
try {
|
||||
val parts = line.substring(3).split(":")
|
||||
val fileName = parts[0]
|
||||
val ignorePattern = if (parts.size > 1) parts[1] else ""
|
||||
val fullTag = match.value
|
||||
val parts = fullTag.split(":", limit = 3)
|
||||
|
||||
if (parts.size >= 2) {
|
||||
val fileName = parts[1]
|
||||
val fullPath = directory + fileName
|
||||
val includedLines = fileRepository.readFileLines(fullPath)
|
||||
val rawIncludedContent = fileRepository.readFileLines(fullPath)
|
||||
|
||||
val regexIgnore = if (ignorePattern.isNotEmpty()) Regex(ignorePattern) else null
|
||||
if (parts.size > 2 && parts[2].startsWith("^")) {
|
||||
val patternInside = parts[2]
|
||||
.substringAfter("(", "")
|
||||
.substringBefore(")", "")
|
||||
|
||||
includedLines.forEach { incLine ->
|
||||
if (regexIgnore == null || !regexIgnore.containsMatchIn(incLine)) {
|
||||
result.append(incLine).append("\n")
|
||||
val prefixesToIgnore = if (patternInside.isNotEmpty()) {
|
||||
patternInside.split("|")
|
||||
} else {
|
||||
listOf(parts[2].substring(1))
|
||||
}
|
||||
|
||||
val filteredLines = rawIncludedContent.filter { incLine ->
|
||||
prefixesToIgnore.none { prefix ->
|
||||
incLine.trim().startsWith(prefix)
|
||||
}
|
||||
}
|
||||
lineBuilder.append(filteredLines.joinToString("\n"))
|
||||
} else {
|
||||
lineBuilder.append(rawIncludedContent.joinToString("\n"))
|
||||
}
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
result.append("// Erreur inclusion: ${e.message}\n")
|
||||
lineBuilder.append("// Erreur: ${e.message}")
|
||||
}
|
||||
lastIndex = match.range.last + 1
|
||||
}
|
||||
lineBuilder.append(line.substring(lastIndex))
|
||||
result.append(lineBuilder.toString()).append("\n")
|
||||
} else {
|
||||
result.append(line).append("\n")
|
||||
}
|
||||
|
|
@ -544,8 +575,8 @@ class Solfa(val sharedScreenModel: SharedScreenModel, private val fileRepository
|
|||
val lyricsContent = if(lineYIdx != -1) smartYLyrics(lyricsBody) else smartELyrics(lyricsBody)
|
||||
|
||||
// Découpage strict pour correspondre au template
|
||||
println("Lyrics BODY $lyricsBody")
|
||||
println("LyricsC $lyricsContent")
|
||||
// println("Lyrics BODY $lyricsBody")
|
||||
// println("LyricsC $lyricsContent")
|
||||
|
||||
val lyricsInLines = sharedScreenModel.synchronizedSyllables.value
|
||||
lyricsInLines.mapIndexed { index, string ->
|
||||
|
|
@ -575,7 +606,7 @@ class Solfa(val sharedScreenModel: SharedScreenModel, private val fileRepository
|
|||
// marker))
|
||||
}
|
||||
|
||||
println("\nAction finale : Remplacer l'index $targetIdx (valeur: ${allTokens.getOrNull(targetIdx)})")
|
||||
// println("\nAction finale : Remplacer l'index $targetIdx (valeur: ${allTokens.getOrNull(targetIdx)})")
|
||||
|
||||
if (targetIdx < allTokens.size) {
|
||||
allTokens[targetIdx] = newSyllable
|
||||
|
|
@ -682,7 +713,7 @@ class Solfa(val sharedScreenModel: SharedScreenModel, private val fileRepository
|
|||
val isStructural = token.contains("#") || token == "/" || token == "(" || token == ")"
|
||||
|
||||
if (isStructural) {
|
||||
println(String.format("%-5d | %-10s | %-8s | %-12s | %-10s", i, token, "STRUCT", "-", "Keep"))
|
||||
// println(String.format("%-5d | %-10s | %-8s | %-12s | %-10s", i, token, "STRUCT", "-", "Keep"))
|
||||
resultTokens.add(token)
|
||||
i++
|
||||
continue
|
||||
|
|
@ -731,7 +762,7 @@ class Solfa(val sharedScreenModel: SharedScreenModel, private val fileRepository
|
|||
resultTokens.add(processedToken)
|
||||
|
||||
if (processedToken.matches(Regex("[drmfsltDRFSTzw].*|[-―]"))) {
|
||||
println(String.format("NEW | %-10s | INSERT | %-12d | REPLACE", processedToken, currentLogicalIdx))
|
||||
// println(String.format("NEW | %-10s | INSERT | %-12d | REPLACE", processedToken, currentLogicalIdx))
|
||||
currentLogicalIdx++
|
||||
}
|
||||
}
|
||||
|
|
@ -772,6 +803,160 @@ class Solfa(val sharedScreenModel: SharedScreenModel, private val fileRepository
|
|||
}
|
||||
}
|
||||
|
||||
private fun updateMarkerSource(
|
||||
templateLine: String,
|
||||
targetPointer: Int,
|
||||
newMarkerValue: String
|
||||
): String {
|
||||
val isT0 = templateLine.startsWith("T0")
|
||||
|
||||
val prefix: String
|
||||
val body: String
|
||||
val suffix = if (isT0) "}" else ""
|
||||
|
||||
if (isT0) {
|
||||
prefix = "T0:{"
|
||||
body = templateLine.substringAfter("{").substringBeforeLast("}")
|
||||
} else {
|
||||
val match = Regex("^(.*?z\\d|.*?z.):").find(templateLine)
|
||||
if (match != null) {
|
||||
prefix = match.value
|
||||
body = templateLine.substring(match.value.length)
|
||||
} else {
|
||||
prefix = templateLine.substringBefore(":") + ":"
|
||||
body = templateLine.substringAfter(":")
|
||||
}
|
||||
}
|
||||
|
||||
// println("\n" + "=".repeat(100))
|
||||
// println("DEBUG COMPACTAGE & MARKERS - préfixe $prefix")
|
||||
// println("Cible Idx: $targetPointer | Nouveau Marqueur: \"$newMarkerValue\"")
|
||||
// println("-".repeat(100))
|
||||
// println(String.format("%-4s | %-25s | %-15s | %-25s", "Idx", "Bloc Étendu (Input)", "Action", "Bloc Compacté (Output)"))
|
||||
// println("-".repeat(100))
|
||||
|
||||
val resultBody = StringBuilder()
|
||||
var currentLogicalIdx = 0
|
||||
var i = 0
|
||||
var currentBlock = StringBuilder()
|
||||
val separators = listOf(':', '|', '!', '/')
|
||||
|
||||
while (i <= body.length) {
|
||||
if (i < body.length && body[i] == '$' && i + 1 < body.length && body[i + 1] == '{') {
|
||||
val endPos = body.indexOf('}', i)
|
||||
if (endPos != -1) {
|
||||
currentBlock.append(body.substring(i, endPos + 1))
|
||||
i = endPos + 1
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
if (i == body.length || body[i] in separators) {
|
||||
if (isT0 && currentBlock.toString().trim().isEmpty()) {
|
||||
if (i < body.length) resultBody.append(body[i])
|
||||
currentBlock.clear()
|
||||
i++
|
||||
continue
|
||||
}
|
||||
if (i == body.length && currentBlock.toString().trim().isEmpty()) {
|
||||
break
|
||||
}
|
||||
|
||||
var rawBlockText = currentBlock.toString()
|
||||
var actionTaken = "Keep"
|
||||
|
||||
if (rawBlockText.trim().isEmpty()) {
|
||||
rawBlockText = if(isT0) "" else "z"
|
||||
actionTaken = if(isT0) "EMPTY->\"\"" else "EMPTY->z"
|
||||
}
|
||||
|
||||
var blockToProcess = if (currentLogicalIdx == targetPointer) {
|
||||
actionTaken = if (newMarkerValue.isEmpty()) "DELETE" else "UPDATE"
|
||||
val note = rawBlockText.replace(Regex("""\$\{.*?\}|\$."""), "").trim()
|
||||
|
||||
newMarkerValue + note
|
||||
} else {
|
||||
rawBlockText
|
||||
}
|
||||
|
||||
val compactedBlock = StringBuilder()
|
||||
if (blockToProcess.isNotEmpty()) {
|
||||
var k = 0
|
||||
while (k < blockToProcess.length) {
|
||||
val char = blockToProcess[k]
|
||||
|
||||
if (char == 'D' || char == 'z' || char == '-') {
|
||||
var count = 1
|
||||
val noteStart = char
|
||||
|
||||
val markerFound = StringBuilder()
|
||||
var mIdx = k + 1
|
||||
while (mIdx < blockToProcess.length) {
|
||||
val nextChar = blockToProcess[mIdx]
|
||||
if (nextChar == '$') {
|
||||
if (mIdx + 1 < blockToProcess.length && blockToProcess[mIdx + 1] == '{') {
|
||||
val end = blockToProcess.indexOf('}', mIdx)
|
||||
if (end != -1) {
|
||||
markerFound.append(blockToProcess.substring(mIdx, end + 1))
|
||||
mIdx = end + 1
|
||||
continue
|
||||
}
|
||||
} else if (mIdx + 1 < blockToProcess.length) {
|
||||
markerFound.append(blockToProcess.substring(mIdx, mIdx + 2))
|
||||
mIdx += 2
|
||||
continue
|
||||
}
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
var scanIdx = mIdx
|
||||
while (scanIdx < blockToProcess.length && blockToProcess[scanIdx] == '-' && count < 4) {
|
||||
if (scanIdx + 1 < blockToProcess.length && blockToProcess[scanIdx + 1] == '$') break
|
||||
count++
|
||||
scanIdx++
|
||||
}
|
||||
|
||||
val compactCode = when (count) {
|
||||
1 -> noteStart
|
||||
2 -> if (noteStart == 'D') '8' else '2'
|
||||
3 -> if (noteStart == 'D') 'C' else '3'
|
||||
4 -> if (noteStart == 'D') 'G' else '4'
|
||||
else -> noteStart
|
||||
}
|
||||
|
||||
compactedBlock.append(compactCode).append(markerFound)
|
||||
k = scanIdx
|
||||
} else {
|
||||
compactedBlock.append(char)
|
||||
k++
|
||||
}
|
||||
}
|
||||
} else {
|
||||
compactedBlock.append("")
|
||||
}
|
||||
|
||||
// println(String.format("%-4d | %-25s | %-15s | %-25s",
|
||||
// currentLogicalIdx, rawBlockText, actionTaken, compactedBlock.toString()))
|
||||
|
||||
resultBody.append(compactedBlock)
|
||||
if (i < body.length) resultBody.append(body[i])
|
||||
|
||||
currentBlock.clear()
|
||||
currentLogicalIdx++
|
||||
} else {
|
||||
currentBlock.append(body[i])
|
||||
}
|
||||
i++
|
||||
}
|
||||
|
||||
val finalResult = "$prefix$resultBody$suffix"
|
||||
// println("-".repeat(100))
|
||||
// println("RÉSULTAT FINAL COMPACTÉ : $finalResult")
|
||||
// println("=".repeat(100))
|
||||
|
||||
return finalResult
|
||||
}
|
||||
|
||||
private fun parseNoteAndOctave(s: String): Pair<String, Int> {
|
||||
val base = s.replace(Regex("[^drmfsltDRFSTzw]"), "")
|
||||
|
|
@ -876,7 +1061,7 @@ class Solfa(val sharedScreenModel: SharedScreenModel, private val fileRepository
|
|||
// "Array = ${templateArray.joinToString("|")}")
|
||||
// println("APRèS T: ${templateArray.joinToString("|")}")
|
||||
} else {
|
||||
val body = templateLine.replaceFirst(Regex("^U0:z.:"), "")
|
||||
val body = templateLine.replaceFirst(Regex("^U0:(?:z[0-9A-Z]:)?"), "")
|
||||
|
||||
//println("Mon contenu est: $body.")
|
||||
val result = mutableListOf<String>()
|
||||
|
|
@ -886,31 +1071,27 @@ class Solfa(val sharedScreenModel: SharedScreenModel, private val fileRepository
|
|||
while (i < body.length) {
|
||||
val char = body[i]
|
||||
|
||||
// 1. GESTION DES MARQUEURS ${...} -> ON IGNORE TOUT LE BLOC
|
||||
if (char == '$' && i + 1 < body.length && body[i + 1] == '{') {
|
||||
val closingBrace = body.indexOf('}', i)
|
||||
if (closingBrace != -1) {
|
||||
i = closingBrace + 1 // On saute jusqu'après le '}'
|
||||
i = closingBrace + 1
|
||||
continue
|
||||
}
|
||||
}
|
||||
if (char == '$') {
|
||||
i += 2 // On saute le '$' et la lettre qui suit
|
||||
i += 2
|
||||
continue
|
||||
}
|
||||
|
||||
// 3. SÉPARATEURS -> ON COUPE LE BLOC
|
||||
if (char in listOf(':', '|', '!', '/')) {
|
||||
if (currentBlock.isNotEmpty()) {
|
||||
result.add(currentBlock.toString())
|
||||
currentBlock.clear()
|
||||
}
|
||||
}
|
||||
// 4. IGNORER LES ESPACES
|
||||
else if (char == ' ') {
|
||||
// ne rien faire
|
||||
|
||||
}
|
||||
// 5. TOUT LE RESTE (D, z, -, ., ,, (, ) )
|
||||
else {
|
||||
currentBlock.append(char)
|
||||
}
|
||||
|
|
@ -918,7 +1099,6 @@ class Solfa(val sharedScreenModel: SharedScreenModel, private val fileRepository
|
|||
i++
|
||||
}
|
||||
|
||||
// Ajouter le dernier morceau s'il n'y a pas de séparateur à la fin
|
||||
if (currentBlock.isNotEmpty()) result.add(currentBlock.toString())
|
||||
|
||||
templateArray = result
|
||||
|
|
@ -929,6 +1109,297 @@ class Solfa(val sharedScreenModel: SharedScreenModel, private val fileRepository
|
|||
return templateArray
|
||||
}
|
||||
|
||||
private fun reconstructIt(finalResult: String, measure: Int): String {
|
||||
if(finalResult.startsWith("T0:")) {
|
||||
/* T0 */
|
||||
return finalResult
|
||||
} else {
|
||||
/* U0 */
|
||||
val prefixMatch = Regex("^(.*?z[048CEKO]:|.*?z.:)").find(finalResult)
|
||||
val prefix = prefixMatch?.value ?: "U0:z0:"
|
||||
val body = finalResult.substring(prefixMatch?.value?.length ?: (finalResult.indexOf(":") + 1))
|
||||
|
||||
val buff = mutableListOf<String>()
|
||||
var currentBlockBuilder = StringBuilder()
|
||||
var idx = 0
|
||||
while (idx < body.length) {
|
||||
if (body[idx] == '$' && idx + 1 < body.length && body[idx + 1] == '{') {
|
||||
val endPos = body.indexOf('}', idx)
|
||||
if (endPos != -1) {
|
||||
currentBlockBuilder.append(body.substring(idx, endPos + 1))
|
||||
idx = endPos + 1
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
val char = body[idx]
|
||||
if (char == ':' || char == '|' || char == '!') {
|
||||
if (currentBlockBuilder.isNotEmpty()) {
|
||||
buff.add(currentBlockBuilder.toString())
|
||||
currentBlockBuilder.setLength(0)
|
||||
}
|
||||
} else if (char == '/') {
|
||||
currentBlockBuilder.append(char)
|
||||
buff.add(currentBlockBuilder.toString())
|
||||
currentBlockBuilder.setLength(0)
|
||||
} else {
|
||||
currentBlockBuilder.append(char)
|
||||
}
|
||||
idx++
|
||||
}
|
||||
if (currentBlockBuilder.isNotEmpty()) {
|
||||
buff.add(currentBlockBuilder.toString())
|
||||
}
|
||||
|
||||
val buff2 = mutableListOf<String>()
|
||||
val consumedGlobally = mutableSetOf<Pair<Int, Int>>()
|
||||
val consumedBlocks = mutableSetOf<Int>()
|
||||
|
||||
fun getDurationAt(targetStr: String, targetCharIdx: Int, blockIdx: Int): Int {
|
||||
val cleanStr = targetStr.replace(Regex("\\$\\{.*?\\}|\\$."), "")
|
||||
val charAtIdx = targetStr[targetCharIdx]
|
||||
|
||||
if (blockIdx > 0) {
|
||||
val prevBlock = buff[blockIdx - 1].replace(Regex("\\$\\{.*?\\}|\\$."), "")
|
||||
val isFirstActiveChar = targetCharIdx == 0 || targetStr.substring(0, targetCharIdx).all {
|
||||
"., !:|(){}$".contains(it) || consumedGlobally.contains(blockIdx to targetStr.indexOf(it))
|
||||
}
|
||||
|
||||
if (prevBlock.endsWith("-.") || prevBlock == ".") {
|
||||
if (isFirstActiveChar) {
|
||||
if (charAtIdx == 'z' || charAtIdx == 'D') {
|
||||
return 4
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!cleanStr.contains(".") && !cleanStr.contains(",")) {
|
||||
if (prevBlock.endsWith("./") || prevBlock.endsWith(".")) return 2
|
||||
if (prevBlock.endsWith(",/") || prevBlock.endsWith(",")) return 1
|
||||
}
|
||||
}
|
||||
|
||||
if (charAtIdx == 'D' || charAtIdx == 'z') {
|
||||
val textAfter = targetStr.substring(targetCharIdx + 1)
|
||||
val cleanAfter = textAfter.replace(Regex("\\$\\{.*?\\}|\\$."), "")
|
||||
if (cleanAfter.startsWith(".") && !cleanAfter.substring(1).any { it == 'D' || it == 'z' || it == '-' }) {
|
||||
return 4
|
||||
}
|
||||
}
|
||||
|
||||
if (!cleanStr.contains(".") && !cleanStr.contains(",")) return 4
|
||||
|
||||
val pByPoint = cleanStr.split(".")
|
||||
val prefixBefore = targetStr.substring(0, targetCharIdx).replace(Regex("\\$\\{.*?\\}|\\$."), "")
|
||||
val cleanIdx = prefixBefore.length
|
||||
|
||||
var curPos = 0
|
||||
var zoneIdx = 0
|
||||
for (i in pByPoint.indices) {
|
||||
if (cleanIdx >= curPos && cleanIdx < curPos + pByPoint[i].length + 1) {
|
||||
zoneIdx = i
|
||||
break
|
||||
}
|
||||
curPos += pByPoint[i].length + 1
|
||||
}
|
||||
|
||||
return if (cleanStr.contains(",")) {
|
||||
if (pByPoint[zoneIdx].contains(",")) 1 else 2
|
||||
} else {
|
||||
2
|
||||
}
|
||||
}
|
||||
|
||||
fun encodeDuration(duration: Int): String {
|
||||
return when (duration) {
|
||||
1 -> "1"; 2 -> "2"; 3 -> "3"; 4 -> "4"
|
||||
6 -> "6"; 8 -> "8"; 10 -> "A"; 11 -> "B";
|
||||
12 -> "C"; 14 -> "E"; 16 -> "G"; 20 -> "K";
|
||||
24 -> "O"; 28 -> "S"; 32 -> "W"
|
||||
else -> duration.toString()
|
||||
}
|
||||
}
|
||||
|
||||
var k = 0
|
||||
while (k < buff.size) {
|
||||
if (consumedBlocks.contains(k)) {
|
||||
// println("Pour $k = ${buff[k]} -> Saut du bloc entier (déjà absorbé dans un silence cumulé)")
|
||||
k++
|
||||
continue
|
||||
}
|
||||
|
||||
val currentStr = buff[k]
|
||||
val sb = StringBuilder()
|
||||
val prefixBuffer = StringBuilder()
|
||||
|
||||
// println("Pour $k = $currentStr")
|
||||
|
||||
if (k > 0) {
|
||||
val prevBlock = buff[k - 1].replace(Regex("\\$\\{.*?\\}|\\$."), "")
|
||||
val cleanCurrent = currentStr.replace(Regex("\\$\\{.*?\\}|\\$."), "")
|
||||
|
||||
if (prevBlock.endsWith("-./") || prevBlock.endsWith("-.)/")) {
|
||||
if (cleanCurrent.contains(".") && cleanCurrent.startsWith("D") && cleanCurrent.count { it == 'D' } >= 2) {
|
||||
val silenceDuration = 2
|
||||
sb.append("z$silenceDuration ")
|
||||
// println(" -> [RÈGLE SPÉCIALE] Silence résiduel injecté ! Insertion de 'z$silenceDuration '")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var charIdx = 0
|
||||
while (charIdx < currentStr.length) {
|
||||
if (consumedGlobally.contains(k to charIdx)) {
|
||||
// println(" -> Saut du caractère '${currentStr[charIdx]}' (déjà absorbé)")
|
||||
charIdx++
|
||||
continue
|
||||
}
|
||||
|
||||
val char = currentStr[charIdx]
|
||||
when {
|
||||
char == '$' && charIdx + 1 < currentStr.length && currentStr[charIdx + 1] == '{' -> {
|
||||
val endIdx = currentStr.indexOf('}', charIdx)
|
||||
if (endIdx != -1) {
|
||||
prefixBuffer.append(currentStr.substring(charIdx, endIdx + 1))
|
||||
charIdx = endIdx
|
||||
}
|
||||
}
|
||||
|
||||
char == '$' && charIdx + 1 < currentStr.length -> {
|
||||
prefixBuffer.append(char).append(currentStr[charIdx + 1])
|
||||
charIdx++
|
||||
}
|
||||
|
||||
char == '(' -> prefixBuffer.append(char)
|
||||
char == ')' || char == '/' -> sb.append(char)
|
||||
|
||||
char == 'D' || char == 'z' -> {
|
||||
var totalDuration = getDurationAt(currentStr, charIdx, k)
|
||||
// println(" -> Note '$char' détectée. Zone initiale, Durée: $totalDuration")
|
||||
|
||||
var lookK = k
|
||||
var lookC = charIdx + 1
|
||||
var searching = true
|
||||
|
||||
while (searching) {
|
||||
if (lookC >= buff[lookK].length) {
|
||||
lookK++; lookC = 0
|
||||
if (lookK >= buff.size) break
|
||||
}
|
||||
|
||||
val next = buff[lookK][lookC]
|
||||
|
||||
when {
|
||||
next == '$' -> {
|
||||
if (lookC + 1 < buff[lookK].length && buff[lookK][lookC + 1] == '{') {
|
||||
val end = buff[lookK].indexOf('}', lookC)
|
||||
lookC = if (end != -1) end + 1 else lookC + 2
|
||||
} else {
|
||||
lookC += 2
|
||||
}
|
||||
}
|
||||
next == '-' -> {
|
||||
val resDur = getDurationAt(buff[lookK], lookC, lookK)
|
||||
totalDuration += resDur
|
||||
consumedGlobally.add(lookK to lookC)
|
||||
// println(" + Absorption tiret à [$lookK,$lookC]. Durée zone: $resDur. Total: $totalDuration")
|
||||
lookC++
|
||||
}
|
||||
|
||||
next == 'z' && lookK == k && char == 'D' -> {
|
||||
var hasFutureResonance = false
|
||||
if (k + 1 < buff.size) {
|
||||
if (buff[k + 1].replace(Regex("\\$\\{.*?\\}|\\$."), "")
|
||||
.startsWith("-")
|
||||
) hasFutureResonance = true
|
||||
}
|
||||
if (!hasFutureResonance && currentStr.contains(".") && currentStr.contains(",")) {
|
||||
val resDur = getDurationAt(currentStr, lookC, k)
|
||||
totalDuration += resDur
|
||||
consumedGlobally.add(lookK to lookC)
|
||||
lookC++
|
||||
} else {
|
||||
searching = false
|
||||
}
|
||||
}
|
||||
|
||||
"., !:|(){}".contains(next) || next == '$' -> lookC++
|
||||
else -> searching = false
|
||||
}
|
||||
}
|
||||
|
||||
val isPureSilenceBlock =
|
||||
char == 'z' && (lookK > k || (lookK == k && lookC >= buff[k].length))
|
||||
|
||||
if (isPureSilenceBlock) {
|
||||
var nextBlockIdx = k + 1
|
||||
var lookingForSilence = true
|
||||
|
||||
while (lookingForSilence && nextBlockIdx < buff.size) {
|
||||
val nextBlockRaw = buff[nextBlockIdx]
|
||||
val nextBlockClean = nextBlockRaw.replace(Regex("\\$\\{.*?\\}|\\$."), "").trim()
|
||||
|
||||
if (nextBlockClean.isNotEmpty() && nextBlockClean.all {
|
||||
it == 'z' || "., !:|()/{}".contains(
|
||||
it
|
||||
)
|
||||
}) {
|
||||
val zIdxInNext = nextBlockRaw.indexOf('z')
|
||||
if (zIdxInNext != -1) {
|
||||
val nextSilenceDur = getDurationAt(nextBlockRaw, zIdxInNext, nextBlockIdx)
|
||||
totalDuration += nextSilenceDur
|
||||
// println(" [CUMUL SILENCE] -> Bloc $nextBlockIdx ('$nextBlockClean') absorbé. Durée ajoutée: $nextSilenceDur. Total temporaire: $totalDuration")
|
||||
consumedBlocks.add(nextBlockIdx)
|
||||
nextBlockIdx++
|
||||
} else {
|
||||
lookingForSilence = false
|
||||
}
|
||||
} else {
|
||||
lookingForSilence = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val code = if (char == 'z') {
|
||||
"z${encodeDuration(totalDuration)}"
|
||||
} else {
|
||||
encodeDuration(totalDuration)
|
||||
}
|
||||
|
||||
sb.append(prefixBuffer.toString())
|
||||
prefixBuffer.setLength(0)
|
||||
sb.append(code)
|
||||
// println(" -> Code généré: $code")
|
||||
}
|
||||
}
|
||||
charIdx++
|
||||
}
|
||||
|
||||
if (prefixBuffer.isNotEmpty()) sb.insert(0, prefixBuffer.toString())
|
||||
var resultBlock = sb.toString()
|
||||
|
||||
if (resultBlock.contains("31")) {
|
||||
resultBlock = resultBlock.replace("31", "y")
|
||||
// println(" -> Motif 'y' détecté")
|
||||
}
|
||||
|
||||
// println("Pour ça ON a $resultBlock \n")
|
||||
if (resultBlock.isNotEmpty() || buff[k].contains("/")) buff2.add(resultBlock)
|
||||
k++
|
||||
}
|
||||
|
||||
val cleanedBlocks = buff2.map { it.replace(" ", "") }
|
||||
val finalLines = (prefix + cleanedBlocks.joinToString(""))
|
||||
.replace("2z11", "y")
|
||||
.replace("/", "/ ")
|
||||
.replace(Regex("\\s+"), " ")
|
||||
.trim()
|
||||
|
||||
// println(finalLines)
|
||||
return finalLines
|
||||
}
|
||||
}
|
||||
|
||||
private fun expandNotes(text: String): String {
|
||||
val regex = Regex("([drmfsltDRFSTzw])([,']*)(\\d+)")
|
||||
return regex.replace(text) { matchResult ->
|
||||
|
|
|
|||
|
|
@ -25,8 +25,7 @@ class SolfaVisualTransformation : VisualTransformation {
|
|||
|
||||
when {
|
||||
line.startsWith("M0:") -> parseMetadataLine(line)
|
||||
line.startsWith("U0:") && hasU0 -> parseControlLine(line)
|
||||
line.startsWith("T0:") && !hasU0 && hasT0 -> parseControlLine(line)
|
||||
line.startsWith("U0:") || line.startsWith("T0:") -> parseControlLine(line)
|
||||
line.startsWith("N") && line.getOrNull(2) == ':' -> parseNoteLine(line)
|
||||
(line.startsWith("Y") || line.startsWith("E")) && (line.getOrNull(2) == ':' || line.getOrNull(3) == ':') -> parseLyricLine(line)
|
||||
else -> append(line)
|
||||
|
|
@ -129,16 +128,21 @@ private fun AnnotatedString.Builder.appendWithMarkers(text: String) {
|
|||
}
|
||||
|
||||
private fun AnnotatedString.Builder.parseControlLine(line: String) {
|
||||
val regex = Regex("^(U0:)(z[0-9A-Z]:)?(.*)$")
|
||||
val regex = Regex("^([UT]0:)(z[0-9A-Z]:)?(.*)$")
|
||||
val match = regex.find(line)
|
||||
match?.let {
|
||||
pushStyle(SpanStyle(color = ColorPrefixA))
|
||||
append(it.groupValues[1])
|
||||
if (match != null) {
|
||||
pushStyle(SpanStyle(color = ColorPrefixA, fontWeight = FontWeight.Bold))
|
||||
append(match.groupValues[1])
|
||||
pop()
|
||||
|
||||
if (match.groupValues[2].isNotEmpty()) {
|
||||
pushStyle(SpanStyle(color = ColorPrefixB))
|
||||
append(it.groupValues[2])
|
||||
append(match.groupValues[2])
|
||||
pop()
|
||||
parseGenericContent(it.groupValues[3])
|
||||
}
|
||||
parseGenericContent(match.groupValues[3])
|
||||
} else {
|
||||
append(line)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -19,10 +19,12 @@ import androidx.compose.ui.Modifier
|
|||
import androidx.compose.ui.graphics.Brush
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.SolidColor
|
||||
import androidx.compose.ui.text.TextRange
|
||||
import androidx.compose.ui.text.TextStyle
|
||||
import androidx.compose.ui.text.font.FontFamily
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.input.KeyboardType
|
||||
import androidx.compose.ui.text.input.TextFieldValue
|
||||
import androidx.compose.ui.unit.*
|
||||
import androidx.compose.ui.window.Popup
|
||||
import androidx.compose.ui.window.PopupProperties
|
||||
|
|
@ -57,7 +59,6 @@ fun TUODetailDialog(
|
|||
var templateFragment by remember { mutableStateOf(editState.templateFragment) }
|
||||
|
||||
var marker by remember { mutableStateOf(editState.marker) }
|
||||
var hairPin by remember { mutableStateOf(editState.hairPin) }
|
||||
|
||||
val lyricsLines = remember {
|
||||
mutableStateListOf<String>().apply {
|
||||
|
|
@ -66,6 +67,7 @@ fun TUODetailDialog(
|
|||
}
|
||||
}
|
||||
|
||||
var canAddMark = mutableStateOf(false)
|
||||
Popup(
|
||||
offset = menuPosition,
|
||||
onDismissRequest = onDismiss,
|
||||
|
|
@ -96,7 +98,6 @@ fun TUODetailDialog(
|
|||
modifier = Modifier
|
||||
.verticalScroll(rememberScrollState())
|
||||
) {
|
||||
|
||||
// --- SECTION TEMPLATE ---
|
||||
Row(horizontalArrangement = Arrangement.spacedBy(8.dp)) {
|
||||
Column(
|
||||
|
|
@ -114,7 +115,7 @@ fun TUODetailDialog(
|
|||
)
|
||||
|
||||
}
|
||||
if(!editState.marker.isNullOrEmpty()) {
|
||||
if(!editState.marker.isNullOrEmpty() || canAddMark.value) {
|
||||
Column(
|
||||
modifier = Modifier.weight(1f)
|
||||
) {
|
||||
|
|
@ -130,20 +131,16 @@ fun TUODetailDialog(
|
|||
onValueChng = { marker = it }
|
||||
)
|
||||
}
|
||||
}
|
||||
if(!editState.hairPin.isNullOrEmpty()) {
|
||||
Column(
|
||||
modifier = Modifier.weight(1f)
|
||||
) {
|
||||
MyTextEditField(
|
||||
value = hairPin,
|
||||
customFontSize = 14.sp,
|
||||
color = Color.Yellow,
|
||||
customPadding = 8.dp,
|
||||
customBrush = SolidColor(Color.White),
|
||||
isEditable = isEditable,
|
||||
isAddable = canAdd,
|
||||
onValueChng = { hairPin = it }
|
||||
} else {
|
||||
IconButton(
|
||||
modifier = Modifier.size(30.dp),
|
||||
onClick = {
|
||||
canAddMark.value = true
|
||||
}) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.Add,
|
||||
tint = Color.Green,
|
||||
contentDescription = null
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -263,7 +260,8 @@ fun TUODetailDialog(
|
|||
originalNotes = originalNotes.toMap(),
|
||||
originalLyricsByStanza = originalLyricsByStz.toMutableMap(),
|
||||
lyricsByStanza = mutableMapOf(currentStanza to lyricsLines.joinToString(" ")),
|
||||
templateFragment = templateFragment
|
||||
templateFragment = templateFragment,
|
||||
marker = marker
|
||||
)
|
||||
onSave(state)
|
||||
},
|
||||
|
|
|
|||
|
|
@ -7,6 +7,5 @@ data class TUOEditState(
|
|||
val lyricsByStanza: MutableMap<Int, String> = mutableMapOf(),
|
||||
val originalLyricsByStanza: MutableMap<Int, String> = mutableMapOf(),
|
||||
val templateFragment: String = "",
|
||||
val marker: String = "",
|
||||
val hairPin: String = ""
|
||||
val marker: String = ""
|
||||
)
|
||||
|
|
@ -843,9 +843,6 @@ fun LazyVerticalGridTUO(
|
|||
|
||||
val editState = TUOEditState(
|
||||
tuoIndex = oneTUO.firstTuoIndex,
|
||||
/*notesByVoice = (0..3).associate { i ->
|
||||
i to (oneTUO.tuNotes.getOrNull(i + 1)?.toString() ?: "")
|
||||
},*/
|
||||
notesByVoice = (0..3).associate { i ->
|
||||
val rawNote = oneTUO.tuNotes.getOrNull(i + 1)?.toString() ?: ""
|
||||
val fixedNote = autoFixNote(rawNote, template)
|
||||
|
|
@ -855,14 +852,11 @@ fun LazyVerticalGridTUO(
|
|||
s to oneTUO.getSingleSyllable(s).firstOrNull().orEmpty()
|
||||
}.toMutableMap(),
|
||||
templateFragment = oneTUO.pTemplate.template,
|
||||
marker = oneTUO.pTemplate.markerToString(),
|
||||
hairPin = oneTUO.hasHairPin()?.toString() ?: ""
|
||||
marker = listOfNotNull(
|
||||
oneTUO.pTemplate.markerToString().takeIf { it.isNotBlank() },
|
||||
oneTUO.hasHairPin()?.toString()?.takeIf { it.isNotBlank() }
|
||||
).joinToString(" ")
|
||||
)
|
||||
// oneTUO.tuNotes.mapIndexed { index, note ->
|
||||
//
|
||||
// println("i$index => ${note.toString()}")
|
||||
// }
|
||||
|
||||
TUODetailDialog(
|
||||
editState = editState,
|
||||
tuo = currentSelected,
|
||||
|
|
|
|||
|
|
@ -523,7 +523,7 @@ class SharedScreenModel(private val fileRepository: FileRepository) : ScreenMode
|
|||
|
||||
fun openTUOEditor(editState: TUOEditState) {
|
||||
_tuoEditState.value = editState
|
||||
println("nMrk = [${editState.marker}] template [${editState.templateFragment}] hp [${editState.hairPin}]")
|
||||
println("nMrk = [${editState.marker}] template [${editState.templateFragment}]")
|
||||
}
|
||||
|
||||
fun closeTUOEditor() {
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue