diff --git a/composeApp/src/commonMain/kotlin/mg/dot/feufaro/solfa/Solfa.kt b/composeApp/src/commonMain/kotlin/mg/dot/feufaro/solfa/Solfa.kt index 64b416a..2093db7 100644 --- a/composeApp/src/commonMain/kotlin/mg/dot/feufaro/solfa/Solfa.kt +++ b/composeApp/src/commonMain/kotlin/mg/dot/feufaro/solfa/Solfa.kt @@ -612,6 +612,8 @@ class Solfa(val sharedScreenModel: SharedScreenModel, private val fileRepository ) { val notesByVoice = editState.notesByVoice val originalNotes = editState.originalNotes + val templateFragment = editState.templateFragment + val pureTemplate = templateFragment.replace(Regex("[()]"), "") for (voiceNum in 0..3) { val linePrefix = "N${voiceNum + 1}:" @@ -631,10 +633,10 @@ class Solfa(val sharedScreenModel: SharedScreenModel, private val fileRepository val isNoteModified = newNot != oldNot // println("est-ce? $isNoteModified = $newNot != $oldNot") - if (!isNoteModified) { -// println("Voix $linePrefix : Aucune modification détectée (Note: $oldNot ).") + /*if (!isNoteModified) { + println("Voix $linePrefix : Aucune modification détectée (Note: $oldNot ).") continue - } + }*/ val newNote = notesByVoice[voiceNum] ?: "" if (newNote.isEmpty()) continue @@ -733,6 +735,7 @@ class Solfa(val sharedScreenModel: SharedScreenModel, private val fileRepository } i++ } + currentLogicalIdx++ } else { // AFFICHAGE NOTE NORMALE // println( @@ -797,6 +800,7 @@ class Solfa(val sharedScreenModel: SharedScreenModel, private val fileRepository var i = 0 var currentBlock = StringBuilder() val separators = listOf(':', '|', '!', '/') + var dropNextCloseParenthesis = false while (i <= body.length) { if (i < body.length && body[i] == '$' && i + 1 < body.length && body[i + 1] == '{') { @@ -833,12 +837,48 @@ class Solfa(val sharedScreenModel: SharedScreenModel, private val fileRepository val markerMatch = Regex("""\$\{.*?\}|\$.""").find(rawBlockText) val existingMarker = markerMatch?.value ?: "" + val leadingCloseParentheses = rawBlockText.takeWhile { it == ')' } + val oldCleaned = rawBlockText.substring(leadingCloseParentheses.length) + val newTemplate = getGlobalTemplate(editState) - newMarkerValue + existingMarker + newTemplate + val oldHadOpen = oldCleaned.contains("(") + val newHasOpen = newTemplate.contains("(") + + + if (oldHadOpen && !newHasOpen) { + dropNextCloseParenthesis = true + } + var adjustedTemplate = newTemplate + if (newTemplate.endsWith(")")) { + val remainingText = body.substring(i).trimStart { it in separators } + if (remainingText.startsWith(")")) { + adjustedTemplate = newTemplate.dropLast(1) + } + } + + val rawProcessed = leadingCloseParentheses + newMarkerValue + existingMarker + adjustedTemplate + + var cleanedProcessed = rawProcessed + while (cleanedProcessed.contains("((") || cleanedProcessed.contains("))")) { + cleanedProcessed = cleanedProcessed.replace("((", "(").replace("))", ")") + } + + cleanedProcessed } else { - rawBlockText + if (dropNextCloseParenthesis && rawBlockText.startsWith(")")) { + actionTaken = "FIX_ORPHEL_)" + dropNextCloseParenthesis = false + rawBlockText.substring(1) + } else { + rawBlockText + } } + if (blockToProcess.contains("((") || blockToProcess.contains("))")) { + while (blockToProcess.contains("((") || blockToProcess.contains("))")) { + blockToProcess = blockToProcess.replace("((", "(").replace("))", ")") + } + } val compactedBlock = StringBuilder() if (blockToProcess.isNotEmpty()) { var k = 0 @@ -896,8 +936,8 @@ class Solfa(val sharedScreenModel: SharedScreenModel, private val fileRepository compactedBlock.append("") } -// println(String.format("%-4d | %-25s | %-15s | %-25s", -// currentLogicalIdx, rawBlockText, actionTaken, compactedBlock.toString())) + println(String.format("%-4d | %-25s | %-15s | %-25s", + currentLogicalIdx, rawBlockText, actionTaken, compactedBlock.toString())) resultBody.append(compactedBlock) if (i < body.length) resultBody.append(body[i]) @@ -911,9 +951,9 @@ class Solfa(val sharedScreenModel: SharedScreenModel, private val fileRepository } val finalResult = "$prefix$resultBody$suffix" -// println("-".repeat(100)) -// println("RÉSULTAT FINAL COMPACTÉ : $finalResult") -// println("=".repeat(100)) + println("-".repeat(100)) + println("RÉSULTAT FINAL COMPACTÉ : $finalResult") + println("=".repeat(100)) return finalResult } diff --git a/composeApp/src/commonMain/kotlin/mg/dot/feufaro/solfa/TUODetailDialog.kt b/composeApp/src/commonMain/kotlin/mg/dot/feufaro/solfa/TUODetailDialog.kt index fddb4dc..dec9a10 100644 --- a/composeApp/src/commonMain/kotlin/mg/dot/feufaro/solfa/TUODetailDialog.kt +++ b/composeApp/src/commonMain/kotlin/mg/dot/feufaro/solfa/TUODetailDialog.kt @@ -60,10 +60,10 @@ fun TUODetailDialog( var marker by remember { mutableStateOf(editState.marker) } - LaunchedEffect(notes.values.toList()) { + LaunchedEffect(notes.toMap()) { val tempState = TUOEditState( tuoIndex = editState.tuoIndex, - notesByVoice = notes, + notesByVoice = notes.toMap(), originalNotes = editState.originalNotes, lyricsByStanza = editState.lyricsByStanza, originalLyricsByStanza = editState.originalLyricsByStanza, @@ -382,7 +382,7 @@ fun validateMusicalInput(input: String, template: String): Boolean { cleanTemplate.forEachIndexed { index, char -> when (char) { in 'A'..'Y', in 'a'..'y' -> { - append("(?:―|di|ri|fi|si|ta|[drmfslt])[¹²³₁₂₃]*") + append("(?:―|di|ri|fi|si|ta|[drmfsltz])[¹²³₁₂₃]*") } 'z' -> { append(" ?") @@ -431,33 +431,84 @@ public fun getGlobalTemplate(editState: TUOEditState): String { tpl } - val maxLen = voiceTemplates.maxOfOrNull { it.length } ?: 1 -// println("Longueur max détectée: $maxLen") + val rythmicTemplates = voiceTemplates.map { tpl -> tpl.replace(Regex("[()]"), "") } + val maxLen = rythmicTemplates.maxOfOrNull { it.length } ?: 1 + // println("Longueur rythmique max détectée: $maxLen") + + val openBefore = BooleanArray(maxLen) + val closeBefore = BooleanArray(maxLen) + val openAfter = BooleanArray(maxLen) + val closeAfter = BooleanArray(maxLen) + + var openAtTheEnd = false + var closeAtTheEnd = false + + voiceTemplates.forEach { tpl -> + var rythmicIdx = 0 + for (i in tpl.indices) { + val char = tpl[i] + if (char == '(' || char == ')') { + val textAfter = tpl.substring(i + 1) + val hasRythmicAfter = textAfter.any { it != '(' && it != ')' } + + if (hasRythmicAfter) { + if (rythmicIdx < maxLen) { + if (char == '(') openBefore[rythmicIdx] = true + if (char == ')') closeBefore[rythmicIdx] = true + } + } else { + if (rythmicIdx > 0) { + if (char == '(') openAfter[rythmicIdx - 1] = true + if (char == ')') closeAfter[rythmicIdx - 1] = true + } else { + if (char == '(') openAtTheEnd = true + if (char == ')') closeAtTheEnd = true + } + } + } else { + rythmicIdx++ + } + } + } val result = StringBuilder() for (i in 0 until maxLen) { - val chars = voiceTemplates.mapNotNull { it.getOrNull(i) }.toSet() + if (openBefore[i]) result.append('(') + if (closeBefore[i]) result.append(')') - val lastChar = if (result.isNotEmpty()) result.last() else null - - if (lastChar == '.' && chars.contains(',')) { - result.append('z') - } - -// println(" Indice $i | Caractères collectés: $chars") + val chars = rythmicTemplates.mapNotNull { it.getOrNull(i) }.toSet() + val previousChars = if(i>0) rythmicTemplates.mapNotNull { it.getOrNull(i-1) }.toSet() else "".toSet() + val activeChars = rythmicTemplates.mapNotNull { it.getOrNull(i) } + val isAllHyphen = activeChars.isNotEmpty() && activeChars.all { it == '-' } val combinedChar = when { - chars.contains('D') || chars.contains('-') -> 'D' + chars.contains('D') -> 'D' + isAllHyphen -> '-' chars.contains('.') -> '.' chars.contains(',') -> ',' - chars.contains('(') -> '(' - chars.contains(')') -> ')' - else -> 'z' + previousChars.contains('.') && chars.contains(' ') -> 'z' + else -> ' ' } - result.append(combinedChar) + // Supprimer espace avant une virgule + if (combinedChar == ',' && result.isNotEmpty() && result.last() == ' ') { + result.deleteCharAt(result.length - 1) + } + println("Pour $i = ${chars.toString()} on a > ${combinedChar.toString()}") + + val lastChar = if (result.isNotEmpty()) result.last() else null + if (!((combinedChar == '.' && lastChar == '.') || (combinedChar == ',' && lastChar == ','))) { + result.append(combinedChar) + } + println("all = ${result.toString()}") + + if (openAfter[i]) result.append('(') + if (closeAfter[i]) result.append(')') } + if (openAtTheEnd) result.append('(') + if (closeAtTheEnd) result.append(')') + // println("Template Global Final: '${result.toString()}'") // println("----------------------------------\n") return result.toString() @@ -476,7 +527,7 @@ private fun notesToTemplate(notes: String): String { .replace("si", "S") .replace("ta", "T") - val noteRegex = Regex("([drmfsltDRFSTz]+|[.,()-])") + val noteRegex = Regex("""([drmfsltDRFSTz]+|[.,()\s-])""") val template = StringBuilder() val matches = noteRegex.findAll(cleaned) @@ -484,6 +535,7 @@ private fun notesToTemplate(notes: String): String { val value = match.value when { value.matches(Regex("[.,()-]")) -> template.append(value) + value.trim().isEmpty() -> template.append(" ") else -> template.append("D") } } diff --git a/composeApp/src/commonMain/kotlin/mg/dot/feufaro/solfa/TimeUnitObject.kt b/composeApp/src/commonMain/kotlin/mg/dot/feufaro/solfa/TimeUnitObject.kt index a923ee7..5e93572 100644 --- a/composeApp/src/commonMain/kotlin/mg/dot/feufaro/solfa/TimeUnitObject.kt +++ b/composeApp/src/commonMain/kotlin/mg/dot/feufaro/solfa/TimeUnitObject.kt @@ -1262,10 +1262,10 @@ fun EditorActionButtons(isUndoVisible: Boolean, onUndo: () -> Unit, onBuild: () fun autoFixNote(rawNote: String, template: String): String { println("ça on a t $template => $rawNote") - if (rawNote.isEmpty() || rawNote == "_") return rawNote + if (rawNote == "_") return rawNote val noteRegex = Regex("([drmfsltDRFSTw][ia]?|―)[0-9'¹²³⁴₁₂₃₄,]*") - val actualNotes = noteRegex.findAll(rawNote).map { it.value }.toList() + val actualNotes = if (rawNote.isEmpty()) emptyList() else noteRegex.findAll(rawNote).map { it.value }.toList() if (rawNote.contains("•") || rawNote.contains("―") || rawNote.contains(",")) { val templateNotesCount = template.count { c -> @@ -1273,6 +1273,9 @@ fun autoFixNote(rawNote: String, template: String): String { } if (actualNotes.size >= templateNotesCount) return rawNote } + val requiredNotesCount = template.count { it in 'A'..'Y' || it in 'a'..'y' } + + val shouldIncludeParentheses = actualNotes.size >= requiredNotesCount val result = StringBuilder() var noteIdx = 0 @@ -1290,7 +1293,11 @@ fun autoFixNote(rawNote: String, template: String): String { result.append(noteToAdd) noteIdx++ } else { - result.append("―") + if (rawNote.isEmpty()) { + result.append("z") + } else { + result.append("―") + } } } char == '-' -> { @@ -1307,8 +1314,12 @@ fun autoFixNote(rawNote: String, template: String): String { val currentText = result.toString() if (!currentText.endsWith(",")) result.append(",") } - char == '(' -> result.append("(") - char == ')' -> result.append(")") + char == '(' -> { + if (shouldIncludeParentheses) result.append("(") + } + char == ')' -> { + if (shouldIncludeParentheses) result.append(")") + } } } return result.toString()