From 82f1a025b8be62f6e55fd8518bc96db909c8c54e Mon Sep 17 00:00:00 2001 From: Dmitry Barashev Date: Thu, 19 Sep 2024 19:02:32 +0400 Subject: [PATCH 1/4] Fixed bugs: - read XML issue, all columns were custom - sync between the visibility toggle and label in the list. Simplified the model: copies are not used anymore, cells are refreshed directly instead of changing the list --- .../biz/ganttproject/core/io/XmlSerializer.kt | 7 ++- .../ganttproject/ganttview/ColumnManager.kt | 63 ++++++++----------- .../ganttproject/ganttview/FilterDialog.kt | 3 +- .../ganttview/SharedUiComponents.kt | 63 ++++++++++++++----- .../ganttview/TaskTableFilters.kt | 31 ++++++--- .../ganttproject/io/ViewSaver.java | 4 +- 6 files changed, 104 insertions(+), 67 deletions(-) diff --git a/biz.ganttproject.core/src/main/java/biz/ganttproject/core/io/XmlSerializer.kt b/biz.ganttproject.core/src/main/java/biz/ganttproject/core/io/XmlSerializer.kt index b717ca669d..a27510d93d 100644 --- a/biz.ganttproject.core/src/main/java/biz/ganttproject/core/io/XmlSerializer.kt +++ b/biz.ganttproject.core/src/main/java/biz/ganttproject/core/io/XmlSerializer.kt @@ -111,17 +111,20 @@ data class XmlView( ) } +/** + * Task filter serialization. + */ data class XmlFilter( @get:JacksonXmlProperty(isAttribute = true) var title: String = "", - @get:JacksonXmlProperty(isAttribute = true) + @get:JacksonXmlProperty(isAttribute = true, localName = "is-built-in") var isBuiltIn: Boolean = false, @get:JacksonXmlProperty(isAttribute = true) var description: String = "", - @get:JacksonXmlProperty(isAttribute = true) + @get:JacksonXmlProperty(isAttribute = true, localName = "is-enabled") var isEnabled: Boolean = false, @get:JsonInclude(JsonInclude.Include.NON_NULL) diff --git a/ganttproject/src/main/java/biz/ganttproject/ganttview/ColumnManager.kt b/ganttproject/src/main/java/biz/ganttproject/ganttview/ColumnManager.kt index a6916e01b7..3203b183dc 100644 --- a/ganttproject/src/main/java/biz/ganttproject/ganttview/ColumnManager.kt +++ b/ganttproject/src/main/java/biz/ganttproject/ganttview/ColumnManager.kt @@ -51,7 +51,7 @@ class ColumnManager( internal val dialogModel: ItemListDialogModel = ItemListDialogModel( listItems, newItemFactory = { - ColumnAsListItem(null, isVisible = true, isCustom = true, customColumnsManager, {customPropertyEditor.updateVisibility(it)}) + ColumnAsListItem(null, isVisible = true, isCustom = true, customColumnsManager) }, ourLocalizer ) @@ -64,7 +64,7 @@ class ColumnManager( calculationMethodValidator = calculationMethodValidator, expressionAutoCompletion = expressionAutoCompletion, nameClash = { tryName -> - listItems.find { it.title == tryName && it != selectedItem.value?.cloneOf } != null + listItems.find { it.title == tryName && it != selectedItem.value } != null }, localizer = ourEditorLocalizer ), @@ -75,7 +75,7 @@ class ColumnManager( internal val dialogPane = ItemListDialogPane( listItems = listItems, selectedItem = selectedItem, - listItemConverter = { ShowHideListItem(it.title, it.isVisibleProperty) }, + listItemConverter = { ShowHideListItem( {it.title}, {it.isEnabledProperty.value}, {it.isEnabledProperty.set(!it.isEnabledProperty.value)}) }, dialogModel = dialogModel, editor = customPropertyEditor, localizer = ourEditorLocalizer @@ -86,13 +86,13 @@ class ColumnManager( // and they are ordered the same way are shown in the table. listItems.setAll(mergedColumns.sortedWith { col1, col2 -> columnsOrder(col1, col2) }.map { col -> val isCustom = customColumnsManager.definitions.find { it.id == col.id } != null - ColumnAsListItem(col, col.isVisible, isCustom, customColumnsManager, customPropertyEditor::updateVisibility) + ColumnAsListItem(col, col.isVisible, isCustom, customColumnsManager) }) customColumnsManager.definitions.forEach { def -> if (mergedColumns.find { it.id == def.id } == null) { val columnStub = ColumnList.ColumnStub(def.id, def.name, false, -1, -1) mergedColumns.add(columnStub) - listItems.add(ColumnAsListItem(columnStub, columnStub.isVisible, true, customColumnsManager, customPropertyEditor::updateVisibility)) + listItems.add(ColumnAsListItem(columnStub, columnStub.isVisible, true, customColumnsManager)) } } } @@ -115,14 +115,14 @@ class ColumnManager( listItems.forEach { columnItem -> columnItem.column?.let { // Apply changes made in the columns which has existed before. - it.isVisible = columnItem.isVisible + it.isVisible = columnItem.isEnabledProperty.value if (columnItem.isCustom) { customColumnsManager.definitions.find { def -> def.id == it.id }?.importColumnItem(columnItem) } } ?: run { // Create custom columns which were added in the dialog val def = customColumnsManager.createDefinition(columnItem.type.getCustomPropertyClass(), columnItem.title, columnItem.defaultValue) - if (columnItem.isVisible) { + if (columnItem.isEnabledProperty.value) { mergedColumns.add(ColumnList.ColumnStub(def.id, def.name, true, mergedColumns.size, 50)) } if (columnItem.isCalculated) { @@ -251,7 +251,7 @@ internal class EditorModel( */ internal class CustomPropertyEditor( private val model: EditorModel, - dialogModel: ItemListDialogModel, + private val dialogModel: ItemListDialogModel, selectedItemProperty: ObservableProperty, private val btnDeleteController: BtnController, escCloseEnabled: BooleanProperty, @@ -263,10 +263,11 @@ internal class CustomPropertyEditor( override fun loadData(item: ColumnAsListItem?) { if (item != null) { + //visibilityToggle.selectedProperty().unbind() model.nameOption.set(item.title) model.typeOption.set(item.type) model.defaultValueOption.set(item.defaultValue) - visibilityToggle.isSelected = item.isVisible + visibilityToggle.isSelected = item.isEnabledProperty.value if (item.isCustom) { propertySheetLabel.text = ourLocalizer.formatText("propertyPane.title.custom") @@ -285,29 +286,30 @@ internal class CustomPropertyEditor( } override fun saveData(item: ColumnAsListItem) { - item.isVisible = visibilityToggle.isSelected + item.isEnabledProperty.set(visibilityToggle.isSelected) item.title = model.nameOption.value ?: "" item.type = model.typeOption.value item.defaultValue = model.defaultValueOption.value ?: "" item.isCalculated = model.isCalculatedOption.value item.expression = model.expressionOption.value ?: "" + dialogModel.requireRefresh.set(true) } init { model.allOptions.forEach { it.addWatcher { onEdit() } } - visibilityToggle.selectedProperty().addListener { _, _, _ -> - onEdit() - } +// visibilityToggle.selectedProperty().addListener { _, _, _ -> +// onEdit() +// } } internal fun updateVisibility(item: ColumnAsListItem) { - editItem.value?.let { - if (it.column?.id == item.column?.id && it.isVisible != item.isVisible) { - it.isVisible = item.isVisible - visibilityToggle.isSelected = item.isVisible - } - } +// editItem.value?.let { +// if (it.column?.id == item.column?.id && it.isVisible != item.isVisible) { +// it.isVisible = item.isVisible +// visibilityToggle.isSelected = item.isVisible +// } +// } } } @@ -320,13 +322,13 @@ internal class ColumnAsListItem( isVisible: Boolean, val isCustom: Boolean, val customColumnsManager: CustomPropertyManager, - val changeListener: (ColumnAsListItem)->Unit, override val cloneOf: ColumnAsListItem? = null ): Item { + override val isEnabledProperty: BooleanProperty = SimpleBooleanProperty(isVisible) + constructor(cloneOf: ColumnAsListItem): this( - cloneOf.column, cloneOf.isVisible, cloneOf.isCustom, cloneOf.customColumnsManager, - cloneOf.changeListener, cloneOf) { + cloneOf.column, cloneOf.isEnabledProperty.value, cloneOf.isCustom, cloneOf.customColumnsManager, cloneOf) { this.title = cloneOf.title this.type = cloneOf.type @@ -335,19 +337,6 @@ internal class ColumnAsListItem( this.expression = cloneOf.expression } - val isVisibleProperty = ObservableBoolean("", isVisible).also { - it.addWatcher { changeListener(this@ColumnAsListItem) } - } - - var isVisible: Boolean - set(value) { - if (isVisibleProperty.value != value) { - isVisibleProperty.set(value) - changeListener(this) - } - } - get() = isVisibleProperty.value - override var title: String = "" var type: PropertyType = PropertyType.STRING @@ -363,10 +352,10 @@ internal class ColumnAsListItem( return "ColumnAsListItem(title='$title')" } - override fun clone(): ColumnAsListItem = ColumnAsListItem(this) + override fun clone(forEditing: Boolean): ColumnAsListItem = ColumnAsListItem(this) init { - this.isVisible = isVisible +// this.isVisible = isVisible if (column != null) { title = column.name val customColumn = customColumnsManager.definitions.find { it.id == column.id } diff --git a/ganttproject/src/main/java/biz/ganttproject/ganttview/FilterDialog.kt b/ganttproject/src/main/java/biz/ganttproject/ganttview/FilterDialog.kt index 9a454d1914..d488a3dce5 100644 --- a/ganttproject/src/main/java/biz/ganttproject/ganttview/FilterDialog.kt +++ b/ganttproject/src/main/java/biz/ganttproject/ganttview/FilterDialog.kt @@ -22,7 +22,6 @@ import biz.ganttproject.app.MappingLocalizer import biz.ganttproject.app.RootLocalizer import biz.ganttproject.app.dialog import biz.ganttproject.core.option.Completion -import biz.ganttproject.core.option.DefaultBooleanOption import biz.ganttproject.core.option.ObservableObject import biz.ganttproject.core.option.ObservableString import biz.ganttproject.core.option.ValidationException @@ -53,7 +52,7 @@ fun showFilterDialog(filterManager: TaskFilterManager, projectDatabase: ProjectD val dialogPane = ItemListDialogPane( listItems, editItem, - { filter -> ShowHideListItem(filter.title, filter.isEnabledProperty) }, + { filter -> ShowHideListItem({filter.title}, {filter.isEnabledProperty.value}, {filter.isEnabledProperty.set(!filter.isEnabledProperty.get())}) }, dialogModel, editor, i18n diff --git a/ganttproject/src/main/java/biz/ganttproject/ganttview/SharedUiComponents.kt b/ganttproject/src/main/java/biz/ganttproject/ganttview/SharedUiComponents.kt index 5bb86d0067..510b4c0f8b 100644 --- a/ganttproject/src/main/java/biz/ganttproject/ganttview/SharedUiComponents.kt +++ b/ganttproject/src/main/java/biz/ganttproject/ganttview/SharedUiComponents.kt @@ -23,12 +23,14 @@ import biz.ganttproject.app.Localizer import biz.ganttproject.app.PropertySheetBuilder import biz.ganttproject.app.RootLocalizer import biz.ganttproject.core.option.GPObservable +import biz.ganttproject.core.option.ObservableBoolean import biz.ganttproject.core.option.ObservableProperty import biz.ganttproject.lib.fx.VBoxBuilder import biz.ganttproject.lib.fx.createToggleSwitch import biz.ganttproject.lib.fx.vbox import de.jensd.fx.glyphs.materialicons.MaterialIcon import de.jensd.fx.glyphs.materialicons.MaterialIconView +import javafx.beans.property.BooleanProperty import javafx.beans.property.SimpleBooleanProperty import javafx.collections.MapChangeListener import javafx.collections.ObservableList @@ -45,12 +47,14 @@ import javafx.scene.layout.HBox import javafx.scene.layout.Priority import javafx.scene.layout.StackPane import javafx.util.Callback +import java.util.WeakHashMap /** * This is an object that is rendered in a list view. */ -data class ShowHideListItem(val text: String, val isVisible: GPObservable) +data class ShowHideListItem(val text: ()->String, val isVisible: ()->Boolean, val toggleVisible: ()->Unit) +private val ourCells = WeakHashMap, Boolean>() /** * A list cell is a UI component that renders items in a list view. It adds a show/hide icon to the item title. */ @@ -59,7 +63,7 @@ internal class ShowHideListCell(private val converter: (T)-> ShowHideListItem private val iconHidden = MaterialIconView(MaterialIcon.VISIBILITY_OFF) private val iconPane = StackPane().also { it.onMouseClicked = EventHandler { _ -> - theItem.isVisible.value = !theItem.isVisible.value + theItem.toggleVisible() updateTheItem(theItem) } } @@ -68,6 +72,7 @@ internal class ShowHideListCell(private val converter: (T)-> ShowHideListItem init { styleClass.add("column-item-cell") alignment = Pos.CENTER_LEFT + ourCells.put(this, true) } override fun updateItem(item: T?, empty: Boolean) { @@ -81,14 +86,14 @@ internal class ShowHideListCell(private val converter: (T)-> ShowHideListItem } private fun updateTheItem(item: ShowHideListItem) { - text = item.text + text = item.text() if (text.isEmpty()) { text = " " } if (graphic == null) { graphic = iconPane } - if (item.isVisible.value) { + if (item.isVisible()) { styleClass.add("is-visible") styleClass.remove("is-hidden") iconPane.children.setAll(iconVisible) @@ -100,6 +105,12 @@ internal class ShowHideListCell(private val converter: (T)-> ShowHideListItem } } } + + fun refresh() { + if (item != null) { + updateTheItem(converter(item)) + } + } } /** @@ -146,10 +157,18 @@ internal open class ItemEditorPane>( init { fields.forEach { it.addWatcher { onEdit() } } - editItem.addWatcher { - if (it.trigger != this) { + visibilityToggle.selectedProperty().subscribe { oldValue, newValue -> + onEdit() + } + editItem.addWatcher {evt -> + if (evt.trigger != this) { isEditIgnored = true - loadData(it.newValue) + evt.newValue?.isEnabledProperty?.addListener { source, oldValue, newValue -> + if (oldValue != newValue && source == editItem.value?.isEnabledProperty) { + visibilityToggle.isSelected = newValue + } + } + loadData(evt.newValue) isEditIgnored = false } } @@ -174,10 +193,11 @@ internal open class ItemEditorPane>( internal fun onEdit() { if (isEditIgnored) return - editItem.value?.clone()?.let { + editItem.value?.let { saveData(it) - editItem.set(it, trigger = this) + //editItem.set(it, trigger = this) } + dialogModel.requireRefresh.set(true) } internal fun focus() { @@ -205,8 +225,9 @@ internal open class ItemEditorPane>( interface Item { var title: String + val isEnabledProperty: BooleanProperty val cloneOf: T? - fun clone(): T + fun clone(forEditing: Boolean): T } data class BtnController( @@ -226,6 +247,7 @@ internal class ItemListDialogModel>( val btnDeleteController = BtnController(onAction = this::onDeleteColumn) val btnApplyController = BtnController(onAction = {}) internal var selection: ()-> Collection = { emptyList() } + val requireRefresh = SimpleBooleanProperty(false) fun onAddColumn(): T { val item = newItemFactory() @@ -271,22 +293,31 @@ internal class ItemListDialogPane>( internal val listView: ListView = ListView() init { - selectedItem.addWatcher { evt -> - if (evt.trigger != listView && evt.newValue != null) { - listItems.replaceAll { if (it.title == evt.newValue?.cloneOf?.title) { evt.newValue } else { it } } - } - } +// selectedItem.addWatcher { evt -> +// if (evt.trigger != listView && evt.newValue != null) { +// listItems.replaceAll { if (it == evt.newValue?.cloneOf) { evt.newValue?.clone(forEditing = false) } else { it } } +// } +// } listView.apply { this@ItemListDialogPane.dialogModel.selection = { selectionModel.selectedItems } items = listItems cellFactory = Callback { ShowHideListCell(listItemConverter)} selectionModel.selectedItemProperty().addListener { _, _, newValue -> if (newValue != null) { - selectedItem.set(newValue.clone(), trigger = this) + selectedItem.set(newValue, trigger = this) } } selectionModel.select(0) } + dialogModel.requireRefresh.subscribe { oldValue, newValue -> + if (newValue == true && oldValue == false) { +// listView.refresh() + ourCells.keys.forEach { + it.refresh() + } + dialogModel.requireRefresh.set(false) + } + } } fun build(dlg: DialogController) { diff --git a/ganttproject/src/main/java/biz/ganttproject/ganttview/TaskTableFilters.kt b/ganttproject/src/main/java/biz/ganttproject/ganttview/TaskTableFilters.kt index c69ad46471..a72cec7a64 100644 --- a/ganttproject/src/main/java/biz/ganttproject/ganttview/TaskTableFilters.kt +++ b/ganttproject/src/main/java/biz/ganttproject/ganttview/TaskTableFilters.kt @@ -25,6 +25,8 @@ import biz.ganttproject.core.option.ObservableBoolean import biz.ganttproject.core.time.CalendarFactory import biz.ganttproject.customproperty.CustomPropertyClass import biz.ganttproject.customproperty.SimpleSelect +import javafx.beans.property.BooleanProperty +import javafx.beans.property.SimpleBooleanProperty import javafx.beans.property.SimpleIntegerProperty import net.sourceforge.ganttproject.GPLogger import net.sourceforge.ganttproject.storage.ColumnConsumer @@ -33,6 +35,7 @@ import net.sourceforge.ganttproject.task.Task import net.sourceforge.ganttproject.task.TaskManager import net.sourceforge.ganttproject.task.event.TaskListenerAdapter import net.sourceforge.ganttproject.undo.GPUndoListener +import javax.swing.event.ChangeListener import javax.swing.event.UndoableEditEvent /** @@ -41,14 +44,14 @@ import javax.swing.event.UndoableEditEvent data class TaskFilter( override var title: String, var description: String, - val isEnabledProperty: GPObservable, + override val isEnabledProperty: BooleanProperty = SimpleBooleanProperty(false), val filterFxn: TaskFilterFxn, var expression: String? = null, val isBuiltIn: Boolean = false, override val cloneOf: TaskFilter? = null ): Item { - override fun clone(): TaskFilter = copy(cloneOf = this) + override fun clone(forEditing: Boolean): TaskFilter = copy(cloneOf = this) } typealias TaskFilterFxn = (parent: Task, child: Task?) -> Boolean @@ -65,7 +68,7 @@ object BuiltInFilters { child?.completionPercentage?.let { it < 100 } != false } val completedTasksFilter = TaskFilter( - "filter.completedTasks", "", filterCompletedTasksOption.asObservableValue(), completedTasksFilterFxn, isBuiltIn = true + "filter.completedTasks", "", filterCompletedTasksOption.asJavafxProperty(), completedTasksFilterFxn, isBuiltIn = true ) // ---------------------------- private val dueTodayFilterFxn: TaskFilterFxn = { _, child -> @@ -74,7 +77,7 @@ object BuiltInFilters { } != false } val dueTodayFilter = TaskFilter( - "filter.dueTodayTasks", "", filterDueTodayOption.asObservableValue(), dueTodayFilterFxn, isBuiltIn = true + "filter.dueTodayTasks", "", filterDueTodayOption.asJavafxProperty(), dueTodayFilterFxn, isBuiltIn = true ) // ---------------------------- private val overdueFilterFxn: TaskFilterFxn = { _, child -> @@ -82,7 +85,7 @@ object BuiltInFilters { } != false } val overdueFilter = TaskFilter( - "filter.overdueTasks", "", filterOverdueOption.asObservableValue(), overdueFilterFxn, isBuiltIn = true + "filter.overdueTasks", "", filterOverdueOption.asJavafxProperty(), overdueFilterFxn, isBuiltIn = true ) // ---------------------------- private val inProgressTodayFilterFxn: TaskFilterFxn = { _, child -> @@ -91,7 +94,7 @@ object BuiltInFilters { } != false } val inProgressTodayFilter = TaskFilter( - "filter.inProgressTodayTasks", "", filterInProgressTodayOption.asObservableValue(), inProgressTodayFilterFxn, isBuiltIn = true + "filter.inProgressTodayTasks", "", filterInProgressTodayOption.asJavafxProperty(), inProgressTodayFilterFxn, isBuiltIn = true ) val allFilters: List get() = listOf( @@ -152,7 +155,7 @@ class TaskFilterManager(val taskManager: TaskManager, val projectDatabase: Proje }) } - fun createCustomFilter() = TaskFilter("", "", DefaultBooleanOption("", false), + fun createCustomFilter() = TaskFilter("", "", filterFxn = { parent, child -> customFilterFxn(parent, child)}, isBuiltIn = false ) @@ -187,10 +190,22 @@ class TaskFilterManager(val taskManager: TaskManager, val projectDatabase: Proje internal var sync: ()->Unit = {} } +private fun DefaultBooleanOption.asJavafxProperty() = SimpleBooleanProperty(this.value).also { + it.subscribe { oldValue, newValue -> + if (newValue != oldValue) { + this.setValue(newValue, it) + } + } + this.asObservableValue().addWatcher { evt -> + if (evt.newValue != evt.oldValue && evt.trigger != it) { + it.value = evt.newValue + } + } +} private fun today() = CalendarFactory.createGanttCalendar(CalendarFactory.newCalendar().time) private fun Task.endsToday() = this.end.displayValue == today() private fun Task.endsBeforeToday() = this.end.displayValue < today() private fun Task.runsToday() = today().let { this.end.displayValue >= it && this.start <= it } val VOID_FILTER_FXN: TaskFilterFxn = { _, _ -> true } -val VOID_FILTER: TaskFilter = TaskFilter("filter.void", "", ObservableBoolean("", false), isBuiltIn = true, filterFxn = VOID_FILTER_FXN) +val VOID_FILTER: TaskFilter = TaskFilter("filter.void", "", isBuiltIn = true, filterFxn = VOID_FILTER_FXN) private val LOGGER = GPLogger.create("TaskTable.Filters") \ No newline at end of file diff --git a/ganttproject/src/main/java/net/sourceforge/ganttproject/io/ViewSaver.java b/ganttproject/src/main/java/net/sourceforge/ganttproject/io/ViewSaver.java index 99e55bbd79..c1e7ecdd38 100644 --- a/ganttproject/src/main/java/net/sourceforge/ganttproject/io/ViewSaver.java +++ b/ganttproject/src/main/java/net/sourceforge/ganttproject/io/ViewSaver.java @@ -107,8 +107,8 @@ private void writeFilters(TransformerHandler handler, TaskFilterManager taskFilt var attrs = new AttributesImpl(); addAttribute("title", filter.getTitle(), attrs); addAttribute("description", filter.getDescription(), attrs); - addAttribute("isBuiltIn", filter.isBuiltIn(), attrs); - addAttribute("isEnabled", filter.isEnabledProperty().getValue(), attrs); + addAttribute("is-built-in", filter.isBuiltIn(), attrs); + addAttribute("is-enabled", filter.isEnabledProperty().getValue(), attrs); try { startElement("filter", attrs, handler); if (filter.getExpression() != null) { From 93e27e63a619ef8e545cee06e3e0e9ab5924301f Mon Sep 17 00:00:00 2001 From: Dmitry Barashev Date: Sat, 21 Sep 2024 15:42:57 +0400 Subject: [PATCH 2/4] Removed obsolete code --- .../core/model/task/TaskDefaultColumn.java | 13 +++ .../ganttproject/ganttview/ColumnManager.kt | 87 +++---------------- .../ganttview/SharedUiComponents.kt | 2 - .../ganttview/TaskTableFilters.kt | 9 +- 4 files changed, 26 insertions(+), 85 deletions(-) diff --git a/biz.ganttproject.core/src/main/java/biz/ganttproject/core/model/task/TaskDefaultColumn.java b/biz.ganttproject.core/src/main/java/biz/ganttproject/core/model/task/TaskDefaultColumn.java index af3940afee..5589ed34c7 100644 --- a/biz.ganttproject.core/src/main/java/biz/ganttproject/core/model/task/TaskDefaultColumn.java +++ b/biz.ganttproject.core/src/main/java/biz/ganttproject/core/model/task/TaskDefaultColumn.java @@ -20,6 +20,7 @@ of the License, or (at your option) any later version. import biz.ganttproject.core.table.ColumnList; import biz.ganttproject.core.table.ColumnList.Column; +import biz.ganttproject.customproperty.CustomPropertyClass; import javax.swing.*; import java.awt.*; @@ -136,6 +137,18 @@ public String getName() { return ourLocaleApi == null ? getNameKey() : ourLocaleApi.i18n(getNameKey()); } + public CustomPropertyClass getCustomPropertyClass() { + if (myValueClass == Integer.class) { + return CustomPropertyClass.INTEGER; + } + if (myValueClass == BigDecimal.class) { + return CustomPropertyClass.DOUBLE; + } + if (myValueClass == GregorianCalendar.class) { + return CustomPropertyClass.DATE; + } + return CustomPropertyClass.TEXT; + } public static class Functions { static Predicate NOT_EDITABLE = input -> false; static Predicate ALWAYS_EDITABLE = input -> true; diff --git a/ganttproject/src/main/java/biz/ganttproject/ganttview/ColumnManager.kt b/ganttproject/src/main/java/biz/ganttproject/ganttview/ColumnManager.kt index 3203b183dc..5f19a33c89 100644 --- a/ganttproject/src/main/java/biz/ganttproject/ganttview/ColumnManager.kt +++ b/ganttproject/src/main/java/biz/ganttproject/ganttview/ColumnManager.kt @@ -121,7 +121,7 @@ class ColumnManager( } } ?: run { // Create custom columns which were added in the dialog - val def = customColumnsManager.createDefinition(columnItem.type.getCustomPropertyClass(), columnItem.title, columnItem.defaultValue) + val def = customColumnsManager.createDefinition(columnItem.type, columnItem.title, columnItem.defaultValue) if (columnItem.isEnabledProperty.value) { mergedColumns.add(ColumnList.ColumnStub(def.id, def.name, true, mergedColumns.size, 50)) } @@ -136,37 +136,10 @@ class ColumnManager( } } - -internal enum class PropertyType(private val displayName: String) { - STRING("text"), - INTEGER("integer"), - DATE("date"), - DECIMAL("double"), - BOOLEAN("boolean"); - - override fun toString() = this.displayName -} - -internal fun CustomPropertyDefinition.getPropertyType(): PropertyType = when (this.propertyClass) { - CustomPropertyClass.TEXT -> PropertyType.STRING - CustomPropertyClass.DATE -> PropertyType.DATE - CustomPropertyClass.INTEGER -> PropertyType.INTEGER - CustomPropertyClass.DOUBLE -> PropertyType.DECIMAL - CustomPropertyClass.BOOLEAN -> PropertyType.BOOLEAN -} - -internal fun PropertyType.getCustomPropertyClass(): CustomPropertyClass = when (this) { - PropertyType.STRING -> CustomPropertyClass.TEXT - PropertyType.INTEGER -> CustomPropertyClass.INTEGER - PropertyType.DATE -> CustomPropertyClass.DATE - PropertyType.BOOLEAN -> CustomPropertyClass.BOOLEAN - PropertyType.DECIMAL -> CustomPropertyClass.DOUBLE -} - -internal fun PropertyType.createValidator(): ValueValidator<*> = when (this) { - PropertyType.INTEGER -> integerValidator - PropertyType.DECIMAL -> doubleValidator - PropertyType.DATE -> createStringDateValidator { +internal fun CustomPropertyClass.createValidator(): ValueValidator<*> = when (this) { + CustomPropertyClass.INTEGER -> integerValidator + CustomPropertyClass.DOUBLE -> doubleValidator + CustomPropertyClass.DATE -> createStringDateValidator { listOf(GanttLanguage.getInstance().shortDateFormat, GanttLanguage.getInstance().mediumDateFormat) } else -> voidValidator @@ -177,7 +150,7 @@ internal fun CustomPropertyDefinition.importColumnItem(item: ColumnAsListItem) { if (item.defaultValue.trim().isNotBlank()) { this.defaultValueAsString = item.defaultValue } - this.propertyClass = item.type.getCustomPropertyClass() + this.propertyClass = item.type if (item.isCalculated) { this.calculationMethod = SimpleSelect(propertyId = this.id, selectExpression = item.expression, resultClass = this.propertyClass.javaClass) } else { @@ -185,13 +158,6 @@ internal fun CustomPropertyDefinition.importColumnItem(item: ColumnAsListItem) { } } -internal fun TaskDefaultColumn.getPropertyType(): PropertyType = when (this) { - TaskDefaultColumn.ID, TaskDefaultColumn.DURATION, TaskDefaultColumn.COMPLETION -> PropertyType.INTEGER - TaskDefaultColumn.BEGIN_DATE, TaskDefaultColumn.END_DATE -> PropertyType.DATE - TaskDefaultColumn.COST -> PropertyType.DECIMAL - else -> PropertyType.STRING -} - /** * This the model of the property editor. It encapsulates the values that are being edited and their validators. */ @@ -210,7 +176,7 @@ internal class EditorModel( } value }) - val typeOption = ObservableEnum(id = "type", initValue = PropertyType.STRING, allValues = PropertyType.values()) + val typeOption = ObservableEnum(id = "type", initValue = CustomPropertyClass.TEXT, allValues = CustomPropertyClass.entries.toTypedArray()) val defaultValueOption = ObservableString( id = "defaultValue", initValue = "", @@ -232,7 +198,7 @@ internal class EditorModel( if (it.isNotBlank()) { calculationMethodValidator.validate( // Incomplete instance just for validation purposes - SimpleSelect(propertyId = "", selectExpression = it, resultClass = typeOption.value.getCustomPropertyClass().javaClass) + SimpleSelect(propertyId = "", selectExpression = it, resultClass = typeOption.value.javaClass) ) it } else { @@ -263,7 +229,6 @@ internal class CustomPropertyEditor( override fun loadData(item: ColumnAsListItem?) { if (item != null) { - //visibilityToggle.selectedProperty().unbind() model.nameOption.set(item.title) model.typeOption.set(item.type) model.defaultValueOption.set(item.defaultValue) @@ -297,21 +262,7 @@ internal class CustomPropertyEditor( init { model.allOptions.forEach { it.addWatcher { onEdit() } } - -// visibilityToggle.selectedProperty().addListener { _, _, _ -> -// onEdit() -// } } - - internal fun updateVisibility(item: ColumnAsListItem) { -// editItem.value?.let { -// if (it.column?.id == item.column?.id && it.isVisible != item.isVisible) { -// it.isVisible = item.isVisible -// visibilityToggle.isSelected = item.isVisible -// } -// } - } - } /** @@ -322,24 +273,13 @@ internal class ColumnAsListItem( isVisible: Boolean, val isCustom: Boolean, val customColumnsManager: CustomPropertyManager, - override val cloneOf: ColumnAsListItem? = null ): Item { override val isEnabledProperty: BooleanProperty = SimpleBooleanProperty(isVisible) - constructor(cloneOf: ColumnAsListItem): this( - cloneOf.column, cloneOf.isEnabledProperty.value, cloneOf.isCustom, cloneOf.customColumnsManager, cloneOf) { - - this.title = cloneOf.title - this.type = cloneOf.type - this.defaultValue = cloneOf.defaultValue - this.isCalculated = cloneOf.isCalculated - this.expression = cloneOf.expression - } - override var title: String = "" - var type: PropertyType = PropertyType.STRING + var type: CustomPropertyClass = CustomPropertyClass.TEXT var defaultValue: String = "" @@ -352,20 +292,17 @@ internal class ColumnAsListItem( return "ColumnAsListItem(title='$title')" } - override fun clone(forEditing: Boolean): ColumnAsListItem = ColumnAsListItem(this) - init { -// this.isVisible = isVisible if (column != null) { title = column.name val customColumn = customColumnsManager.definitions.find { it.id == column.id } type = run { if (isCustom) { - customColumn?.getPropertyType() + customColumn?.propertyClass } else { - TaskDefaultColumn.find(column?.id)?.getPropertyType() + TaskDefaultColumn.find(column?.id)?.customPropertyClass } - } ?: PropertyType.STRING + } ?: CustomPropertyClass.TEXT defaultValue = if (!isCustom) "" else customColumn?.defaultValueAsString ?: "" diff --git a/ganttproject/src/main/java/biz/ganttproject/ganttview/SharedUiComponents.kt b/ganttproject/src/main/java/biz/ganttproject/ganttview/SharedUiComponents.kt index 510b4c0f8b..c38b2a4613 100644 --- a/ganttproject/src/main/java/biz/ganttproject/ganttview/SharedUiComponents.kt +++ b/ganttproject/src/main/java/biz/ganttproject/ganttview/SharedUiComponents.kt @@ -226,8 +226,6 @@ internal open class ItemEditorPane>( interface Item { var title: String val isEnabledProperty: BooleanProperty - val cloneOf: T? - fun clone(forEditing: Boolean): T } data class BtnController( diff --git a/ganttproject/src/main/java/biz/ganttproject/ganttview/TaskTableFilters.kt b/ganttproject/src/main/java/biz/ganttproject/ganttview/TaskTableFilters.kt index a72cec7a64..a882b319b7 100644 --- a/ganttproject/src/main/java/biz/ganttproject/ganttview/TaskTableFilters.kt +++ b/ganttproject/src/main/java/biz/ganttproject/ganttview/TaskTableFilters.kt @@ -19,9 +19,7 @@ along with GanttProject. If not, see . package biz.ganttproject.ganttview import biz.ganttproject.core.option.DefaultBooleanOption -import biz.ganttproject.core.option.GPObservable import biz.ganttproject.core.option.GPOption -import biz.ganttproject.core.option.ObservableBoolean import biz.ganttproject.core.time.CalendarFactory import biz.ganttproject.customproperty.CustomPropertyClass import biz.ganttproject.customproperty.SimpleSelect @@ -35,7 +33,6 @@ import net.sourceforge.ganttproject.task.Task import net.sourceforge.ganttproject.task.TaskManager import net.sourceforge.ganttproject.task.event.TaskListenerAdapter import net.sourceforge.ganttproject.undo.GPUndoListener -import javax.swing.event.ChangeListener import javax.swing.event.UndoableEditEvent /** @@ -48,11 +45,7 @@ data class TaskFilter( val filterFxn: TaskFilterFxn, var expression: String? = null, val isBuiltIn: Boolean = false, - override val cloneOf: TaskFilter? = null - ): Item { - - override fun clone(forEditing: Boolean): TaskFilter = copy(cloneOf = this) -} + ): Item typealias TaskFilterFxn = (parent: Task, child: Task?) -> Boolean typealias FilterChangedListener = (filter: TaskFilter?) -> Unit From 4eef0e040bb50ef8eeacda75fd26903ff02cf5ad Mon Sep 17 00:00:00 2001 From: Dmitry Barashev Date: Sat, 21 Sep 2024 16:19:12 +0400 Subject: [PATCH 3/4] Fixed bug when changing custom property type --- .../src/main/java/biz/ganttproject/ganttview/ColumnManager.kt | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/ganttproject/src/main/java/biz/ganttproject/ganttview/ColumnManager.kt b/ganttproject/src/main/java/biz/ganttproject/ganttview/ColumnManager.kt index 5f19a33c89..bc277bb597 100644 --- a/ganttproject/src/main/java/biz/ganttproject/ganttview/ColumnManager.kt +++ b/ganttproject/src/main/java/biz/ganttproject/ganttview/ColumnManager.kt @@ -147,10 +147,8 @@ internal fun CustomPropertyClass.createValidator(): ValueValidator<*> = when (th internal fun CustomPropertyDefinition.importColumnItem(item: ColumnAsListItem) { this.name = item.title - if (item.defaultValue.trim().isNotBlank()) { - this.defaultValueAsString = item.defaultValue - } this.propertyClass = item.type + this.defaultValueAsString = item.defaultValue.trim().ifBlank { null } if (item.isCalculated) { this.calculationMethod = SimpleSelect(propertyId = this.id, selectExpression = item.expression, resultClass = this.propertyClass.javaClass) } else { From a7036a9571559e82f9a9c08ba92b1ba75b10ba94 Mon Sep 17 00:00:00 2001 From: Dmitry Barashev Date: Sat, 21 Sep 2024 17:37:04 +0400 Subject: [PATCH 4/4] - Disable the property sheet and "Delete" button when built in item is selected - i18n for the filters --- biz.ganttproject.app.localization | 2 +- .../ganttproject/ganttview/ColumnManager.kt | 21 +++++++++++-------- .../ganttproject/ganttview/FilterDialog.kt | 11 ++++++++-- .../ganttview/SharedUiComponents.kt | 2 +- 4 files changed, 23 insertions(+), 13 deletions(-) diff --git a/biz.ganttproject.app.localization b/biz.ganttproject.app.localization index 6ac30a2eec..d313bae516 160000 --- a/biz.ganttproject.app.localization +++ b/biz.ganttproject.app.localization @@ -1 +1 @@ -Subproject commit 6ac30a2eeca95361ca9cf8acbd0daf2c779118fd +Subproject commit d313bae51664105f2df7adfaa48d8fb907720764 diff --git a/ganttproject/src/main/java/biz/ganttproject/ganttview/ColumnManager.kt b/ganttproject/src/main/java/biz/ganttproject/ganttview/ColumnManager.kt index bc277bb597..155b9fdee6 100644 --- a/ganttproject/src/main/java/biz/ganttproject/ganttview/ColumnManager.kt +++ b/ganttproject/src/main/java/biz/ganttproject/ganttview/ColumnManager.kt @@ -72,14 +72,7 @@ class ColumnManager( ) private val mergedColumns: MutableList = mutableListOf() - internal val dialogPane = ItemListDialogPane( - listItems = listItems, - selectedItem = selectedItem, - listItemConverter = { ShowHideListItem( {it.title}, {it.isEnabledProperty.value}, {it.isEnabledProperty.set(!it.isEnabledProperty.value)}) }, - dialogModel = dialogModel, - editor = customPropertyEditor, - localizer = ourEditorLocalizer - ) + init { mergedColumns.addAll(currentTableColumns.exportData()) // First go columns which are shown in the table now @@ -97,6 +90,15 @@ class ColumnManager( } } + internal val dialogPane = ItemListDialogPane( + listItems = listItems, + selectedItem = selectedItem, + listItemConverter = { ShowHideListItem( {it.title}, {it.isEnabledProperty.value}, {it.isEnabledProperty.set(!it.isEnabledProperty.value)}) }, + dialogModel = dialogModel, + editor = customPropertyEditor, + localizer = ourEditorLocalizer + ) + internal fun onApply() { when (applyExecutor) { ApplyExecutorType.DIRECT -> applyChanges() @@ -376,5 +378,6 @@ internal val ourEditorLocalizer = run { } val fallback2 = RootLocalizer.createWithRootKey("option.taskProperties.customColumn", fallback1) val fallback3 = RootLocalizer.createWithRootKey("option.customPropertyDialog", fallback2) - RootLocalizer.createWithRootKey("", fallback3) + val fallback4 = RootLocalizer.createWithRootKey("customPropertyDialog", fallback3) + RootLocalizer.createWithRootKey("", fallback4) } diff --git a/ganttproject/src/main/java/biz/ganttproject/ganttview/FilterDialog.kt b/ganttproject/src/main/java/biz/ganttproject/ganttview/FilterDialog.kt index d488a3dce5..7df39270ed 100644 --- a/ganttproject/src/main/java/biz/ganttproject/ganttview/FilterDialog.kt +++ b/ganttproject/src/main/java/biz/ganttproject/ganttview/FilterDialog.kt @@ -48,7 +48,7 @@ fun showFilterDialog(filterManager: TaskFilterManager, projectDatabase: ProjectD dialogModel.btnApplyController.onAction = { filterManager.importFilters(listItems) } - val editor = FilterEditor(editorModel, editItem, dialogModel) + val editor = FilterEditor(dialogModel, editorModel, editItem, dialogModel) val dialogPane = ItemListDialogPane( listItems, editItem, @@ -98,7 +98,11 @@ internal class FilterEditorModel( * Editor pane for editing the selected filter properties. */ internal class FilterEditor( - private val editorModel: FilterEditorModel, editItem: ObservableObject, model: ItemListDialogModel) + private val dialogModel: ItemListDialogModel, + private val editorModel: FilterEditorModel, + editItem: ObservableObject, + model: ItemListDialogModel +) : ItemEditorPane( editorModel.fields, editItem, model, i18n ) { @@ -108,10 +112,13 @@ internal class FilterEditor( editorModel.descriptionField.set(item.description) editorModel.expressionField.set(item.expression) propertySheet.isDisable = item.isBuiltIn + dialogModel.btnDeleteController.isDisabled.set(item.isBuiltIn) } else { editorModel.nameField.set("") editorModel.descriptionField.set("") editorModel.expressionField.set("") + propertySheet.isDisable = true + dialogModel.btnDeleteController.isDisabled.set(true) } } diff --git a/ganttproject/src/main/java/biz/ganttproject/ganttview/SharedUiComponents.kt b/ganttproject/src/main/java/biz/ganttproject/ganttview/SharedUiComponents.kt index c38b2a4613..9ed6d25e33 100644 --- a/ganttproject/src/main/java/biz/ganttproject/ganttview/SharedUiComponents.kt +++ b/ganttproject/src/main/java/biz/ganttproject/ganttview/SharedUiComponents.kt @@ -141,7 +141,7 @@ internal open class ItemEditorPane>( internal val visibilityTogglePane = HBox().also { it.styleClass.add("visibility-pane") it.children.add(visibilityToggle) - it.children.add(Label(localizer.formatText("customPropertyDialog.visibility.label"))) + it.children.add(Label(localizer.formatText("visibility.label"))) } internal val propertySheetLabel = Label().also { it.styleClass.add("title")