package ctbrec.ui.controls; import java.io.File; import java.io.IOException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import ctbrec.StringUtil; import ctbrec.ui.AutosizeAlert; import javafx.beans.property.SimpleStringProperty; import javafx.beans.property.StringProperty; import javafx.beans.value.ChangeListener; import javafx.geometry.Point2D; import javafx.scene.Node; import javafx.scene.control.Alert; import javafx.scene.control.Button; import javafx.scene.control.TextField; import javafx.scene.control.Tooltip; import javafx.scene.layout.Border; import javafx.scene.layout.BorderStroke; import javafx.scene.layout.BorderStrokeStyle; import javafx.scene.layout.BorderWidths; import javafx.scene.layout.CornerRadii; import javafx.scene.layout.HBox; import javafx.scene.layout.Priority; import javafx.scene.paint.Color; import javafx.stage.FileChooser; public abstract class AbstractFileSelectionBox extends HBox { private static final Logger LOG = LoggerFactory.getLogger(AbstractFileSelectionBox.class); private StringProperty fileProperty = new SimpleStringProperty(); protected TextField fileInput; protected boolean allowEmptyValue = false; private Tooltip validationError = new Tooltip(); public AbstractFileSelectionBox() { super(5); fileInput = new TextField(); fileInput.textProperty().addListener(textListener()); fileInput.focusedProperty().addListener((obs, o, n) -> { if (!n.booleanValue()) { validationError.hide(); } }); Node browse = createBrowseButton(); browse.disableProperty().bind(disableProperty()); fileInput.disableProperty().bind(disableProperty()); fileInput.textProperty().bindBidirectional(fileProperty); getChildren().addAll(fileInput, browse); HBox.setHgrow(fileInput, Priority.ALWAYS); disabledProperty().addListener((obs, oldV, newV) -> { if (newV.booleanValue()) { hideValidationHints(); } else { if (StringUtil.isNotBlank(fileInput.getText())) { setFile(new File(fileInput.getText())); } } }); } public AbstractFileSelectionBox(String initialValue) { this(); fileInput.setText(initialValue); } private ChangeListener textListener() { return (obs, o, n) -> { String input = fileInput.getText(); if (StringUtil.isBlank(input)) { if (allowEmptyValue) { fileProperty.set(""); hideValidationHints(); return; } } else { File program = new File(input); setFile(program); } }; } protected void setFile(File file) { String msg = validate(file); if (msg != null) { fileInput.setBorder(new Border(new BorderStroke(Color.RED, BorderStrokeStyle.DASHED, new CornerRadii(2), new BorderWidths(2)))); validationError.setText(msg); fileInput.setTooltip(validationError); Point2D p = fileInput.localToScreen(fileInput.getTranslateY(), fileInput.getTranslateY()); if (!validationError.isShowing() && getScene() != null) { validationError.show(getScene().getWindow(), p.getX(), p.getY() + fileInput.getHeight() + 4); } } else { fileProperty.set(file.getAbsolutePath()); hideValidationHints(); } } private void hideValidationHints() { fileInput.setBorder(Border.EMPTY); fileInput.setTooltip(null); validationError.hide(); } protected String validate(File file) { if (isDisabled()) { return null; } if (file == null || !file.exists()) { return "File does not exist"; } else { return null; } } public void allowEmptyValue() { this.allowEmptyValue = true; } private Button createBrowseButton() { Button button = new Button("Select"); button.setOnAction(e -> choose()); button.prefHeightProperty().bind(this.heightProperty()); button.prefWidthProperty().set(70); return button; } protected void choose() { FileChooser chooser = new FileChooser(); File program = chooser.showOpenDialog(null); if (program != null) { try { fileInput.setText(program.getCanonicalPath()); } catch (IOException e1) { LOG.error("Couldn't determine path", e1); Alert alert = new AutosizeAlert(Alert.AlertType.ERROR, getScene()); alert.setTitle("Whoopsie"); alert.setContentText("Couldn't determine path"); alert.showAndWait(); } setFile(program); } } public StringProperty fileProperty() { return fileProperty; } }