package ctbrec.ui.settings; import ctbrec.Config; import ctbrec.recorder.postprocessing.*; import ctbrec.ui.controls.Dialogs; import javafx.beans.property.SimpleBooleanProperty; import javafx.beans.property.SimpleStringProperty; import javafx.collections.FXCollections; import javafx.collections.ListChangeListener; import javafx.collections.ObservableList; import javafx.geometry.Insets; import javafx.scene.control.*; import javafx.scene.control.cell.CheckBoxTableCell; import javafx.scene.image.Image; import javafx.scene.layout.GridPane; import javafx.scene.layout.Priority; import javafx.scene.layout.VBox; import javafx.stage.Stage; import java.io.IOException; import java.io.InputStream; import java.lang.reflect.InvocationTargetException; import java.util.Optional; public class PostProcessingStepPanel extends GridPane { private final Config config; private static final Class[] POST_PROCESSOR_CLASSES = new Class[]{ // @formatter: off Copy.class, Rename.class, Move.class, Remux.class, Script.class, DeleteOriginal.class, DeleteTooShort.class, RemoveKeepFile.class, CreateContactSheet.class }; // @formatter: on TableView stepView; ObservableList stepList; Button up; Button down; Button add; Button remove; Button edit; public PostProcessingStepPanel(Config config) { this.config = config; initGui(); } private void initGui() { setHgap(5); vgapProperty().bind(hgapProperty()); up = createUpButton(); down = createDownButton(); add = createAddButton(); remove = createRemoveButton(); edit = createEditButton(); var buttons = new VBox(5, add, edit, up, down, remove); stepList = FXCollections.observableList(config.getSettings().postProcessors); stepList.addListener((ListChangeListener) change -> safelySaveConfig()); stepView = new TableView<>(stepList); stepView.setEditable(true); stepView.setStyle("-fx-table-cell-border-color: transparent;"); var postProcessorColumn = new TableColumn("Step"); postProcessorColumn.setCellValueFactory(cdf -> new SimpleStringProperty(cdf.getValue().getName())); postProcessorColumn.setStyle("-fx-alignment: CENTER-LEFT;"); var enabledColumn = new TableColumn("Enabled"); enabledColumn.setCellValueFactory(cdf -> { var prop = new SimpleBooleanProperty(cdf.getValue().isEnabled()); prop.addListener((src, oldV, newV) -> { cdf.getValue().setEnabled(newV); safelySaveConfig(); }); return prop; }); enabledColumn.setCellFactory(CheckBoxTableCell.forTableColumn(enabledColumn)); enabledColumn.setEditable(true); enabledColumn.setStyle("-fx-alignment: CENTER;"); postProcessorColumn.prefWidthProperty().bind(stepView.widthProperty().subtract(enabledColumn.widthProperty()).subtract(5)); stepView.getColumns().add(enabledColumn); stepView.getColumns().add(postProcessorColumn); GridPane.setHgrow(stepView, Priority.ALWAYS); GridPane.setFillWidth(stepView, true); add(stepView, 0, 0); add(buttons, 1, 0); stepView.getSelectionModel().selectedIndexProperty().addListener((obs, oldV, newV) -> { var idx = newV.intValue(); boolean noSelection = idx == -1; up.setDisable(noSelection || idx == 0); down.setDisable(noSelection || idx == stepList.size() - 1); edit.setDisable(noSelection); remove.setDisable(noSelection); }); setPadding(new Insets(10)); } private void safelySaveConfig() { try { config.save(); } catch (IOException e) { Dialogs.showError(getScene(), "Couldn't save configuration", "An error occurred while saving the configuration", e); } } private Button createUpButton() { var button = createButton("\u25B4", "Move step up"); button.setOnAction(evt -> { int idx = stepView.getSelectionModel().getSelectedIndex(); PostProcessor selectedItem = stepView.getSelectionModel().getSelectedItem(); stepList.remove(idx); stepList.add(idx - 1, selectedItem); stepView.getSelectionModel().select(idx - 1); }); return button; } private Button createDownButton() { var button = createButton("\u25BE", "Move step down"); button.setOnAction(evt -> { int idx = stepView.getSelectionModel().getSelectedIndex(); PostProcessor selectedItem = stepView.getSelectionModel().getSelectedItem(); stepList.remove(idx); stepList.add(idx + 1, selectedItem); stepView.getSelectionModel().select(idx + 1); }); return button; } private Button createAddButton() { var button = createButton("+", "Add a new step"); button.setDisable(false); button.setOnAction(evt -> { PostProcessor[] options = createOptions(); ChoiceDialog choice = new ChoiceDialog<>(options[0], options); choice.setTitle("New Post-Processing Step"); choice.setHeaderText("Select the new step type"); choice.setResizable(true); choice.setWidth(600); choice.getDialogPane().setMinWidth(400); var stage = (Stage) choice.getDialogPane().getScene().getWindow(); stage.getScene().getStylesheets().addAll(getScene().getStylesheets()); InputStream icon = Dialogs.class.getResourceAsStream("/icon.png"); stage.getIcons().add(new Image(icon)); Optional result = choice.showAndWait(); result.ifPresent(pp -> PostProcessingDialogFactory.openNewDialog(pp, getScene(), stepList)); saveConfig(); }); return button; } private void saveConfig() { try { config.save(); } catch (IOException e) { Dialogs.showError(getScene(), "Post-Processing", "Couldn't save post-processing step", e); } } private PostProcessor[] createOptions() { try { var options = new PostProcessor[POST_PROCESSOR_CLASSES.length]; for (var i = 0; i < POST_PROCESSOR_CLASSES.length; i++) { Class cls = POST_PROCESSOR_CLASSES[i]; PostProcessor pp; pp = (PostProcessor) cls.getDeclaredConstructor().newInstance(); options[i] = pp; } return options; } catch (InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException | NoSuchMethodException | SecurityException e) { Dialogs.showError(getScene(), "Create post-processor selection", "Error while reaing in post-processing options", e); return new PostProcessor[0]; } } private Button createRemoveButton() { var button = createButton("-", "Remove selected step"); button.setOnAction(evt -> { PostProcessor selectedItem = stepView.getSelectionModel().getSelectedItem(); if (selectedItem != null) { stepList.remove(selectedItem); } }); return button; } private Button createEditButton() { var button = createButton("\u270E", "Edit selected step"); button.setOnAction(evt -> { PostProcessor selectedItem = stepView.getSelectionModel().getSelectedItem(); PostProcessingDialogFactory.openEditDialog(selectedItem, getScene(), stepList); stepView.refresh(); saveConfig(); }); return button; } private Button createButton(String text, String tooltip) { var b = new Button(text); b.setTooltip(new Tooltip(tooltip)); b.setDisable(true); b.setPrefSize(32, 32); return b; } }