package com.twasyl.slideshowfx.setup.controllers; import com.twasyl.slideshowfx.setup.enums.SetupStatus; import com.twasyl.slideshowfx.setup.exceptions.SetupStepException; import com.twasyl.slideshowfx.setup.step.ISetupStep; import com.twasyl.slideshowfx.utils.DialogHelper; import javafx.beans.property.ObjectProperty; import javafx.beans.property.SimpleObjectProperty; import javafx.collections.FXCollections; import javafx.collections.ListChangeListener; import javafx.collections.ObservableList; import javafx.event.ActionEvent; import javafx.fxml.FXML; import javafx.fxml.Initializable; import javafx.scene.Node; import javafx.scene.control.Button; import javafx.scene.control.ButtonType; import javafx.scene.control.ScrollPane; import javafx.scene.layout.BorderPane; import javafx.scene.layout.HBox; import javafx.scene.layout.VBox; import javafx.scene.text.Font; import javafx.scene.text.FontWeight; import javafx.scene.text.Text; import javafx.stage.Stage; import javafx.stage.WindowEvent; import java.net.URL; import java.util.ResourceBundle; import java.util.logging.Level; import java.util.logging.Logger; import static javafx.stage.WindowEvent.WINDOW_CLOSE_REQUEST; /** * Controller for the {SetupView.xml} file. * * @author Thierry Wasylczenko * @since SlideshowFX 1.0 * @version 1.0 */ public class SetupViewController implements Initializable { @FunctionalInterface static interface Operation { void execute() throws SetupStepException; } private static final Logger LOGGER = Logger.getLogger(SetupViewController.class.getName()); @FXML private ObservableList<Button> buttonBarActions; @FXML private BorderPane root; @FXML private ScrollPane content; @FXML private VBox stepsContainer; @FXML private HBox buttonBar; @FXML private Button cancel; @FXML private Button previous; @FXML private Button next; @FXML private Button finish; private ObservableList<ISetupStep> steps = FXCollections.observableArrayList(); private ObjectProperty<ISetupStep> currentStep = new SimpleObjectProperty<>(); private SetupStatus setupStatus = SetupStatus.IN_PROGRESS; @FXML private void nextStep(final ActionEvent event) throws Exception { this.executeSafely(() -> { if(currentStep.get().next() != null) { this.currentStep.get().execute(); this.currentStep.set(this.currentStep.get().next()); } }); } @FXML private void previousStep(final ActionEvent event) throws Exception { this.executeSafely(() -> { if(this.currentStep.get().previous() != null) { this.currentStep.get().rollback(); this.currentStep.set(this.currentStep.get().previous()); } }); } @FXML private void cancelSetup(final ActionEvent event) throws Exception { this.cancelSetup(); if(this.setupStatus == SetupStatus.ABORTED) { this.requestClose(); } } @FXML private void finishSetup(final ActionEvent event) throws Exception { this.finishSetup(); this.requestClose(); } protected void executeSafely(final Operation operation) { try { operation.execute(); } catch(SetupStepException ex) { LOGGER.log(Level.SEVERE, "Error when executing the operation", ex); DialogHelper.showError("Error", ex.getMessage()); } catch(Exception ex) { LOGGER.log(Level.SEVERE, "Unmanaged error when executing the operation", ex); DialogHelper.showError("Unmanaged error", ex.getMessage()); } } public SetupStatus getSetupStatus() { return this.setupStatus; } public void cancelSetup() { final ButtonType answer = DialogHelper.showConfirmationAlert("Cancelling the installation", "Are you sure you want to cancel the installation?"); if(answer == ButtonType.YES) { this.rollbackAllSteps(); this.setupStatus = SetupStatus.ABORTED; } } public void finishSetup() { this.executeSafely(() -> { this.currentStep.get().execute(); this.setupStatus = SetupStatus.SUCCESSFUL; }); } public void rollbackAllSteps() { this.executeSafely(() -> { ISetupStep stepToRollback = this.currentStep.get().previous(); while(stepToRollback != null) { stepToRollback.rollback(); stepToRollback = stepToRollback.previous(); } }); } public void requestClose() { final WindowEvent closeEvent = new WindowEvent(this.root.getScene().getWindow(), WINDOW_CLOSE_REQUEST); this.root.getScene().getWindow().fireEvent(closeEvent); } public SetupViewController addStep(final ISetupStep step) { if(step != null) { if(!steps.isEmpty()) { final ISetupStep lastStep = steps.get(steps.size() - 1); lastStep.next(step); step.previous(lastStep); } else { this.currentStep.set(step); } this.steps.add(step); } return this; } @Override public void initialize(URL location, ResourceBundle resources) { this.root.sceneProperty().addListener((sceneValue, oldScene, newScene) -> { newScene.windowProperty().addListener((windowValue, oldWindow, newWindow) -> { if(newWindow != null && newWindow instanceof Stage) { ((Stage) newWindow).setTitle(this.currentStep.get().title()); manageCurrentStepText(); } }); }); this.steps.addListener((ListChangeListener) change -> { this.stepsContainer.getChildren().clear(); this.steps.forEach(step -> { final Text text = new Text(step.title()); text.setWrappingWidth(256); this.stepsContainer.getChildren().add(text); }); }); this.currentStep.addListener((value, oldStep, newStep) -> { if(newStep != null) { final Node view = newStep.getView(); view.maxWidth(800); this.content.setContent(view); if(this.root.getScene() != null) { ((Stage) this.root.getScene().getWindow()).setTitle(newStep.title()); } manageNextButtonState(newStep); managePreviousButtonState(newStep); manageFinishButtonState(newStep); manageButtonBar(); manageCurrentStepText(); } }); this.buttonBarActions.forEach(button -> { button.visibleProperty().addListener((value, oldVisible, newVisible) -> { this.manageButtonBar(); }); }); } protected void manageNextButtonState(final ISetupStep withStep) { if(this.next.visibleProperty().isBound()) { this.next.visibleProperty().unbind(); } if(this.next.disableProperty().isBound()) { this.next.disableProperty().unbind(); } this.next.visibleProperty().bind(withStep.nextProperty().isNotNull()); this.next.disableProperty().bind(withStep.validProperty().not()); } protected void managePreviousButtonState(final ISetupStep withStep) { if(this.previous.visibleProperty().isBound()) { this.previous.visibleProperty().unbind(); } this.previous.visibleProperty().bind(withStep.previousProperty().isNotNull()); } protected void manageFinishButtonState(final ISetupStep withStep) { if(this.finish.visibleProperty().isBound()) { this.finish.visibleProperty().unbind(); } if(this.finish.disableProperty().isBound()) { this.finish.disableProperty().unbind(); } this.finish.visibleProperty().bind(withStep.nextProperty().isNull()); this.finish.disableProperty().bind(withStep.validProperty().not()); } protected void manageButtonBar() { this.buttonBar.getChildren().clear(); this.buttonBarActions.forEach(button -> { if(button.isVisible())this.buttonBar.getChildren().add(button); }); } protected void manageCurrentStepText() { this.stepsContainer.getChildren() .stream() .filter(child -> child instanceof Text) .map(child -> (Text) child) .forEach(text -> { final Font currentFont = text.getFont(); FontWeight weight = FontWeight.NORMAL; if(text.getText().equals(this.currentStep.get().title())) { weight = FontWeight.BOLD; } text.setFont(Font.font(currentFont.getFamily(), weight, currentFont.getSize())); }); } }