Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Some bugfixes in the custom columns and filters #2557

Merged
merged 4 commits into from
Sep 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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.*;
Expand Down Expand Up @@ -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<Object> NOT_EDITABLE = input -> false;
static Predicate<Object> ALWAYS_EDITABLE = input -> true;
Expand Down
145 changes: 36 additions & 109 deletions ganttproject/src/main/java/biz/ganttproject/ganttview/ColumnManager.kt
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ class ColumnManager(
internal val dialogModel: ItemListDialogModel<ColumnAsListItem> = ItemListDialogModel<ColumnAsListItem>(
listItems,
newItemFactory = {
ColumnAsListItem(null, isVisible = true, isCustom = true, customColumnsManager, {customPropertyEditor.updateVisibility(it)})
ColumnAsListItem(null, isVisible = true, isCustom = true, customColumnsManager)
},
ourLocalizer
)
Expand All @@ -64,39 +64,41 @@ 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
),
dialogModel = dialogModel
)

private val mergedColumns: MutableList<ColumnList.Column> = mutableListOf()
internal val dialogPane = ItemListDialogPane<ColumnAsListItem>(
listItems = listItems,
selectedItem = selectedItem,
listItemConverter = { ShowHideListItem(it.title, it.isVisibleProperty) },
dialogModel = dialogModel,
editor = customPropertyEditor,
localizer = ourEditorLocalizer
)

init {
mergedColumns.addAll(currentTableColumns.exportData())
// First go columns which are shown in the table now
// 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))
}
}
}

internal val dialogPane = ItemListDialogPane<ColumnAsListItem>(
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()
Expand All @@ -115,14 +117,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) {
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))
}
if (columnItem.isCalculated) {
Expand All @@ -136,62 +138,26 @@ 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
}

internal fun CustomPropertyDefinition.importColumnItem(item: ColumnAsListItem) {
this.name = item.title
if (item.defaultValue.trim().isNotBlank()) {
this.defaultValueAsString = item.defaultValue
}
this.propertyClass = item.type.getCustomPropertyClass()
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 {
this.calculationMethod = null
}
}

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.
*/
Expand All @@ -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 = "",
Expand All @@ -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 {
Expand All @@ -251,7 +217,7 @@ internal class EditorModel(
*/
internal class CustomPropertyEditor(
private val model: EditorModel,
dialogModel: ItemListDialogModel<ColumnAsListItem>,
private val dialogModel: ItemListDialogModel<ColumnAsListItem>,
selectedItemProperty: ObservableProperty<ColumnAsListItem?>,
private val btnDeleteController: BtnController<Unit>,
escCloseEnabled: BooleanProperty,
Expand All @@ -266,7 +232,7 @@ internal class CustomPropertyEditor(
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")
Expand All @@ -285,31 +251,18 @@ 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()
}
}

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
}
}
}

}

/**
Expand All @@ -320,37 +273,13 @@ internal class ColumnAsListItem(
isVisible: Boolean,
val isCustom: Boolean,
val customColumnsManager: CustomPropertyManager,
val changeListener: (ColumnAsListItem)->Unit,
override val cloneOf: ColumnAsListItem? = null
): Item<ColumnAsListItem> {

constructor(cloneOf: ColumnAsListItem): this(
cloneOf.column, cloneOf.isVisible, cloneOf.isCustom, cloneOf.customColumnsManager,
cloneOf.changeListener, cloneOf) {

this.title = cloneOf.title
this.type = cloneOf.type
this.defaultValue = cloneOf.defaultValue
this.isCalculated = cloneOf.isCalculated
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 val isEnabledProperty: BooleanProperty = SimpleBooleanProperty(isVisible)

override var title: String = ""

var type: PropertyType = PropertyType.STRING
var type: CustomPropertyClass = CustomPropertyClass.TEXT

var defaultValue: String = ""

Expand All @@ -363,20 +292,17 @@ internal class ColumnAsListItem(
return "ColumnAsListItem(title='$title')"
}

override fun clone(): 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 ?: ""
Expand Down Expand Up @@ -452,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)
}
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -49,11 +48,11 @@ 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<TaskFilter>(
listItems,
editItem,
{ filter -> ShowHideListItem(filter.title, filter.isEnabledProperty) },
{ filter -> ShowHideListItem({filter.title}, {filter.isEnabledProperty.value}, {filter.isEnabledProperty.set(!filter.isEnabledProperty.get())}) },
dialogModel,
editor,
i18n
Expand Down Expand Up @@ -99,7 +98,11 @@ internal class FilterEditorModel(
* Editor pane for editing the selected filter properties.
*/
internal class FilterEditor(
private val editorModel: FilterEditorModel, editItem: ObservableObject<TaskFilter?>, model: ItemListDialogModel<TaskFilter>)
private val dialogModel: ItemListDialogModel<TaskFilter>,
private val editorModel: FilterEditorModel,
editItem: ObservableObject<TaskFilter?>,
model: ItemListDialogModel<TaskFilter>
)
: ItemEditorPane<TaskFilter>(
editorModel.fields, editItem, model, i18n
) {
Expand All @@ -109,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)
}
}

Expand Down
Loading
Loading