/* Copyright (c) 2016 Jesper Öqvist <jesper@llbit.se> * * This file is part of Chunky. * * Chunky is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * Chunky is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * You should have received a copy of the GNU General Public License * along with Chunky. If not, see <http://www.gnu.org/licenses/>. */ package se.llbit.chunky.ui; import javafx.fxml.FXML; import javafx.fxml.FXMLLoader; import javafx.fxml.Initializable; import javafx.scene.Parent; import javafx.scene.Scene; import javafx.scene.control.Button; import javafx.scene.control.CheckBox; import javafx.scene.control.Label; import javafx.scene.control.TextField; import javafx.scene.image.Image; import javafx.scene.image.ImageView; import javafx.scene.input.KeyCode; import javafx.scene.input.KeyEvent; import javafx.stage.DirectoryChooser; import javafx.stage.Modality; import javafx.stage.Stage; import se.llbit.chunky.PersistentSettings; import se.llbit.chunky.main.ChunkyOptions; import se.llbit.chunky.world.Icon; import se.llbit.log.Log; import java.io.File; import java.io.IOException; import java.net.URL; import java.util.ResourceBundle; /** * @author Jesper Öqvist <jesper@llbit.se> */ public class SceneDirectoryPicker extends Stage implements Initializable { @FXML TextField sceneDirectory; @FXML CheckBox createDirectory; @FXML Button browseBtn; @FXML Button okBtn; @FXML Button cancelBtn; @FXML Label warningLabel; private File selectedDirectory; private boolean accepted = false; /** * Constructor */ public SceneDirectoryPicker(ChunkyOptions options) throws IOException { FXMLLoader loader = new FXMLLoader(getClass().getResource("SceneDirectoryChooser.fxml")); loader.setController(this); Parent root = loader.load(); setTitle("Scene Directory Picker"); initModality(Modality.APPLICATION_MODAL); getIcons().add(new Image(getClass().getResourceAsStream("/chunky-icon.png"))); setScene(new Scene(root)); addEventFilter(KeyEvent.ANY, e -> { if (e.getCode() == KeyCode.ESCAPE) { e.consume(); close(); } }); createDirectory.selectedProperty() .addListener(observable -> updateSelectedDirectory(selectedDirectory)); updatePathField(options.sceneDir); updateSelectedDirectory(options.sceneDir); browseBtn.setOnAction(event -> { DirectoryChooser fileChooser = new DirectoryChooser(); if (selectedDirectory != null && selectedDirectory.isDirectory()) { fileChooser.setInitialDirectory(selectedDirectory); } fileChooser.setTitle("Select Scene Directory"); File result = fileChooser.showDialog(getScene().getWindow()); if (result != null) { updateSelectedDirectory(result); updatePathField(result); } }); cancelBtn.setOnAction(event -> hide()); okBtn.setOnAction(event -> { if (createDirectory.isSelected()) { tryCreateSceneDir(selectedDirectory); } accepted = true; hide(); }); sceneDirectory.textProperty().addListener( (observable, oldValue, newValue) -> updateSelectedDirectory(new File(newValue))); warningLabel.setGraphic(new ImageView(Icon.failed.fxImage())); } protected void updateSelectedDirectory(File path) { selectedDirectory = path; boolean directoryExists = isValidDirectory(path); boolean invalid = !(createDirectory.isSelected() || directoryExists); createDirectory.setVisible(!directoryExists); warningLabel.setVisible(invalid); okBtn.setDisable(invalid); } private static boolean isValidDirectory(File path) { return path.isDirectory() && path.canWrite(); } protected void updatePathField(File path) { sceneDirectory.setText(path.getAbsolutePath()); } /** * @return The selected scene directory */ public File getSelectedDirectory() { return selectedDirectory; } /** * @return <code>true</code> if the OK button was clicked */ public boolean isAccepted() { return accepted; } /** * Opens a dialog asking the user to specify a new scene directory. * * @return The file representing the selected directory */ public static File changeSceneDirectory(ChunkyOptions options) { try { while (true) { SceneDirectoryPicker sceneDirPicker = new SceneDirectoryPicker(options); sceneDirPicker.showAndWait(); if (!sceneDirPicker.isAccepted()) { return null; } File sceneDir = sceneDirPicker.getSelectedDirectory(); if (isValidDirectory(sceneDir)) { PersistentSettings.setSceneDirectory(sceneDir); // TODO: It may be a good idea to not write directly to the shared scene directory. // It does not matter much right now, but it might be useful in the future to have // an API for getting/setting the scene directory so that custom render contexts can // use a fixed scene directory. options.sceneDir = sceneDir; return sceneDir; } } } catch (IOException e) { Log.error("Failed to open scene directory chooser.", e); return null; } } private static boolean tryCreateSceneDir(File sceneDir) { if (!sceneDir.exists()) { //noinspection ResultOfMethodCallIgnored sceneDir.mkdirs(); } if (!isValidDirectory(sceneDir)) { Log.warnf("Could not open or create the scene directory %s", sceneDir.getAbsolutePath()); return false; } else { return true; } } @Override public void initialize(URL location, ResourceBundle resources) { } }