From 6b8ce0cecf05ed10eb9b240aba7abec983b2c89e Mon Sep 17 00:00:00 2001 From: Hasinjato Date: Fri, 29 May 2026 13:03:30 +0300 Subject: [PATCH] Add Modify silent duration on first index partition & fix bug on multiple includ sources ... --- .../kotlin/mg/dot/feufaro/solfa/Solfa.kt | 93 ++++++++++----- .../mg/dot/feufaro/solfa/TUODetailDialog.kt | 99 ++++++++++++++-- .../mg/dot/feufaro/solfa/TUOEditState.kt | 3 +- .../mg/dot/feufaro/solfa/TimeUnitObject.kt | 107 +++++++++++++++--- 4 files changed, 247 insertions(+), 55 deletions(-) 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 2c9ee53..5793e40 100644 --- a/composeApp/src/commonMain/kotlin/mg/dot/feufaro/solfa/Solfa.kt +++ b/composeApp/src/commonMain/kotlin/mg/dot/feufaro/solfa/Solfa.kt @@ -384,13 +384,27 @@ class Solfa(val sharedScreenModel: SharedScreenModel, private val fileRepository // println("fullLine = '$fullLine'") val afterU0 = fullLine.substringAfter("U0:") - val blankPrefixRegex = Regex("^(.*?z[0-9A-Z]:)") + val blankPrefixRegex = Regex("^(.*?z)([0-9A-Z_+\\-]+)(:)") val match = blankPrefixRegex.find(afterU0) - val blankPrefix = match?.value ?: "" - val body = if (match != null) { - afterU0.substring(blankPrefix.length) + var blankPrefix: String = "" + var body: String = "" + if (match != null) { + body = afterU0.substring(blankPrefix.length) + + if (editState.silentDuration == "-1") { + blankPrefix = match.value + } + else { + println("voici ${match.groupValues.joinToString(",")}") + val beforeDuration = match.groups[1]?.value ?: "" + val restOfSilenceBlock = match.groups[3]?.value ?: ":" + + blankPrefix = "$beforeDuration${editState.silentDuration}$restOfSilenceBlock" + } + body = afterU0.substring(match.value.length) } else { - afterU0 + blankPrefix = afterU0.substringBefore(":") + ":" + body = afterU0.substringAfter(":") } val measure = measureString.split("/")[0].toIntOrNull() ?: 4 @@ -506,29 +520,26 @@ class Solfa(val sharedScreenModel: SharedScreenModel, private val fileRepository val fullPath = directory + fileName val rawIncludedContent = fileRepository.readFileLines(fullPath) - if (parts.size > 2 && parts[2].startsWith("^")) { - val patternInside = parts[2] - .substringAfter("(", "") - .substringBefore(")", "") + if (parts.size > 2 && parts[2].isNotEmpty()) { + val regexIgnoreString = parts[2] - val prefixesToIgnore = if (patternInside.isNotEmpty()) { - patternInside.split("|") - } else { - listOf(parts[2].substring(1)) + val ignoredLinesRegex = try { + Regex(regexIgnoreString) + } catch (e: Exception) { + Regex("^:") } val filteredLines = rawIncludedContent.filter { incLine -> - prefixesToIgnore.none { prefix -> - incLine.trim().startsWith(prefix) - } + ignoredLinesRegex.find(incLine) == null } + lineBuilder.append(filteredLines.joinToString("\n")) } else { lineBuilder.append(rawIncludedContent.joinToString("\n")) } } } catch (e: Exception) { - lineBuilder.append("// Erreur: ${e.message}") + lineBuilder.append("// Erreur inclusion: ${e.message}") } lastIndex = match.range.last + 1 } @@ -768,6 +779,12 @@ class Solfa(val sharedScreenModel: SharedScreenModel, private val fileRepository newMarkerValue: String, editState: TUOEditState ): String { + /* On ne modifie pas le template tant que les notes ne sont pas modifiées + * Et tant qu'une marker est ajouter */ + if (newMarkerValue.isEmpty()) { + println("Aucune modification détectée (marqueur vide), on retourne le template original.") + return templateLine + } val isT0 = templateLine.startsWith("T0") val prefix: String @@ -1291,6 +1308,7 @@ class Solfa(val sharedScreenModel: SharedScreenModel, private val fileRepository var lookK = k var lookC = charIdx + 1 var searching = true + var crossedParenthesis = false while (searching) { if (lookC >= buff[lookK].length) { @@ -1317,20 +1335,28 @@ class Solfa(val sharedScreenModel: SharedScreenModel, private val fileRepository lookC++ } + next == '(' || next == ')' -> { + crossedParenthesis = true + 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 { + if (crossedParenthesis) { searching = false + } else { + 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 + } } } @@ -1411,6 +1437,15 @@ class Solfa(val sharedScreenModel: SharedScreenModel, private val fileRepository } val cleanedBlocks = buff2.map { it.replace(" ", "") } + /*val durationModifierRegex = Regex("^(.*?z)[0-9A-Z_+\\-](:|/)") + val match = durationModifierRegex.find(prefix) + + val newPrefix = if (match != null) { + val beforeDuration = match.groups[1]?.value ?: "" + val separator = match.groups[2]?.value ?: ":" + "$beforeDuration${editState.silentDuration}$separator" + } else prefix*/ + val finalLines = (prefix + cleanedBlocks.joinToString("")) .replace("2z11", "y") .replace("/", "/ ") 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 9bdf194..63dadf2 100644 --- a/composeApp/src/commonMain/kotlin/mg/dot/feufaro/solfa/TUODetailDialog.kt +++ b/composeApp/src/commonMain/kotlin/mg/dot/feufaro/solfa/TUODetailDialog.kt @@ -14,6 +14,8 @@ import androidx.compose.material.icons.automirrored.filled.ArrowForward import androidx.compose.material.icons.filled.Add import androidx.compose.material.icons.filled.Build import androidx.compose.material.icons.filled.Clear +import androidx.compose.material.icons.filled.KeyboardDoubleArrowLeft +import androidx.compose.material.icons.filled.KeyboardDoubleArrowRight import androidx.compose.material3.* import androidx.compose.runtime.* import androidx.compose.ui.Alignment @@ -63,6 +65,7 @@ fun TUODetailDialog( var templateFragment by remember { mutableStateOf(editState.templateFragment) } var marker by remember { mutableStateOf(editState.marker) } + val initialMarker = remember { editState.marker } LaunchedEffect(notes.toMap()) { val tempState = TUOEditState( @@ -116,6 +119,79 @@ fun TUODetailDialog( horizontalArrangement = Arrangement.SpaceBetween, verticalAlignment = Alignment.CenterVertically ) { + // Modif silence duration + if (currentIndex == 0) { + val silentDurationOrder = listOf( + "0", "4", "8", "A", "C", "E", "G", "K", "O", "S", "W" + ) + Row( + modifier = Modifier.width(125.dp), + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.SpaceBetween + ) { + IconButton( + onClick = { + val currentIdx = silentDurationOrder.indexOf(editState.silentDuration) + val newIdx = if (currentIdx > 0) currentIdx - 1 else 0 + val newDuration = silentDurationOrder[newIdx] + + val state = TUOEditState( + tuoIndex = globalIndex, + notesByVoice = notes.toMap(), + originalNotes = originalNotes.toMap(), + originalLyricsByStanza = originalLyricsByStz.toMutableMap(), + lyricsByStanza = mutableMapOf(currentStanza to lyricsLines.joinToString(" ")), + templateFragment = templateFragment, + marker = if(marker != initialMarker) marker else "", + silentDuration = newDuration + ) + onSave(state) + }, + enabled = silentDurationOrder.indexOf(editState.silentDuration) >= 0, + modifier = Modifier.size(20.dp) + ) { + Icon( + imageVector = Icons.Default.KeyboardDoubleArrowLeft, + contentDescription = "Diminuer", + tint = if (silentDurationOrder.indexOf(editState.silentDuration) > 0) Color.LightGray else Color.DarkGray + ) + } + + Text( + text = editState.silentDuration, + color = Color.White, + style = MaterialTheme.typography.bodyLarge + ) + + IconButton( + onClick = { + val currentIdx = silentDurationOrder.indexOf(editState.silentDuration) + val newIdx = if (currentIdx != -1 && currentIdx < silentDurationOrder.lastIndex) currentIdx + 1 else currentIdx + val newDuration = silentDurationOrder[newIdx] + + val state = TUOEditState( + tuoIndex = globalIndex, + notesByVoice = notes.toMap(), + originalNotes = originalNotes.toMap(), + originalLyricsByStanza = originalLyricsByStz.toMutableMap(), + lyricsByStanza = mutableMapOf(currentStanza to lyricsLines.joinToString(" ")), + templateFragment = templateFragment, + marker = if(marker != initialMarker) marker else "", + silentDuration = newDuration + ) + onSave(state) + }, + enabled = silentDurationOrder.indexOf(editState.silentDuration) < silentDurationOrder.lastIndex, + modifier = Modifier.size(20.dp) + ) { + Icon( + imageVector = Icons.Default.KeyboardDoubleArrowRight, + contentDescription = "Augmenter", + tint = if (silentDurationOrder.indexOf(editState.silentDuration) < silentDurationOrder.lastIndex) Color.LightGray else Color.DarkGray + ) + } + } + } if (currentIndex > 0) { IconButton( onClick = { @@ -130,14 +206,14 @@ fun TUODetailDialog( tint = Color.White ) } - } - Text( - text = "N°$currentIndex", - fontSize = 15.sp, - fontWeight = FontWeight.Bold, - color = Color.White - ) + Text( + text = "N°$currentIndex", + fontSize = 15.sp, + fontWeight = FontWeight.Bold, + color = Color.White + ) + } IconButton( onClick = { @@ -288,7 +364,9 @@ fun TUODetailDialog( if (isEditable || canAdd) { IconButton( onClick = { - if (index == 0) lyricsLines.add("_") else lyricsLines.removeAt(index) + if (index >= 0) { + if (index == 0) lyricsLines.add("_") else lyricsLines.removeAt(index) + } }) { Icon( imageVector = if (index == 0) Icons.Default.Add else Icons.Default.Clear, @@ -323,7 +401,8 @@ fun TUODetailDialog( originalLyricsByStanza = originalLyricsByStz.toMutableMap(), lyricsByStanza = mutableMapOf(currentStanza to lyricsLines.joinToString(" ")), templateFragment = templateFragment, - marker = marker + /* On ne ré-insere pas la même marker*/ + marker = if(marker != initialMarker) marker else "", ) onSave(state) }, @@ -567,7 +646,7 @@ private fun notesToTemplate(notes: String): String { .replace("si", "S") .replace("ta", "T") - val noteRegex = Regex("""([drmfsltDRFSTz]+|[.,()\s-])""") + val noteRegex = Regex("""([drmfsltDRFSTz][ia]?|[.,()\s-])""") val template = StringBuilder() val matches = noteRegex.findAll(cleaned) diff --git a/composeApp/src/commonMain/kotlin/mg/dot/feufaro/solfa/TUOEditState.kt b/composeApp/src/commonMain/kotlin/mg/dot/feufaro/solfa/TUOEditState.kt index 79540d9..5a280b5 100644 --- a/composeApp/src/commonMain/kotlin/mg/dot/feufaro/solfa/TUOEditState.kt +++ b/composeApp/src/commonMain/kotlin/mg/dot/feufaro/solfa/TUOEditState.kt @@ -7,5 +7,6 @@ data class TUOEditState( val lyricsByStanza: MutableMap = mutableMapOf(), val originalLyricsByStanza: MutableMap = mutableMapOf(), val templateFragment: String = "", - val marker: String = "" + val marker: String = "", + val silentDuration: String = "-1" ) \ No newline at end of file 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 617376e..1dc2531 100644 --- a/composeApp/src/commonMain/kotlin/mg/dot/feufaro/solfa/TimeUnitObject.kt +++ b/composeApp/src/commonMain/kotlin/mg/dot/feufaro/solfa/TimeUnitObject.kt @@ -16,9 +16,11 @@ import androidx.compose.foundation.text.BasicTextField import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.material.icons.Icons import androidx.compose.material.icons.automirrored.filled.Undo +import androidx.compose.material.icons.filled.ArrowBackIosNew import androidx.compose.material.icons.filled.Build import androidx.compose.material.icons.filled.Close import androidx.compose.material.icons.filled.Description +import androidx.compose.material.icons.filled.KeyboardDoubleArrowLeft import androidx.compose.material.icons.filled.Save import androidx.compose.material3.* import androidx.compose.runtime.* @@ -274,7 +276,7 @@ fun TimeUnitComposable( ) Column( modifier = Modifier - .background(/*if(gridActive) Color.Cyan.copy(alpha = 0.5f) else col*/animatedColor) + .background(animatedColor) ) { if (TimeUnitObject._hasMarker) { val lineHeight = 20.sp @@ -841,6 +843,25 @@ fun LazyVerticalGridTUO( val template = oneTUO.pTemplate.template val expectedTemplate = template.count { it.isLetter() } == 2 && template.contains(".") + val fileC = sharedScreenModel.fileContent.value ?: "" + val lines = fileC.split("\n").toMutableList() + val u0Idx = lines.indexOfFirst { it.startsWith("U0:") } + val t0Idx = lines.indexOfFirst { it.startsWith("T0:") } + + val fullLine = when { + t0Idx != -1 -> { + lines[t0Idx] + } + u0Idx != -1 -> { + lines[u0Idx] + } + else -> "" + } + val afterU0T0 = fullLine.substringAfter("0:") + val blankPrefixRegex =Regex("""z([0-9A-Z])[:]""") + val match = blankPrefixRegex.find(afterU0T0) + val blankPrefix = match?.groups?.get(1)?.value ?: "" + val editState = TUOEditState( tuoIndex = oneTUO.firstTuoIndex, notesByVoice = (0..3).associate { i -> @@ -855,7 +876,8 @@ fun LazyVerticalGridTUO( marker = listOfNotNull( oneTUO.pTemplate.markerToString().takeIf { it.isNotBlank() }, oneTUO.hasHairPin()?.toString()?.takeIf { it.isNotBlank() } - ).joinToString(" ") + ).joinToString(" "), + silentDuration = blankPrefix ) TUODetailDialog( editState = editState, @@ -1274,12 +1296,16 @@ fun EditorActionButtons(isUndoVisible: Boolean, onUndo: () -> Unit, onBuild: () } fun autoFixNote(rawNote: String, template: String): String { - println("ça on a t $template => $rawNote") +// println("\n==================================================") +// println(" ENTRÉE autoFixNote") +// println(" • Template : \"$template\"") +// println(" • RawNote : \"$rawNote\"") +// println("==================================================") if (rawNote == "_") return rawNote val noteRegex = Regex("([drmfsltDRFSTw][ia]?|―)[0-9'¹²³⁴₁₂₃₄,]*") val actualNotes = if (rawNote.isEmpty()) emptyList() else noteRegex.findAll(rawNote).map { it.value }.toList() - +// println(" • Notes détectées dans RawNote : $actualNotes (Taille: ${actualNotes.size})") /*if (rawNote.contains("•") || rawNote.contains("―") || rawNote.contains(",")) { val templateNotesCount = template.count { c -> c in 'A'..'Y' || c in 'a'..'y' @@ -1289,24 +1315,32 @@ fun autoFixNote(rawNote: String, template: String): String { val requiredNotesCount = template.count { it in 'A'..'Y' || it in 'a'..'y' } val shouldIncludeParentheses = actualNotes.size >= requiredNotesCount - +// println(" • Notes requises par le template : $requiredNotesCount") +// println(" • Inclusion des parenthèses autorisée ? : $shouldIncludeParentheses") +// println("--------------------------------------------------") val result = StringBuilder() var noteIdx = 0 for (i in template.indices) { val char = template[i] +// print("[Étape $i] Caractère template = '$char' | Index note actuel = $noteIdx -> ") when { (char in 'A'..'Y' || char in 'a'..'y') -> { if (noteIdx < actualNotes.size) { val noteToAdd = actualNotes[noteIdx] if (noteIdx > 0 && result.isNotEmpty()) { val lastChar = result.last() - if (lastChar != '•' && lastChar != ',' && lastChar != '(' && lastChar != ' ') { +// println() +// println(" [INFO ESPACE] lastC '$lastChar' avant d'ajouter la note \"$noteToAdd\"") + /* if (lastChar != '•' && lastChar != ',' && lastChar != '(' && lastChar != ')') { result.append(" ") - } + } */ +// println(" [INFO ESPACE] Maintenant res est \"${result.toString()}\"") +// print(" -> Continuité Étape $i : ") } result.append(noteToAdd) +// println("Action : Ajout de la note utilisateur \"$noteToAdd\"") noteIdx++ } else { /*if (rawNote.isEmpty()) {*/ @@ -1314,35 +1348,78 @@ fun autoFixNote(rawNote: String, template: String): String { /*} else { result.append("―") }*/ +// println("Action : Plus de notes utilisateur ! Ajout du silence par défaut \"z\"") } } char == '-' -> { if (noteIdx < actualNotes.size && actualNotes[noteIdx] == "―") { result.append("―") +// println("Action : Consommation et ajout du tiret utilisateur \"―\"") noteIdx++ } else { val prev = if (i > 0) template[i - 1] else null val next = if (i < template.lastIndex) template[i + 1] else null - if (prev == '.' && next == ',') result.append(" ") else result.append("―") + if (prev == '.' && next == ',') { + result.append(" ") +// println("Action : Structure .-, détectée -> Ajout d'un espace \" \"") + } else { + result.append("―") +// println("Action : Ajout du tiret automatique \"―\"") + } } } - char == 'z' -> result.append(" ") + char == 'z' -> { + result.append(" ") +// println("Action : Caractère 'z' du template -> Ajout d'un espace \" \"") + } char == '.' -> { val currentText = result.toString() - if (!currentText.endsWith("•")) result.append("•") + if (!currentText.endsWith("•")) { + result.append("•") +// println("Action : Ajout du point musical \"•\"") + } else { +// println("Action : Ignoré (se termine déjà par un point)") + } } char == ',' -> { val currentText = result.toString() - if (!currentText.endsWith(",")) result.append(",") + if (!currentText.endsWith(",")) { + result.append(",") +// println("Action : Ajout de la virgule \",\"") + } else { +// println("Action : Ignoré (se termine déjà par une virgule)") + } } char == '(' -> { - if (shouldIncludeParentheses) result.append("(") + if (shouldIncludeParentheses) { + result.append("(") +// println("Action : Ajout parenthèse ouvrante \"(\"") + } else { +// println("Action : Ignoré (pas assez de notes)") + } } char == ')' -> { - if (shouldIncludeParentheses) result.append(")") + if (shouldIncludeParentheses) { + result.append(")") +// println("Action : Ajout parenthèse fermante \")\"") + } else { +// println("Action : Ignoré (pas assez de notes)") + } + } + else -> { +// println("Action : Aucun comportement défini pour '$char'") } } +// println(" ↳ État actuel du buffer : \"${result.toString()}\"") } -// println("on a RES ${result.toString()}") - return result.toString() + var finalResult = result.toString() +// println(" • Avant formatage final : \"$finalResult\"") + + finalResult = finalResult + .replace("( ", "(") + .replace(" )", ")") + .replace("•,", "• ,") + +// println("on a RES ${finalResult}") + return finalResult } \ No newline at end of file