Hasinjato : FilePicker

This commit is contained in:
dotmg 2025-11-28 10:06:54 +01:00
parent dfbdb2da2c
commit bba28fd7c1
6 changed files with 272 additions and 103 deletions

View file

@ -0,0 +1,8 @@
package mg.dot.feufaro
import androidx.compose.runtime.Composable
expect fun launchFilePicker(
mimeTypes: Array<String>,
onFileSelected: (path: String?) -> Unit
): Unit

View file

@ -61,7 +61,9 @@ object ScreenSolfa : Screen {
val gridTUOData = GridTUOData(measure, tuoList, stanza) val gridTUOData = GridTUOData(measure, tuoList, stanza)
val coroutineScope = rememberCoroutineScope() val coroutineScope = rememberCoroutineScope()
val scrollState = rememberScrollState() val scrollState = rememberScrollState()
Box(Modifier.fillMaxSize()
Box(
Modifier.fillMaxSize()
) { ) {
Column( Column(
Modifier Modifier
@ -122,6 +124,10 @@ object ScreenSolfa : Screen {
gridWidthPx = gridWidthPx, gridWidthPx = gridWidthPx,
onGridWidthMeasured = { width -> gridWidthPx = width } onGridWidthMeasured = { width -> gridWidthPx = width }
) )
FlowRow(
horizontalArrangement = Arrangement.spacedBy(16.dp),
verticalArrangement = Arrangement.spacedBy(4.dp)
) {
MGButton(onClick = { MGButton(onClick = {
//showContent = !showContent //showContent = !showContent
@ -130,6 +136,24 @@ object ScreenSolfa : Screen {
val nextLabel: String by sharedScreenModel.nextLabel.collectAsState() val nextLabel: String by sharedScreenModel.nextLabel.collectAsState()
Text(nextLabel) Text(nextLabel)
} }
MGButton(onClick = {
/*println("Load btn clicked")
launchFilePicker(
mimeTypes = arrayOf("text/plain"),
onFileSelected = { path ->
if (path != null) {
println("fichier $path");
} else {
println("Pas de dichier")
}
}
)*/
solfaScreenModel.loadCustomFile()
}) {
val loadFile: String by sharedScreenModel.loadFile.collectAsState()
Text(loadFile)
}
}
FlowRow( FlowRow(
modifier = Modifier.fillMaxWidth() modifier = Modifier.fillMaxWidth()
.windowInsetsPadding(WindowInsets.safeDrawing) .windowInsetsPadding(WindowInsets.safeDrawing)
@ -168,6 +192,7 @@ object ScreenSolfa : Screen {
) )
} }
} }
@Throws(ObjectStreamException::class) // C'est une méthode de sérialisation Java, donc l'exception est nécessaire @Throws(ObjectStreamException::class) // C'est une méthode de sérialisation Java, donc l'exception est nécessaire
private fun readResolve(): Any { private fun readResolve(): Any {
return this // Toujours retourner l'instance unique de ce singleton return this // Toujours retourner l'instance unique de ce singleton

View file

@ -6,6 +6,7 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import mg.dot.feufaro.FileRepository import mg.dot.feufaro.FileRepository
import mg.dot.feufaro.launchFilePicker
import mg.dot.feufaro.midi.MidiPitch import mg.dot.feufaro.midi.MidiPitch
import mg.dot.feufaro.midi.MidiWriterKotlin import mg.dot.feufaro.midi.MidiWriterKotlin
@ -23,6 +24,7 @@ class Solfa(val sharedScreenModel: SharedScreenModel, private val fileRepository
private var refrainBeginsAt = -1 private var refrainBeginsAt = -1
private var smartLyricsType = "L" private var smartLyricsType = "L"
private val pitches = mutableListOf<MidiPitch>() private val pitches = mutableListOf<MidiPitch>()
companion object { companion object {
var currentFile = "" var currentFile = ""
val REGEX_SHIFT_PAREN_UPWARD = Regex("([^a-yA-Y\\(\\[]+)([\\)\\]])") val REGEX_SHIFT_PAREN_UPWARD = Regex("([^a-yA-Y\\(\\[]+)([\\)\\]])")
@ -36,7 +38,8 @@ class Solfa(val sharedScreenModel: SharedScreenModel, private val fileRepository
val REGEX_PARSE_META = Regex("\\|(?=[a-z]:)") val REGEX_PARSE_META = Regex("\\|(?=[a-z]:)")
val REGEX_LYRICS_COMMENT = Regex("\\$\\{([^\\}][^:]*:([^\\}]*))\\}|\\$\\{R!\\}") val REGEX_LYRICS_COMMENT = Regex("\\$\\{([^\\}][^:]*:([^\\}]*))\\}|\\$\\{R!\\}")
val REGEX_LYRICS_REPETITION = Regex("_(\\d+)") val REGEX_LYRICS_REPETITION = Regex("_(\\d+)")
val REGEX_VOWELS_STAGE1 = Regex("[aeiouyòàéìỳ](?![,;\\.\\-:!\\?\\}»_\"]*([ aeiouyòàéìỳ/]|_[1-9]))", RegexOption.IGNORE_CASE) val REGEX_VOWELS_STAGE1 =
Regex("[aeiouyòàéìỳ](?![,;\\.\\-:!\\?\\}»_\"]*([ aeiouyòàéìỳ/]|_[1-9]))", RegexOption.IGNORE_CASE)
val REGEX_VOWELS_STAGE2 = Regex("(?<=[aeiouyòàéìỳ])_([,;\\.\\-:!\\?\\}»_\"]+)", RegexOption.IGNORE_CASE) val REGEX_VOWELS_STAGE2 = Regex("(?<=[aeiouyòàéìỳ])_([,;\\.\\-:!\\?\\}»_\"]+)", RegexOption.IGNORE_CASE)
val REGEX_VOWELS_STAGE3 = Regex("_([\\?\\!:,;\\\\)]+|(\\$\\{[^\\}]*\\})+)") val REGEX_VOWELS_STAGE3 = Regex("_([\\?\\!:,;\\\\)]+|(\\$\\{[^\\}]*\\})+)")
val REGEX_MALAGASY_MN = Regex("([aeio])_([nm])([tdjkbp])") val REGEX_MALAGASY_MN = Regex("([aeio])_([nm])([tdjkbp])")
@ -45,6 +48,7 @@ class Solfa(val sharedScreenModel: SharedScreenModel, private val fileRepository
val REGEX_COMMENT = Regex("\\$\\{[^\\}]*\\}") val REGEX_COMMENT = Regex("\\$\\{[^\\}]*\\}")
val REGEX_STRIP_DC = Regex("\\$\\{D[^:]*:[^\\}]*\\}") val REGEX_STRIP_DC = Regex("\\$\\{D[^:]*:[^\\}]*\\}")
} }
var nextTIndex: Int = -1 var nextTIndex: Int = -1
var nextNIndex: Int = -1 var nextNIndex: Int = -1
var nextLIndex: Int = -1 var nextLIndex: Int = -1
@ -75,7 +79,8 @@ class Solfa(val sharedScreenModel: SharedScreenModel, private val fileRepository
templateCharArray.forEach { // D.-R templateCharArray.forEach { // D.-R
when (it) { when (it) {
'D', 'R', 'M', 'F', 'S', 'L', 'T', 'D', 'R', 'M', 'F', 'S', 'L', 'T',
'd', 'r', 'm', 'f', 's', 'l', 't', -> { 'd', 'r', 'm', 'f', 's', 'l', 't',
-> {
nextNIndex++ nextNIndex++
if (!inGroup) { if (!inGroup) {
nextLIndex++ nextLIndex++
@ -83,18 +88,22 @@ class Solfa(val sharedScreenModel: SharedScreenModel, private val fileRepository
unitObject.addLyrics(L[nextLIndex]) unitObject.addLyrics(L[nextLIndex])
} }
} }
N.forEachIndexed { voiceNumber, pOneVoiceNote -> run { N.forEachIndexed { voiceNumber, pOneVoiceNote ->
run {
unitObject.addNote(voiceNumber, pOneVoiceNote.oneVoiceNote, nextNIndex, nextMarker) unitObject.addNote(voiceNumber, pOneVoiceNote.oneVoiceNote, nextNIndex, nextMarker)
} }
} }
nextMarker = '0' nextMarker = '0'
} }
'.', ',', ->
'.', ',' ->
unitObject.addMarker(it) unitObject.addMarker(it)
'-', 'Z', 'z', 'w' -> '-', 'Z', 'z', 'w' ->
N.indices.forEach { voiceNumber -> N.indices.forEach { voiceNumber ->
unitObject.addBlank(voiceNumber, it) unitObject.addBlank(voiceNumber, it)
} }
'(', '[' -> { '(', '[' -> {
nextLIndex++ nextLIndex++
if (L.size > nextLIndex) { if (L.size > nextLIndex) {
@ -102,6 +111,7 @@ class Solfa(val sharedScreenModel: SharedScreenModel, private val fileRepository
} }
inGroup = true inGroup = true
} }
')', ']' -> { ')', ']' -> {
inGroup = false inGroup = false
} }
@ -112,11 +122,13 @@ class Solfa(val sharedScreenModel: SharedScreenModel, private val fileRepository
} }
nextTimeUnitObject() nextTimeUnitObject()
} }
fun loadSolfa() { fun loadSolfa() {
val sourceFileName: String = sharedScreenModel.currentPlayed() val sourceFileName: String = sharedScreenModel.currentPlayed()
sharedScreenModel.reset() sharedScreenModel.reset()
parse(sourceFileName) parse(sourceFileName)
} }
fun loadNextInPlaylist() { fun loadNextInPlaylist() {
val playlist = sharedScreenModel.playlist.value val playlist = sharedScreenModel.playlist.value
if (playlist.isNotEmpty()) { if (playlist.isNotEmpty()) {
@ -124,6 +136,24 @@ class Solfa(val sharedScreenModel: SharedScreenModel, private val fileRepository
loadSolfa() loadSolfa()
} }
} }
fun loadFile() {
println("Load btn clicked")
launchFilePicker(
mimeTypes = arrayOf("text/plain"),
onFileSelected = { path ->
if (path != null) {
println("fichier $path");
sharedScreenModel.reset()
parse(path)
//loadSolfa()
} else {
println("Pas de dichier")
}
}
)
}
fun parse(sourceFile: String) { fun parse(sourceFile: String) {
currentFile = sourceFile currentFile = sourceFile
val parseScope = CoroutineScope(Dispatchers.Default) val parseScope = CoroutineScope(Dispatchers.Default)
@ -173,6 +203,7 @@ class Solfa(val sharedScreenModel: SharedScreenModel, private val fileRepository
} }
} }
private fun rearrangeNote(noteString: String, infiniteIter: Int = 0): String { private fun rearrangeNote(noteString: String, infiniteIter: Int = 0): String {
var result: String = noteString var result: String = noteString
result = result.replace(REGEX_PAREN_RECURSIVE, "$1$2") result = result.replace(REGEX_PAREN_RECURSIVE, "$1$2")
@ -192,11 +223,11 @@ class Solfa(val sharedScreenModel: SharedScreenModel, private val fileRepository
} }
return result return result
} }
private fun preloadN(noteLine: String) { private fun preloadN(noteLine: String) {
val voiceNumber = noteLine.substring(0, 1).toInt() val voiceNumber = noteLine.substring(0, 1).toInt()
val templateComments = mutableListOf<String>() val templateComments = mutableListOf<String>()
val templateStripped = REGEX_TEMPLATE_COMMENT.replace(templateString) { val templateStripped = REGEX_TEMPLATE_COMMENT.replace(templateString) { matchResult ->
matchResult ->
templateComments.add(matchResult.value) templateComments.add(matchResult.value)
"&" "&"
} }
@ -257,7 +288,8 @@ class Solfa(val sharedScreenModel: SharedScreenModel, private val fileRepository
templateCharArray.forEach { templateCharArray.forEach {
when (it) { when (it) {
'D', 'R', 'M', 'F', 'S', 'L', 'T', 'D', 'R', 'M', 'F', 'S', 'L', 'T',
'd', 'r', 'm', 'f', 's', 'l', 't', -> { 'd', 'r', 'm', 'f', 's', 'l', 't',
-> {
if (firstInLoop) { if (firstInLoop) {
replacement = if (noteCharIterator.hasNext()) noteCharIterator.next() else 'd' replacement = if (noteCharIterator.hasNext()) noteCharIterator.next() else 'd'
firstInLoop = false firstInLoop = false
@ -269,12 +301,16 @@ class Solfa(val sharedScreenModel: SharedScreenModel, private val fileRepository
for (repetition in 0 until 6) { for (repetition in 0 until 6) {
result += replacement result += replacement
replacement = if (noteCharIterator.hasNext()) noteCharIterator.next() else 'd' replacement = if (noteCharIterator.hasNext()) noteCharIterator.next() else 'd'
if (replacement in setOf('d', 'r', 'm', 'f', 's', 'l', 't' , 'z', '(', if (replacement in setOf(
'D', 'R', 'M', 'F', 'S', 'L', 'T', '-', 'w', 'Z')) { 'd', 'r', 'm', 'f', 's', 'l', 't', 'z', '(',
'D', 'R', 'M', 'F', 'S', 'L', 'T', '-', 'w', 'Z'
)
) {
break break
} }
} }
} }
'w', '-' -> result += "~" 'w', '-' -> result += "~"
'z', 'Z' -> result += "@" 'z', 'Z' -> result += "@"
',' -> result += ";" ',' -> result += ";"
@ -285,6 +321,7 @@ class Solfa(val sharedScreenModel: SharedScreenModel, private val fileRepository
result = rearrangeNote(result) result = rearrangeNote(result)
loadN(voiceNumber, result, templateComments) loadN(voiceNumber, result, templateComments)
} }
private suspend fun parseOneLine(line: String) { private suspend fun parseOneLine(line: String) {
val index: Int val index: Int
val value: String val value: String
@ -305,27 +342,36 @@ class Solfa(val sharedScreenModel: SharedScreenModel, private val fileRepository
"T" -> if (index == 0) { "T" -> if (index == 0) {
loadT(value) loadT(value)
} }
"N" -> "N" ->
// Les lignes de type N seront parsées à la fin. preloadN() se chargera d'abord de réarranger la ligne // Les lignes de type N seront parsées à la fin. preloadN() se chargera d'abord de réarranger la ligne
// pour bien gérer les parenthèses. // pour bien gérer les parenthèses.
unparsedNote.add(index.toString() + value) unparsedNote.add(index.toString() + value)
"M" -> "M" ->
loadM(value) loadM(value)
"L" -> "L" ->
loadL(index, value) loadL(index, value)
"Y" -> "Y" ->
loadY(index, value) loadY(index, value)
"E" -> "E" ->
loadE(index, value) loadE(index, value)
"U" -> "U" ->
loadU(value) loadU(value)
"O" -> "O" ->
loadO(index, value) loadO(index, value)
"I" -> "I" ->
loadI(value) loadI(value)
} }
} }
suspend fun loadI(line: String) { suspend fun loadI(line: String) {
val lineSplit = line.split(":") val lineSplit = line.split(":")
val lastSlashInFilename = currentFile.lastIndexOf('/') val lastSlashInFilename = currentFile.lastIndexOf('/')
@ -353,6 +399,7 @@ class Solfa(val sharedScreenModel: SharedScreenModel, private val fileRepository
} }
} }
private fun loadM(value: String) { private fun loadM(value: String) {
val metaChunks: List<String> = value.split(REGEX_PARSE_META) val metaChunks: List<String> = value.split(REGEX_PARSE_META)
metaChunks.forEach { parseMeta(it) } metaChunks.forEach { parseMeta(it) }
@ -363,6 +410,7 @@ class Solfa(val sharedScreenModel: SharedScreenModel, private val fileRepository
sharedScreenModel.setSongComposer(meta["h"] ?: "") sharedScreenModel.setSongComposer(meta["h"] ?: "")
sharedScreenModel.setSongRhythm(meta["r"] ?: "") sharedScreenModel.setSongRhythm(meta["r"] ?: "")
} }
private fun loadN(voiceNumber: Int, line: String, templateComments: MutableList<String>) { private fun loadN(voiceNumber: Int, line: String, templateComments: MutableList<String>) {
var midiDuration = 0 var midiDuration = 0
var nextDuration = 0 var nextDuration = 0
@ -421,11 +469,13 @@ class Solfa(val sharedScreenModel: SharedScreenModel, private val fileRepository
lineChar.forEach { lineChar.forEach {
when (it) { when (it) {
'|', ':', '!', '/' -> { '|', ':', '!', '/' -> {
if (lastIt == '|' || lastIt == ':' || lastIt == '!' || lastIt == '/') {} else { if (lastIt == '|' || lastIt == ':' || lastIt == '!' || lastIt == '/') {
} else {
midiDuration += nextDuration midiDuration += nextDuration
nextDuration = 60 nextDuration = 60
} }
} }
'.' -> { '.' -> {
if (lastIt == ';') { if (lastIt == ';') {
pushMidi() pushMidi()
@ -436,6 +486,7 @@ class Solfa(val sharedScreenModel: SharedScreenModel, private val fileRepository
midiDuration += nextDuration - 30 midiDuration += nextDuration - 30
nextDuration = 30 nextDuration = 30
} }
';' -> { ';' -> {
if (lastIt == '.') { if (lastIt == '.') {
pushMidi() pushMidi()
@ -451,10 +502,12 @@ class Solfa(val sharedScreenModel: SharedScreenModel, private val fileRepository
nextDuration = 15 nextDuration = 15
} }
} }
'\'', ',' -> { '\'', ',' -> {
newN.appendLastNote(it) newN.appendLastNote(it)
lastNoteString += it lastNoteString += it
} }
'(', '[' -> newN.addNextMarker(it) '(', '[' -> newN.addNextMarker(it)
')', ']' -> newN.appendLastMarker(it) ')', ']' -> newN.appendLastMarker(it)
'd', 'r', 'm', 'f', 's', 'l', 't', 'd', 'r', 'm', 'f', 's', 'l', 't',
@ -474,6 +527,7 @@ class Solfa(val sharedScreenModel: SharedScreenModel, private val fileRepository
midiDuration += nextDuration midiDuration += nextDuration
nextDuration = 0 nextDuration = 0
} }
"z", "@" -> { "z", "@" -> {
if (lastNoteString == "z") { if (lastNoteString == "z") {
midiDuration += nextDuration midiDuration += nextDuration
@ -484,6 +538,7 @@ class Solfa(val sharedScreenModel: SharedScreenModel, private val fileRepository
} }
lastNoteString = "z" lastNoteString = "z"
} }
"'", "(", ")", "[", "]", "," -> {} "'", "(", ")", "[", "]", "," -> {}
else -> { else -> {
pushMidi() pushMidi()
@ -491,6 +546,7 @@ class Solfa(val sharedScreenModel: SharedScreenModel, private val fileRepository
} }
} }
} }
'@' -> { '@' -> {
if (lastNoteString == "z") { if (lastNoteString == "z") {
midiDuration += nextDuration midiDuration += nextDuration
@ -499,8 +555,10 @@ class Solfa(val sharedScreenModel: SharedScreenModel, private val fileRepository
} }
lastNoteString = "z" lastNoteString = "z"
} }
'i', 'a' -> 'i', 'a' ->
lastNoteString += it lastNoteString += it
'&' -> { '&' -> {
commentNumber++ commentNumber++
nextComment.add(templateComments[commentNumber]) nextComment.add(templateComments[commentNumber])
@ -516,6 +574,7 @@ class Solfa(val sharedScreenModel: SharedScreenModel, private val fileRepository
midiPitch.setNote("z", 0) midiPitch.setNote("z", 0)
pitches.add(midiPitch.copy()) pitches.add(midiPitch.copy())
} }
private fun loadT(line: String) { private fun loadT(line: String) {
templateString = line templateString = line
var nextTemplate = "" var nextTemplate = ""
@ -528,7 +587,8 @@ class Solfa(val sharedScreenModel: SharedScreenModel, private val fileRepository
if (tMarker != "") { if (tMarker != "") {
tMarker += it tMarker += it
if ((it == '}') if ((it == '}')
|| (tMarker.length == 2 && it != '{')) { || (tMarker.length == 2 && it != '{')
) {
TimeUnitObject.hasMarker(true) TimeUnitObject.hasMarker(true)
nextMarker.add(PMarkers(tMarker, offset - templateOffset)) nextMarker.add(PMarkers(tMarker, offset - templateOffset))
tMarker = "" tMarker = ""
@ -562,9 +622,11 @@ class Solfa(val sharedScreenModel: SharedScreenModel, private val fileRepository
val parsedULine = uObject.parsed() val parsedULine = uObject.parsed()
loadT(parsedULine) loadT(parsedULine)
} }
fun loadO(index: Int, line: String) { fun loadO(index: Int, line: String) {
O.getOrPut(index) { mutableListOf() }.add(line) O.getOrPut(index) { mutableListOf() }.add(line)
} }
fun parseMeta(line: String) { fun parseMeta(line: String) {
/* $_a_keyAbbrev = array( /* $_a_keyAbbrev = array(
'a' => 'author', 'a' => 'author',
@ -610,6 +672,7 @@ class Solfa(val sharedScreenModel: SharedScreenModel, private val fileRepository
.replace("--_", "-_") .replace("--_", "-_")
.replace(Regex("_$"), "") .replace(Regex("_$"), "")
} }
private fun unpackLyrics(lyrics: String): String { private fun unpackLyrics(lyrics: String): String {
val comments = REGEX_COMMENT.findAll(lyrics) val comments = REGEX_COMMENT.findAll(lyrics)
val commentsIterator = comments.iterator() val commentsIterator = comments.iterator()
@ -624,16 +687,19 @@ class Solfa(val sharedScreenModel: SharedScreenModel, private val fileRepository
} }
return lyricsFinal return lyricsFinal
} }
private fun loadL(stanzaNumber: Int, lyrics: String) { private fun loadL(stanzaNumber: Int, lyrics: String) {
val unpackedLyrics = unpackLyrics(lyrics) val unpackedLyrics = unpackLyrics(lyrics)
loadLyrics(stanzaNumber, unpackedLyrics) loadLyrics(stanzaNumber, unpackedLyrics)
} }
fun setOverrideLyrics(stanzaNumber: Int, i: Int, voice: Int, text: String) { fun setOverrideLyrics(stanzaNumber: Int, i: Int, voice: Int, text: String) {
while (L.size <= i) { while (L.size <= i) {
L.add(POneStanzaLyrics()) L.add(POneStanzaLyrics())
} }
L[i].setAlternativeLyrics(stanzaNumber, voice, text) L[i].setAlternativeLyrics(stanzaNumber, voice, text)
} }
private fun loadLyrics(stanzaNumber: Int, lyrics: String) { private fun loadLyrics(stanzaNumber: Int, lyrics: String) {
val overrideIterator: MutableMap<Int, Iterator<String>> = mutableMapOf() val overrideIterator: MutableMap<Int, Iterator<String>> = mutableMapOf()
try { try {
@ -708,6 +774,7 @@ class Solfa(val sharedScreenModel: SharedScreenModel, private val fileRepository
} }
lyricsComment.clear() lyricsComment.clear()
} }
fun smartYLyrics(lyrics: String): String { fun smartYLyrics(lyrics: String): String {
// Les ${O:1} risquent de changer en ${O:_1}. Sauvegardons-les dans comments. // Les ${O:1} risquent de changer en ${O:_1}. Sauvegardons-les dans comments.
val comments = REGEX_COMMENT.findAll(lyrics) val comments = REGEX_COMMENT.findAll(lyrics)
@ -729,12 +796,14 @@ class Solfa(val sharedScreenModel: SharedScreenModel, private val fileRepository
} }
return unpackLyrics(lyricsFinal) return unpackLyrics(lyricsFinal)
} }
// loadY is a smart lyrics parser for Malagasy language // loadY is a smart lyrics parser for Malagasy language
private fun loadY(intKey: Int, lyrics: String) { private fun loadY(intKey: Int, lyrics: String) {
smartLyricsType = "Y" smartLyricsType = "Y"
val smartLyrics = smartYLyrics(lyrics) val smartLyrics = smartYLyrics(lyrics)
loadL(intKey, smartLyrics) loadL(intKey, smartLyrics)
} }
fun smartELyrics(lyrics: String): String { fun smartELyrics(lyrics: String): String {
val loadedLyrics = lyrics val loadedLyrics = lyrics
.replace(" ", " _") .replace(" ", " _")
@ -745,12 +814,14 @@ class Solfa(val sharedScreenModel: SharedScreenModel, private val fileRepository
//getLyricsComments(loadedLyrics) //getLyricsComments(loadedLyrics)
return unpackLyrics(loadedLyrics) return unpackLyrics(loadedLyrics)
} }
// loadE is a smart Lyrics parser for English language // loadE is a smart Lyrics parser for English language
private fun loadE(intKey: Int, lyrics: String) { private fun loadE(intKey: Int, lyrics: String) {
smartLyricsType = "E" smartLyricsType = "E"
val smartLyrics = smartELyrics(lyrics) val smartLyrics = smartELyrics(lyrics)
loadL(intKey, smartLyrics) loadL(intKey, smartLyrics)
} }
private fun getLyricsComments(lyrics: String): String { private fun getLyricsComments(lyrics: String): String {
val matchResult = REGEX_LYRICS_COMMENT.findAll(lyrics) val matchResult = REGEX_LYRICS_COMMENT.findAll(lyrics)
.map { it.value }.toList() .map { it.value }.toList()
@ -759,15 +830,18 @@ class Solfa(val sharedScreenModel: SharedScreenModel, private val fileRepository
} }
return REGEX_STRIP_DC.replace(lyrics, "") return REGEX_STRIP_DC.replace(lyrics, "")
} }
private fun defineRefrainFrom(tuoNumber: Int) { private fun defineRefrainFrom(tuoNumber: Int) {
refrainBeginsAt = tuoNumber refrainBeginsAt = tuoNumber
} }
private fun copyRefrainToStanza(stanzaNumber: Int) { private fun copyRefrainToStanza(stanzaNumber: Int) {
for (i in refrainBeginsAt..L.size - 1) { for (i in refrainBeginsAt..L.size - 1) {
L[i].setLyrics(stanzaNumber, L[i].getLyrics(1)) L[i].setLyrics(stanzaNumber, L[i].getLyrics(1))
L[i].copyAlternativeLyrics(1, stanzaNumber) L[i].copyAlternativeLyrics(1, stanzaNumber)
} }
} }
private fun addLyricsItem(stanzaNumber: Int, i: Int, lyricsItem: String) { private fun addLyricsItem(stanzaNumber: Int, i: Int, lyricsItem: String) {
while (L.size <= i) { while (L.size <= i) {
L.add(POneStanzaLyrics()) L.add(POneStanzaLyrics())
@ -783,11 +857,13 @@ class Solfa(val sharedScreenModel: SharedScreenModel, private val fileRepository
} }
L[i].setLyrics(stanzaNumber, strippedLyrics) L[i].setLyrics(stanzaNumber, strippedLyrics)
} }
private fun appendLyricsItem(stanzaNumber: Int, i: Int, lyricsItem: String) { private fun appendLyricsItem(stanzaNumber: Int, i: Int, lyricsItem: String) {
if (L.size > i) { if (L.size > i) {
L[i].appendDSLyrics(stanzaNumber, lyricsItem) L[i].appendDSLyrics(stanzaNumber, lyricsItem)
} }
} }
fun MutableList<POneVoiceNote>.addWithPadding(index: Int, element: POneVoiceNote) { fun MutableList<POneVoiceNote>.addWithPadding(index: Int, element: POneVoiceNote) {
if (index < this.size) { if (index < this.size) {
// L'index existe déjà, on remplace l'élément. // L'index existe déjà, on remplace l'élément.

View file

@ -8,6 +8,10 @@ import mg.dot.feufaro.solfa.TimeUnitObject
class SharedScreenModel() : ScreenModel { class SharedScreenModel() : ScreenModel {
private val _nextLabel = MutableStateFlow<String>("Next ...") private val _nextLabel = MutableStateFlow<String>("Next ...")
val nextLabel: StateFlow<String> = _nextLabel.asStateFlow() val nextLabel: StateFlow<String> = _nextLabel.asStateFlow()
private val _loadFile = MutableStateFlow<String>("Load ...")
val loadFile: StateFlow<String> = _loadFile.asStateFlow()
private val _measure = MutableStateFlow<String>("") private val _measure = MutableStateFlow<String>("")
val measure: StateFlow<String> = _measure.asStateFlow() val measure: StateFlow<String> = _measure.asStateFlow()
private val _songTitle = MutableStateFlow<String>("") private val _songTitle = MutableStateFlow<String>("")
@ -43,24 +47,31 @@ class SharedScreenModel() : ScreenModel {
fun appendData(otherData: String) { fun appendData(otherData: String) {
_nextLabel.value += otherData _nextLabel.value += otherData
} }
fun reset() { fun reset() {
tempTimeUnitObjectList.clear() tempTimeUnitObjectList.clear()
} }
fun lastTUO(): TimeUnitObject? { fun lastTUO(): TimeUnitObject? {
return tempTimeUnitObjectList.lastOrNull() return tempTimeUnitObjectList.lastOrNull()
} }
fun addTUO(newTUO: TimeUnitObject) { fun addTUO(newTUO: TimeUnitObject) {
tempTimeUnitObjectList.add(newTUO) tempTimeUnitObjectList.add(newTUO)
} }
fun doneTUOList() { fun doneTUOList() {
_tuoList.value = tempTimeUnitObjectList.toList() _tuoList.value = tempTimeUnitObjectList.toList()
} }
fun setMeasure(theMeasure: String) { fun setMeasure(theMeasure: String) {
_measure.value = theMeasure _measure.value = theMeasure
} }
fun setSongTitle(theTitle: String) { fun setSongTitle(theTitle: String) {
_songTitle.value = theTitle _songTitle.value = theTitle
} }
fun setStanza(theStanza: Int) { fun setStanza(theStanza: Int) {
try { try {
_stanza.value = theStanza _stanza.value = theStanza
@ -68,39 +79,50 @@ class SharedScreenModel() : ScreenModel {
_stanza.value = 0 _stanza.value = 0
} }
} }
fun setSongKey(theSongKey: String) { fun setSongKey(theSongKey: String) {
_songKey.value = theSongKey _songKey.value = theSongKey
} }
fun setTransposeTo(key: String) { fun setTransposeTo(key: String) {
_transposeTo.value = key _transposeTo.value = key
} }
fun setTransposeAsIf(key: String) { fun setTransposeAsIf(key: String) {
_transposeAsIf.value = key _transposeAsIf.value = key
} }
fun setSongAuthor(theSongAuthor: String) { fun setSongAuthor(theSongAuthor: String) {
_songAuthor.value = theSongAuthor _songAuthor.value = theSongAuthor
} }
fun setSongComposer(theSongComposer: String) { fun setSongComposer(theSongComposer: String) {
_songComposer.value = theSongComposer _songComposer.value = theSongComposer
} }
fun setSongRhythm(theSongRhythm: String) { fun setSongRhythm(theSongRhythm: String) {
_songRhythm.value = theSongRhythm _songRhythm.value = theSongRhythm
} }
fun setNbStanzas(nbStanzas: Int) { fun setNbStanzas(nbStanzas: Int) {
_nbStanzas.value = nbStanzas _nbStanzas.value = nbStanzas
} }
fun setPlaylist(thePlaylist: List<String>) { fun setPlaylist(thePlaylist: List<String>) {
_playlist.value = thePlaylist _playlist.value = thePlaylist
} }
fun setHasMarker(theHasMarker: Boolean) { fun setHasMarker(theHasMarker: Boolean) {
_hasMarker.value = theHasMarker _hasMarker.value = theHasMarker
} }
fun playNext() { fun playNext() {
val nextIndex = (_nextPlayed.value + 1) % _playlist.value.size val nextIndex = (_nextPlayed.value + 1) % _playlist.value.size
_nextPlayed.value = nextIndex _nextPlayed.value = nextIndex
setStanza(1) setStanza(1)
} }
fun currentPlayed(): String { fun currentPlayed(): String {
val playlistIndex = _nextPlayed.value val playlistIndex = _nextPlayed.value
return _playlist.value[playlistIndex] return _playlist.value[playlistIndex]

View file

@ -7,10 +7,16 @@ class SolfaScreenModel (
private val solfa: Solfa private val solfa: Solfa
) : ScreenModel { ) : ScreenModel {
init {} init {}
fun loadNextInPlaylist() { fun loadNextInPlaylist() {
solfa.loadNextInPlaylist() solfa.loadNextInPlaylist()
} }
fun reload() { fun reload() {
solfa.loadSolfa() solfa.loadSolfa()
} }
fun loadCustomFile() {
solfa.loadFile()
}
} }

View file

@ -0,0 +1,32 @@
package mg.dot.feufaro
import javax.swing.JFileChooser
import javax.swing.filechooser.FileNameExtensionFilter
import java.awt.EventQueue
actual fun launchFilePicker(
mimeTypes: Array<String>,
onFileSelected: (path: String?) -> Unit
) {
EventQueue.invokeLater {
val fileChooser = JFileChooser()
if (mimeTypes.isNotEmpty()) {
val filter = FileNameExtensionFilter(
"Fichiers texte",
"txt"
)
fileChooser.fileFilter = filter
}
fileChooser.dialogTitle = "Sélectionnez votre fichier personnalisé"
val result = fileChooser.showOpenDialog(null)
if (result == JFileChooser.APPROVE_OPTION) {
onFileSelected(fileChooser.selectedFile.absolutePath)
} else {
onFileSelected(null)
}
}
}