diff --git a/ChangeLog.md b/ChangeLog.md index 9aecd840a6..cf310972bc 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -1,3 +1,20 @@ +# 1.5.8 +## What's Changed +* Fix the following issues by @mgrati in [#646](https://github.com/kopiLeft/Galite/pull/646) + * Do not lose database connection when refreshing a galite application navigator tab. + * Minimize the number of actors called by the same keyboard shortcuts. + * Fix the issue of the random crash observed in galite applications when trying to open a list dialog. +* Fix calendar picker component : Force applying the changes added by the date picker component to the galite field model by @sebai-dhia in [#645](https://github.com/kopiLeft/Galite/pull/645) + +**Full Changelog**: [1.5.7 ... 1.5.8](https://github.com/kopiLeft/Galite/compare/1.5.7...1.5.8) + +# 1.5.7 +## What's Changed +* Fix : Implement ImageHandler in Vaadin Flow to fix fatal error when loading an image list and show images on the list by @sebai-dhia in [#642](https://github.com/kopiLeft/Galite/pull/642) +* Fix abnormal behavior of the list button and menu query in the ProductForm by @sebai-dhia in [#644](https://github.com/kopiLeft/Galite/pull/644) + +**Full Changelog**: [1.5.6 ... 1.5.7](https://github.com/kopiLeft/Galite/compare/1.5.6...1.5.7) + # 1.5.6 ## What's Changed * Fix : Allow LEFT JOIN for nullable fields with non nullable fields in Galite forms by @mgrati in [#640](https://github.com/kopiLeft/Galite/pull/640) @@ -15,10 +32,10 @@ # 1.5.4 ## What's Changed -* Feat : Add color picker field to Galite : +* Feat : Add color picker field to Galite : * Add a new field type "Color" to Galite by @achraf-dridi in [#625](https://github.com/kopiLeft/Galite/pull/625) * Convert the value type of the color field to an integer instead of blob by @achraf-dridi in [#631](https://github.com/kopiLeft/Galite/pull/631) -* Fix : Fix the found bugs in the module "Factory Generator" of Galite Utils +* Fix : Fix the found bugs in the module "Factory Generator" of Galite Utils * Fix generated classes in the event when the xsd contains attributes that are named as one of the hard keywords of Kotlin by @achraf-dridi in [#629](https://github.com/kopiLeft/Galite/pull/629) * Avoid creating the schemaorg_apache_xmlbeans package containing copies of the *.xsd files by @yahiaoui97 [#628](https://github.com/kopiLeft/Galite/pull/628) * Add toCalendar method in the generated factory classes by @achraf-dridi [#627](https://github.com/kopiLeft/Galite/pull/627) diff --git a/build.gradle.kts b/build.gradle.kts index 69b7fe6176..90313ef354 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -21,7 +21,7 @@ import org.kopi.galite.gradle.configureMavenCentralPom import org.kopi.galite.gradle.signPublication plugins { - id("org.jetbrains.kotlin.jvm") version "1.9.0" apply false + id("org.jetbrains.kotlin.jvm") version "2.0.0" apply false id("maven-publish") id("io.github.gradle-nexus.publish-plugin") version "1.1.0" } @@ -82,7 +82,7 @@ allprojects { pom { configureMavenCentralPom(project) } - signPublication(project) +// signPublication(project) } } } diff --git a/buildSrc/src/main/kotlin/org/kopi/galite/gradle/Versions.kt b/buildSrc/src/main/kotlin/org/kopi/galite/gradle/Versions.kt index 63dbfed5e4..2cdbf33455 100644 --- a/buildSrc/src/main/kotlin/org/kopi/galite/gradle/Versions.kt +++ b/buildSrc/src/main/kotlin/org/kopi/galite/gradle/Versions.kt @@ -21,7 +21,7 @@ object Versions { const val KARIBU_TESTING = "1.3.23" const val ENHANCED_DIALOG = "23.1.2" - const val EXPOSED = "0.42.1" + const val EXPOSED = "0.54.0" const val HIKARI = "5.1.0" const val H2 = "1.4.199" const val POSTGRES_NG = "0.8.6" diff --git a/galite-core/src/main/java/org/kopi/vkopi/lib/ui/swing/visual/JImageHandler.java b/galite-core/src/main/java/org/kopi/vkopi/lib/ui/swing/visual/JImageHandler.java index ff34162fbe..da33d21080 100644 --- a/galite-core/src/main/java/org/kopi/vkopi/lib/ui/swing/visual/JImageHandler.java +++ b/galite-core/src/main/java/org/kopi/vkopi/lib/ui/swing/visual/JImageHandler.java @@ -102,14 +102,14 @@ public JImage(URL location) { /** * */ - public int getWidth() { + public int getImageWidth() { return getIconWidth(); } /** * */ - public int getHeight() { + public int getImageHeight() { return getIconHeight(); } diff --git a/galite-core/src/main/kotlin/org/kopi/galite/visual/Message.kt b/galite-core/src/main/kotlin/org/kopi/galite/visual/Message.kt index a7f80d34f9..bf5d1c9608 100644 --- a/galite-core/src/main/kotlin/org/kopi/galite/visual/Message.kt +++ b/galite-core/src/main/kotlin/org/kopi/galite/visual/Message.kt @@ -1,6 +1,6 @@ /* - * Copyright (c) 2013-2022 kopiLeft Services SARL, Tunis TN - * Copyright (c) 1990-2022 kopiRight Managed Solutions GmbH, Wien AT + * Copyright (c) 2013-2024 kopiLeft Services SARL, Tunis TN + * Copyright (c) 1990-2024 kopiRight Managed Solutions GmbH, Wien AT * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -62,7 +62,10 @@ object Message { fun getMessage(source: String = VISUAL_KOPI_MESSAGES_LOCALIZATION_RESOURCE, ident: String, params: Any? = null): String { - val params = if (params is Array<*>?) params as Array? else arrayOf(params) + val params = when (params) { + is Array<*> -> params.map { it }.toTypedArray() // Convert to Array + else -> arrayOf(params) + } val manager = if (ApplicationContext.isApplicationContextInitialized) { ApplicationContext.getLocalizationManager() diff --git a/galite-core/src/main/kotlin/org/kopi/galite/visual/MessageCode.kt b/galite-core/src/main/kotlin/org/kopi/galite/visual/MessageCode.kt index 967719a010..4a90c47019 100644 --- a/galite-core/src/main/kotlin/org/kopi/galite/visual/MessageCode.kt +++ b/galite-core/src/main/kotlin/org/kopi/galite/visual/MessageCode.kt @@ -1,6 +1,6 @@ /* - * Copyright (c) 2013-2022 kopiLeft Services SARL, Tunis TN - * Copyright (c) 1990-2022 kopiRight Managed Solutions GmbH, Wien AT + * Copyright (c) 2013-2024 kopiLeft Services SARL, Tunis TN + * Copyright (c) 1990-2024 kopiRight Managed Solutions GmbH, Wien AT * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -59,7 +59,10 @@ object MessageCode { @Suppress("UNCHECKED_CAST") @JvmOverloads fun getMessage(key: String, params: Any? = null, withKey: Boolean = true): String { - val params = if (params is Array<*>?) params as Array? else arrayOf(params) + val params = when (params) { + is Array<*> -> params.map { it }.toTypedArray() // Convert to Array + else -> arrayOf(params) + } if (!keyPattern.matcher(key).matches()) { throw InconsistencyException("Malformed message key '$key'") diff --git a/galite-core/src/main/kotlin/org/kopi/galite/visual/VDatabaseUtils.kt b/galite-core/src/main/kotlin/org/kopi/galite/visual/VDatabaseUtils.kt index 1f1c678565..56c4686467 100644 --- a/galite-core/src/main/kotlin/org/kopi/galite/visual/VDatabaseUtils.kt +++ b/galite-core/src/main/kotlin/org/kopi/galite/visual/VDatabaseUtils.kt @@ -1,6 +1,6 @@ /* - * Copyright (c) 2013-2022 kopiLeft Services SARL, Tunis TN - * Copyright (c) 1990-2022 kopiRight Managed Solutions GmbH, Wien AT + * Copyright (c) 2013-2024 kopiLeft Services SARL, Tunis TN + * Copyright (c) 1990-2024 kopiRight Managed Solutions GmbH, Wien AT * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -36,11 +36,11 @@ object VDatabaseUtils { fun checkForeignKeys_(id: Int, queryTable: Table){ transaction { - val query1 = References.slice( + val query1 = References.select( References.table, References.column, References.action - ).select { + ).where { References.reference eq queryTable.tableName }.orderBy(References.action to SortOrder.DESC) val action = query1.forEach { query1Row -> @@ -50,14 +50,14 @@ object VDatabaseUtils { } when (query1Row[References.action][0]) { 'R' -> transaction { - val query2 = auxTable.slice(auxTable.id).select { auxTable.column eq id } + val query2 = auxTable.select(auxTable.id).where { auxTable.column eq id } if (query2.toList()[1] != null) { throw VExecFailedException(MessageCode.getMessage("VIS-00021", arrayOf(query1Row[References.column], query1Row[References.table]))) } } 'C' -> transaction { - val query2 = auxTable.slice(auxTable.id).select { auxTable.column eq id } + val query2 = auxTable.select(auxTable.id).where { auxTable.column eq id } query2.forEach { checkForeignKeys(it[auxTable.id], query1Row[References.table]) } @@ -78,11 +78,11 @@ object VDatabaseUtils { fun checkForeignKeys(id: Int, table: String) { // FIXME : this should be re-implemented transaction { - val query1 = References.slice( + val query1 = References.select( References.table, References.column, References.action - ).select { + ).where { References.reference eq table }.orderBy(References.action to SortOrder.DESC) val action = query1.forEach { query1Row -> @@ -92,14 +92,14 @@ object VDatabaseUtils { } when (query1Row[References.action][0]) { 'R' -> transaction { - val query2 = auxTable.slice(auxTable.id).select { auxTable.column eq id } + val query2 = auxTable.select(auxTable.id).where { auxTable.column eq id } if (query2.toList()[1] != null) { throw VExecFailedException(MessageCode.getMessage("VIS-00021", arrayOf(query1Row[References.column], query1Row[References.table]))) } } 'C' -> transaction { - val query2 = auxTable.slice(auxTable.id).select { auxTable.column eq id } + val query2 = auxTable.select(auxTable.id).where { auxTable.column eq id } query2.forEach { checkForeignKeys(it[auxTable.id], query1Row[References.table]) } @@ -123,9 +123,9 @@ object VDatabaseUtils { var id = integer("ID") } val query = if (condition != null) { - auxTable.slice(auxTable.id).select { condition }.forUpdate() + auxTable.select(auxTable.id).where { condition }.forUpdate() } else { - auxTable.slice(auxTable.id).selectAll().forUpdate() + auxTable.select(auxTable.id).forUpdate() } query.forEach { diff --git a/galite-core/src/main/kotlin/org/kopi/galite/visual/VMenuTree.kt b/galite-core/src/main/kotlin/org/kopi/galite/visual/VMenuTree.kt index 1bbc220843..ca39d09031 100644 --- a/galite-core/src/main/kotlin/org/kopi/galite/visual/VMenuTree.kt +++ b/galite-core/src/main/kotlin/org/kopi/galite/visual/VMenuTree.kt @@ -1,6 +1,6 @@ /* - * Copyright (c) 2013-2022 kopiLeft Services SARL, Tunis TN - * Copyright (c) 1990-2022 kopiRight Managed Solutions GmbH, Wien AT + * Copyright (c) 2013-2024 kopiLeft Services SARL, Tunis TN + * Copyright (c) 1990-2024 kopiRight Managed Solutions GmbH, Wien AT * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -88,7 +88,7 @@ class VMenuTree constructor(ctxt: Connection? = ApplicationContext.getDBConnecti companion object { - private val SELECT_MODULES = Modules.slice( + private val SELECT_MODULES = Modules.select( Modules.id, Modules.parent, Modules.shortName, @@ -96,7 +96,7 @@ class VMenuTree constructor(ctxt: Connection? = ApplicationContext.getDBConnecti Modules.objectName, Modules.priority, Modules.symbol - ).selectAll().orderBy(Modules.priority to SortOrder.DESC) + ).orderBy(Modules.priority to SortOrder.DESC) const val CMD_QUIT = 0 const val CMD_OPEN = 1 @@ -360,7 +360,7 @@ class VMenuTree constructor(ctxt: Connection? = ApplicationContext.getDBConnecti if (it[Modules.symbol] != null && it[Modules.symbol] != 0) { val symbol = it[Modules.symbol] as Int - Symbols.select { Symbols.id eq symbol }.forEach { res -> + Symbols.selectAll().where { Symbols.id eq symbol }.forEach { res -> icon = res[Symbols.objectName] } } @@ -388,8 +388,8 @@ class VMenuTree constructor(ctxt: Connection? = ApplicationContext.getDBConnecti modules, Modules .innerJoin(GroupRights.innerJoin(GroupParties, { group }, { group }), { id }, { GroupRights.module }) - .slice(Modules.id, GroupRights.access, Modules.priority) - .select { GroupParties.user inSubQuery (Groups.slice(Groups.id).select { Groups.shortName eq groupName }) } + .select(Modules.id, GroupRights.access, Modules.priority) + .where { GroupParties.user inSubQuery (Groups.select(Groups.id).where { Groups.shortName eq groupName }) } .orderBy(Modules.priority to SortOrder.ASC, Modules.id to SortOrder.ASC) .withDistinct() ) @@ -399,8 +399,8 @@ class VMenuTree constructor(ctxt: Connection? = ApplicationContext.getDBConnecti modules, Modules .innerJoin(GroupRights.innerJoin(GroupParties, { group }, { group }), { id }, { GroupRights.module }) - .slice(Modules.id, GroupRights.access, Modules.priority) - .select { GroupParties.user inSubQuery (Users.slice(Users.id).select { Users.shortName eq menuTreeUser }) } + .select(Modules.id, GroupRights.access, Modules.priority) + .where { GroupParties.user inSubQuery (Users.select(Users.id).where { Users.shortName eq menuTreeUser }) } .orderBy(Modules.priority to SortOrder.ASC, Modules.id to SortOrder.ASC) .withDistinct() ) @@ -410,8 +410,8 @@ class VMenuTree constructor(ctxt: Connection? = ApplicationContext.getDBConnecti modules, Modules .innerJoin(GroupRights.innerJoin(GroupParties, { group }, { group }), { id }, { GroupRights.module }) - .slice(Modules.id, GroupRights.access, Modules.priority) - .select { GroupParties.user eq getUserID() } + .select(Modules.id, GroupRights.access, Modules.priority) + .where { GroupParties.user eq getUserID() } .orderBy(Modules.priority to SortOrder.ASC, Modules.id to SortOrder.ASC) .withDistinct() ) @@ -424,9 +424,9 @@ class VMenuTree constructor(ctxt: Connection? = ApplicationContext.getDBConnecti groupName != null -> { fetchRights(modules, Modules.innerJoin(GroupRights, { id }, { module }) - .slice(Modules.id, GroupRights.access, Modules.priority) - .select { - (GroupRights.group inSubQuery (Groups.slice(Groups.id).select { Groups.shortName eq groupName })) + .select(Modules.id, GroupRights.access, Modules.priority) + .where { + (GroupRights.group inSubQuery (Groups.select(Groups.id).where { Groups.shortName eq groupName })) } .orderBy(Modules.priority to SortOrder.ASC, Modules.id to SortOrder.ASC) .withDistinct()) @@ -434,9 +434,9 @@ class VMenuTree constructor(ctxt: Connection? = ApplicationContext.getDBConnecti menuTreeUser != null -> { fetchRights(modules, Modules.innerJoin(GroupRights, { id }, { module }) - .slice(Modules.id, GroupRights.access, Modules.priority) - .select { - (GroupRights.group inSubQuery (Users.slice(Users.id).select { Users.shortName eq menuTreeUser })) + .select(Modules.id, GroupRights.access, Modules.priority) + .where { + (GroupRights.group inSubQuery (Users.select(Users.id).where { Users.shortName eq menuTreeUser })) } .orderBy(Modules.priority to SortOrder.ASC, Modules.id to SortOrder.ASC) .withDistinct()) @@ -444,8 +444,8 @@ class VMenuTree constructor(ctxt: Connection? = ApplicationContext.getDBConnecti else -> { fetchRights(modules, Modules.innerJoin(GroupRights, { id }, { module }) - .slice(Modules.id, GroupRights.access, Modules.priority) - .select { (GroupRights.group eq getUserID()) } + .select(Modules.id, GroupRights.access, Modules.priority) + .where { (GroupRights.group eq getUserID()) } .orderBy(Modules.priority to SortOrder.ASC, Modules.id to SortOrder.ASC) .withDistinct()) } @@ -457,26 +457,26 @@ class VMenuTree constructor(ctxt: Connection? = ApplicationContext.getDBConnecti groupName != null -> { fetchRights(modules, Modules.innerJoin(UserRights, { id }, { module }) - .slice(Modules.id, UserRights.access, Modules.priority) - .select { - (UserRights.user inSubQuery ( Groups.slice(Groups.id).select { Groups.shortName eq groupName })) + .select(Modules.id, UserRights.access, Modules.priority) + .where { + (UserRights.user inSubQuery ( Groups.select(Groups.id).where { Groups.shortName eq groupName })) } .orderBy(Modules.priority to SortOrder.ASC, Modules.id to SortOrder.ASC)) } menuTreeUser != null -> { fetchRights(modules, Modules.innerJoin(UserRights, { id }, { module }) - .slice(Modules.id, UserRights.access, Modules.priority) - .select { - (UserRights.user inSubQuery (Users.slice(Users.id).select { Users.shortName eq menuTreeUser })) + .select(Modules.id, UserRights.access, Modules.priority) + .where { + (UserRights.user inSubQuery (Users.select(Users.id).where { Users.shortName eq menuTreeUser })) } .orderBy(Modules.priority to SortOrder.ASC, Modules.id to SortOrder.ASC)) } else -> { fetchRights(modules, Modules.innerJoin(UserRights, { id }, { module }) - .slice(Modules.id, UserRights.access, Modules.priority) - .select { UserRights.user eq getUserID() } + .select(Modules.id, UserRights.access, Modules.priority) + .where { UserRights.user eq getUserID() } .orderBy(Modules.priority to SortOrder.ASC, Modules.id to SortOrder.ASC)) } } @@ -507,11 +507,11 @@ class VMenuTree constructor(ctxt: Connection? = ApplicationContext.getDBConnecti private fun fetchFavorites() { transaction { val query = if (isSuperUser && menuTreeUser != null) { - Favorites.slice(Favorites.module, Favorites.id).select { - Favorites.user inSubQuery (Users.slice(Users.id).select { Users.shortName eq menuTreeUser }) + Favorites.select(Favorites.module, Favorites.id).where { + Favorites.user inSubQuery (Users.select(Users.id).where { Users.shortName eq menuTreeUser }) }.orderBy(Favorites.id) } else { - Favorites.slice(Favorites.module, Favorites.id).select { Favorites.user eq getUserID() }.orderBy(Favorites.id) + Favorites.select(Favorites.module, Favorites.id).where { Favorites.user eq getUserID() }.orderBy(Favorites.id) } query.forEach { if (it[Favorites.module] != 0) { @@ -572,7 +572,7 @@ class VMenuTree constructor(ctxt: Connection? = ApplicationContext.getDBConnecti Favorites.insert { it[this.id] = FavoritesId.nextIntVal() it[ts] = (System.currentTimeMillis() / 1000).toInt() - it[user] = Users.slice(Users.id).select { Users.shortName eq menuTreeUser.toString() } + it[user] = Users.select(Users.id).where { Users.shortName eq menuTreeUser.toString() } it[module] = id } } else { @@ -596,7 +596,7 @@ class VMenuTree constructor(ctxt: Connection? = ApplicationContext.getDBConnecti try { transaction { if (menuTreeUser != null) { - val idSubQuery = Users.slice(Users.id).select { Users.shortName eq menuTreeUser.orEmpty() } + val idSubQuery = Users.select(Users.id).where { Users.shortName eq menuTreeUser.orEmpty() } Favorites.deleteWhere { (Favorites.user eqSubQuery idSubQuery) and (Favorites.module eq id) } diff --git a/galite-core/src/main/kotlin/org/kopi/galite/visual/base/Image.kt b/galite-core/src/main/kotlin/org/kopi/galite/visual/base/Image.kt index 51002c8853..99d4a73d0d 100644 --- a/galite-core/src/main/kotlin/org/kopi/galite/visual/base/Image.kt +++ b/galite-core/src/main/kotlin/org/kopi/galite/visual/base/Image.kt @@ -30,13 +30,13 @@ interface Image : Serializable { * Returns the `Image` width * @return The image width */ - fun getWidth(): Int + fun getImageWidth(): Int /** * Returns the `Image` height * @return The image height */ - fun getHeight(): Int + fun getImageHeight(): Int /** * Returns the `Image` description diff --git a/galite-core/src/main/kotlin/org/kopi/galite/visual/cross/VDynamicReport.kt b/galite-core/src/main/kotlin/org/kopi/galite/visual/cross/VDynamicReport.kt index cecf13a9fa..a6d0f9211a 100644 --- a/galite-core/src/main/kotlin/org/kopi/galite/visual/cross/VDynamicReport.kt +++ b/galite-core/src/main/kotlin/org/kopi/galite/visual/cross/VDynamicReport.kt @@ -1,6 +1,6 @@ /* - * Copyright (c) 2013-2022 kopiLeft Services SARL, Tunis TN - * Copyright (c) 1990-2022 kopiRight Managed Solutions GmbH, Wien AT + * Copyright (c) 2013-2024 kopiLeft Services SARL, Tunis TN + * Copyright (c) 1990-2024 kopiRight Managed Solutions GmbH, Wien AT * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -22,8 +22,6 @@ import java.math.BigDecimal import java.sql.SQLException import org.jetbrains.exposed.sql.Table -import org.jetbrains.exposed.sql.select -import org.jetbrains.exposed.sql.selectAll import org.kopi.galite.visual.database.transaction import org.kopi.galite.visual.form.VBlock import org.kopi.galite.visual.form.VBooleanCodeField @@ -349,9 +347,9 @@ class VDynamicReport(block: VBlock) : VReport() { block.activeField = null } val query = if (searchCondition == null) { - searchTables!!.slice(searchColumns.toList()).selectAll() + searchTables!!.select(searchColumns.toList()) } else { - searchTables!!.slice(searchColumns.toList()).select(searchCondition) + searchTables!!.select(searchColumns.toList()).where(searchCondition) } val iterator = query.iterator() diff --git a/galite-core/src/main/kotlin/org/kopi/galite/visual/dsl/form/Form.kt b/galite-core/src/main/kotlin/org/kopi/galite/visual/dsl/form/Form.kt index 2bcd3c1da4..315c3af75d 100644 --- a/galite-core/src/main/kotlin/org/kopi/galite/visual/dsl/form/Form.kt +++ b/galite-core/src/main/kotlin/org/kopi/galite/visual/dsl/form/Form.kt @@ -555,21 +555,21 @@ abstract class Form(title: String, locale: Locale? = null) : Window(title, local class Validate : Actor(menu = FileMenu(), label = "Validate", help = "Validate form informations.", userActor = false) { init { - key = Key.F8 + key = Key.SHIFT_F1 icon = Icon.VALIDATE } } class Print : Actor(menu = FileMenu(), label = "Print", help = "Print report.", userActor = false) { init { - key = Key.F6 + key = Key.SHIFT_F7 icon = Icon.PRINT } } class PrintLabel : Actor(menu = FileMenu(), label = "Label", help = "Print labels.", userActor = false) { init { - key = Key.F6 + key = Key.SHIFT_F7 icon = Icon.PRINT } } @@ -588,7 +588,7 @@ abstract class Form(title: String, locale: Locale? = null) : Window(title, local } } - inner class Autofill : DefaultActor( + inner class Autofill : DefaultActor( menu = EditMenu(), label = "Standard", help = "List possible values.", command = PredefinedCommand.AUTOFILL, userActor = false) { init { key = Key.F2 @@ -621,14 +621,14 @@ abstract class Form(title: String, locale: Locale? = null) : Window(title, local class SearchOperator : Actor(menu = EditMenu(), label = "Condition", help = "Change search operator.", userActor = false) { init { - key = Key.F5 + key = Key.SHIFT_F5 icon = Icon.SEARCH_OP } } class ChangeBlock : Actor(menu = EditMenu(), label = "Block", help = "Moves cursor to another block.", userActor = false) { init { - key = Key.F8 + key = Key.SHIFT_F8 icon = Icon.BLOCK } } @@ -636,7 +636,7 @@ abstract class Form(title: String, locale: Locale? = null) : Window(title, local class CopyDocument : Actor(menu = EditMenu(), label = "Copy", help = "Provide a copy of the currently called document.", userActor = false) { init { - key = Key.F4 + key = Key.SHIFT_F10 icon = Icon.COPY } } @@ -664,7 +664,7 @@ abstract class Form(title: String, locale: Locale? = null) : Window(title, local class Nothing : Actor(menu = EditMenu(), label = "Nothing", help = "Select nothing.", userActor = false) { init { - key = Key.F5 + key = Key.SHIFT_F4 icon = Icon.NOTHING } } @@ -706,14 +706,14 @@ abstract class Form(title: String, locale: Locale? = null) : Window(title, local class CreateReport : Actor(menu = ActionMenu(), label = "Report", help = "Create report.", userActor = false) { init { - key = Key.F8 + key = Key.F9 icon = Icon.REPORT } } class CreateChart : Actor(menu = ActionMenu(), label = "Chart", help = "Create chart.", userActor = false) { init { - key = Key.F9 + key = Key.SHIFT_F9 icon = Icon.CHART_VIEW } } diff --git a/galite-core/src/main/kotlin/org/kopi/galite/visual/form/VBlock.kt b/galite-core/src/main/kotlin/org/kopi/galite/visual/form/VBlock.kt index 3d88779647..9c84f1649d 100644 --- a/galite-core/src/main/kotlin/org/kopi/galite/visual/form/VBlock.kt +++ b/galite-core/src/main/kotlin/org/kopi/galite/visual/form/VBlock.kt @@ -1,6 +1,6 @@ /* - * Copyright (c) 2013-2022 kopiLeft Services SARL, Tunis TN - * Copyright (c) 1990-2022 kopiRight Managed Solutions GmbH, Wien AT + * Copyright (c) 2013-2024 kopiLeft Services SARL, Tunis TN + * Copyright (c) 1990-2024 kopiRight Managed Solutions GmbH, Wien AT * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -40,8 +40,6 @@ import org.jetbrains.exposed.sql.deleteWhere import org.jetbrains.exposed.sql.insert import org.jetbrains.exposed.sql.intLiteral import org.jetbrains.exposed.sql.lowerCase -import org.jetbrains.exposed.sql.select -import org.jetbrains.exposed.sql.selectAll import org.jetbrains.exposed.sql.statements.api.ExposedBlob import org.jetbrains.exposed.sql.update import org.jetbrains.exposed.sql.upperCase @@ -991,6 +989,8 @@ abstract class VBlock(var title: String, if (target == null) { old!!.enter() throw VExecFailedException() + } else if (target is VBooleanField) { + target.focusOnFirst = true } target.enter() } @@ -1024,6 +1024,8 @@ abstract class VBlock(var title: String, if (target == null) { old!!.enter() throw VExecFailedException() + } else if (target is VBooleanField) { + target.focusOnFirst = false } target.enter() } @@ -1673,9 +1675,9 @@ abstract class VBlock(var title: String, // open database query, fetch tuples val query = if (condition != null) { - table!!.slice(columns).select(condition).orderBy(*orderBy.toTypedArray()) + table!!.select(columns).where(condition).orderBy(*orderBy.toTypedArray()) } else { - table!!.slice(columns).selectAll().orderBy(*orderBy.toTypedArray()) + table!!.select(columns).orderBy(*orderBy.toTypedArray()) } fetchCount = 0 @@ -1760,7 +1762,7 @@ abstract class VBlock(var title: String, } try { - val result = table!!.slice(columns).select(condition.compoundAnd()).single() + val result = table!!.select(columns).where(condition.compoundAnd()).single() /* set values */ var j = 0 @@ -2266,7 +2268,7 @@ abstract class VBlock(var title: String, try { form.transaction { val condition: Op = conditions.compoundAnd() - val query = table.slice(columns).select(condition) + val query = table.select(columns).where(condition) val result = query.single() var j = 0 @@ -2448,9 +2450,9 @@ abstract class VBlock(var title: String, var rows = 0 val query = if (conditions == null) { - tables!!.slice(columns).selectAll().orderBy(*orderBys.toTypedArray()) + tables!!.select(columns).orderBy(*orderBys.toTypedArray()) } else { - tables!!.slice(columns).select(conditions).orderBy(*orderBys.toTypedArray()) + tables!!.select(columns).where(conditions).orderBy(*orderBys.toTypedArray()) } for (result in query) { if (rows == fetchSize) { @@ -3189,7 +3191,7 @@ abstract class VBlock(var title: String, } try { - val result = table.slice(columns).select(conditions.compoundAnd()).single() + val result = table.select(columns).where(conditions.compoundAnd()).single() var j = 0 fields.forEach { field -> @@ -3241,7 +3243,7 @@ abstract class VBlock(var title: String, } if (condition.isNotEmpty()) { - val result = tables[0].slice(idColumn).select { condition.compoundAnd() } + val result = tables[0].select(idColumn).where { condition.compoundAnd() } val resultCount = result.count() if (resultCount > 0) { @@ -3487,7 +3489,7 @@ abstract class VBlock(var title: String, Column(table, "TS", IntegerColumnType()) } - val query = table.slice(ucColumn, tsColumn).select { idColumn eq value!! } + val query = table.select(ucColumn, tsColumn).where { idColumn eq value!! } if (query.empty()) { activeRecord = recno diff --git a/galite-core/src/main/kotlin/org/kopi/galite/visual/form/VBlockDefaultOuterJoin.kt b/galite-core/src/main/kotlin/org/kopi/galite/visual/form/VBlockDefaultOuterJoin.kt index b19abaa880..d46fe865a3 100644 --- a/galite-core/src/main/kotlin/org/kopi/galite/visual/form/VBlockDefaultOuterJoin.kt +++ b/galite-core/src/main/kotlin/org/kopi/galite/visual/form/VBlockDefaultOuterJoin.kt @@ -83,6 +83,7 @@ class VBlockDefaultOuterJoin(block: VBlock) { addToJoinedTables(field.getColumn(tableColumn)!!.getTable()) joinTables = joinTables.join(joinTable, joinType, field.getColumn(tableColumn)!!.column, field.getColumn(j)!!.column, + false, additionalConstraint) } if (j == field.getColumnCount() || field.getColumnCount() == 2) { @@ -122,6 +123,7 @@ class VBlockDefaultOuterJoin(block: VBlock) { addToJoinedTables(field.getColumn(j)!!.getTable()) joinTables = joinTables.join(joinTable, joinType, field.getColumn(tableColumn)!!.column, field.getColumn(j)!!.column, + false, additionalConstraint) } if (j == field.getColumnCount() || field.getColumnCount() == 2) { diff --git a/galite-core/src/main/kotlin/org/kopi/galite/visual/form/VBooleanField.kt b/galite-core/src/main/kotlin/org/kopi/galite/visual/form/VBooleanField.kt index 53fe486ca0..a6128e4147 100644 --- a/galite-core/src/main/kotlin/org/kopi/galite/visual/form/VBooleanField.kt +++ b/galite-core/src/main/kotlin/org/kopi/galite/visual/form/VBooleanField.kt @@ -28,6 +28,7 @@ class VBooleanField(bufferSize: Int) : VBooleanCodeField(bufferSize, booleanNames, booleanCodes, true) { + var focusOnFirst = true /** * return the name of this field diff --git a/galite-core/src/main/kotlin/org/kopi/galite/visual/form/VDateField.kt b/galite-core/src/main/kotlin/org/kopi/galite/visual/form/VDateField.kt index eec94733f1..9a354d9459 100644 --- a/galite-core/src/main/kotlin/org/kopi/galite/visual/form/VDateField.kt +++ b/galite-core/src/main/kotlin/org/kopi/galite/visual/form/VDateField.kt @@ -155,9 +155,7 @@ class VDateField(val bufferSize: Int) : VField(10, 1) { * Sets the field value of given record to a date value. */ override fun setDate(r: Int, v: LocalDate?) { - if (isChangedUI - || value[r] == null && v != null - || value[r] != null && value[r]!! != v) { + if (isChangedUI || value[r] == null && v != null || value[r] != null && value[r]!! != v) { // trails (backup) the record if necessary trail(r) // set value in the defined row diff --git a/galite-core/src/main/kotlin/org/kopi/galite/visual/form/VField.kt b/galite-core/src/main/kotlin/org/kopi/galite/visual/form/VField.kt index ab8659fe5a..ecb4badb11 100644 --- a/galite-core/src/main/kotlin/org/kopi/galite/visual/form/VField.kt +++ b/galite-core/src/main/kotlin/org/kopi/galite/visual/form/VField.kt @@ -1,6 +1,6 @@ /* - * Copyright (c) 2013-2022 kopiLeft Services SARL, Tunis TN - * Copyright (c) 1990-2022 kopiRight Managed Solutions GmbH, Wien AT + * Copyright (c) 2013-2024 kopiLeft Services SARL, Tunis TN + * Copyright (c) 1990-2024 kopiRight Managed Solutions GmbH, Wien AT * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -46,8 +46,6 @@ import org.jetbrains.exposed.sql.SqlExpressionBuilder.wrap import org.jetbrains.exposed.sql.Table import org.jetbrains.exposed.sql.intLiteral import org.jetbrains.exposed.sql.lowerCase -import org.jetbrains.exposed.sql.select -import org.jetbrains.exposed.sql.selectAll import org.jetbrains.exposed.sql.stringLiteral import org.jetbrains.exposed.sql.substring import org.jetbrains.exposed.sql.transactions.TransactionManager @@ -278,6 +276,7 @@ abstract class VField protected constructor(width: Int, height: Int) : VConstant @Deprecated("use fetchColumn(table: Table)") fun fetchColumn(table: Int): Int { + println("In USE ****") //not working if (columns != null) { for (i in columns!!.indices) { if (columns!![i]!!._getTable() == table) { @@ -412,6 +411,7 @@ abstract class VField protected constructor(width: Int, height: Int) : VConstant if (!isNull(block!!.activeRecord)) { callTrigger(VConstants.TRG_FORMAT) } + println("Start from here") checkList() try { if (!isNull(block!!.activeRecord)) { @@ -1722,7 +1722,7 @@ abstract class VField protected constructor(width: Int, height: Int) : VConstant val table = evalListTable() val column = list!!.getColumn(0).column as ExpressionWithColumnType - val query = table.slice(intLiteral(1)).select { column eq getSql(block!!.activeRecord) } + val query = table.select(intLiteral(1)).where { column eq getSql(block!!.activeRecord) } if (alreadyProtected) { exists = !query.empty() @@ -1773,8 +1773,8 @@ abstract class VField protected constructor(width: Int, height: Int) : VConstant while (true) { try { val column = list!!.getColumn(0).column as ExpressionWithColumnType - val query = evalListTable().slice(column).select { - column.substring(1, getString(block!!.activeRecord)!!.length) eq getString(block!!.activeRecord) + val query = evalListTable().select(column).where { + column.substring(1, getString(block!!.activeRecord)!!.length) eq getString(block!!.activeRecord)!! }.orderBy(column) val transaction = TransactionManager.currentOrNull() @@ -1837,7 +1837,7 @@ abstract class VField protected constructor(width: Int, height: Int) : VConstant } val column = list!!.getColumn(0).column as ExpressionWithColumnType - val query = evalListTable().slice(columns).select { + val query = evalListTable().select(columns).where { column.substring(1, condition.toString().length) eq condition.toString() }.orderBy(columns[0]) @@ -1871,7 +1871,7 @@ abstract class VField protected constructor(width: Int, height: Int) : VConstant while (true) { try { getForm().transaction { - val query = table.slice(idColumn).select { column eq getSql(block!!.activeRecord) } + val query = table.select(idColumn).where { column eq getSql(block!!.activeRecord) } if (!query.empty()) { id = query.first()[idColumn] @@ -1986,7 +1986,7 @@ abstract class VField protected constructor(width: Int, height: Int) : VConstant val column = list!!.getColumn(0).column!! val idColumn = table.columns.find { it.name == "ID" } as Column - table.slice(column).select { idColumn eq selected }.first()[column] + table.select(column).where { idColumn eq selected }.first()[column] } break } catch (e: SQLException) { @@ -2040,9 +2040,9 @@ abstract class VField protected constructor(width: Int, height: Int) : VConstant } val query = if (searchCondition == null) { - evalListTable().slice(columns).selectAll().orderBy(list!!.getColumn(0).column!!) + evalListTable().select(columns).orderBy(list!!.getColumn(0).column!!) } else { - evalListTable().slice(columns).select(searchCondition).orderBy(list!!.getColumn(0).column!!) + evalListTable().select(columns).where(searchCondition).orderBy(list!!.getColumn(0).column!!) } val result = displayQueryList(query, list!!.columns) @@ -2074,9 +2074,9 @@ abstract class VField protected constructor(width: Int, height: Int) : VConstant val orderBy = if (desc) SortOrder.ASC else SortOrder.DESC val query = if (isNull(block!!.activeRecord)) { - table.slice(column!!).selectAll() + table.select(column!!) } else { - table.slice(column!!).select(condition).orderBy(column to orderBy) + table.select(column!!).where(condition).orderBy(column to orderBy) } while (true) { @@ -2148,7 +2148,7 @@ abstract class VField protected constructor(width: Int, height: Int) : VConstant } } - val query = table.slice(columns).select(cond).orderBy(columns[0]) + val query = table.select(columns).where(cond).orderBy(columns[0]) while (true) { try { @@ -2237,7 +2237,7 @@ abstract class VField protected constructor(width: Int, height: Int) : VConstant result = getForm().transaction { val table = evalListTable() val idColumn = table.columns.find { it.name == "ID" } as Column - val firstRecord = table.slice(list!!.getColumn(0).column!!).select { + val firstRecord = table.select(list!!.getColumn(0).column!!).where { idColumn eq id }.firstOrNull() diff --git a/galite-core/src/main/kotlin/org/kopi/galite/visual/fullcalendar/VFullCalendarBlock.kt b/galite-core/src/main/kotlin/org/kopi/galite/visual/fullcalendar/VFullCalendarBlock.kt index e1f57b5187..37e99e9eb3 100644 --- a/galite-core/src/main/kotlin/org/kopi/galite/visual/fullcalendar/VFullCalendarBlock.kt +++ b/galite-core/src/main/kotlin/org/kopi/galite/visual/fullcalendar/VFullCalendarBlock.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013-2022 kopiLeft Services SARL, Tunis TN + * Copyright (c) 2013-2024 kopiLeft Services SARL, Tunis TN * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -27,7 +27,6 @@ import org.jetbrains.exposed.sql.Column import org.jetbrains.exposed.sql.SortOrder import org.jetbrains.exposed.sql.and import org.jetbrains.exposed.sql.or -import org.jetbrains.exposed.sql.select import org.kopi.galite.visual.cross.VFullCalendarForm import org.kopi.galite.database.DBDeadLockException import org.kopi.galite.database.DBInterruptionException @@ -180,18 +179,18 @@ abstract class VFullCalendarBlock(title: String, buffer: Int, visible: Int) : VB val lastDay = week.getLastDay() lastDay.plusDays(1) - tables!!.slice(columns) - .select { (dateColumn greaterEq startDate) and (dateColumn less lastDay) } + tables!!.select(columns) + .where { (dateColumn greaterEq startDate) and (dateColumn less lastDay) } .orderBy(*orderBys.toTypedArray()) } else { val fromColumn = fromField!!.getColumn(0)!!.column val toColumn = toField!!.getColumn(0)!!.column - val firstDayOfWeek = java.sql.Timestamp.valueOf(week.getFirstDay().atStartOfDay()) + val firstDayOfWeek = week.getFirstDay().atStartOfDay() val lastDay = week.getLastDay() - val firstDayOfNextWeek = java.sql.Timestamp.valueOf(lastDay.plusDays(1).atStartOfDay()) + val firstDayOfNextWeek = lastDay.plusDays(1).atStartOfDay() - tables!!.slice(columns) - .select { + tables!!.select(columns) + .where { ((fromColumn greaterEq firstDayOfWeek) and (fromColumn less firstDayOfNextWeek)) or ((toColumn greaterEq firstDayOfWeek) and (toColumn less firstDayOfNextWeek)) } diff --git a/galite-core/src/main/kotlin/org/kopi/galite/visual/list/VColorColumn.kt b/galite-core/src/main/kotlin/org/kopi/galite/visual/list/VColorColumn.kt index 46cde34bb0..03b4105b58 100644 --- a/galite-core/src/main/kotlin/org/kopi/galite/visual/list/VColorColumn.kt +++ b/galite-core/src/main/kotlin/org/kopi/galite/visual/list/VColorColumn.kt @@ -37,7 +37,7 @@ class VColorColumn(title: String, column, table, VConstants.ALG_LEFT, - 7, + 12, sortAscending) { // -------------------------------------------------------------------- diff --git a/galite-core/src/main/kotlin/org/kopi/galite/visual/ui/vaadin/base/BackgroundThreadHandler.kt b/galite-core/src/main/kotlin/org/kopi/galite/visual/ui/vaadin/base/BackgroundThreadHandler.kt index 8781a3b733..4ea5f279e9 100644 --- a/galite-core/src/main/kotlin/org/kopi/galite/visual/ui/vaadin/base/BackgroundThreadHandler.kt +++ b/galite-core/src/main/kotlin/org/kopi/galite/visual/ui/vaadin/base/BackgroundThreadHandler.kt @@ -22,137 +22,116 @@ import java.util.concurrent.ExecutionException import com.vaadin.flow.component.UI /** - * Collects some utilities for background threads in a vaadin application. - * - * - * Note that all performed background tasks are followed by an client UI update - * using the push mechanism incorporated with vaadin. + * Utility object for managing background threads in a Vaadin application. * + * Each background task is followed by a client UI update using Vaadin's push mechanism. */ object BackgroundThreadHandler { + private val uiThreadLocal = ThreadLocal() + /** - * Exclusive access to the UI from a background thread to perform some updates. - * @param command the command which accesses the UI. + * If a current UI context is found, directly execute the command */ - fun access(currentUI: UI? = null, command: () -> Unit) { - if (UI.getCurrent() != null) { - command() - - return - } + private fun useCurrentUIContext(command: () -> Unit): Boolean { + return UI.getCurrent()?.let { command() ; true } ?: false + } - val currentUI = currentUI ?: locateUI() + /** + * Provides exclusive access to the UI from a background thread. + * @param command The command to execute, which can access the UI. + */ + fun access(currentUI: UI? = null, command: () -> Unit) { + if (useCurrentUIContext(command)) { return } - if (currentUI == null) { + val ui = currentUI ?: locateUI() + if (ui == null) { command() } else { - currentUI.access(command) + ui.access(command) } } /** - * Exclusive access to the UI from a background thread to perform some updates. - * @param command the command which accesses the UI. + * Provides exclusive access to the UI and pushes an update. + * @param command The command to execute, which can access the UI. */ fun accessAndPush(currentUI: UI? = null, command: () -> Unit) { - if (UI.getCurrent() != null) { - command() - - return - } - - val currentUI = currentUI ?: locateUI() + if (useCurrentUIContext(command)) { return } - if (currentUI == null) { + val ui = currentUI ?: locateUI() + if (ui == null) { command() } else { - currentUI.access { + ui.access { try { command() } finally { - currentUI.push() + ui.push() } } } } /** - * Exclusive access to the UI from a background thread to perform some updates. - * - * This will awaits until computation completes. - * - * This method is used when you are creating a Vaadin component from a background thread. This will wait until - * initialization is finished to avoid NPE later. - * - * - * @param command the command which accesses the UI. + * Provides exclusive access to the UI, waits for the command to complete, and catches execution exceptions. + * @param command The command to execute, which can access the UI. */ fun accessAndAwait(currentUI: UI? = null, command: () -> Unit) { - if (UI.getCurrent() != null) { - command() - - return - } - - val currentUI = currentUI ?: locateUI() + if (useCurrentUIContext(command)) { return } - if (currentUI == null) { + val ui = currentUI ?: locateUI() + if (ui == null) { command() } else { - try { - currentUI - .access(command) - .get() - } catch (executionException: ExecutionException) { - executionException.cause?.let { - throw it + runCatching { + ui.access(command).get() + }.onFailure { + (it as? ExecutionException)?.cause?.let { cause -> + cause.printStackTrace() + throw cause } } } } /** - * Starts a task asynchronously and blocks the current thread. The lock will be released - * if a notify signal is send to the blocking object. - * - * @param lock The lock object. - * @param command The command which accesses the UI. + * Starts a task asynchronously and blocks until notified. + * @param lock The lock object for synchronization. + * @param command The command to execute, which can access the UI. */ fun startAndWait(lock: Object, currentUI: UI? = null, command: () -> Unit) { - access(currentUI = currentUI, command = command) - + access(currentUI, command) synchronized(lock) { try { lock.wait() } catch (e: InterruptedException) { + Thread.currentThread().interrupt() e.printStackTrace() } } } /** - * Starts a task asynchronously and blocks the current thread. The lock will be released - * if a notify signal is send to the blocking object. - * - * @param lock The lock object. - * @param command The command which accesses the UI. + * Starts a task asynchronously with UI access and push, blocking until notified. + * @param lock The lock object for synchronization. + * @param command The command to execute, which can access the UI. */ fun startAndWaitAndPush(lock: Object, currentUI: UI? = null, command: () -> Unit) { - accessAndPush(currentUI = currentUI, command = command) - + accessAndPush(currentUI, command) synchronized(lock) { try { lock.wait() } catch (e: InterruptedException) { + Thread.currentThread().interrupt() e.printStackTrace() } } } /** - * Releases the lock based on an object. - * @param lock The lock object. + * Notifies all threads waiting on the provided lock. + * @param lock The lock object to release. */ fun releaseLock(lock: Object) { synchronized(lock) { @@ -160,20 +139,25 @@ object BackgroundThreadHandler { } } + /** + * Sets the UI in a thread-local variable for later retrieval. + * Useful for scenarios where `UI.getCurrent()` is null. + * @param ui The UI instance to set. + */ fun setUI(ui: UI?) { uiThreadLocal.set(ui) } + /** + * Forces an immediate push of the current UI. + * @param ui The UI instance to push. + */ fun updateUI(ui: UI?) { - ui?.accessSynchronously { - ui.push() - } + ui?.accessSynchronously { ui.push() } } + /** + * Attempts to retrieve the current UI from `UI.getCurrent()` or the thread-local storage. + */ fun locateUI(): UI? = UI.getCurrent() ?: uiThreadLocal.get() - - //--------------------------------------------------- - // DATA MEMBERS - //--------------------------------------------------- - private val uiThreadLocal = ThreadLocal() } diff --git a/galite-core/src/main/kotlin/org/kopi/galite/visual/ui/vaadin/base/Image.kt b/galite-core/src/main/kotlin/org/kopi/galite/visual/ui/vaadin/base/Image.kt index a4016c0ead..c8d1f0822e 100644 --- a/galite-core/src/main/kotlin/org/kopi/galite/visual/ui/vaadin/base/Image.kt +++ b/galite-core/src/main/kotlin/org/kopi/galite/visual/ui/vaadin/base/Image.kt @@ -18,22 +18,23 @@ package org.kopi.galite.visual.ui.vaadin.base import org.kopi.galite.visual.base.Image +import org.kopi.galite.visual.ui.vaadin.common.VImage /** * The vaadin implementation of an image model. * * @param resource The resource file attached to this image. */ -class Image(val resource: String) : Image { +class Image(val resource: String = "image", val source: ByteArray? = null) : VImage(), Image { //--------------------------------------------------- // IMAGE IMPLEMENTATION //--------------------------------------------------- - override fun getWidth(): Int { + override fun getImageWidth(): Int { return -1 } - override fun getHeight(): Int { + override fun getImageHeight(): Int { return -1 } diff --git a/galite-core/src/main/kotlin/org/kopi/galite/visual/ui/vaadin/base/ShortcutAction.kt b/galite-core/src/main/kotlin/org/kopi/galite/visual/ui/vaadin/base/ShortcutAction.kt index ef41d4b716..3bb36ba9b2 100644 --- a/galite-core/src/main/kotlin/org/kopi/galite/visual/ui/vaadin/base/ShortcutAction.kt +++ b/galite-core/src/main/kotlin/org/kopi/galite/visual/ui/vaadin/base/ShortcutAction.kt @@ -102,7 +102,7 @@ fun V.runAfterGetValue(function: () -> Unit) where V : Component, V : HasVal this.element.executeJs("return $0.value") .then { // Synchronize with server side - this.value = it?.asString() + (this as? HasValue<*,String>)?.value = it?.asString() function() } } diff --git a/galite-core/src/main/kotlin/org/kopi/galite/visual/ui/vaadin/common/VImage.kt b/galite-core/src/main/kotlin/org/kopi/galite/visual/ui/vaadin/common/VImage.kt index d2b74315cc..286446e936 100644 --- a/galite-core/src/main/kotlin/org/kopi/galite/visual/ui/vaadin/common/VImage.kt +++ b/galite-core/src/main/kotlin/org/kopi/galite/visual/ui/vaadin/common/VImage.kt @@ -17,13 +17,16 @@ */ package org.kopi.galite.visual.ui.vaadin.common +import java.time.LocalDateTime +import java.time.format.DateTimeFormatter + import com.vaadin.flow.component.Focusable import com.vaadin.flow.component.html.Image /** * A widget that wraps image element. */ -class VImage : Image(), Focusable { +open class VImage : Image(), Focusable { //--------------------------------------------------- // IMPLEMENTATIONS //--------------------------------------------------- @@ -41,4 +44,13 @@ class VImage : Image(), Focusable { */ val isEmpty: Boolean get() = src == null || "" == src + + /** + * Creates the dynamic image name. + * @param baseName The base name. + * @return The dynamic image name. + */ + fun createFileName(baseName: String): String = + baseName + LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmssSSS")) + ".png" } + diff --git a/galite-core/src/main/kotlin/org/kopi/galite/visual/ui/vaadin/field/BooleanField.kt b/galite-core/src/main/kotlin/org/kopi/galite/visual/ui/vaadin/field/BooleanField.kt index 7d9c942487..f0eec42065 100644 --- a/galite-core/src/main/kotlin/org/kopi/galite/visual/ui/vaadin/field/BooleanField.kt +++ b/galite-core/src/main/kotlin/org/kopi/galite/visual/ui/vaadin/field/BooleanField.kt @@ -15,71 +15,72 @@ * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ + package org.kopi.galite.visual.ui.vaadin.field -import org.kopi.galite.visual.ui.vaadin.base.Styles +import kotlin.streams.toList -import com.vaadin.flow.component.BlurNotifier import com.vaadin.flow.component.Component -import com.vaadin.flow.component.FocusNotifier -import com.vaadin.flow.component.HasValue +import com.vaadin.flow.component.BlurNotifier +import com.vaadin.flow.component.Focusable +import com.vaadin.flow.component.Key +import com.vaadin.flow.component.KeyNotifier import com.vaadin.flow.component.checkbox.Checkbox +import com.vaadin.flow.component.checkbox.CheckboxGroup import com.vaadin.flow.component.dependency.CssImport -import com.vaadin.flow.component.orderedlayout.HorizontalLayout + +import org.kopi.galite.visual.ui.vaadin.base.Styles /** * The boolean field * * @param trueRepresentation The representation of the true value. - * @param falseRepresentation The representation of the false false + * @param falseRepresentation The representation of the false value */ @CssImport.Container(value = [ CssImport("./styles/galite/checkbox.css"), CssImport(value = "./styles/galite/checkbox.css", themeFor = "vaadin-checkbox") ]) -class BooleanField(trueRepresentation: String?, falseRepresentation: String?) : ObjectField() { - - /** - * Sets the boolean field to be mandatory - * This will remove to choose the null option - * from the two check boxes - */ +class BooleanField(val trueRepresentation: String?, val falseRepresentation: String?) : AbstractField(), + KeyNotifier, + BlurNotifier> +{ + // Sets the boolean field to be mandatory. + // This will remove to choose the null option from the two check boxes var mandatory = false - - private val content: HorizontalLayout = HorizontalLayout() - - private val yes: Checkbox = Checkbox() - - private val no: Checkbox = Checkbox() - - private var forceHiddenVisibility = false - - init { - className = Styles.BOOLEAN_FIELD - content.className = "k-boolean-field-content" - yes.classNames.add("true") - no.classNames.add("false") - setLabel(trueRepresentation, falseRepresentation) - content.add(yes) - content.add(no) - add(content) - yes.addValueChangeListener(::onYesChange) - no.addValueChangeListener(::onNoChange) - yes.element.style["visibility"] = "hidden" - no.element.style["visibility"] = "hidden" - - addFocusListener(::onFocus) - addBlurListener(::onBlur) - content.element.addEventListener("mouseover") { - isVisible = true - } - content.element.addEventListener("mouseout") { - if (value == null) { - isVisible = false + // Variable to keep track of the last focused checkbox item + private var focusedIndex = 0 + private var focusOnFirst = true + // Initialize the field checkboxGroup Component + private val checkboxGroup: FocusableCheckboxGroup = FocusableCheckboxGroup().apply { + label = null + // Define the items for true, false, and optionally an empty state + setItems(trueRepresentation.orEmpty(), falseRepresentation.orEmpty()) + value = setOf() // Initialize with no selection + addValueChangeListener { event -> + // Ensure only one item is selected, or none at all + if (event.value.size > 1) { + // Keep only the last selected item + val lastSelected = event.value + lastSelected.remove(event.oldValue.iterator().next()) + value = setOf(lastSelected.iterator().next()) + } else if (event.value.isEmpty() && mandatory) { + // If mandatory, remove the null option choice + value = event.oldValue } + // Update internal model and fire change event + setModelValue(getBooleanValue(value), true) } } + init { + // Remove the "Yes" and "No" labels + checkboxGroup.setItemLabelGenerator { "" } + checkboxGroup.addClassName(Styles.BOOLEAN_FIELD) + + add(checkboxGroup) + } + //--------------------------------------------------- // IMPLEMENTATION //--------------------------------------------------- @@ -88,8 +89,9 @@ class BooleanField(trueRepresentation: String?, falseRepresentation: String?) : * Sets the field focus. * @param focus The field focus */ - fun setFocus(focus: Boolean) { + fun setFocus(focus: Boolean, focusOnFirst: Boolean) { if (focus) { + this.focusOnFirst = focusOnFirst focus() } else { blur() @@ -108,141 +110,111 @@ class BooleanField(trueRepresentation: String?, falseRepresentation: String?) : } } - private fun onBlur(event: BlurNotifier.BlurEvent>) { - if (value == null) { - isVisible = false - } - } - - private fun onFocus(event: FocusNotifier.FocusEvent>) { - isVisible = true - } - - override fun setParentVisibility(visible: Boolean) { - if (value == null) { - yes.element.style["visibility"] = "hidden" - no.element.style["visibility"] = "hidden" - element.classList.remove(Styles.BOOLEAN_FIELD + "-visible") - } else { - isVisible = visible + /** + * Gets the field's boolean value + */ + private fun getBooleanValue(selectedValues: Set): Boolean? { + return when { + selectedValues.isEmpty() -> null + selectedValues.contains(trueRepresentation) -> true + selectedValues.contains(falseRepresentation) -> false + else -> null } - forceHiddenVisibility = !visible } - override fun setVisible(visible: Boolean) { - if (!forceHiddenVisibility && visible) { - yes.element.style["visibility"] = "visible" - no.element.style["visibility"] = "visible" - element.classList.add(Styles.BOOLEAN_FIELD + "-visible") - } else { - yes.element.style["visibility"] = "hidden" - no.element.style["visibility"] = "hidden" - element.classList.remove(Styles.BOOLEAN_FIELD + "-visible") - } + /** + * Focus on the appropriate checkbox element + */ + override fun focus() { + focusedIndex = if (focusOnFirst) 0 else 1 + val focusedCheckbox = checkboxGroup.getChildren().toList()[focusedIndex] as? Checkbox + focusedCheckbox?.focus() } - override fun isVisible(): Boolean = - yes.element.style["visibility"].equals("visible") - && no.element.style["visibility"].equals("visible") - - override val isNull: Boolean - get() = !yes.value && !no.value - /** - * Sets the value of this boolean field. - * @param value The field value. + * Sets the component value from a boolean value */ override fun setValue(value: Boolean?) { - when { - value == null -> { - yes.value = false - no.value = false - } - value -> { - yes.value = true - no.value = false - } - else -> { - yes.value = false - no.value = true - } + checkboxGroup.value = when (value) { + true -> setOf(trueRepresentation) + false -> setOf(falseRepresentation) + else -> emptySet() } - handleComponentVisiblity() } + /** + * Updates the presentation of this field to display the provided value. + */ override fun setPresentationValue(newPresentationValue: Boolean?) { - value = newPresentationValue + setValue(newPresentationValue) } - override fun addFocusListener(function: () -> Unit) { - yes.addFocusListener { - function() - } - no.addFocusListener { - function() - } - } + /** + * Checks if the component value is null + */ + override val isNull: Boolean + get() = checkboxGroup.value.isEmpty() - override fun getContent(): Component = content + /** + * @return the field's checkbox group component + */ + override fun getContent(): Component = checkboxGroup + /** + * Enables the checkbox group component + */ override fun setEnabled(enabled: Boolean) { super.setEnabled(enabled) - yes.isEnabled = enabled - no.isEnabled = enabled + checkboxGroup.isEnabled = enabled } - override fun setColor(foreground: String?, background: String?) { - // NOT SUPPORTED FOR BOOLEAN FIELDS - } + /** + * @return the boolean value represented by the field + */ + override fun getValue(): Boolean? = getBooleanValue(checkboxGroup.value) + + /** + * Checks the boolean field value : No specific actions to execute + */ + override fun checkValue(rec: Int) {} - override fun getValue(): Boolean? = - if (!yes.value && !no.value) { - null + /** + * Adds Custom focus listener for BooleanField + */ + override fun addFocusListener(function: () -> Unit) {} + + /** + * Adds custom Key Down listener for BooleanField. + */ + fun addKeyDownListener(gotoNext: () -> Unit, gotoPrevious: () -> Unit) { + checkboxGroup.addKeyDownListener { event -> + val items = checkboxGroup.getChildren().toList() // Retrieve child components (checkboxes) + + when (event.key) { + Key.TAB -> { + val modifier = event.modifiers.singleOrNull() + + if (modifier != null && modifier.name == "SHIFT") { + if (focusedIndex <= 0) { gotoPrevious() } else { focusedIndex-- } } else { - yes.value + if (focusedIndex >= 1) { gotoNext() } else { focusedIndex++ } } + } + Key.ENTER, Key.SPACE -> { // Change the value of the currently focused checkbox + val checkbox = items.getOrNull(focusedIndex) as? Checkbox - override fun checkValue(rec: Int) {} - - private fun onYesChange(event: HasValue.ValueChangeEvent) { - if (event.isFromClient) { - if (event.value) { - no.value = false - } else if (mandatory && !no.value) { - yes.value = true + checkbox?.value = !(checkbox?.value ?: false) + } } } - setModelValue(value, event.isFromClient) - handleComponentVisiblity() } - private fun onNoChange(event: HasValue.ValueChangeEvent) { - if (event.isFromClient) { - if (event.value) { - yes.value = false - } else if (mandatory && !yes.value) { - no.value = true - } + // Inner class to encapsulate CheckboxGroup component and make it focusable + inner class FocusableCheckboxGroup : CheckboxGroup(), Focusable>, KeyNotifier { + init { + // Make the component part of the tab order by setting tab index + // set tabindex to -1 to make the container non-focusable + element.setAttribute("tabindex", "-1") } - setModelValue(value, event.isFromClient) - handleComponentVisiblity() - } - - /** - * Handles the component visibility according to its value. - */ - internal fun handleComponentVisiblity() { - isVisible = value != null - } - - /** - * Sets the tooltip of the checkbox buttons inside the boolean field. - * - * @param yes The localized label for true value. - * @param no The localized label for false value. - */ - fun setLabel(yes: String?, no: String?) { - this.yes.element.setProperty("title", yes) - this.no.element.setProperty("title", no) } } diff --git a/galite-core/src/main/kotlin/org/kopi/galite/visual/ui/vaadin/field/InputTextField.kt b/galite-core/src/main/kotlin/org/kopi/galite/visual/ui/vaadin/field/InputTextField.kt index 601a7c7ba7..a8921d9314 100644 --- a/galite-core/src/main/kotlin/org/kopi/galite/visual/ui/vaadin/field/InputTextField.kt +++ b/galite-core/src/main/kotlin/org/kopi/galite/visual/ui/vaadin/field/InputTextField.kt @@ -1,6 +1,6 @@ /* - * Copyright (c) 2013-2022 kopiLeft Services SARL, Tunis TN - * Copyright (c) 1990-2022 kopiRight Managed Solutions GmbH, Wien AT + * Copyright (c) 2013-2024 kopiLeft Services SARL, Tunis TN + * Copyright (c) 1990-2024 kopiRight Managed Solutions GmbH, Wien AT * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -102,7 +102,8 @@ open class InputTextField internal constructor(protected val internalField: C //--------------------------------------------------- override fun setPresentationValue(newPresentationValue: String?) { - content.value = newPresentationValue + // Cast content to AbstractField that can accept a String + (content as? AbstractField<*, String>)?.value = newPresentationValue } open fun addTextValueChangeListener(listener: HasValue.ValueChangeListener>) { diff --git a/galite-core/src/main/kotlin/org/kopi/galite/visual/ui/vaadin/form/DBooleanField.kt b/galite-core/src/main/kotlin/org/kopi/galite/visual/ui/vaadin/form/DBooleanField.kt index 47fc4d56cb..eb02dec2f3 100644 --- a/galite-core/src/main/kotlin/org/kopi/galite/visual/ui/vaadin/form/DBooleanField.kt +++ b/galite-core/src/main/kotlin/org/kopi/galite/visual/ui/vaadin/form/DBooleanField.kt @@ -17,15 +17,15 @@ */ package org.kopi.galite.visual.ui.vaadin.form -import org.kopi.galite.visual.form.UTextField +import com.vaadin.flow.component.AbstractField +import com.vaadin.flow.component.HasValue + +import org.kopi.galite.visual.form.VBooleanField import org.kopi.galite.visual.form.VConstants import org.kopi.galite.visual.form.VFieldUI import org.kopi.galite.visual.ui.vaadin.base.BackgroundThreadHandler.access import org.kopi.galite.visual.ui.vaadin.field.BooleanField -import com.vaadin.flow.component.AbstractField -import com.vaadin.flow.component.HasValue - /** * Boolean field. * @@ -36,14 +36,13 @@ import com.vaadin.flow.component.HasValue * @param detail is it a detail field view ? */ class DBooleanField( - model: VFieldUI, - label: DLabel?, - align: Int, - options: Int, - detail: Boolean -) : DObjectField(model, label, align, options, detail), - UTextField, - HasValue.ValueChangeListener, Boolean?>> { + model: VFieldUI, + label: DLabel?, + align: Int, + options: Int, + detail: Boolean +) : DField(model, label, align, options, detail), + HasValue.ValueChangeListener, Boolean?>> { // -------------------------------------------------- // DATA MEMBERS @@ -56,15 +55,16 @@ class DBooleanField( // -------------------------------------------------- init { field.addValueChangeListener(this) - field.addObjectFieldListener(this) + field.addKeyDownListener(gotoNext = { gotoNextField() }, gotoPrevious = { gotoPrevField() }) setFieldContent(field) } // -------------------------------------------------- // IMPLEMENTATION // -------------------------------------------------- - override fun blinkOnFocus(): Boolean { - return false + + override fun valueChanged() { + // Nothing to do } override fun updateColor() { @@ -87,13 +87,12 @@ class DBooleanField( } else { if (!inside) { inside = true - enterMe() + enterMe((getModel() as? VBooleanField)?.focusOnFirst ?: true) } } super.updateFocus() } - override fun valueChanged(event: AbstractField.ComponentValueChangeEvent, Boolean?>) { val text = getModel().toText(event.value) @@ -115,42 +114,36 @@ class DBooleanField( override fun getObject(): Any? = wrappedField.value - override fun setBlink(b: Boolean) { + override fun setBlink(blink: Boolean) { access(currentUI) { - field.setBlink(b) + field.setBlink(blink) } } override fun getText(): String? = getModel().toText(field.value) - override fun setHasCriticalValue(b: Boolean) {} - - override fun addSelectionFocusListener() {} - - override fun removeSelectionFocusListener() {} - - override fun setSelectionAfterUpdateDisabled(disable: Boolean) {} - /** * Returns the true representation of this boolean field. * @return The true representation of this boolean field. */ - internal val trueRepresentation: String? + private val trueRepresentation: String? get() = getModel().toText(true) /** * Returns the false representation of this boolean field. * @return The false representation of this boolean field. */ - internal val falseRepresentation: String? + private val falseRepresentation: String? get() = getModel().toText(false) /** * Gets the focus to this field. + * + * @param focusOnFirst : Sets the focus on the first checkbox of the boolean field */ - internal fun enterMe() { + private fun enterMe(focusOnFirst: Boolean) { access(currentUI) { - field.setFocus(true) + field.setFocus(true, focusOnFirst) } } } diff --git a/galite-core/src/main/kotlin/org/kopi/galite/visual/ui/vaadin/form/DGridEditorBooleanField.kt b/galite-core/src/main/kotlin/org/kopi/galite/visual/ui/vaadin/form/DGridEditorBooleanField.kt index 6166bf1856..3ac8903378 100644 --- a/galite-core/src/main/kotlin/org/kopi/galite/visual/ui/vaadin/form/DGridEditorBooleanField.kt +++ b/galite-core/src/main/kotlin/org/kopi/galite/visual/ui/vaadin/form/DGridEditorBooleanField.kt @@ -15,14 +15,8 @@ * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -package org.kopi.galite.visual.ui.vaadin.form -import org.kopi.galite.visual.form.UTextField -import org.kopi.galite.visual.form.VConstants -import org.kopi.galite.visual.form.VFieldUI -import org.kopi.galite.visual.ui.vaadin.base.BackgroundThreadHandler.access -import org.kopi.galite.visual.ui.vaadin.grid.GridEditorBooleanField -import org.kopi.galite.visual.ui.vaadin.grid.GridEditorField +package org.kopi.galite.visual.ui.vaadin.form import com.vaadin.flow.component.AbstractField import com.vaadin.flow.component.HasValue @@ -31,18 +25,25 @@ import com.vaadin.flow.data.binder.ValueContext import com.vaadin.flow.data.converter.Converter import com.vaadin.flow.data.renderer.Renderer +import org.kopi.galite.visual.form.VBooleanField +import org.kopi.galite.visual.form.VConstants +import org.kopi.galite.visual.form.VFieldUI +import org.kopi.galite.visual.ui.vaadin.base.BackgroundThreadHandler.access +import org.kopi.galite.visual.ui.vaadin.grid.GridEditorBooleanField +import org.kopi.galite.visual.ui.vaadin.grid.GridEditorField + class DGridEditorBooleanField( - columnView: VFieldUI, - label: DGridEditorLabel?, - align: Int, - options: Int + columnView: VFieldUI, + label: DGridEditorLabel?, + align: Int, + options: Int ) : DGridEditorField(columnView, label, align, options), - UTextField, - HasValue.ValueChangeListener, Boolean?>> { + HasValue.ValueChangeListener, Boolean?>> { //--------------------------------------------------- // DATA MEMBERS //--------------------------------------------------- + private var inside = false private var rendrerValue: Boolean? = null @@ -51,6 +52,8 @@ class DGridEditorBooleanField( //--------------------------------------------------- init { editor.addValueChangeListener(this) + (editor as GridEditorBooleanField).addKeyDownListener(gotoNext = { onGotoNextField() }, + gotoPrevious = { onGotoPrevField() }) } //--------------------------------------------------- @@ -71,7 +74,7 @@ class DGridEditorBooleanField( } else { if (!inside) { inside = true - enterMe() + enterMe((getModel() as? VBooleanField)?.focusOnFirst ?: true) if (rendrerValue != null) { getModel().isChangedUI = true getModel().setBoolean(getBlockView().model.activeRecord, rendrerValue) @@ -89,13 +92,13 @@ class DGridEditorBooleanField( override fun updateAccess() { super.updateAccess() + label!!.update(columnView, getBlockView().getRecordFromDisplayLine(position)) access { - // editor.setLabel(label.text) TODO (editor as GridEditorBooleanField).mandatory = getAccess() == VConstants.ACS_MUSTFILL } } - override fun getObject(): String? = getText() + override fun getObject(): String? = getModel().toText(editor.value) override fun createEditor(): GridEditorField { return GridEditorBooleanField(trueRepresentation, falseRepresentation) @@ -116,28 +119,12 @@ class DGridEditorBooleanField( override fun format(input: Any?): Any? { return when (input) { - true -> { - trueRepresentation - } - false -> { - falseRepresentation - } - else -> { - input - } + true -> trueRepresentation + false -> falseRepresentation + else -> input } } - override fun getText(): String? = getModel().toText(editor.value) - - override fun setHasCriticalValue(b: Boolean) {} - - override fun addSelectionFocusListener() {} - - override fun removeSelectionFocusListener() {} - - override fun setSelectionAfterUpdateDisabled(disable: Boolean) {} - override fun valueChanged(event: AbstractField.ComponentValueChangeEvent, Boolean?>) { if (!event.isFromClient) { return @@ -163,22 +150,24 @@ class DGridEditorBooleanField( * Returns the true representation of this boolean field. * @return The true representation of this boolean field. */ - internal val trueRepresentation: String? + private val trueRepresentation: String? get() = getModel().toText(true) /** * Returns the false representation of this boolean field. * @return The false representation of this boolean field. */ - internal val falseRepresentation: String? + private val falseRepresentation: String? get() = getModel().toText(false) /** * Gets the focus to this field. + * + * @param focusOnFirst : Sets the focus on the first checkbox of the boolean field */ - internal fun enterMe() { - /*BackgroundThreadHandler.access(Runnable { TODO - getEditor().focus() - })*/ + private fun enterMe(focusOnFirst: Boolean) { + access(currentUI) { + (editor as GridEditorBooleanField).setFocus(true, focusOnFirst) + } } } diff --git a/galite-core/src/main/kotlin/org/kopi/galite/visual/ui/vaadin/form/DGridTextEditorField.kt b/galite-core/src/main/kotlin/org/kopi/galite/visual/ui/vaadin/form/DGridTextEditorField.kt index db03349993..df2ce4894f 100644 --- a/galite-core/src/main/kotlin/org/kopi/galite/visual/ui/vaadin/form/DGridTextEditorField.kt +++ b/galite-core/src/main/kotlin/org/kopi/galite/visual/ui/vaadin/form/DGridTextEditorField.kt @@ -93,6 +93,9 @@ class DGridTextEditorField( fun valueChanged(event: AbstractField.ComponentValueChangeEvent, String>) { if (event.isFromClient) { + if (!getModel().hasFocus()) { + getModel().block!!.gotoField(getModel()) + } checkText(event.value.toString(), true) } } diff --git a/galite-core/src/main/kotlin/org/kopi/galite/visual/ui/vaadin/form/DTextField.kt b/galite-core/src/main/kotlin/org/kopi/galite/visual/ui/vaadin/form/DTextField.kt index 02bb79980e..cdea130955 100644 --- a/galite-core/src/main/kotlin/org/kopi/galite/visual/ui/vaadin/form/DTextField.kt +++ b/galite-core/src/main/kotlin/org/kopi/galite/visual/ui/vaadin/form/DTextField.kt @@ -17,6 +17,8 @@ */ package org.kopi.galite.visual.ui.vaadin.form +import com.vaadin.flow.component.contextmenu.ContextMenu + import org.kopi.galite.visual.form.ModelTransformer import org.kopi.galite.visual.form.UTextField import org.kopi.galite.visual.form.VConstants @@ -27,8 +29,6 @@ import org.kopi.galite.visual.Action import org.kopi.galite.visual.VException import org.kopi.galite.visual.VlibProperties -import com.vaadin.flow.component.contextmenu.ContextMenu - /** * The `DTextField` is the vaadin implementation * of the [UTextField] specifications. @@ -40,11 +40,11 @@ import com.vaadin.flow.component.contextmenu.ContextMenu * @param detail Does the field belongs to the detail view ? */ open class DTextField( - model: VFieldUI, - label: DLabel?, - align: Int, - options: Int, - detail: Boolean, + model: VFieldUI, + label: DLabel?, + align: Int, + options: Int, + detail: Boolean, ) : DField(model, label, align, options, detail), UTextField { // -------------------------------------------------- @@ -60,13 +60,9 @@ open class DTextField( init { transformer = if (getModel().height == 1 || !scanner && getModel().getTypeOptions() and VConstants.FDO_DYNAMIC_NL > 0) { - DefaultTransformer(getModel().width, - getModel().height) + DefaultTransformer(getModel().width, getModel().height) } else if (!scanner) { - NewlineTransformer( - getModel().width, - getModel().height - ) + NewlineTransformer(getModel().width, getModel().height) } else { ScannerTransformer(this) } @@ -74,6 +70,9 @@ open class DTextField( field.inputField.addTextValueChangeListener { if (it.isFromClient) { + if (!getModel().hasFocus()) { + getModel().block!!.gotoField(getModel()) + } valueChanged() } } @@ -383,55 +382,55 @@ open class DTextField( * @return The converted string. */ private fun convertToSingleLine(source: String?, col: Int, row: Int): String = - buildString { - val length = source!!.length - var start = 0 - while (start < length) { - var index = source.indexOf('\n', start) - if (index - start < col && index != -1) { - append(source.substring(start, index)) - for (j in index - start until col) { - append(' ') - } - start = index + 1 - if (start == length) { - // last line ends with a "new line" -> add an empty line - for (j in 0 until col) { - append(' ') - } - } - } else { - if (start + col >= length) { - append(source.substring(start, length)) - for (j in length until start + col) { - append(' ') - } - start = length - } else { - // find white space to break line - var i = start + col - 1 - while (i > start) { - if (Character.isWhitespace(source[i])) { - break - } - i-- - } - index = if (i == start) { - start + col - } else { - i + 1 - } - append(source.substring(start, index)) - var j = (index - start) % col - while (j != 0 && j < col) { - append(' ') - j++ - } - start = index - } + buildString { + val length = source!!.length + var start = 0 + while (start < length) { + var index = source.indexOf('\n', start) + if (index - start < col && index != -1) { + append(source.substring(start, index)) + for (j in index - start until col) { + append(' ') + } + start = index + 1 + if (start == length) { + // last line ends with a "new line" -> add an empty line + for (j in 0 until col) { + append(' ') + } + } + } else { + if (start + col >= length) { + append(source.substring(start, length)) + for (j in length until start + col) { + append(' ') + } + start = length + } else { + // find white space to break line + var i = start + col - 1 + while (i > start) { + if (Character.isWhitespace(source[i])) { + break } + i-- + } + index = if (i == start) { + start + col + } else { + i + 1 + } + append(source.substring(start, index)) + var j = (index - start) % col + while (j != 0 && j < col) { + append(' ') + j++ } + start = index } + } + } + } /** * Converts a given string to a fixed line string. diff --git a/galite-core/src/main/kotlin/org/kopi/galite/visual/ui/vaadin/grid/GridEditorBooleanField.kt b/galite-core/src/main/kotlin/org/kopi/galite/visual/ui/vaadin/grid/GridEditorBooleanField.kt index 91106a12af..232ff75170 100644 --- a/galite-core/src/main/kotlin/org/kopi/galite/visual/ui/vaadin/grid/GridEditorBooleanField.kt +++ b/galite-core/src/main/kotlin/org/kopi/galite/visual/ui/vaadin/grid/GridEditorBooleanField.kt @@ -17,13 +17,17 @@ */ package org.kopi.galite.visual.ui.vaadin.grid -import org.kopi.galite.visual.ui.vaadin.base.Styles -import org.kopi.galite.visual.VColor +import kotlin.streams.toList import com.vaadin.flow.component.Component -import com.vaadin.flow.component.HasValue +import com.vaadin.flow.component.Focusable +import com.vaadin.flow.component.Key +import com.vaadin.flow.component.KeyNotifier import com.vaadin.flow.component.checkbox.Checkbox -import com.vaadin.flow.component.orderedlayout.HorizontalLayout +import com.vaadin.flow.component.checkbox.CheckboxGroup + +import org.kopi.galite.visual.ui.vaadin.base.Styles +import org.kopi.galite.visual.VColor /** * An editor for boolean field. @@ -38,52 +42,42 @@ import com.vaadin.flow.component.orderedlayout.HorizontalLayout * * yes and no cannot be checked at the same time */ -class GridEditorBooleanField(trueRepresentation: String?, falseRepresentation: String?) : GridEditorField() { - /** - * Sets the boolean field to be mandatory - * This will remove to choose the null option - * from the two check boxes - */ +class GridEditorBooleanField(val trueRepresentation: String?,val falseRepresentation: String?) : GridEditorField() { + // Sets the boolean field to be mandatory. + // This will remove to choose the null option from the two check boxes var mandatory = false - - private var content: HorizontalLayout = HorizontalLayout() - - private var yes: Checkbox = Checkbox() - - private var no: Checkbox = Checkbox() - - private var forceHiddenVisibility = false - - init { - addClassNames(Styles.BOOLEAN_FIELD, "editor-field", "editor-booleanfield", "k-boolean-field-content") - yes.classNames.add("true") - no.classNames.add("false") - setWidthFull() - setLabel(trueRepresentation, falseRepresentation) - content.add(yes) - content.add(no) - // content.setCellVerticalAlignment(yes, HasVerticalAlignment.ALIGN_BOTTOM) TODO - // content.setCellVerticalAlignment(no, HasVerticalAlignment.ALIGN_BOTTOM) TODO - yes.addValueChangeListener(::onYesChange) - no.addValueChangeListener(::onNoChange) - yes.element.style["visibility"] = "hidden" - no.element.style["visibility"] = "hidden" - content.element.addEventListener("mouseover") { - isVisible = true - } - - content.element.addEventListener("mouseout") { - if (value == null) { - isVisible = false + // Variable to keep track of the last focused checkbox item + private var focusedIndex = 0 + private var focusOnFirst = true + // Initialize the field checkboxGroup Component + private val checkboxGroup: FocusableCheckboxGroup = FocusableCheckboxGroup().apply { + label = null + // Define the items for true, false, and optionally an empty state + setItems(trueRepresentation.orEmpty(), falseRepresentation.orEmpty()) + value = setOf() // Initialize with no selection + addValueChangeListener { event -> + // Ensure only one item is selected, or none at all + if (event.value.size > 1) { + // Keep only the last selected item + val lastSelected = event.value + lastSelected.remove(event.oldValue.iterator().next()) + value = setOf(lastSelected.iterator().next()) + } else if (event.value.isEmpty() && mandatory) { + // If mandatory, remove the null option choice + value = event.oldValue } + // Update internal model and fire change event + setModelValue(getBooleanValue(value), true) + (getChildren().toList().getOrNull(focusedIndex) as? Checkbox)?.focus() } + } - addFocusListener { - onFocus() - } - addBlurListener { - onBlur() - } + init { + // Remove the "Yes" and "No" labels + checkboxGroup.setItemLabelGenerator { "" } + checkboxGroup.addClassNames(Styles.BOOLEAN_FIELD, "editor-field") + + setWidthFull() } //--------------------------------------------------- @@ -94,14 +88,27 @@ class GridEditorBooleanField(trueRepresentation: String?, falseRepresentation: S * Sets the field focus. * @param focus The field focus */ - fun setFocus(focus: Boolean) { + fun setFocus(focus: Boolean, focusOnFirst: Boolean) { if (focus) { + this.focusOnFirst = focusOnFirst focus() } else { blur() } } + /** + * Gets the field's boolean value + */ + private fun getBooleanValue(selectedValues: Set): Boolean? { + return when { + selectedValues.isEmpty() -> null + selectedValues.contains(trueRepresentation) -> true + selectedValues.contains(falseRepresentation) -> false + else -> null + } + } + /** * Sets the blink state of the boolean field. * @param blink The blink state. @@ -118,127 +125,88 @@ class GridEditorBooleanField(trueRepresentation: String?, falseRepresentation: S // NOT SUPPORTED } - fun onBlur() { - if (value == null) { - isVisible = false - } - } - - fun onFocus() { - isVisible = true - } - - override fun setVisible(visible: Boolean) { - if (!forceHiddenVisibility && visible) { - yes.element.style["visibility"] = "visible" - no.element.style["visibility"] = "visible" - element.classList.add(Styles.BOOLEAN_FIELD + "-visible") - } else { - yes.element.style["visibility"] = "hidden" - no.element.style["visibility"] = "hidden" - element.classList.remove(Styles.BOOLEAN_FIELD + "-visible") - } - } - - override fun isVisible(): Boolean = - yes.element.style["visibility"].equals("visible") - && no.element.style["visibility"].equals("visible") - /** - * Sets the value of this boolean field. - * @param value The field value. + * Sets the component value from a boolean value */ override fun setValue(value: Boolean?) { - when { - value == null -> { - yes.value = false - no.value = false - } - value -> { - yes.value = true - no.value = false - } - else -> { - yes.value = false - no.value = true - } + checkboxGroup.value = when (value) { + true -> setOf(trueRepresentation) + false -> setOf(falseRepresentation) + else -> emptySet() } - handleComponentVisibility() } + /** + * Updates the presentation of this field to display the provided value. + */ override fun setPresentationValue(newPresentationValue: Boolean?) { value = newPresentationValue } - override fun addFocusListener(function: () -> Unit) { - yes.addFocusListener { - function() - } - no.addFocusListener { - function() - } + /** + * @return the field's checkbox group component + */ + override fun initContent(): Component { + return checkboxGroup } - + /** + * Enables the checkbox group component + */ override fun setEnabled(enabled: Boolean) { super.setEnabled(enabled) - yes.isEnabled = enabled - no.isEnabled = enabled + checkboxGroup.isEnabled = enabled } - override fun getValue(): Boolean? = - if (!yes.value && !no.value) { - null - } else { - yes.value - } - - private fun onYesChange(event: HasValue.ValueChangeEvent) { - if (event.value) { - no.value = false - } else if (mandatory && !no.value) { - yes.value = true - } - if (value == true || value == null) { - setModelValue(value, event.isFromClient) - } - handleComponentVisibility() - } - - private fun onNoChange(event: HasValue.ValueChangeEvent) { - if (event.value) { - yes.value = false - } else if (mandatory && !yes.value) { - no.value = true - } - if (value == false || value == null) { - setModelValue(value, event.isFromClient) - } - handleComponentVisibility() - } + /** + * @return the boolean value represented by the field + */ + override fun getValue(): Boolean? = getBooleanValue(checkboxGroup.value) /** - * Handles the component visibility according to its value. + * Focus on the appropriate checkbox element */ - private fun handleComponentVisibility() { - isVisible = value != null + override fun doFocus() { + focusedIndex = if (focusOnFirst) 0 else 1 + val focusedCheckbox = checkboxGroup.getChildren().toList()[focusedIndex] as? Checkbox + focusedCheckbox?.focus() } /** - * Sets the tooltip of the checkbox buttons inside the boolean field. - * - * @param yes The localized label for true value. - * @param no The localized label for false value. + * Adds Custom focus listener for BooleanField */ - fun setLabel(yes: String?, no: String?) { - this.yes.element.setProperty("title", yes) - this.no.element.setProperty("title", no) - } + override fun addFocusListener(focusFunction: () -> Unit) {} - override fun initContent(): Component { - return content + /** + * Adds custom Key Down listener for BooleanField. + */ + fun addKeyDownListener(gotoNext: () -> Unit, gotoPrevious: () -> Unit) { + checkboxGroup.addKeyDownListener { event -> + val items = checkboxGroup.getChildren().toList() // Retrieve child components (checkboxes) + + when (event.key) { + Key.TAB -> { + val modifier = event.modifiers.singleOrNull() + + if (modifier != null && modifier.name == "SHIFT") { + if (focusedIndex <= 0) { gotoPrevious() } else { focusedIndex-- } + } else { + if (focusedIndex >= 1) { gotoNext() } else { focusedIndex++ } + } + } + Key.ENTER, Key.SPACE -> { // Change the value of the currently focused checkbox + val checkbox = items.getOrNull(focusedIndex) as? Checkbox + + checkbox?.value = !(checkbox?.value ?: false) + } + } + } } - override fun doFocus() { - // DO NOTHING + // Inner class to encapsulate CheckboxGroup component and make it focusable + inner class FocusableCheckboxGroup : CheckboxGroup(), Focusable>, KeyNotifier { + init { + // Make the component part of the tab order by setting tab index + element.setAttribute("tabindex", "0") + } } } diff --git a/galite-core/src/main/kotlin/org/kopi/galite/visual/ui/vaadin/list/ListFilter.kt b/galite-core/src/main/kotlin/org/kopi/galite/visual/ui/vaadin/list/ListFilter.kt index f66c980934..7f6859c273 100644 --- a/galite-core/src/main/kotlin/org/kopi/galite/visual/ui/vaadin/list/ListFilter.kt +++ b/galite-core/src/main/kotlin/org/kopi/galite/visual/ui/vaadin/list/ListFilter.kt @@ -31,16 +31,20 @@ class ListFilter(private val filterFields: List, override fun test(t: ListTable.ListDialogItem): Boolean { for (i in model.columns.indices) { - val filterString = if(ignoreCase) filterFields[i].value.lowercase(Locale.getDefault()) else filterFields[i].value - val value = if (ignoreCase) t.getValueAt(i).lowercase(Locale.getDefault()) else t.getValueAt(i) + val item = t.getValueAt(i) - if (onlyMatchPrefix) { - if (!value.startsWith(filterString)) { - return false - } - } else { - if (!value.contains(filterString)) { - return false + if (item is String) { + val filterString = if(ignoreCase) filterFields[i].value.lowercase(Locale.getDefault()) else filterFields[i].value + val value = if (ignoreCase) item.lowercase(Locale.getDefault()) else item + + if (onlyMatchPrefix) { + if (!value.startsWith(filterString)) { + return false + } + } else { + if (!value.contains(filterString)) { + return false + } } } } diff --git a/galite-core/src/main/kotlin/org/kopi/galite/visual/ui/vaadin/list/ListTable.kt b/galite-core/src/main/kotlin/org/kopi/galite/visual/ui/vaadin/list/ListTable.kt index 5184d0e4a6..8808c5d7d0 100644 --- a/galite-core/src/main/kotlin/org/kopi/galite/visual/ui/vaadin/list/ListTable.kt +++ b/galite-core/src/main/kotlin/org/kopi/galite/visual/ui/vaadin/list/ListTable.kt @@ -17,7 +17,12 @@ */ package org.kopi.galite.visual.ui.vaadin.list +import java.io.ByteArrayInputStream + import org.kopi.galite.visual.form.VListDialog +import org.kopi.galite.visual.list.VImageColumn +import org.kopi.galite.visual.ui.vaadin.base.Image +import org.kopi.galite.visual.ui.vaadin.form.DImageField import com.vaadin.flow.component.ComponentEventListener import com.vaadin.flow.component.KeyDownEvent @@ -30,6 +35,7 @@ import com.vaadin.flow.component.icon.Icon import com.vaadin.flow.component.icon.VaadinIcon import com.vaadin.flow.component.textfield.TextField import com.vaadin.flow.data.provider.ListDataProvider +import com.vaadin.flow.data.renderer.ComponentRenderer import com.vaadin.flow.data.value.ValueChangeMode @CssImport("./styles/galite/list.css") @@ -52,10 +58,31 @@ class ListTable(val model: VListDialog) : Grid() { private fun buildColumns() { for(col in 0 until model.getColumnCount()) { - addColumn { - it.getValueAt(col) - }.setHeader(Span(model.getColumnName(col))) - .setKey(col.toString()) + if (model.columns[col] is VImageColumn) { + addColumn( + ComponentRenderer { item: ListDialogItem -> + val image = (item.getValueAt(col) as? Image) + + if (image != null) { + image.apply { + element.style["outline"] = "1px solid lightgreen" + width = "100px" + height = "100px" + setBorder(0) + element.setProperty("borderStyle", "none") + setSrc(DImageField.DynamicImageResource(createFileName("image")) { ByteArrayInputStream(image.source) }) + } + image + } else { + Image() + } + } + ).setHeader(Span(model.getColumnName(col))).setKey(col.toString()) + } else { + addColumn { + it.getValueAt(col) + }.setHeader(Span(model.getColumnName(col))).setKey(col.toString()) + } } } @@ -66,7 +93,7 @@ class ListTable(val model: VListDialog) : Grid() { val filterRow = appendHeaderRow() filterRow.also { element.classList.add("list-filter") } - val filterFields = this.columns.mapIndexed { _, column -> + val filterFields = this.columns.mapIndexed { i, column -> val cell = filterRow.getCell(column) val filterField = TextField() val search = Icon(VaadinIcon.SEARCH) @@ -74,11 +101,15 @@ class ListTable(val model: VListDialog) : Grid() { filterField.setWidthFull() filterField.suffixComponent = search filterField.className = "filter-text" - filterField.addValueChangeListener { - (dataProvider as ListDataProvider).refreshAll() + if (this.model.columns[i] !is VImageColumn) { + filterField.addValueChangeListener { + (dataProvider as ListDataProvider).refreshAll() + } + + filterField.valueChangeMode = ValueChangeMode.EAGER + } else { + filterField.isReadOnly = true } - - filterField.valueChangeMode = ValueChangeMode.EAGER cell.setComponent(filterField) filterField } @@ -122,7 +153,11 @@ class ListTable(val model: VListDialog) : Grid() { width = 0 for (row in 0 until model.count) { val value = model.columns[col]!!.formatObject(model.getValueAt(row, col)).toString() - width = width.coerceAtLeast(value.length.coerceAtLeast(model.titles[col]!!.length)) + width = if (model.columns[col]!! is VImageColumn) { + model.columns[col]!!.width + } else { + width.coerceAtLeast(value.length.coerceAtLeast(model.titles[col]!!.length)) + } } return 8 * width } @@ -150,8 +185,12 @@ class ListTable(val model: VListDialog) : Grid() { * @param o The object to be formatted. * @return The formatted property object. */ - private fun formatObject(o: Any?, col: Int): String { - return model.columns[col]!!.formatObject(o).toString() + private fun formatObject(o: Any?, col: Int): Any?{ + return if (model.columns[col]!!.formatObject(o) is Image) { + model.columns[col]!!.formatObject(o) + } else { + model.columns[col]!!.formatObject(o).toString() + } } } } diff --git a/galite-core/src/main/kotlin/org/kopi/galite/visual/ui/vaadin/main/MainWindow.kt b/galite-core/src/main/kotlin/org/kopi/galite/visual/ui/vaadin/main/MainWindow.kt index 373ace6d17..bfe4b6fecd 100644 --- a/galite-core/src/main/kotlin/org/kopi/galite/visual/ui/vaadin/main/MainWindow.kt +++ b/galite-core/src/main/kotlin/org/kopi/galite/visual/ui/vaadin/main/MainWindow.kt @@ -38,6 +38,7 @@ import com.vaadin.flow.component.Key import com.vaadin.flow.component.KeyModifier import com.vaadin.flow.component.ShortcutEvent import com.vaadin.flow.component.Shortcuts +import com.vaadin.flow.component.UI import com.vaadin.flow.component.contextmenu.MenuItem import com.vaadin.flow.component.dependency.CssImport import com.vaadin.flow.component.html.Div @@ -103,6 +104,7 @@ class MainWindow(locale: Locale, val logo: String, val href: String, val applica addLinksListeners() Shortcuts.addShortcutListener(this, this::goToPreviousPage, Key.PAGE_UP, KeyModifier.of("Alt")) Shortcuts.addShortcutListener(this, this::goToNextPage, Key.PAGE_DOWN, KeyModifier.of("Alt")) + addBeforeUnloadListener() instance = this } @@ -241,6 +243,88 @@ class MainWindow(locale: Locale, val logo: String, val href: String, val applica } } + /** + * Adds a detatch listener to be excecuted only when a window is closed or navigated away + */ + fun addWindowDetachListener(onDetach: () -> Unit) { + addDetachListener { + isWindowActuallyClosing { isClosing -> + if (isClosing) { + println("The window is closing or navigating away.") + onDetach() + } else { + println("The window was refreshed.") + } + } + } + } + + /** + * Determines if the window is actually closing (i.e., the user is closing a tab or + * navigating away) as opposed to simply refreshing the page. + * + * This method uses a `localStorage` flag, `pageReload`, that is set or removed by the + * `addBeforeUnloadListener` method. The logic works as follows: + * + * - If `pageReload` is found and set to `'true'`, it indicates that a page refresh + * occurred. In this case, the method returns `false` to indicate that the window + * is not actually closing. + * - If `pageReload` is not found, it suggests that the window is closing (either + * a tab close or navigation away), so the method returns `true`. + * + * @param callback A lambda function to handle the result: `true` if the window is closing (not a refresh), `false` otherwise. + */ + private fun isWindowActuallyClosing(callback: (Boolean) -> Unit) { + UI.getCurrent().page.executeJs("return localStorage.getItem('pageReload') === 'true';").then { + val isRefresh = it.asBoolean() + + callback(!isRefresh) + } + } + + /** + * Adds a JavaScript listener for the `beforeunload` event to detect when a page is + * refreshed or when a tab is being closed/navigated away from. + * + * This listener distinguishes between a page refresh (navigation type `1`) and other + * events such as closing the browser tab or navigating away. It uses `localStorage` + * to set a flag based on the detected action, which can be used server-side to infer + * user actions when combined with a Vaadin `DetachListener`. + * + * - On page refresh: A flag `pageReload` is set in the browser's `localStorage`. + * - On tab close or navigation away: The `pageReload` flag is removed from `localStorage`. + * + * Example Usage: + * This function is useful for determining whether to perform cleanup operations (like + * closing a database connection) only on tab close or navigation events but not on a refresh. + */ + private fun addBeforeUnloadListener() { + UI.getCurrent().page.executeJs( + """ + let isUnloading = false; + + window.addEventListener('beforeunload', function(event) { + isUnloading = true; + // Save a flag to detect page reload + if (performance.navigation.type === 1) { + // Page refresh detected + localStorage.setItem('pageReload', 'true'); + } else { + // Tab close or navigation away + localStorage.removeItem('pageReload'); + } + }); + + // Use 'unload' event as a fallback for closing/navigating away cases + window.addEventListener('unload', function(event) { + if (!isUnloading) { + localStorage.removeItem('pageReload'); + } + }); + """ + ) + } + /** * Adds a main window listener. * @param l the listener to be registered. diff --git a/galite-core/src/main/kotlin/org/kopi/galite/visual/ui/vaadin/visual/DWindow.kt b/galite-core/src/main/kotlin/org/kopi/galite/visual/ui/vaadin/visual/DWindow.kt index 9d2b311ba0..378fa816b4 100644 --- a/galite-core/src/main/kotlin/org/kopi/galite/visual/ui/vaadin/visual/DWindow.kt +++ b/galite-core/src/main/kotlin/org/kopi/galite/visual/ui/vaadin/visual/DWindow.kt @@ -38,7 +38,6 @@ import org.kopi.galite.visual.MessageListener import org.kopi.galite.visual.PropertyException import org.kopi.galite.visual.UWindow import org.kopi.galite.visual.VActor -import org.kopi.galite.visual.VException import org.kopi.galite.visual.VRuntimeException import org.kopi.galite.visual.VWindow import org.kopi.galite.visual.VlibProperties @@ -644,11 +643,7 @@ abstract class DWindow protected constructor(private var model: VWindow?) : Wind if (!waitIndicator.isOpened) { waitIndicator.show() } - doAfter(delay) { - access(currentUI) { - currentUI?.push() - } - } + currentUI?.push() } } } diff --git a/galite-core/src/main/kotlin/org/kopi/galite/visual/ui/vaadin/visual/VApplication.kt b/galite-core/src/main/kotlin/org/kopi/galite/visual/ui/vaadin/visual/VApplication.kt index fb32f34d91..b4750cab11 100644 --- a/galite-core/src/main/kotlin/org/kopi/galite/visual/ui/vaadin/visual/VApplication.kt +++ b/galite-core/src/main/kotlin/org/kopi/galite/visual/ui/vaadin/visual/VApplication.kt @@ -63,6 +63,7 @@ import org.kopi.galite.visual.ui.vaadin.base.BackgroundThreadHandler import org.kopi.galite.visual.ui.vaadin.base.BackgroundThreadHandler.access import org.kopi.galite.visual.ui.vaadin.base.BackgroundThreadHandler.accessAndAwait import org.kopi.galite.visual.ui.vaadin.base.BackgroundThreadHandler.accessAndPush +import org.kopi.galite.visual.ui.vaadin.base.BackgroundThreadHandler.startAndWaitAndPush import org.kopi.galite.visual.ui.vaadin.base.FontMetrics import org.kopi.galite.visual.ui.vaadin.base.StyleManager import org.kopi.galite.visual.ui.vaadin.main.MainWindow @@ -212,7 +213,7 @@ abstract class VApplication(override val registry: Registry) : VerticalLayout(), * @param notification The notification to be shown */ protected open fun showNotification(notification: AbstractNotification, lock: Object) { - BackgroundThreadHandler.startAndWaitAndPush(lock, currentUI) { + startAndWaitAndPush(lock, currentUI) { notification.show() } } @@ -251,7 +252,7 @@ abstract class VApplication(override val registry: Registry) : VerticalLayout(), mainWindow!!.setBookmarksMenu(DBookmarkMenu(menu!!)) mainWindow!!.setWorkspaceContextItemMenu(DBookmarkMenu(menu!!)) mainWindow!!.connectedUser = userName - mainWindow!!.addDetachListener { + mainWindow!!.addWindowDetachListener { //closing DB connection if the UI is closed after 3 heartbeats closeConnection() } diff --git a/galite-core/src/main/kotlin/org/kopi/galite/visual/ui/vaadin/visual/VImageHandler.kt b/galite-core/src/main/kotlin/org/kopi/galite/visual/ui/vaadin/visual/VImageHandler.kt index 5fbddea4e5..fc58fd670b 100644 --- a/galite-core/src/main/kotlin/org/kopi/galite/visual/ui/vaadin/visual/VImageHandler.kt +++ b/galite-core/src/main/kotlin/org/kopi/galite/visual/ui/vaadin/visual/VImageHandler.kt @@ -18,13 +18,13 @@ package org.kopi.galite.visual.ui.vaadin.visual -import org.kopi.galite.visual.base.Image +import org.kopi.galite.visual.ui.vaadin.base.Image import org.kopi.galite.visual.ImageHandler class VImageHandler : ImageHandler() { override fun getImage(image: String): Image? = null - override fun getImage(image: ByteArray): Image? = null + override fun getImage(image: ByteArray): Image = Image(source = image) override fun getURL(image: String): String = "ui/vaadin/$image" // FIXME } diff --git a/galite-core/src/main/kotlin/org/kopi/galite/visual/ui/vaadin/visual/VWindowController.kt b/galite-core/src/main/kotlin/org/kopi/galite/visual/ui/vaadin/visual/VWindowController.kt index c3125669ef..f4969a4c95 100644 --- a/galite-core/src/main/kotlin/org/kopi/galite/visual/ui/vaadin/visual/VWindowController.kt +++ b/galite-core/src/main/kotlin/org/kopi/galite/visual/ui/vaadin/visual/VWindowController.kt @@ -17,6 +17,13 @@ */ package org.kopi.galite.visual.ui.vaadin.visual +import org.kopi.galite.visual.UWindow +import org.kopi.galite.visual.VException +import org.kopi.galite.visual.VHelpViewer +import org.kopi.galite.visual.VMenuTree +import org.kopi.galite.visual.VRuntimeException +import org.kopi.galite.visual.VWindow +import org.kopi.galite.visual.WindowController import org.kopi.galite.visual.cross.VFullCalendarForm import org.kopi.galite.visual.dsl.common.Window import org.kopi.galite.visual.preview.VPreviewWindow @@ -27,13 +34,6 @@ import org.kopi.galite.visual.ui.vaadin.base.BackgroundThreadHandler.startAndWai import org.kopi.galite.visual.ui.vaadin.field.TextField import org.kopi.galite.visual.ui.vaadin.grid.GridEditorTextField import org.kopi.galite.visual.ui.vaadin.window.PopupWindow -import org.kopi.galite.visual.UWindow -import org.kopi.galite.visual.VException -import org.kopi.galite.visual.VHelpViewer -import org.kopi.galite.visual.VMenuTree -import org.kopi.galite.visual.VRuntimeException -import org.kopi.galite.visual.VWindow -import org.kopi.galite.visual.WindowController /** * The `VWindowController` is the vaadin implementation diff --git a/galite-core/src/main/resources/META-INF/resources/frontend/styles/galite/checkbox.css b/galite-core/src/main/resources/META-INF/resources/frontend/styles/galite/checkbox.css index 8a5a4f4169..5ea704686f 100644 --- a/galite-core/src/main/resources/META-INF/resources/frontend/styles/galite/checkbox.css +++ b/galite-core/src/main/resources/META-INF/resources/frontend/styles/galite/checkbox.css @@ -16,10 +16,10 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -/** - * Boolean field style +/* + * Boolean field style for CheckboxGroup */ -.true::part(checkbox), .false::part(checkbox) { +.k-boolean-field vaadin-checkbox::part(checkbox) { width: 1em; height: 1em; border-radius: 50%; @@ -27,45 +27,21 @@ font-size: 14px !important; } -.true::part(checkbox)::after, .false::part(checkbox)::after { - border-width: 0.3em 0 0 0.3em; - transform-origin: -1px -0.5px -} - -.false::part(checkbox)::after { - opacity: none; - transform: none; +.k-boolean-field vaadin-checkbox:nth-child(2)::part(checkbox)::after { + content: "✕"; /* Adds the 'x' mark to the second checkbox */ font-size: 13px; font-weight: bold; position: absolute; top: 0em; left: 0.09em; - box-sizing: border-box; - transform-origin: 0 0; - border-width: 0; - content: "\2715"; - display: inline-block; - width: 0; - height: 0; color: white; } -.multiple .k-boolean-field-content { - display: flex; - background-color: #ffffff; - align-items: center; - justify-content: center; -} - -.editor-booleanfield, .k-boolean-field { - --lumo-primary-color: var(--background-color); -} - -.k-boolean-field-content { - display: table; - border-bottom: 1px solid #dadada; +.k-boolean-field-blink { + animation: blinkEffect 0.3s ease-in-out 2; } -.k-boolean-field-content:focus-within { - border-bottom: 1px solid var(--background-color); +@keyframes blinkEffect { + 0%, 100% { background-color: inherit; } + 50% { background-color: #ffdddd; } } diff --git a/galite-data/src/main/kotlin/org/kopi/galite/database/Connection.kt b/galite-data/src/main/kotlin/org/kopi/galite/database/Connection.kt index d68eee19cd..37c0dc4795 100644 --- a/galite-data/src/main/kotlin/org/kopi/galite/database/Connection.kt +++ b/galite-data/src/main/kotlin/org/kopi/galite/database/Connection.kt @@ -1,6 +1,6 @@ /* - * Copyright (c) 2013-2022 kopiLeft Services SARL, Tunis TN - * Copyright (c) 1990-2022 kopiRight Managed Solutions GmbH, Wien AT + * Copyright (c) 2013-2024 kopiLeft Services SARL, Tunis TN + * Copyright (c) 1990-2024 kopiRight Managed Solutions GmbH, Wien AT * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -21,14 +21,8 @@ package org.kopi.galite.database import java.sql.SQLException import com.zaxxer.hikari.HikariDataSource +import org.jetbrains.exposed.sql.* -import org.jetbrains.exposed.sql.Database -import org.jetbrains.exposed.sql.DatabaseConfig -import org.jetbrains.exposed.sql.Schema -import org.jetbrains.exposed.sql.SqlLogger -import org.jetbrains.exposed.sql.Transaction -import org.jetbrains.exposed.sql.exposedLogger -import org.jetbrains.exposed.sql.select import org.jetbrains.exposed.sql.statements.StatementContext import org.jetbrains.exposed.sql.statements.expandArgs import org.jetbrains.exposed.sql.transactions.TransactionManager @@ -171,7 +165,7 @@ class Connection { } else { try { transaction(db = dbConnection) { - user = Users.slice(Users.id).select { + user = Users.select(Users.id).where { Users.shortName eq userName }.single()[Users.id] } @@ -277,6 +271,7 @@ class Connection { java.sql.Connection.TRANSACTION_SERIALIZABLE to "TRANSACTION_SERIALIZABLE") } +@OptIn(ExperimentalKeywordApi::class) fun databaseConfig(schema: Schema? = null, traceLevel: Int? = null, isolationLevel: Int = java.sql.Connection.TRANSACTION_SERIALIZABLE, @@ -288,9 +283,10 @@ fun databaseConfig(schema: Schema? = null, sqlLogger = logger ?: Slf4jSqlInfoLogger(traceLevel) schema?.let { defaultSchema = it } // Feature added in https://github.com/JetBrains/Exposed/pull/1367 defaultIsolationLevel = isolationLevel - defaultRepetitionAttempts = maxRetries ?: 0 - defaultMinRepetitionDelay = waitMin ?: 0L - defaultMaxRepetitionDelay = waitMax ?: 0L + defaultMaxAttempts = maxRetries ?: 5 + defaultMinRetryDelay = waitMin ?: 0L + defaultMaxRetryDelay = waitMax ?: 0L + preserveKeywordCasing = false } class Slf4jSqlInfoLogger(private val traceLevel: Int? = null) : SqlLogger { diff --git a/galite-data/src/main/kotlin/org/kopi/galite/database/Migration.kt b/galite-data/src/main/kotlin/org/kopi/galite/database/Migration.kt index d50bc4df86..ae217dd4b3 100644 --- a/galite-data/src/main/kotlin/org/kopi/galite/database/Migration.kt +++ b/galite-data/src/main/kotlin/org/kopi/galite/database/Migration.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013-2023 kopiLeft Services SARL, Tunis TN + * Copyright (c) 2013-2024 kopiLeft Services SARL, Tunis TN * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -20,7 +20,6 @@ package org.kopi.galite.database import org.jetbrains.exposed.sql.SortOrder import org.jetbrains.exposed.sql.SqlLogger import org.jetbrains.exposed.sql.exists -import org.jetbrains.exposed.sql.select import org.jetbrains.exposed.sql.transactions.transaction import org.kopi.galite.database.installed.TransDB @@ -133,7 +132,7 @@ abstract class Migration(private val logger: SqlLogger? = null) { private fun loadModuleVersion(module: String): Int { return if (Versions.exists()) { // If table Versionen exists, return last saved value. - Versions.slice(Versions.number).select { Versions.packageName eq module }.orderBy(Versions.date, SortOrder.DESC).map { + Versions.select(Versions.number).where { Versions.packageName eq module }.orderBy(Versions.date, SortOrder.DESC).map { it[Versions.number] }.firstOrNull() ?: -1 } else if (module == "galite") { diff --git a/galite-data/src/main/kotlin/org/kopi/galite/database/Types.kt b/galite-data/src/main/kotlin/org/kopi/galite/database/Types.kt index b4022e0574..f7f72fbd26 100644 --- a/galite-data/src/main/kotlin/org/kopi/galite/database/Types.kt +++ b/galite-data/src/main/kotlin/org/kopi/galite/database/Types.kt @@ -48,7 +48,7 @@ fun Table.color(name: String) = registerColumn(name, ColorColumnType()) /** * Week column for storing weeks. */ -class WeekColumnType : ColumnType() { +class WeekColumnType : ColumnType() { override fun sqlType(): String = currentDialect.dataTypeProvider.integerType() override fun valueFromDB(value: Any): Week = when (value) { is Int -> Week(value / 100, value % 100) @@ -57,7 +57,7 @@ class WeekColumnType : ColumnType() { else -> error("Unexpected value of type Week: $value of ${value::class.qualifiedName}") } - override fun valueToDB(value: Any?): Any? = when (value) { + override fun valueToDB(value: Week?): Any? = when (value) { is Week -> value.toSql() else -> value } @@ -66,7 +66,7 @@ class WeekColumnType : ColumnType() { /** * Months column for storing months. */ -class MonthColumnType : ColumnType() { +class MonthColumnType : ColumnType() { override fun sqlType(): String = currentDialect.dataTypeProvider.integerType() override fun valueFromDB(value: Any): Month = when (value) { is Int -> Month(value / 100, value % 100) @@ -75,7 +75,7 @@ class MonthColumnType : ColumnType() { else -> error("Unexpected value of type Month: $value of ${value::class.qualifiedName}") } - override fun valueToDB(value: Any?): Any? = when (value) { + override fun valueToDB(value: Month?): Any? = when (value) { is Month -> value.toSql() else -> value } @@ -84,7 +84,7 @@ class MonthColumnType : ColumnType() { /** * Color column for storing colors. */ -class ColorColumnType : ColumnType() { +class ColorColumnType : ColumnType() { override fun sqlType(): String = currentDialect.dataTypeProvider.integerType() override fun valueFromDB(value: Any): Color { return when (value) { @@ -95,7 +95,7 @@ class ColorColumnType : ColumnType() { } } - override fun valueToDB(value: Any?): Any? { + override fun valueToDB(value: Color?): Any? { return when (value) { is Color -> value.toSql() else -> value diff --git a/galite-data/src/main/kotlin/org/kopi/galite/database/Utils.kt b/galite-data/src/main/kotlin/org/kopi/galite/database/Utils.kt index a615be7c26..287bedb9c9 100644 --- a/galite-data/src/main/kotlin/org/kopi/galite/database/Utils.kt +++ b/galite-data/src/main/kotlin/org/kopi/galite/database/Utils.kt @@ -1,6 +1,6 @@ /* - * Copyright (c) 2013-2022 kopiLeft Services SARL, Tunis TN - * Copyright (c) 1990-2022 kopiRight Managed Solutions GmbH, Wien AT + * Copyright (c) 2013-2024 kopiLeft Services SARL, Tunis TN + * Copyright (c) 1990-2024 kopiRight Managed Solutions GmbH, Wien AT * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -24,7 +24,6 @@ import org.jetbrains.exposed.sql.NextVal import org.jetbrains.exposed.sql.Sequence import org.jetbrains.exposed.sql.Table import org.jetbrains.exposed.sql.nextIntVal -import org.jetbrains.exposed.sql.selectAll class Utils { companion object { @@ -43,12 +42,12 @@ class Utils { return try { seqNextVal = (sequence ?: Sequence("${table.nameInDatabaseCase()}Id")).nextIntVal() - Table.Dual.slice(seqNextVal).selectAll().single()[seqNextVal] + Table.Dual.select(seqNextVal).single()[seqNextVal] } catch (e: SQLException) { try { seqNextVal = Sequence("${table.nameInDatabaseCase()}_${id}_seq").nextIntVal() - Table.Dual.slice(seqNextVal).selectAll().single()[seqNextVal] + Table.Dual.select(seqNextVal).single()[seqNextVal] } catch (e: SQLException) { throw RuntimeException("Unable to get the sequence next value for table ${table.nameInDatabaseCase()} : ${e.message}") } diff --git a/galite-data/src/main/kotlin/org/kopi/galite/database/installed/TransDB.kt b/galite-data/src/main/kotlin/org/kopi/galite/database/installed/TransDB.kt index 0c1657daaf..90e924ba7d 100644 --- a/galite-data/src/main/kotlin/org/kopi/galite/database/installed/TransDB.kt +++ b/galite-data/src/main/kotlin/org/kopi/galite/database/installed/TransDB.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013-2023 kopiLeft Services SARL, Tunis TN + * Copyright (c) 2013-2024 kopiLeft Services SARL, Tunis TN * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -22,7 +22,6 @@ import java.time.LocalDateTime import org.jetbrains.exposed.sql.exists import org.jetbrains.exposed.sql.insert import org.jetbrains.exposed.sql.nextIntVal -import org.jetbrains.exposed.sql.select import org.kopi.galite.database.Modules import org.kopi.galite.database.ModulesId @@ -65,7 +64,7 @@ abstract class TransDB(val module: String, val version: Int) { it[uc] = 0 it[ts] = 0 it[shortName] = menu - it[Modules.parent] = parentId ?: Modules.slice(id).select { shortName eq parent!! }.map { module -> module[id] }.first() + it[Modules.parent] = parentId ?: Modules.select(id).where { shortName eq parent!! }.map { module -> module[id] }.first() it[sourceName] = SOURCE it[Modules.priority] = priority it[Modules.objectName] = objectName diff --git a/galite-demo/galite-vaadin/src/main/kotlin/org/kopi/galite/demo/client/ClientP.kt b/galite-demo/galite-vaadin/src/main/kotlin/org/kopi/galite/demo/client/ClientP.kt index 458e82d34f..f84e5a6a3c 100644 --- a/galite-demo/galite-vaadin/src/main/kotlin/org/kopi/galite/demo/client/ClientP.kt +++ b/galite-demo/galite-vaadin/src/main/kotlin/org/kopi/galite/demo/client/ClientP.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013-2022 kopiLeft Services SARL, Tunis TN + * Copyright (c) 2013-2024 kopiLeft Services SARL, Tunis TN * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -104,7 +104,7 @@ class ClientP : PivotTable(title = "Clients_Pivot_Table", locale = Locale.UK) { Purchase.idClt eq Client.idClt }.join(Product, JoinType.LEFT) { Purchase.idPdt eq Product.idPdt - }.slice( + }.select( Client.firstNameClt, Client.lastNameClt, Client.ageClt, @@ -115,7 +115,7 @@ class ClientP : PivotTable(title = "Clients_Pivot_Table", locale = Locale.UK) { Product.description, Purchase.quantity, Product.price - ).selectAll() + ) init { transaction { diff --git a/galite-demo/galite-vaadin/src/main/kotlin/org/kopi/galite/demo/command/CommandForm.kt b/galite-demo/galite-vaadin/src/main/kotlin/org/kopi/galite/demo/command/CommandForm.kt index 2600d69bab..b23543b6ca 100644 --- a/galite-demo/galite-vaadin/src/main/kotlin/org/kopi/galite/demo/command/CommandForm.kt +++ b/galite-demo/galite-vaadin/src/main/kotlin/org/kopi/galite/demo/command/CommandForm.kt @@ -59,10 +59,6 @@ class CommandForm : DictionaryForm(title = "Commands", locale = Locale.UK) { } } - command(item = list) { - recursiveQuery() - } - command(item = _break) { resetBlock() } diff --git a/galite-demo/galite-vaadin/src/main/kotlin/org/kopi/galite/demo/database/Migration.kt b/galite-demo/galite-vaadin/src/main/kotlin/org/kopi/galite/demo/database/Migration.kt index c4a741f9e4..fef67576a0 100644 --- a/galite-demo/galite-vaadin/src/main/kotlin/org/kopi/galite/demo/database/Migration.kt +++ b/galite-demo/galite-vaadin/src/main/kotlin/org/kopi/galite/demo/database/Migration.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013-2022 kopiLeft Services SARL, Tunis TN + * Copyright (c) 2013-2024 kopiLeft Services SARL, Tunis TN * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -30,7 +30,7 @@ import org.jetbrains.exposed.sql.Schema import org.jetbrains.exposed.sql.SchemaUtils import org.jetbrains.exposed.sql.insert import org.jetbrains.exposed.sql.nextIntVal -import org.jetbrains.exposed.sql.select +import org.jetbrains.exposed.sql.selectAll import org.jetbrains.exposed.sql.transactions.transaction import org.kopi.galite.database.* @@ -189,8 +189,8 @@ fun insertIntoUserRights(userName: String, accessUser: Boolean) { UserRights.insert { it[ts] = 0 - it[module] = Modules.slice(Modules.id).select { Modules.shortName eq moduleName }.single()[Modules.id] - it[user] = Users.slice(Users.id).select { Users.shortName eq userName }.single()[Users.id] + it[module] = Modules.select(Modules.id).where { Modules.shortName eq moduleName }.single()[Modules.id] + it[user] = Users.select(Users.id).where { Users.shortName eq userName }.single()[Users.id] it[access] = accessUser } } @@ -208,7 +208,7 @@ fun insertIntoModule(shortname: String, it[uc] = 0 it[ts] = 0 it[shortName] = shortname - it[parent] = if (parentName != "-1") Modules.select { shortName eq parentName }.single()[id] else -1 + it[parent] = if (parentName != "-1") Modules.selectAll().where { shortName eq parentName }.single()[id] else -1 it[sourceName] = source it[priority] = priorityNumber it[objectName] = if (className != null) className.qualifiedName!! else null diff --git a/galite-demo/galite-vaadin/src/main/kotlin/org/kopi/galite/demo/product/ProductChart.kt b/galite-demo/galite-vaadin/src/main/kotlin/org/kopi/galite/demo/product/ProductChart.kt index 4923f17fdf..95bd852c2f 100644 --- a/galite-demo/galite-vaadin/src/main/kotlin/org/kopi/galite/demo/product/ProductChart.kt +++ b/galite-demo/galite-vaadin/src/main/kotlin/org/kopi/galite/demo/product/ProductChart.kt @@ -19,7 +19,6 @@ package org.kopi.galite.demo.product import java.util.Locale import org.jetbrains.exposed.sql.count -import org.jetbrains.exposed.sql.selectAll import org.kopi.galite.demo.database.Product import org.kopi.galite.visual.chart.VChartType import org.kopi.galite.visual.database.transaction @@ -95,8 +94,7 @@ class ProductChart : Chart( init { transaction { - val products = Product.slice(Product.category, Product.category.count()) - .selectAll() + val products = Product.select(Product.category, Product.category.count()) .groupBy(Product.category) products.forEach { result -> diff --git a/galite-demo/galite-vaadin/src/main/kotlin/org/kopi/galite/demo/product/ProductForm.kt b/galite-demo/galite-vaadin/src/main/kotlin/org/kopi/galite/demo/product/ProductForm.kt index 386d522205..d6829118db 100644 --- a/galite-demo/galite-vaadin/src/main/kotlin/org/kopi/galite/demo/product/ProductForm.kt +++ b/galite-demo/galite-vaadin/src/main/kotlin/org/kopi/galite/demo/product/ProductForm.kt @@ -41,7 +41,7 @@ class ProductForm : DictionaryForm(title = "Products", locale = Locale.UK) { val block = page.insertBlock(BlockProduct()) - inner class BlockProduct : Block("Products", 1, 1) { + inner class BlockProduct : Block("Products", 1, 100) { val u = table(Product, sequence = Sequence("PRODUCTS_SEQ")) val idPdt = hidden(domain = INT(20)) { @@ -52,22 +52,30 @@ class ProductForm : DictionaryForm(title = "Products", locale = Locale.UK) { val description = mustFill(domain = STRING(50), position = at(1, 1)) { label = "Description" help = "The product description" - columns(u.description) + columns(u.description) { + priority = 4 + } } val price = mustFill(domain = DECIMAL(20, 10), follow(description)) { label = "Price" help = "The product unit price excluding VAT" - columns(u.price) + columns(u.price) { + priority = 3 + } } val category = mustFill(domain = Category, position = at(2, 1)) { label = "Category" help = "The product category" - columns(u.category) + columns(u.category) { + priority = 2 + } } val taxName = mustFill(domain = Tax, position = at(3, 1)) { label = "Tax" help = "The product tax name" - columns(u.taxName) + columns(u.taxName) { + priority = 1 + } } val photo = visit(domain = IMAGE(width = 100, height = 100), position = at(5, 1)) { label = "Image" diff --git a/galite-demo/galite-vaadin/src/main/kotlin/org/kopi/galite/demo/stock/StockR.kt b/galite-demo/galite-vaadin/src/main/kotlin/org/kopi/galite/demo/stock/StockR.kt index bf459132cf..712cf522ff 100644 --- a/galite-demo/galite-vaadin/src/main/kotlin/org/kopi/galite/demo/stock/StockR.kt +++ b/galite-demo/galite-vaadin/src/main/kotlin/org/kopi/galite/demo/stock/StockR.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013-2022 kopiLeft Services SARL, Tunis TN + * Copyright (c) 2013-2024 kopiLeft Services SARL, Tunis TN * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -19,7 +19,6 @@ package org.kopi.galite.demo.stock import java.util.Locale import org.jetbrains.exposed.sql.JoinType -import org.jetbrains.exposed.sql.selectAll import org.kopi.galite.demo.database.Product import org.kopi.galite.demo.database.Provider @@ -94,8 +93,7 @@ class StockR : Report(title = "Stocks", locale = Locale.UK) { val stocks = Stock.join(Provider, JoinType.INNER, Stock.idStckProv, Provider.idProvider) .join(Product, JoinType.INNER, Stock.idStckProv, Product.idPdt) - .slice(Stock.minAlert, Product.description, Provider.nameProvider) - .selectAll() + .select(Stock.minAlert, Product.description, Provider.nameProvider) init { transaction { diff --git a/galite-domain/src/main/kotlin/org/kopi/galite/domain/DomainColumn.kt b/galite-domain/src/main/kotlin/org/kopi/galite/domain/DomainColumn.kt index 09ba246b1a..d02e618855 100644 --- a/galite-domain/src/main/kotlin/org/kopi/galite/domain/DomainColumn.kt +++ b/galite-domain/src/main/kotlin/org/kopi/galite/domain/DomainColumn.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013-2022 kopiLeft Services SARL, Tunis TN + * Copyright (c) 2013-2024 kopiLeft Services SARL, Tunis TN * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -101,7 +101,7 @@ fun Table.week(name: String) = registerColumn(name, WeekColumnType()) /** * Week column for storing weeks. */ -class WeekColumnType : ColumnType() { +class WeekColumnType : ColumnType() { override fun sqlType(): String = currentDialect.dataTypeProvider.integerType() override fun valueFromDB(value: Any): Week = when (value) { is Int -> Week(value / 100, value % 100) @@ -110,7 +110,7 @@ class WeekColumnType : ColumnType() { else -> error("Unexpected value of type Week: $value of ${value::class.qualifiedName}") } - override fun valueToDB(value: Any?): Any? = when (value) { + override fun valueToDB(value: Week?): Any? = when (value) { is Week -> value.toSql() else -> value } @@ -119,7 +119,7 @@ class WeekColumnType : ColumnType() { /** * Months column for storing months. */ -class MonthColumnType : ColumnType() { +class MonthColumnType : ColumnType() { override fun sqlType(): String = currentDialect.dataTypeProvider.integerType() override fun valueFromDB(value: Any): Month = when (value) { is Int -> Month(value / 100, value % 100) @@ -128,7 +128,7 @@ class MonthColumnType : ColumnType() { else -> error("Unexpected value of type Month: $value of ${value::class.qualifiedName}") } - override fun valueToDB(value: Any?): Any? = when (value) { + override fun valueToDB(value: Month?): Any? = when (value) { is Month -> value.toSql() else -> value } diff --git a/galite-localizer/src/main/kotlin/org/kopi/galite/localizer/Localization.kt b/galite-localizer/src/main/kotlin/org/kopi/galite/localizer/Localization.kt index dfe60b2359..28e5d2c419 100644 --- a/galite-localizer/src/main/kotlin/org/kopi/galite/localizer/Localization.kt +++ b/galite-localizer/src/main/kotlin/org/kopi/galite/localizer/Localization.kt @@ -1,6 +1,6 @@ /* - * Copyright (c) 2013-2022 kopiLeft Services SARL, Tunis TN - * Copyright (c) 1990-2022 kopiRight Managed Solutions GmbH, Wien AT + * Copyright (c) 2013-2024 kopiLeft Services SARL, Tunis TN + * Copyright (c) 1990-2024 kopiRight Managed Solutions GmbH, Wien AT * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -21,7 +21,6 @@ import java.io.File import java.util.Locale import org.jetbrains.exposed.sql.SortOrder -import org.jetbrains.exposed.sql.select import org.jetbrains.exposed.sql.selectAll import org.jetbrains.exposed.sql.transactions.transaction @@ -79,14 +78,13 @@ private fun fetchModules(): MutableList { val localModules: ArrayList = ArrayList() var icon: String? = null val modulesQuery = - Modules.slice(Modules.id, + Modules.select(Modules.id, Modules.parent, Modules.shortName, Modules.sourceName, Modules.objectName, Modules.priority, Modules.symbol) - .selectAll() .orderBy(Modules.priority to SortOrder.DESC) transaction { @@ -94,7 +92,7 @@ private fun fetchModules(): MutableList { if (it[Modules.symbol] != null && it[Modules.symbol] != 0) { val symbol = it[Modules.symbol] as Int - Symbols.select { Symbols.id eq symbol }.forEach { res -> + Symbols.selectAll().where { Symbols.id eq symbol }.forEach { res -> icon = res[Symbols.objectName] } } diff --git a/galite-localizer/src/test/kotlin/org/kopi/galite/tests/localizer/Initialization.kt b/galite-localizer/src/test/kotlin/org/kopi/galite/tests/localizer/Initialization.kt index 3fe0a02507..5e39756f58 100644 --- a/galite-localizer/src/test/kotlin/org/kopi/galite/tests/localizer/Initialization.kt +++ b/galite-localizer/src/test/kotlin/org/kopi/galite/tests/localizer/Initialization.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013-2022 kopiLeft Services SARL, Tunis TN + * Copyright (c) 2013-2024 kopiLeft Services SARL, Tunis TN * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -24,7 +24,7 @@ import org.jetbrains.exposed.sql.Database import org.jetbrains.exposed.sql.SchemaUtils import org.jetbrains.exposed.sql.Table import org.jetbrains.exposed.sql.insert -import org.jetbrains.exposed.sql.select +import org.jetbrains.exposed.sql.selectAll import org.jetbrains.exposed.sql.transactions.transaction import org.kopi.galite.database.Modules @@ -71,7 +71,7 @@ fun insertIntoModule(shortname: String, it[uc] = 0 it[ts] = 0 it[shortName] = shortname - it[parent] = if (parentName != "-1") Modules.select { shortName eq parentName }.single()[id] else -1 + it[parent] = if (parentName != "-1") Modules.selectAll().where { shortName eq parentName }.single()[id] else -1 it[sourceName] = source it[priority] = priorityNumber it[objectName] = if (className != null) className.qualifiedName!! else null @@ -79,7 +79,7 @@ fun insertIntoModule(shortname: String, } } -object User : Table() { +object User : Table("USER") { val id = integer("ID") val uc = integer("UC") val ts = integer("TS") diff --git a/galite-testing/src/main/kotlin/org/kopi/galite/testing/Field.kt b/galite-testing/src/main/kotlin/org/kopi/galite/testing/Field.kt index 5e300148a8..0f67302992 100644 --- a/galite-testing/src/main/kotlin/org/kopi/galite/testing/Field.kt +++ b/galite-testing/src/main/kotlin/org/kopi/galite/testing/Field.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013-2022 kopiLeft Services SARL, Tunis TN + * Copyright (c) 2013-2024 kopiLeft Services SARL, Tunis TN * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -28,6 +28,7 @@ import org.kopi.galite.visual.form.UField import org.kopi.galite.visual.form.VBlock import org.kopi.galite.visual.form.VField import org.kopi.galite.type.format +import org.kopi.galite.visual.ui.vaadin.field.AbstractField import org.kopi.galite.visual.ui.vaadin.field.BooleanField import org.kopi.galite.visual.ui.vaadin.field.DatePickerLight import org.kopi.galite.visual.ui.vaadin.field.InputTextField @@ -135,7 +136,7 @@ private fun FormField.editInSimpleBlock(value: T?, mainWindow: MainWindow checkbox._value = true } else -> { - editorField._value = value + (editorField as AbstractField)._value = value } } diff --git a/galite-testing/src/main/kotlin/org/kopi/galite/testing/MainWindow.kt b/galite-testing/src/main/kotlin/org/kopi/galite/testing/MainWindow.kt index 9e4b75efac..0ea2a08981 100644 --- a/galite-testing/src/main/kotlin/org/kopi/galite/testing/MainWindow.kt +++ b/galite-testing/src/main/kotlin/org/kopi/galite/testing/MainWindow.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013-2022 kopiLeft Services SARL, Tunis TN + * Copyright (c) 2013-2024 kopiLeft Services SARL, Tunis TN * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -77,8 +77,8 @@ fun Form.open(duration: Long? = null, menu: String? = null) { fun Form.lookupFormCaption(menu: String? = null): String { val sources = transaction { Modules - .slice(Modules.sourceName, Modules.shortName) - .select { Modules.objectName eq this@lookupFormCaption::class.qualifiedName } + .select(Modules.sourceName, Modules.shortName) + .where { Modules.objectName eq this@lookupFormCaption::class.qualifiedName } .map { it[Modules.sourceName] to it[Modules.shortName] } } diff --git a/galite-tests/src/main/kotlin/org/kopi/galite/tests/database/Migration.kt b/galite-tests/src/main/kotlin/org/kopi/galite/tests/database/Migration.kt index 6d2eb9ca1e..5a7030e37b 100644 --- a/galite-tests/src/main/kotlin/org/kopi/galite/tests/database/Migration.kt +++ b/galite-tests/src/main/kotlin/org/kopi/galite/tests/database/Migration.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013-2022 kopiLeft Services SARL, Tunis TN + * Copyright (c) 2013-2024 kopiLeft Services SARL, Tunis TN * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -23,7 +23,7 @@ import kotlin.reflect.KClass import org.jetbrains.exposed.sql.Database import org.jetbrains.exposed.sql.SchemaUtils import org.jetbrains.exposed.sql.insert -import org.jetbrains.exposed.sql.select +import org.jetbrains.exposed.sql.selectAll import org.kopi.galite.database.Modules import org.kopi.galite.database.UserRights import org.kopi.galite.database.Users @@ -92,8 +92,8 @@ fun insertIntoUsers(shortname: String, userName: String) { fun insertIntoUserRights(userName: String, moduleName: String, accessUser: Boolean) { UserRights.insert { it[ts] = 0 - it[module] = Modules.slice(Modules.id).select { Modules.shortName eq moduleName }.single()[Modules.id] - it[user] = Users.slice(Users.id).select { Users.shortName eq userName }.single()[Users.id] + it[module] = Modules.select(Modules.id).where { Modules.shortName eq moduleName }.single()[Modules.id] + it[user] = Users.select(Users.id).where { Users.shortName eq userName }.single()[Users.id] it[access] = accessUser } } @@ -111,7 +111,7 @@ fun insertIntoModule(shortname: String, it[uc] = 0 it[ts] = 0 it[shortName] = shortname - it[parent] = if (parentName != "-1") Modules.select { shortName eq parentName }.single()[id] else -1 + it[parent] = if (parentName != "-1") Modules.selectAll().where { shortName eq parentName }.single()[id] else -1 it[sourceName] = source it[priority] = priorityNumber it[objectName] = if (className != null) className.qualifiedName!! else null diff --git a/galite-tests/src/test/kotlin/org/kopi/galite/tests/database/DBExceptionTests.kt b/galite-tests/src/test/kotlin/org/kopi/galite/tests/database/DBExceptionTests.kt index d2d91734ba..f8ba2ecc1d 100644 --- a/galite-tests/src/test/kotlin/org/kopi/galite/tests/database/DBExceptionTests.kt +++ b/galite-tests/src/test/kotlin/org/kopi/galite/tests/database/DBExceptionTests.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013-2022 kopiLeft Services SARL, Tunis TN + * Copyright (c) 2013-2024 kopiLeft Services SARL, Tunis TN * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -25,7 +25,7 @@ import org.junit.Test import org.jetbrains.exposed.sql.SchemaUtils import org.jetbrains.exposed.sql.Table import org.jetbrains.exposed.sql.insert -import org.jetbrains.exposed.sql.select +import org.jetbrains.exposed.sql.selectAll import org.jetbrains.exposed.sql.transactions.transaction import org.kopi.galite.tests.common.ApplicationTestBase @@ -52,17 +52,17 @@ class DBExceptionTests : ApplicationTestBase() { it[title] = "b1" } - Book.select { Book.id eq 0 }.into { + Book.selectAll().where { Book.id eq 0 }.into { assertEquals(0, it[Book.id]) assertEquals("b1", it[Book.title]) } assertFailsWith { - Book.select { Book.id eq 2 }.into {} + Book.selectAll().where { Book.id eq 2 }.into {} } assertFailsWith { - Book.select { Book.title eq "b1" }.into {} + Book.selectAll().where { Book.title eq "b1" }.into {} } } finally { SchemaUtils.drop(Book) diff --git a/galite-tests/src/test/kotlin/org/kopi/galite/tests/form/FormSample.kt b/galite-tests/src/test/kotlin/org/kopi/galite/tests/form/FormSample.kt index bdb92f5c5e..428e2ef068 100644 --- a/galite-tests/src/test/kotlin/org/kopi/galite/tests/form/FormSample.kt +++ b/galite-tests/src/test/kotlin/org/kopi/galite/tests/form/FormSample.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013-2022 kopiLeft Services SARL, Tunis TN + * Copyright (c) 2013-2024 kopiLeft Services SARL, Tunis TN * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -38,7 +38,7 @@ import org.kopi.galite.visual.dsl.form.Block import org.kopi.galite.visual.dsl.form.Key import org.kopi.galite.visual.FileHandler -object User : Table() { +object User : Table("USER") { val id = integer("ID") val uc = integer("UC") val ts = integer("TS") diff --git a/galite-tests/src/test/kotlin/org/kopi/galite/tests/form/FormWithListDomains.kt b/galite-tests/src/test/kotlin/org/kopi/galite/tests/form/FormWithListDomains.kt index 0fe53ac242..bc32263387 100644 --- a/galite-tests/src/test/kotlin/org/kopi/galite/tests/form/FormWithListDomains.kt +++ b/galite-tests/src/test/kotlin/org/kopi/galite/tests/form/FormWithListDomains.kt @@ -28,7 +28,6 @@ import org.jetbrains.exposed.sql.VarCharColumnType import org.jetbrains.exposed.sql.alias import org.jetbrains.exposed.sql.castTo import org.jetbrains.exposed.sql.countDistinct -import org.jetbrains.exposed.sql.select import org.jetbrains.exposed.sql.selectAll import org.kopi.galite.tests.desktop.runForm import org.kopi.galite.database.Modules @@ -107,7 +106,7 @@ class UsersListBlock : Block("UsersListBlock", 1, 1) { } class UsersList: ListDomain(20) { - override val table = query(Users.select { Users.id greater 0 }) + override val table = query(Users.selectAll().where { Users.id greater 0 }) override val access = { SomeDictionnaryForm() } val autoComplete = complete(AutoComplete.LEFT, 1) @@ -150,10 +149,9 @@ object Names : ListDomain(30) { } object AgesUsers : ListDomain(3) { - override val table = AgesUsers.query(User.slice(User.age.minus(1).alias("age"), + override val table = AgesUsers.query(User.select(User.age.minus(1).alias("age"), User.name, - User.id.castTo(VarCharColumnType()).alias("id")) - .selectAll()) + User.id.castTo(VarCharColumnType()).alias("id"))) init { "age" keyOf User.age diff --git a/galite-tests/src/test/kotlin/org/kopi/galite/tests/form/FormWithListDomainsTests.kt b/galite-tests/src/test/kotlin/org/kopi/galite/tests/form/FormWithListDomainsTests.kt index 115a86c026..b1af1185b2 100644 --- a/galite-tests/src/test/kotlin/org/kopi/galite/tests/form/FormWithListDomainsTests.kt +++ b/galite-tests/src/test/kotlin/org/kopi/galite/tests/form/FormWithListDomainsTests.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013-2022 kopiLeft Services SARL, Tunis TN + * Copyright (c) 2013-2024 kopiLeft Services SARL, Tunis TN * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -21,7 +21,7 @@ import kotlin.test.assertEquals import org.jetbrains.exposed.sql.QueryAlias import org.jetbrains.exposed.sql.alias -import org.jetbrains.exposed.sql.select +import org.jetbrains.exposed.sql.selectAll import org.jetbrains.exposed.sql.targetTables import org.junit.Test import org.kopi.galite.tests.ui.swing.JApplicationTestBase @@ -33,7 +33,7 @@ class FormWithListDomainsTests : JApplicationTestBase() { @Test fun formWithListDomainsTests() { val model = FormWithListDomains.userListBlock.user.vField - val query = Users.select { Users.id greater 0 }.alias("syn__0__") + val query = Users.selectAll().where { Users.id greater 0 }.alias("syn__0__") assertEquals(query.alias, (model.list!!.table() as QueryAlias).alias) assertEquals(query.columns, model.list!!.table().columns) diff --git a/galite-tests/src/test/kotlin/org/kopi/galite/tests/form/FormWithNullableColumnTest.kt b/galite-tests/src/test/kotlin/org/kopi/galite/tests/form/FormWithNullableColumnTest.kt index 492399a334..69fb9470ef 100644 --- a/galite-tests/src/test/kotlin/org/kopi/galite/tests/form/FormWithNullableColumnTest.kt +++ b/galite-tests/src/test/kotlin/org/kopi/galite/tests/form/FormWithNullableColumnTest.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013-2022 kopiLeft Services SARL, Tunis TN + * Copyright (c) 2013-2024 kopiLeft Services SARL, Tunis TN * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -36,7 +36,7 @@ class FormWithNullableColumnsTest : JApplicationTestBase() { val table = VBlockDefaultOuterJoin.getSearchTables(block.block) val columns = block.block.getSearchColumns() - val query = table!!.slice(columns).selectAll() + val query = table!!.select(columns) transaction { assertEquals("SELECT CLIENTS.ID, CLIENTS.\"NAME\", CLIENTS.MAIL," + @@ -53,7 +53,7 @@ class FormWithNullableColumnsTest : JApplicationTestBase() { val table = VBlockDefaultOuterJoin.getSearchTables(block.block) val columns = block.block.getSearchColumns() - val query = table!!.slice(columns).selectAll() + val query = table!!.select(columns) transaction { assertEquals("SELECT \"ORDER\".CLIENT_ID, CLIENTS.\"NAME\", CLIENTS.MAIL, \"ORDER\".QUANTITY FROM CLIENTS" + @@ -70,7 +70,7 @@ class FormWithNullableColumnsTest : JApplicationTestBase() { val table = VBlockDefaultOuterJoin.getSearchTables(block.block) val columns = block.block.getSearchColumns() - val query = table!!.slice(columns).selectAll() + val query = table!!.select(columns) transaction { assertEquals("SELECT \"ORDER\".ID, CLIENTS.ID, PRODUCTS.ID, CLIENTS.TS, CLIENTS.UC, CLIENTS.\"NAME\"," + @@ -89,7 +89,7 @@ class FormWithNullableColumnsTest : JApplicationTestBase() { val table = VBlockDefaultOuterJoin.getSearchTables(block.block) val columns = block.block.getSearchColumns() - val query = table!!.slice(columns).selectAll() + val query = table!!.select(columns) transaction { assertEquals("SELECT \"ORDER\".ID, CLIENTS.ID, PRODUCTS.ID, CLIENTS.TS, CLIENTS.UC, CLIENTS.\"NAME\"," + @@ -108,7 +108,7 @@ class FormWithNullableColumnsTest : JApplicationTestBase() { val table = VBlockDefaultOuterJoin.getSearchTables(block.block) val columns = block.block.getSearchColumns() - val query = table!!.slice(columns).selectAll() + val query = table!!.select(columns) transaction { assertEquals("SELECT \"ORDER\".ID, CLIENTS.ID, CLIENTS.TS, CLIENTS.UC, CLIENTS.\"NAME\", CLIENTS.MAIL," + @@ -127,7 +127,7 @@ class FormWithNullableColumnsTest : JApplicationTestBase() { val table = VBlockDefaultOuterJoin.getSearchTables(block.block) val columns = block.block.getSearchColumns() - val query = table!!.slice(columns).selectAll() + val query = table!!.select(columns) transaction { assertEquals("SELECT \"ORDER\".ID, CLIENTS.ID, CLIENTS.TS, CLIENTS.UC, CLIENTS.\"NAME\", CLIENTS.MAIL," + @@ -145,7 +145,7 @@ class FormWithNullableColumnsTest : JApplicationTestBase() { val table = VBlockDefaultOuterJoin.getSearchTables(block.block) val columns = block.block.getSearchColumns() - val query = table!!.slice(columns).selectAll() + val query = table!!.select(columns) transaction { assertEquals("SELECT \"ORDER\".ID, CLIENTS.ID, CLIENTS.TS, CLIENTS.UC, CLIENTS.\"NAME\", CLIENTS.MAIL," + @@ -165,7 +165,7 @@ class FormWithNullableColumnsTest : JApplicationTestBase() { val table = VBlockDefaultOuterJoin.getSearchTables(block.block) val columns = block.block.getSearchColumns() - val query = table!!.slice(columns).selectAll() + val query = table!!.select(columns) transaction { assertEquals("SELECT \"ORDER\".ID, CLIENTS.ID, CLIENTS.TS, CLIENTS.UC, CLIENTS.\"NAME\", CLIENTS.MAIL," + @@ -185,7 +185,7 @@ class FormWithNullableColumnsTest : JApplicationTestBase() { val table = VBlockDefaultOuterJoin.getSearchTables(block.block) val columns = block.block.getSearchColumns() - val query = table!!.slice(columns).selectAll() + val query = table!!.select(columns) transaction { assertEquals("SELECT CLIENTS.ID, ADRESS.ID, PRODUCTS.ID, CLIENTS.\"NAME\", CLIENTS.MAIL," + @@ -204,7 +204,7 @@ class FormWithNullableColumnsTest : JApplicationTestBase() { val table = VBlockDefaultOuterJoin.getSearchTables(block.block) val columns = block.block.getSearchColumns() - val query = table!!.slice(columns).selectAll() + val query = table!!.select(columns) transaction { assertEquals("SELECT ADRESS.ID, CLIENTS.ID, \"ORDER\".PRODUCT_ID, CLIENTS.\"NAME\", CLIENTS.MAIL," + @@ -221,7 +221,7 @@ class FormWithNullableColumnsTest : JApplicationTestBase() { val table = VBlockDefaultOuterJoin.getSearchTables(block.block) val columns = block.block.getSearchColumns() - val query = table!!.slice(columns).selectAll() + val query = table!!.select(columns) transaction { assertEquals("SELECT ADRESS.ID, \"ORDER\".CLIENT_ID " + @@ -236,7 +236,7 @@ class FormWithNullableColumnsTest : JApplicationTestBase() { val table = VBlockDefaultOuterJoin.getSearchTables(block.block) val columns = block.block.getSearchColumns() - val query = table!!.slice(columns).selectAll() + val query = table!!.select(columns) transaction { assertEquals("SELECT \"ORDER\".CLIENT_ID, ADRESS.ID " + @@ -251,7 +251,7 @@ class FormWithNullableColumnsTest : JApplicationTestBase() { val table = VBlockDefaultOuterJoin.getSearchTables(block.block) val columns = block.block.getSearchColumns() - val query = table!!.slice(columns).selectAll() + val query = table!!.select(columns) transaction { assertEquals("SELECT ADRESS.CLIENT_ID, \"ORDER\".QUANTITY " + @@ -266,7 +266,7 @@ class FormWithNullableColumnsTest : JApplicationTestBase() { val table = VBlockDefaultOuterJoin.getSearchTables(block.block) val columns = block.block.getSearchColumns() - val query = table!!.slice(columns).selectAll() + val query = table!!.select(columns) transaction { assertEquals("SELECT \"ORDER\".QUANTITY, ADRESS.CLIENT_ID " + diff --git a/galite-tests/src/test/kotlin/org/kopi/galite/tests/form/FormWithNullableColumns.kt b/galite-tests/src/test/kotlin/org/kopi/galite/tests/form/FormWithNullableColumns.kt index fdef844064..d51b2a08a3 100644 --- a/galite-tests/src/test/kotlin/org/kopi/galite/tests/form/FormWithNullableColumns.kt +++ b/galite-tests/src/test/kotlin/org/kopi/galite/tests/form/FormWithNullableColumns.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013-2022 kopiLeft Services SARL, Tunis TN + * Copyright (c) 2013-2024 kopiLeft Services SARL, Tunis TN * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -42,7 +42,7 @@ object Clients : Table() { override val primaryKey = PrimaryKey(id) } -object Order : Table() { +object Order : Table("ORDER") { val id = integer("ID") val uc = integer("UC") val ts = integer("TS") diff --git a/galite-tests/src/test/kotlin/org/kopi/galite/tests/form/VBlockTests.kt b/galite-tests/src/test/kotlin/org/kopi/galite/tests/form/VBlockTests.kt index 6686ad388a..92c0d4490b 100644 --- a/galite-tests/src/test/kotlin/org/kopi/galite/tests/form/VBlockTests.kt +++ b/galite-tests/src/test/kotlin/org/kopi/galite/tests/form/VBlockTests.kt @@ -33,11 +33,9 @@ import org.jetbrains.exposed.sql.SchemaUtils import org.jetbrains.exposed.sql.SortOrder import org.jetbrains.exposed.sql.and import org.jetbrains.exposed.sql.insert -import org.jetbrains.exposed.sql.select import org.jetbrains.exposed.sql.selectAll import org.kopi.galite.database.Users -import org.kopi.galite.tests.database.connectToDatabase import org.kopi.galite.tests.examples.Center import org.kopi.galite.tests.examples.FormToTestSaveMultipleBlock import org.kopi.galite.tests.examples.Training @@ -135,7 +133,7 @@ class VBlockTests : VApplicationTestBase() { transaction { initSampleFormTables() FormSample.tb1.block.load() - val query = User.slice(User.name, User.age).selectAll() + val query = User.select(User.name, User.age) FormSample.tb1.block.deleteRecord(0) val deleteRecordList = query.map { @@ -440,7 +438,7 @@ class VBlockTests : VApplicationTestBase() { FormSample.tb1.block.load() FormSample.tb1.block.fetchNextRecord(0) - val query = User.select { User.name.eq("AUDREY") and User.age.eq(26) }.single() + val query = User.selectAll().where { User.name.eq("AUDREY") and User.age.eq(26) }.single() val listInfoUser = listOf(query[User.id], query[User.name], query[User.age], query[User.job]) assertEquals( @@ -514,7 +512,7 @@ class VBlockTests : VApplicationTestBase() { FormSample.tb1.setMode(Mode.UPDATE) FormSample.tb1.block.save() - val query = User.select { User.id eq 1 }.single() + val query = User.selectAll().where { User.id eq 1 }.single() val listInfoUser = listOf(query[User.id], query[User.name], query[User.age], query[User.job]) assertEquals( @@ -655,7 +653,7 @@ class VBlockTests : VApplicationTestBase() { val FormSample = FormSample() transaction { initSampleFormTables() - var count = User.select { User.id eq 1 }.count() + var count = User.selectAll().where { User.id eq 1 }.count() assertEquals(1, count) @@ -669,7 +667,7 @@ class VBlockTests : VApplicationTestBase() { FormSample.tb1.block.setRecordFetched(0, true) FormSample.tb1.block.delete() - count = User.select { User.id eq 1 }.count() + count = User.selectAll().where { User.id eq 1 }.count() assertEquals(0, count) SchemaUtils.drop(User) } @@ -767,7 +765,7 @@ class VBlockTests : VApplicationTestBase() { FormSample.tb1.age.value = 25 FormSample.tb1.block.load() - val query = User.select { User.id eq 3 }.single() + val query = User.selectAll().where { User.id eq 3 }.single() val listInfoUser = listOf(query[User.id], query[User.ts], query[User.uc], query[User.name], query[User.age], query[User.job]) assertEquals(listOf(FormSample.tb1.id.value, @@ -849,7 +847,7 @@ class VBlockTests : VApplicationTestBase() { FormSample.tb1.id.value = 1 FormSample.tb1.block.fetchLookup(FormSample.tb1.id.vField) - val query = User.select { User.name.eq("AUDREY") and User.age.eq(26) }.single() + val query = User.selectAll().where { User.name.eq("AUDREY") and User.age.eq(26) }.single() val listInfoUser = listOf(query[User.id], query[User.ts], query[User.uc], query[User.name], query[User.age], query[User.job]) assertEquals(listOf(FormSample.tb1.id.value, @@ -875,7 +873,7 @@ class VBlockTests : VApplicationTestBase() { val vExecFailedException = assertThrows(VExecFailedException::class.java) { FormSample.tb1.block.fetchLookup(FormSample.tb1.id.vField) } - assertEquals("VIS-00016: No matching value in User.", vExecFailedException.message) + assertEquals("VIS-00016: No matching value in ${User.tableName}.", vExecFailedException.message) SchemaUtils.drop(User) } } @@ -919,7 +917,7 @@ class VBlockTests : VApplicationTestBase() { val vExecFailedException = assertThrows(VExecFailedException::class.java) { FormSample.tb1.block.fetchLookup(FormSample.tb1.id.vField) } - assertEquals("VIS-00020: The value in User is not unique.", vExecFailedException.message) + assertEquals("VIS-00020: The value in ${User.tableName} is not unique.", vExecFailedException.message) SchemaUtils.drop(User) } } diff --git a/galite-tests/src/test/kotlin/org/kopi/galite/tests/ui/vaadin/chart/DocumentationChartTests.kt b/galite-tests/src/test/kotlin/org/kopi/galite/tests/ui/vaadin/chart/DocumentationChartTests.kt index b8bc8a6868..913242f00c 100644 --- a/galite-tests/src/test/kotlin/org/kopi/galite/tests/ui/vaadin/chart/DocumentationChartTests.kt +++ b/galite-tests/src/test/kotlin/org/kopi/galite/tests/ui/vaadin/chart/DocumentationChartTests.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013-2022 kopiLeft Services SARL, Tunis TN + * Copyright (c) 2013-2024 kopiLeft Services SARL, Tunis TN * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -25,7 +25,7 @@ import org.junit.Test import com.github.mvysny.kaributesting.v10._expectOne -import org.jetbrains.exposed.sql.select +import org.jetbrains.exposed.sql.selectAll import org.kopi.galite.testing.expectInformationNotification import org.kopi.galite.testing.open @@ -86,7 +86,7 @@ class DocumentationChartTests : GaliteVUITestBase() { // check that INIT trigger insert value in tha database transaction { - val value = TestTriggers.select{TestTriggers.id eq 5 }.last()[TestTriggers.INS] + val value = TestTriggers.selectAll().where { TestTriggers.id eq 5 }.last()[TestTriggers.INS] assertEquals("INITCHART Trigger", value) } @@ -99,7 +99,7 @@ class DocumentationChartTests : GaliteVUITestBase() { // check that PRECHART trigger insert value in tha database transaction { - val value = TestTriggers.select{TestTriggers.id eq 6 }.last()[TestTriggers.INS] + val value = TestTriggers.selectAll().where { TestTriggers.id eq 6 }.last()[TestTriggers.INS] assertEquals("PRECHART Trigger", value) } @@ -115,7 +115,7 @@ class DocumentationChartTests : GaliteVUITestBase() { // check that POSTCHART trigger insert value in tha database transaction { - val value = TestTriggers.select{TestTriggers.id eq 7 }.last()[TestTriggers.INS] + val value = TestTriggers.selectAll().where { TestTriggers.id eq 7 }.last()[TestTriggers.INS] assertEquals("POSTCHART Trigger", value) } diff --git a/galite-tests/src/test/kotlin/org/kopi/galite/tests/ui/vaadin/field/DocumentationFieldsFormTests.kt b/galite-tests/src/test/kotlin/org/kopi/galite/tests/ui/vaadin/field/DocumentationFieldsFormTests.kt index 8c09f5c5a7..b2840fe1cd 100644 --- a/galite-tests/src/test/kotlin/org/kopi/galite/tests/ui/vaadin/field/DocumentationFieldsFormTests.kt +++ b/galite-tests/src/test/kotlin/org/kopi/galite/tests/ui/vaadin/field/DocumentationFieldsFormTests.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013-2022 kopiLeft Services SARL, Tunis TN + * Copyright (c) 2013-2024 kopiLeft Services SARL, Tunis TN * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -43,7 +43,6 @@ import com.vaadin.flow.data.provider.SortDirection import org.jetbrains.exposed.sql.and import org.jetbrains.exposed.sql.JoinType -import org.jetbrains.exposed.sql.select import org.jetbrains.exposed.sql.selectAll import org.kopi.galite.testing._enter @@ -289,7 +288,7 @@ class DocumentationFieldsFormTests : GaliteVUITestBase() { form.serialQuery.triggerCommand() transaction { - val data = TestTable.select { (TestTable.name like "NA%") and (TestTable.lastName like "last%") }.map { + val data = TestTable.selectAll().where { (TestTable.name like "NA%") and (TestTable.lastName like "last%") }.map { arrayOf( it[TestTable.name], it[TestTable.lastName] diff --git a/galite-tests/src/test/kotlin/org/kopi/galite/tests/ui/vaadin/pivottable/DocumentationPivotTableTests.kt b/galite-tests/src/test/kotlin/org/kopi/galite/tests/ui/vaadin/pivottable/DocumentationPivotTableTests.kt index 8fae02690e..9ea1546f51 100644 --- a/galite-tests/src/test/kotlin/org/kopi/galite/tests/ui/vaadin/pivottable/DocumentationPivotTableTests.kt +++ b/galite-tests/src/test/kotlin/org/kopi/galite/tests/ui/vaadin/pivottable/DocumentationPivotTableTests.kt @@ -1,6 +1,6 @@ /* - * Copyright (c) 2013-2023 kopiLeft Services SARL, Tunis TN - * Copyright (c) 1990-2023 kopiRight Managed Solutions GmbH, Wien AT + * Copyright (c) 2013-2024 kopiLeft Services SARL, Tunis TN + * Copyright (c) 1990-2024 kopiRight Managed Solutions GmbH, Wien AT * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -27,7 +27,7 @@ import org.junit.Test import com.github.mvysny.kaributesting.v10._expectOne -import org.jetbrains.exposed.sql.select +import org.jetbrains.exposed.sql.selectAll import org.kopi.galite.testing.expectInformationNotification import org.kopi.galite.testing.open @@ -85,7 +85,7 @@ class DocumentationPivotTableTests : GaliteVUITestBase() { // check that INIT trigger insert value in tha database transaction { - val value = TestTriggers.select{TestTriggers.id eq 5 }.last()[TestTriggers.INS] + val value = TestTriggers.selectAll().where { TestTriggers.id eq 5 }.last()[TestTriggers.INS] assertEquals("INIT Trigger", value) } diff --git a/gradle.properties b/gradle.properties index 0cfaf24723..a67231af4f 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,4 +1,13 @@ org.gradle.jvmargs=-Xmx4g # group=org.kopi -version=1.5.6 +version=1.5.8-02EM + +systemProp.http.proxyHost=proxy.kopileft.com +systemProp.http.proxyPort=3128 +systemProp.https.proxyHost=proxy.kopileft.com +systemProp.https.proxyPort=3128 + +# Bypass proxy for all other IP addresses except those starting with 192.161.191 +systemProp.http.nonProxyHosts=!(192.161.191.*) +systemProp.https.nonProxyHosts=!(192.161.191.*)