Skip to content

Commit 2163c59

Browse files
authored
Merge pull request #2783 from bardsoftware/dbarashev/refactor-impex-file-chooser-page
Refactored the file chooser page in the export/import wizards
2 parents b7edbbc + 17ef5b7 commit 2163c59

File tree

15 files changed

+329
-709
lines changed

15 files changed

+329
-709
lines changed

biz.ganttproject.core/src/main/java/biz/ganttproject/core/option/PropertyPaneBuilder.kt

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -67,8 +67,13 @@ data class FileExtensionFilter(val description: String, val extensions: List<Str
6767
/**
6868
* Options for displaying file chooser fields in a property pane.
6969
*/
70-
data class FileDisplayOptions(val extensionFilters: MutableList<FileExtensionFilter> = mutableListOf<FileExtensionFilter>()) :
71-
PropertyDisplayOptions<File>()
70+
data class FileDisplayOptions(
71+
var isSaveNotOpen: Boolean = false,
72+
var allowMultipleSelection: Boolean = false,
73+
var browseButtonText: String = "Browse...",
74+
var chooserTitle: String = "Choose a file",
75+
val extensionFilters: MutableList<FileExtensionFilter> = mutableListOf())
76+
: PropertyDisplayOptions<File>()
7277

7378
/**
7479
* Options for displaying integer fields in a property pane.

ganttproject/src/main/java/biz/ganttproject/app/Dialog.kt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ along with GanttProject. If not, see <http://www.gnu.org/licenses/>.
1919
package biz.ganttproject.app
2020

2121
import biz.ganttproject.FXUtil
22+
import biz.ganttproject.app.DialogPlacement.applicationWindow
2223
import biz.ganttproject.centerOnOwner
2324
import biz.ganttproject.colorFromUiManager
2425
import biz.ganttproject.lib.fx.VBoxBuilder
@@ -169,6 +170,8 @@ class DialogPaneExt : DialogPane() {
169170
}
170171

171172
private val dialogStack = mutableListOf<Dialog<*>>()
173+
fun topWindow(): Window? = dialogStack.lastOrNull()?.dialogPane?.scene?.window ?: applicationWindow
174+
172175
fun dialog(title: String? = null, id: String? = null, contentBuilder: (DialogController) -> Unit) {
173176
Platform.runLater {
174177
try {

ganttproject/src/main/java/biz/ganttproject/app/PropertySheet.kt

Lines changed: 3 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ class PropertyPaneBuilderImpl(private val localizer: Localizer, private val grid
100100

101101
fun file(property: ObservableFile, optionValues: (FileDisplayOptions.()->Unit)? = null) {
102102
rowBuilders.add(run {
103-
val options = optionValues?.let { FileDisplayOptions().apply(it) }
103+
val options = optionValues?.let { FileDisplayOptions().apply(it) } ?: FileDisplayOptions()
104104
createOptionItem(property, createFileOptionEditor(property, options))
105105
})
106106
}
@@ -272,52 +272,9 @@ class PropertyPaneBuilderImpl(private val localizer: Localizer, private val grid
272272
return textField
273273
}
274274

275-
private fun createFileOptionEditor(option: ObservableFile, displayOptions: FileDisplayOptions? = null): Node {
276-
val textField = CustomTextField()
277-
val onBrowse = {
278-
val fileChooser = FileChooser();
279-
var initialFile: File? = File(textField.text)
280-
while (initialFile?.exists() == false) {
281-
initialFile = initialFile.parentFile
282-
}
283-
initialFile?.let {
284-
if (it.isDirectory) {
285-
fileChooser.initialDirectory = it
286-
} else {
287-
fileChooser.initialDirectory = it.parentFile
288-
}
289-
}
290-
fileChooser.title = "Choose a file"
291-
displayOptions?.let {
292-
it.extensionFilters.forEach {filter ->
293-
fileChooser.extensionFilters.add(FileChooser.ExtensionFilter(filter.description, filter.extensions))
294-
}
295-
}
296-
297-
val resultFile = fileChooser.showOpenDialog(null)
298-
option.value = resultFile
299-
resultFile?.let {
300-
textField.text = it.absolutePath
301-
}
302-
}
303-
textField.right = buildFontAwesomeButton(
304-
iconName = FontAwesomeIcon.SEARCH.name,
305-
label = "Browse...",
306-
onClick = { onBrowse() },
307-
styleClass = "btn"
308-
)
309-
textField.id = option.id
310-
displayOptions?.editorStyles?.let(textField.styleClass::addAll)
311-
return textField
312-
// return HBox().apply {
313-
// HBox.setHgrow(textField, Priority.ALWAYS)
314-
// children.add(textField)
315-
// children.add(Region().also {
316-
// it.padding = Insets(0.0, 5.0, 0.0, 0.0)
317-
// })
318-
// children.add(btn)
319-
// }
320275

276+
private fun createFileOptionEditor(option: ObservableFile, displayOptions: FileDisplayOptions = FileDisplayOptions()): Node {
277+
return FileOptionEditor(option, displayOptions).node
321278
}
322279

323280
fun createDateOptionEditor(option: ObservableDate, displayOptions: DateDisplayOptions = DateDisplayOptions(createDateConverter())): DatePicker {

ganttproject/src/main/java/biz/ganttproject/app/PropertySheetEditors.kt

Lines changed: 78 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,17 +18,21 @@ along with GanttProject. If not, see <http://www.gnu.org/licenses/>.
1818
*/
1919
package biz.ganttproject.app
2020

21-
import biz.ganttproject.core.option.DropdownDisplayOptions
22-
import biz.ganttproject.core.option.ObservableChoice
23-
import biz.ganttproject.core.option.ObservableProperty
21+
import biz.ganttproject.core.option.*
22+
import biz.ganttproject.lib.fx.buildFontAwesomeButton
23+
import de.jensd.fx.glyphs.fontawesome.FontAwesomeIcon
2424
import javafx.collections.FXCollections
2525
import javafx.event.EventHandler
2626
import javafx.scene.Node
2727
import javafx.scene.control.ComboBox
2828
import javafx.scene.control.ListCell
2929
import javafx.scene.layout.HBox
30+
import javafx.stage.FileChooser
3031
import javafx.util.Callback
3132
import javafx.util.StringConverter
33+
import org.controlsfx.control.textfield.CustomTextField
34+
import java.io.File
35+
import java.util.*
3236

3337
/**
3438
* Creates a dropdown editor for the given choice option.
@@ -72,3 +76,74 @@ fun <E> createDropdownEditor(option: ObservableProperty<E>, key2i18n: List<Pair<
7276
comboBox.value = key2i18n.find { it.first == option.value }
7377
})
7478
}
79+
80+
private val ourTimer = Timer()
81+
82+
class FileOptionEditor(private val option: ObservableFile, private val displayOptions: FileDisplayOptions = FileDisplayOptions()) {
83+
private val textField = CustomTextField()
84+
private var myTimerTask: TimerTask? = null
85+
86+
val node: Node = textField
87+
init {
88+
textField.right = buildFontAwesomeButton(
89+
iconName = FontAwesomeIcon.SEARCH.name,
90+
label = displayOptions.browseButtonText,
91+
onClick = { onBrowse() },
92+
styleClass = "btn"
93+
)
94+
textField.text = option.value?.absolutePath ?: ""
95+
textField.id = option.id
96+
textField.textProperty().addListener {
97+
onTextChange()
98+
}
99+
displayOptions.editorStyles.let(textField.styleClass::addAll)
100+
option.addWatcher {
101+
if (it.trigger != textField) {
102+
textField.text = option.value?.absolutePath ?: ""
103+
}
104+
}
105+
}
106+
107+
private fun onBrowse() {
108+
val fileChooser = FileChooser()
109+
var initialFile: File? = File(textField.text)
110+
while (initialFile?.exists() == false) {
111+
initialFile = initialFile.parentFile
112+
}
113+
initialFile?.let {
114+
if (it.isDirectory) {
115+
fileChooser.initialDirectory = it
116+
} else {
117+
fileChooser.initialDirectory = it.parentFile
118+
}
119+
}
120+
fileChooser.title = displayOptions.chooserTitle.ifBlank { "Choose a file" }
121+
displayOptions.let {
122+
it.extensionFilters.forEach {filter ->
123+
fileChooser.extensionFilters.add(FileChooser.ExtensionFilter(filter.description, filter.extensions))
124+
}
125+
}
126+
127+
val ownerWindow = topWindow()
128+
val resultFile =
129+
if (displayOptions.isSaveNotOpen) fileChooser.showSaveDialog(ownerWindow)
130+
else fileChooser.showOpenDialog(ownerWindow)
131+
resultFile?.let {
132+
option.set(resultFile, textField)
133+
textField.text = it.absolutePath
134+
}
135+
}
136+
137+
private fun onTextChange() {
138+
if (myTimerTask == null) {
139+
myTimerTask = object : TimerTask() {
140+
override fun run() {
141+
val file = File(textField.text)
142+
option.set(file, textField)
143+
myTimerTask = null
144+
}
145+
}
146+
ourTimer.schedule(myTimerTask, 1000)
147+
}
148+
}
149+
}

ganttproject/src/main/java/net/sourceforge/ganttproject/export/ExportFileChooserPage.kt

Lines changed: 21 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -19,50 +19,51 @@
1919
package net.sourceforge.ganttproject.export
2020

2121
import biz.ganttproject.app.RootLocalizer
22+
import biz.ganttproject.core.option.FileExtensionFilter
2223
import biz.ganttproject.core.option.GPOptionGroup
2324
import biz.ganttproject.storage.asLocalDocument
2425
import biz.ganttproject.storage.getDefaultLocalFolder
2526
import com.github.michaelbull.result.Err
2627
import com.github.michaelbull.result.Ok
2728
import com.github.michaelbull.result.Result
2829
import net.sourceforge.ganttproject.IGanttProject
29-
import net.sourceforge.ganttproject.filter.ExtensionBasedFileFilter
3030
import net.sourceforge.ganttproject.gui.FileChooserPageBase
31-
import net.sourceforge.ganttproject.gui.UIFacade
3231
import net.sourceforge.ganttproject.gui.UIUtil
3332
import net.sourceforge.ganttproject.util.FileUtil.replaceExtension
3433
import org.osgi.service.prefs.Preferences
3534
import java.awt.Component
3635
import java.io.File
3736
import javax.swing.JFileChooser
38-
import javax.swing.filechooser.FileFilter
3937

4038
/**
4139
* A wizard page for choosing a file to export to.
4240
*/
4341
internal class ExportFileChooserPage(
44-
private val myState: ExportFileWizardImpl.State,
42+
private val myState: ExportWizardModel,
4543
private val myProject: IGanttProject,
46-
override val preferences: Preferences,
47-
uiFacade: UIFacade?
44+
override val preferences: Preferences
4845
) : FileChooserPageBase(
49-
myProject.document, uiFacade,
46+
myProject.document,
5047
fileChooserTitle = i18n.formatText("selectFileToExport"),
5148
fileChooserSelectionMode = JFileChooser.FILES_AND_DIRECTORIES,
52-
pageTitle = i18n.formatText("selectFileToExport")
49+
pageTitle = i18n.formatText("selectFileToExport"),
50+
errorMessage = myState.errorMessage
5351
) {
5452
private val myWebPublishingGroup: GPOptionGroup = GPOptionGroup(
5553
"exporter.webPublishing", myState.publishInWebOption
5654
).also { it.isTitled = false }
5755

5856
init {
59-
updateChosenFile = { replaceExtension(it, myState.exporter.proposeFileExtension()) }
57+
updateChosenFile = { file ->
58+
myState.exporter?.let {
59+
replaceExtension(file, it.proposeFileExtension())
60+
} ?: file
61+
}
6062
proposeChosenFile = {
61-
proposeOutputFile(myProject, myState.exporter) ?: File(defaultFileName)
63+
myState.exporter?.let {proposeOutputFile(myProject, it) } ?: File(defaultFileName)
6264
}
63-
overwriteOption.addChangeValueListener { tryChosenFile(chooser.file) }
64-
selectedFileProperty.addListener { _, _, newValue ->
65-
myState.file = newValue
65+
fxFile.addWatcher {
66+
myState.file = it.newValue
6667
}
6768
}
6869

@@ -71,7 +72,7 @@ internal class ExportFileChooserPage(
7172
return Err("File cannot be null")
7273
}
7374

74-
overwriteOption.getIsWritableProperty().set(false, this)
75+
fxOverwrite.isWritable.value = false
7576
if (!file.exists()) {
7677
val parent = file.getParentFile()
7778
if (!parent.exists()) {
@@ -101,22 +102,21 @@ internal class ExportFileChooserPage(
101102
)
102103
}
103104
} else {
104-
overwriteOption.getIsWritableProperty().set(true, this)
105-
if (!overwriteOption.isChecked()) {
105+
fxOverwrite.isWritable.value = true
106+
if (!fxOverwrite.value) {
106107
return Err(i18n.formatText("fileChooser.warning.fileExists"))
107108
}
108109
}
109110
return Ok(file)
110111
}
111112

112113
override fun createSecondaryOptionsPanel(): Component {
113-
return myState.exporter.getCustomOptionsUI() ?: super.createSecondaryOptionsPanel()
114+
return myState.exporter?.getCustomOptionsUI() ?: super.createSecondaryOptionsPanel()
114115
}
115116

116-
override fun createFileFilter(): FileFilter =
117-
ExtensionBasedFileFilter(
118-
myState.exporter.getFileNamePattern(), myState.exporter.getFileTypeDescription()
119-
)
117+
override fun createFileFilter(): FileExtensionFilter? = myState.exporter?.let {
118+
FileExtensionFilter(it.getFileTypeDescription(), listOf(it.getFileNamePattern()))
119+
}
120120

121121
override val optionGroups: List<GPOptionGroup>
122122
get() = listOf(myWebPublishingGroup) + (myState.exporter?.secondaryOptions ?: emptyList())

0 commit comments

Comments
 (0)