508 lines
19 KiB
Kotlin
508 lines
19 KiB
Kotlin
package main
|
|
|
|
import androidx.compose.foundation.VerticalScrollbar
|
|
import androidx.compose.foundation.layout.*
|
|
import androidx.compose.foundation.lazy.LazyColumn
|
|
import androidx.compose.foundation.lazy.items
|
|
import androidx.compose.foundation.lazy.rememberLazyListState
|
|
import androidx.compose.foundation.rememberScrollbarAdapter
|
|
import androidx.compose.material.*
|
|
import androidx.compose.material.icons.Icons
|
|
import androidx.compose.material.icons.filled.Delete
|
|
import androidx.compose.runtime.Composable
|
|
import androidx.compose.runtime.MutableState
|
|
import androidx.compose.runtime.mutableStateOf
|
|
import androidx.compose.runtime.remember
|
|
import org.apache.poi.ss.usermodel.CellType
|
|
import org.apache.poi.ss.usermodel.Workbook
|
|
import org.apache.poi.xssf.usermodel.XSSFWorkbook
|
|
import java.awt.FileDialog
|
|
import java.awt.Frame
|
|
import java.io.FileInputStream
|
|
import java.io.FileWriter
|
|
import java.io.IOException
|
|
import java.io.File
|
|
import androidx.compose.runtime.*
|
|
import androidx.compose.ui.Alignment
|
|
import androidx.compose.ui.ExperimentalComposeUiApi
|
|
import androidx.compose.ui.Modifier
|
|
import androidx.compose.ui.graphics.Color
|
|
import androidx.compose.ui.input.key.*
|
|
import androidx.compose.ui.unit.dp
|
|
import main.component.dropdownButtonComponent
|
|
|
|
|
|
data class Glossary(val name: String, val jsonFilePath: String)
|
|
|
|
val frame = Frame()
|
|
|
|
@Composable
|
|
fun glossariesPage(
|
|
languageManager: LanguageManager,
|
|
currentPage: MutableState<String>,
|
|
onProjectClick: () -> Unit,
|
|
onCodeToVerifyClick: () -> Unit
|
|
) {
|
|
|
|
var glossaries by remember { mutableStateOf(emptyList<Glossary>()) }
|
|
var isCompareClicked by remember { mutableStateOf(false) }
|
|
val noFileSnackbarVisibleState = remember { mutableStateOf(false) }
|
|
|
|
Column(
|
|
modifier = Modifier.fillMaxSize(),
|
|
verticalArrangement = Arrangement.Center,
|
|
horizontalAlignment = Alignment.CenterHorizontally
|
|
) {
|
|
Text(languageManager.getSelectGlossaryText(), style = MaterialTheme.typography.h5)
|
|
|
|
Spacer(modifier = Modifier.height(16.dp))
|
|
|
|
Box(
|
|
modifier = Modifier
|
|
.height(300.dp)
|
|
.width(250.dp)
|
|
.padding(10.dp),
|
|
contentAlignment = Alignment.Center
|
|
) {
|
|
val scrollState = rememberLazyListState()
|
|
glossaries = loadGlossaries(appState.selectedProject!!)
|
|
if (glossaries.isEmpty()) {
|
|
Text(languageManager.getNoGlossaryText())
|
|
} else {
|
|
LazyColumn(
|
|
state = scrollState,
|
|
modifier = Modifier.padding(10.dp),
|
|
verticalArrangement = Arrangement.spacedBy(10.dp)
|
|
) {
|
|
println(glossaries)
|
|
items(glossaries) { glossary ->
|
|
Row(
|
|
modifier = Modifier.width(200.dp).fillMaxWidth(),
|
|
horizontalArrangement = Arrangement.SpaceBetween
|
|
) {
|
|
Button(
|
|
onClick = {
|
|
appState.selectedGlossary = glossary
|
|
currentPage.value = "glossaireDetail"
|
|
},
|
|
modifier = Modifier
|
|
.width(150.dp),
|
|
colors = ButtonDefaults.buttonColors(
|
|
backgroundColor = customRedColor,
|
|
contentColor = Color.White
|
|
)
|
|
) {
|
|
Text(glossary.name)
|
|
}
|
|
|
|
IconButton(
|
|
onClick = {
|
|
// Handle delete glossary action
|
|
glossaries = glossaries.filterNot { it == glossary }
|
|
val file =
|
|
File(glossaryPath + (appState.selectedProject?.name) + "/" + glossary.jsonFilePath)
|
|
file.delete()
|
|
}
|
|
) {
|
|
Icon(
|
|
imageVector = Icons.Default.Delete,
|
|
contentDescription = "Delete Glossary"
|
|
)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
VerticalScrollbar(
|
|
modifier = Modifier.align(Alignment.CenterEnd).fillMaxHeight(),
|
|
adapter = rememberScrollbarAdapter(scrollState)
|
|
)
|
|
}
|
|
|
|
Spacer(modifier = Modifier.height(16.dp))
|
|
// mettre un texte "ou"
|
|
Text(languageManager.getOuText(), style = MaterialTheme.typography.h5)
|
|
Spacer(modifier = Modifier.height(16.dp))
|
|
|
|
|
|
Button(
|
|
onClick = {
|
|
currentPage.value = "nouveauGlossaire"
|
|
},
|
|
modifier = Modifier
|
|
.width(300.dp),
|
|
colors = ButtonDefaults.buttonColors(
|
|
backgroundColor = customRedColor,
|
|
contentColor = Color.White
|
|
)
|
|
) {
|
|
Text(languageManager.getNewGlossaryText())
|
|
}
|
|
|
|
Spacer(modifier = Modifier.height(6.dp))
|
|
|
|
Button(
|
|
onClick = {
|
|
currentPage.value = "projects"
|
|
},
|
|
modifier = Modifier
|
|
.width(250.dp),
|
|
colors = ButtonDefaults.buttonColors(
|
|
backgroundColor = customRedColor,
|
|
contentColor = Color.White
|
|
)
|
|
) {
|
|
Text(languageManager.getBackText())
|
|
}
|
|
}
|
|
dropdownButtonComponent(
|
|
languageManager = languageManager,
|
|
items = listOf(languageManager.getGlossaryText(), languageManager.getCodeToVerifyText(), languageManager.getCompareText())
|
|
) { selectedOption ->
|
|
// Handle the selected option
|
|
when (selectedOption) {
|
|
languageManager.getGlossaryText() -> onProjectClick()
|
|
languageManager.getCodeToVerifyText() -> onCodeToVerifyClick()
|
|
languageManager.getCompareText() -> {
|
|
if (mostUsedWordList.isEmpty()) {
|
|
noFileSnackbarVisibleState.value = true
|
|
return@dropdownButtonComponent
|
|
} else {
|
|
isCompareClicked = true
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
@OptIn(ExperimentalStdlibApi::class, ExperimentalComposeUiApi::class)
|
|
@Composable
|
|
fun newGlossary(
|
|
languageManager: LanguageManager,
|
|
currentPage: MutableState<String>,
|
|
) {
|
|
val isEmptySnackbarVisibleState = remember { mutableStateOf(false) }
|
|
val containsSpaceSnackbarVisibleState = remember { mutableStateOf(false) }
|
|
val glossaryAlreadyExistsSnackbarVisibleState = remember { mutableStateOf(false) }
|
|
val invalidCharacterSnackbarVisibleState = remember { mutableStateOf(false) }
|
|
|
|
var glossaries by remember { mutableStateOf(emptyList<Glossary>()) }
|
|
|
|
var nouveauGlossaireName by remember { mutableStateOf("") }
|
|
|
|
Column(
|
|
modifier = Modifier.fillMaxSize(),
|
|
verticalArrangement = Arrangement.Center,
|
|
horizontalAlignment = Alignment.CenterHorizontally
|
|
) {
|
|
Text("Nom du glossaire", style = MaterialTheme.typography.h5)
|
|
|
|
Spacer(modifier = Modifier.height(16.dp))
|
|
|
|
TextField(
|
|
value = nouveauGlossaireName,
|
|
singleLine = true,
|
|
onValueChange = { nouveauGlossaireName = it },
|
|
label = { Text("Nom du glossaire") },
|
|
colors = TextFieldDefaults.textFieldColors(
|
|
focusedIndicatorColor = customRedColor,
|
|
unfocusedIndicatorColor = Color.Gray,
|
|
focusedLabelColor = customRedColor
|
|
),
|
|
modifier = Modifier
|
|
.width(300.dp)
|
|
.fillMaxWidth()
|
|
.onKeyEvent { event ->
|
|
if (event.key == Key.Enter && event.type == KeyEventType.KeyDown) {
|
|
when {
|
|
nouveauGlossaireName.isEmpty() -> {
|
|
println("Veuillez saisir un nom pour le nouveau glossaire")
|
|
isEmptySnackbarVisibleState.value = true
|
|
}
|
|
nouveauGlossaireName.contains(" ") -> {
|
|
println("Le nom du glossaire ne doit pas contenir d'espace")
|
|
containsSpaceSnackbarVisibleState.value = true
|
|
}
|
|
glossaries.any { it.name == nouveauGlossaireName } -> {
|
|
println("Le nom du glossaire existe déjà")
|
|
glossaryAlreadyExistsSnackbarVisibleState.value = true
|
|
}
|
|
!isValidFileName(nouveauGlossaireName) -> {
|
|
println("Le nom du glossaire contient des caractères non autorisés")
|
|
invalidCharacterSnackbarVisibleState.value = true
|
|
}
|
|
else -> {
|
|
val newGlossary = Glossary(nouveauGlossaireName, "$nouveauGlossaireName.json")
|
|
glossaries = glossaries + newGlossary
|
|
// create new json file
|
|
val newFile =
|
|
File(glossaryPath + (appState.selectedProject?.name) + "/" + newGlossary.jsonFilePath)
|
|
newFile.createNewFile()
|
|
// update glossaries list
|
|
glossaries = loadGlossaries(appState.selectedProject!!)
|
|
currentPage.value = "glossaires" // Revenir à la liste des glossaires
|
|
}
|
|
}
|
|
true
|
|
} else {
|
|
false
|
|
}
|
|
}
|
|
)
|
|
|
|
Button(
|
|
onClick = {
|
|
// Handle the Enter key event by calling the common function
|
|
when {
|
|
nouveauGlossaireName.isEmpty() -> {
|
|
println("Veuillez saisir un nom pour le nouveau glossaire")
|
|
isEmptySnackbarVisibleState.value = true
|
|
}
|
|
nouveauGlossaireName.contains(" ") -> {
|
|
println("Le nom du glossaire ne doit pas contenir d'espace")
|
|
containsSpaceSnackbarVisibleState.value = true
|
|
}
|
|
glossaries.any { it.name == nouveauGlossaireName } -> {
|
|
println("Le nom du glossaire existe déjà")
|
|
glossaryAlreadyExistsSnackbarVisibleState.value = true
|
|
}
|
|
!isValidFileName(nouveauGlossaireName) -> {
|
|
println("Le nom du glossaire contient des caractères non autorisés")
|
|
invalidCharacterSnackbarVisibleState.value = true
|
|
}
|
|
else -> {
|
|
val newGlossary = Glossary(nouveauGlossaireName, "$nouveauGlossaireName.json")
|
|
glossaries = glossaries + newGlossary
|
|
// create new json file
|
|
val newFile =
|
|
File(glossaryPath + (appState.selectedProject?.name) + "/" + newGlossary.jsonFilePath)
|
|
newFile.createNewFile()
|
|
// update glossaries list
|
|
glossaries = loadGlossaries(appState.selectedProject!!)
|
|
currentPage.value = "glossaires" // Revenir à la liste des glossaires
|
|
}
|
|
}
|
|
},
|
|
modifier = Modifier
|
|
.width(200.dp),
|
|
colors = ButtonDefaults.buttonColors(
|
|
backgroundColor = customRedColor,
|
|
contentColor = Color.White
|
|
)
|
|
) {
|
|
Text("Créer")
|
|
}
|
|
|
|
Button(
|
|
onClick = {
|
|
currentPage.value = "glossaires"
|
|
},
|
|
modifier = Modifier
|
|
.width(200.dp),
|
|
colors = ButtonDefaults.buttonColors(
|
|
backgroundColor = customRedColor,
|
|
contentColor = Color.White
|
|
)
|
|
) {
|
|
Text("Annuler")
|
|
}
|
|
}
|
|
}
|
|
|
|
fun selectFile(extensions: Set<String>, onFileSelected: (String) -> Unit) {
|
|
|
|
val fileDialog = FileDialog(frame, "Select a file", FileDialog.LOAD)
|
|
fileDialog.isMultipleMode = true // To enable selecting only one file
|
|
fileDialog.file = "*." + extensions.joinToString(";*.")
|
|
fileDialog.isVisible = true
|
|
|
|
val selectedFiles = fileDialog.files
|
|
|
|
if (selectedFiles != null) {
|
|
for (file in selectedFiles) {
|
|
println("Selected file: $file")
|
|
// Verify that the file extension is valid
|
|
val fileExtension = File(file.absolutePath).extension.lowercase()
|
|
if (extensions.contains(fileExtension)) {
|
|
println("Opening: $file")
|
|
onFileSelected(file.absolutePath)
|
|
} else {
|
|
println("Invalid file extension.")
|
|
}
|
|
}
|
|
} else {
|
|
println("Open command cancelled by user.")
|
|
}
|
|
frame.dispose()
|
|
}
|
|
|
|
fun exportToCSV(glossary: Glossary, csvFilePath: String) {
|
|
val glossary = loadDatasFromFile(glossary.jsonFilePath)
|
|
val csvContent = buildString {
|
|
appendLine("Mot;Description;Contexte principal;Contexte secondaire;Lié à;Synonyme;Antonyme;")
|
|
glossary.forEach { entry ->
|
|
appendLine(
|
|
"${entry.name};${entry.description};${entry.mainContext};" +
|
|
"${entry.secondaryContext};${entry.relatedTo};${entry.synonymous};${entry.antonym}"
|
|
)
|
|
}
|
|
}
|
|
try {
|
|
FileWriter(csvFilePath + ".csv").use { fileWriter ->
|
|
fileWriter.write(csvContent)
|
|
}
|
|
exportedSuccessfully.value = true
|
|
} catch (e: IOException) {
|
|
e.printStackTrace()
|
|
}
|
|
}
|
|
|
|
fun importFile(glossary: Glossary, filePath: String) {
|
|
val fileExtension = File(filePath).extension.lowercase()
|
|
|
|
when (fileExtension) {
|
|
"csv" -> {
|
|
importCSVFile(glossary, filePath)
|
|
}
|
|
|
|
"xlsx" -> {
|
|
importXLSXFile(glossary, filePath)
|
|
}
|
|
|
|
else -> {
|
|
println("Unsupported file format.")
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
fun importCSVFile(glossary : Glossary, filePath: String) {
|
|
val file = File(filePath).readText(Charsets.UTF_8)
|
|
|
|
val lines = file.split("\n")
|
|
val header = lines.first().split(";")
|
|
val dataLines = lines.drop(1)
|
|
|
|
dataLines.forEach { line ->
|
|
// Check if the line is not empty or blank
|
|
if (line.isNotBlank()) {
|
|
val values = line.replace("\r", "").replace("\n", "").split(";")
|
|
|
|
values.forEach { value ->
|
|
println(value)
|
|
}
|
|
val mot = header.zip(values).toMap().toMutableMap()
|
|
try {
|
|
val nouveauWord = Word(
|
|
name = mot["Mot"]!!,
|
|
description = mot["Description"]!!,
|
|
mainContext = mot["Contexte principal"]!!,
|
|
secondaryContext = mot["Contexte secondaire"]!!,
|
|
relatedTo = mot["Lié à"]!!,
|
|
synonymous = mot["Synonyme"]!!,
|
|
antonym = mot["Antonyme"]!!
|
|
)
|
|
|
|
if (mot["Antonyme"] == "\r") {
|
|
mot["Antonyme"] = ""
|
|
}
|
|
if (mot["Mot"] == null || mot["Mot"] == "") {
|
|
return
|
|
}
|
|
if (!verifyRequiredFields(mot)) {
|
|
return
|
|
}
|
|
addWordToGlossary(glossary.jsonFilePath, nouveauWord)
|
|
}catch(e : NullPointerException){
|
|
errorImportation.value = true
|
|
}
|
|
|
|
}
|
|
importedSuccessfully.value = true
|
|
}
|
|
|
|
}
|
|
|
|
|
|
private fun importXLSXFile(glossary: Glossary, cheminFichier: String) {
|
|
val workbook: Workbook
|
|
try {
|
|
FileInputStream(cheminFichier).use { fileInputStream ->
|
|
workbook = XSSFWorkbook(fileInputStream)
|
|
}
|
|
|
|
val sheet = workbook.getSheetAt(0) // Assuming the data is in the first sheet
|
|
|
|
val headerRow = sheet.getRow(0)
|
|
val header = mutableListOf<String>()
|
|
for (i in 0 until headerRow.lastCellNum) {
|
|
header.add(headerRow.getCell(i).stringCellValue)
|
|
}
|
|
|
|
val dataLines = mutableListOf<Map<String, String>>()
|
|
|
|
for (i in 1 until sheet.physicalNumberOfRows) {
|
|
val currentRow = sheet.getRow(i)
|
|
val data = mutableMapOf<String, String>()
|
|
|
|
for (j in 0 until currentRow.lastCellNum) {
|
|
val cell = currentRow.getCell(j)
|
|
val cellValue = when (cell.cellType) {
|
|
CellType.STRING -> cell.stringCellValue
|
|
CellType.NUMERIC -> cell.numericCellValue.toString()
|
|
else -> ""
|
|
}
|
|
data[header[j]] = cellValue
|
|
}
|
|
|
|
dataLines.add(data)
|
|
}
|
|
|
|
dataLines.forEach { line ->
|
|
// Check if the line is not empty or blank
|
|
if (line.isNotEmpty()) {
|
|
// Process each line as needed
|
|
val newWord = Word(
|
|
name = line["Mot"] ?: "",
|
|
description = line["Description"] ?: "",
|
|
mainContext = line["Contexte principal"] ?: "",
|
|
secondaryContext = line["Contexte secondaire"] ?: "",
|
|
relatedTo = line["Lié à"] ?: "",
|
|
synonymous = line["Synonyme"] ?: "",
|
|
antonym = line["Antonyme"] ?: ""
|
|
)
|
|
|
|
if (newWord.name.isNotBlank() && verifyRequiredFields(line)) {
|
|
addWordToGlossary(glossary.jsonFilePath, newWord)
|
|
}
|
|
}
|
|
}
|
|
} catch (e: IOException) {
|
|
e.printStackTrace()
|
|
}
|
|
}
|
|
|
|
fun verifyRequiredFields(mot: Map<String, String>): Boolean {
|
|
//Verify that the required fields are present
|
|
val requiredFields = listOf(
|
|
"Mot",
|
|
"Description",
|
|
"Contexte principal",
|
|
"Contexte secondaire",
|
|
"Lié à",
|
|
"Synonyme",
|
|
"Antonyme"
|
|
)
|
|
|
|
val motKeys = mot.keys.map { it.trim() }
|
|
|
|
motKeys.forEach { key ->
|
|
if (!requiredFields.contains(key)) {
|
|
println("Le champ $key n'est pas reconnu")
|
|
return false
|
|
}
|
|
}
|
|
|
|
return true
|
|
|
|
} |