Add all settings to the new settings panel
This commit is contained in:
parent
e7349b2118
commit
83775c805a
|
@ -54,6 +54,7 @@ import ctbrec.sites.stripchat.Stripchat;
|
||||||
import ctbrec.ui.controls.Dialogs;
|
import ctbrec.ui.controls.Dialogs;
|
||||||
import ctbrec.ui.news.NewsTab;
|
import ctbrec.ui.news.NewsTab;
|
||||||
import ctbrec.ui.settings.SettingsTab;
|
import ctbrec.ui.settings.SettingsTab;
|
||||||
|
import ctbrec.ui.settings.SettingsTab2;
|
||||||
import ctbrec.ui.tabs.DonateTabFx;
|
import ctbrec.ui.tabs.DonateTabFx;
|
||||||
import ctbrec.ui.tabs.HelpTab;
|
import ctbrec.ui.tabs.HelpTab;
|
||||||
import ctbrec.ui.tabs.RecordedModelsTab;
|
import ctbrec.ui.tabs.RecordedModelsTab;
|
||||||
|
@ -189,7 +190,7 @@ public class CamrecApplication extends Application {
|
||||||
recordingsTab = new RecordingsTab("Recordings", recorder, config, sites);
|
recordingsTab = new RecordingsTab("Recordings", recorder, config, sites);
|
||||||
tabPane.getTabs().add(recordingsTab);
|
tabPane.getTabs().add(recordingsTab);
|
||||||
settingsTab = new SettingsTab(sites, recorder);
|
settingsTab = new SettingsTab(sites, recorder);
|
||||||
//tabPane.getTabs().add(new SettingsTab2(sites, recorder));
|
tabPane.getTabs().add(new SettingsTab2(sites, recorder));
|
||||||
tabPane.getTabs().add(settingsTab);
|
tabPane.getTabs().add(settingsTab);
|
||||||
tabPane.getTabs().add(new NewsTab());
|
tabPane.getTabs().add(new NewsTab());
|
||||||
tabPane.getTabs().add(new DonateTabFx());
|
tabPane.getTabs().add(new DonateTabFx());
|
||||||
|
|
|
@ -46,9 +46,10 @@ public abstract class AbstractFileSelectionBox extends HBox {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
Node browse = createBrowseButton();
|
Node browse = createBrowseButton();
|
||||||
getChildren().addAll(fileInput, browse);
|
|
||||||
fileInput.disableProperty().bind(disableProperty());
|
|
||||||
browse.disableProperty().bind(disableProperty());
|
browse.disableProperty().bind(disableProperty());
|
||||||
|
fileInput.disableProperty().bind(disableProperty());
|
||||||
|
fileInput.textProperty().bindBidirectional(fileProperty);
|
||||||
|
getChildren().addAll(fileInput, browse);
|
||||||
HBox.setHgrow(fileInput, Priority.ALWAYS);
|
HBox.setHgrow(fileInput, Priority.ALWAYS);
|
||||||
|
|
||||||
disabledProperty().addListener((obs, oldV, newV) -> {
|
disabledProperty().addListener((obs, oldV, newV) -> {
|
||||||
|
@ -70,10 +71,12 @@ public abstract class AbstractFileSelectionBox extends HBox {
|
||||||
private ChangeListener<? super String> textListener() {
|
private ChangeListener<? super String> textListener() {
|
||||||
return (obs, o, n) -> {
|
return (obs, o, n) -> {
|
||||||
String input = fileInput.getText();
|
String input = fileInput.getText();
|
||||||
if (StringUtil.isBlank(input) && allowEmptyValue) {
|
if (StringUtil.isBlank(input)) {
|
||||||
fileProperty.set("");
|
if (allowEmptyValue) {
|
||||||
hideValidationHints();
|
fileProperty.set("");
|
||||||
return;
|
hideValidationHints();
|
||||||
|
return;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
File program = new File(input);
|
File program = new File(input);
|
||||||
setFile(program);
|
setFile(program);
|
||||||
|
@ -122,6 +125,8 @@ public abstract class AbstractFileSelectionBox extends HBox {
|
||||||
private Button createBrowseButton() {
|
private Button createBrowseButton() {
|
||||||
Button button = new Button("Select");
|
Button button = new Button("Select");
|
||||||
button.setOnAction(e -> choose());
|
button.setOnAction(e -> choose());
|
||||||
|
button.prefHeightProperty().bind(this.heightProperty());
|
||||||
|
button.prefWidthProperty().set(70);
|
||||||
return button;
|
return button;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
package ctbrec.ui.controls.range;
|
package ctbrec.ui.controls.range;
|
||||||
|
|
||||||
import javafx.beans.property.DoubleProperty;
|
import javafx.beans.property.ObjectProperty;
|
||||||
import javafx.beans.property.SimpleDoubleProperty;
|
import javafx.beans.property.SimpleObjectProperty;
|
||||||
import javafx.geometry.Orientation;
|
import javafx.geometry.Orientation;
|
||||||
import javafx.scene.control.Control;
|
import javafx.scene.control.Control;
|
||||||
import javafx.scene.control.Skin;
|
import javafx.scene.control.Skin;
|
||||||
|
@ -11,16 +11,17 @@ public class RangeSlider<T extends Number> extends Control {
|
||||||
private static final String DEFAULT_STYLE_CLASS = "rangeslider";
|
private static final String DEFAULT_STYLE_CLASS = "rangeslider";
|
||||||
|
|
||||||
private Range<T> range;
|
private Range<T> range;
|
||||||
private DoubleProperty low;
|
private ObjectProperty<T> low;
|
||||||
private DoubleProperty high;
|
private ObjectProperty<T> high;
|
||||||
private boolean showTickMarks = false;
|
private boolean showTickMarks = false;
|
||||||
private boolean showTickLabels = false;
|
private boolean showTickLabels = false;
|
||||||
private Orientation orientation = Orientation.HORIZONTAL;
|
private Orientation orientation = Orientation.HORIZONTAL;
|
||||||
|
|
||||||
|
@SuppressWarnings({ "unchecked", "rawtypes" })
|
||||||
public RangeSlider(Range<T> range) {
|
public RangeSlider(Range<T> range) {
|
||||||
this.range = range;
|
this.range = range;
|
||||||
low = new SimpleDoubleProperty(getMinimum().doubleValue());
|
low = new SimpleObjectProperty(getMinimum());
|
||||||
high = new SimpleDoubleProperty(getMaximum().doubleValue());
|
high = new SimpleObjectProperty(getMaximum());
|
||||||
getStyleClass().setAll(DEFAULT_STYLE_CLASS);
|
getStyleClass().setAll(DEFAULT_STYLE_CLASS);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -34,20 +35,20 @@ public class RangeSlider<T extends Number> extends Control {
|
||||||
return RangeSlider.class.getResource("rangeslider.css").toExternalForm();
|
return RangeSlider.class.getResource("rangeslider.css").toExternalForm();
|
||||||
}
|
}
|
||||||
|
|
||||||
public DoubleProperty getLow() {
|
public ObjectProperty<T> getLow() {
|
||||||
return low;
|
return low;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setLow(T newPosition) {
|
public void setLow(T newPosition) {
|
||||||
low.set(newPosition.doubleValue());
|
low.set(newPosition);
|
||||||
}
|
}
|
||||||
|
|
||||||
public DoubleProperty getHigh() {
|
public ObjectProperty<T> getHigh() {
|
||||||
return high;
|
return high;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setHigh(T newPosition) {
|
public void setHigh(T newPosition) {
|
||||||
this.high.set(newPosition.doubleValue());
|
this.high.set(newPosition);
|
||||||
}
|
}
|
||||||
|
|
||||||
public T getMinimum() {
|
public T getMinimum() {
|
||||||
|
|
|
@ -70,11 +70,11 @@ public class RangeSliderBehavior<T extends Number> extends BehaviorBase<RangeSli
|
||||||
}
|
}
|
||||||
|
|
||||||
private T getLow() {
|
private T getLow() {
|
||||||
return rangeSlider.getRange().getTicks().get(rangeSlider.getLow().intValue());
|
return rangeSlider.getRange().getTicks().get(rangeSlider.getLow().get().intValue());
|
||||||
}
|
}
|
||||||
|
|
||||||
private T getHigh() {
|
private T getHigh() {
|
||||||
return rangeSlider.getRange().getTicks().get(rangeSlider.getHigh().intValue());
|
return rangeSlider.getRange().getTicks().get(rangeSlider.getHigh().get().intValue());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -42,7 +42,7 @@ public class RangeSliderSkin extends SkinBase<RangeSlider<?>> {
|
||||||
|
|
||||||
t.low.setOnMousePressed(me -> {
|
t.low.setOnMousePressed(me -> {
|
||||||
preDragThumbPoint = t.low.localToParent(me.getX(), me.getY());
|
preDragThumbPoint = t.low.localToParent(me.getX(), me.getY());
|
||||||
preDragPos = (getSkinnable().getLow().doubleValue() - getSkinnable().getMinimum().doubleValue()) / (getMaxMinusMinNoZero());
|
preDragPos = (getSkinnable().getLow().get().doubleValue() - getSkinnable().getMinimum().doubleValue()) / (getMaxMinusMinNoZero());
|
||||||
});
|
});
|
||||||
t.low.setOnMouseDragged(me -> {
|
t.low.setOnMouseDragged(me -> {
|
||||||
Point2D cur = t.low.localToParent(me.getX(), me.getY());
|
Point2D cur = t.low.localToParent(me.getX(), me.getY());
|
||||||
|
@ -52,7 +52,7 @@ public class RangeSliderSkin extends SkinBase<RangeSlider<?>> {
|
||||||
|
|
||||||
t.high.setOnMousePressed(me -> {
|
t.high.setOnMousePressed(me -> {
|
||||||
preDragThumbPoint = t.high.localToParent(me.getX(), me.getY());
|
preDragThumbPoint = t.high.localToParent(me.getX(), me.getY());
|
||||||
preDragPos = (getSkinnable().getHigh().doubleValue() - getSkinnable().getMinimum().doubleValue()) / (getMaxMinusMinNoZero());
|
preDragPos = (getSkinnable().getHigh().get().doubleValue() - getSkinnable().getMinimum().doubleValue()) / (getMaxMinusMinNoZero());
|
||||||
});
|
});
|
||||||
t.high.setOnMouseDragged(me -> {
|
t.high.setOnMouseDragged(me -> {
|
||||||
boolean orientation = getSkinnable().getOrientation() == Orientation.HORIZONTAL;
|
boolean orientation = getSkinnable().getOrientation() == Orientation.HORIZONTAL;
|
||||||
|
@ -160,8 +160,8 @@ public class RangeSliderSkin extends SkinBase<RangeSlider<?>> {
|
||||||
private void positionThumbs() {
|
private void positionThumbs() {
|
||||||
RangeSlider<?> s = getSkinnable();
|
RangeSlider<?> s = getSkinnable();
|
||||||
|
|
||||||
double lxl = trackStart + (trackLength * ((s.getLow().doubleValue() - s.getMinimum().doubleValue()) / (getMaxMinusMinNoZero())) - thumbWidth / 2D);
|
double lxl = trackStart + (trackLength * ((s.getLow().get().doubleValue() - s.getMinimum().doubleValue()) / (getMaxMinusMinNoZero())) - thumbWidth / 2D);
|
||||||
double lxh = trackStart + (trackLength * ((s.getHigh().doubleValue() - s.getMinimum().doubleValue()) / (getMaxMinusMinNoZero())) - thumbWidth / 2D);
|
double lxh = trackStart + (trackLength * ((s.getHigh().get().doubleValue() - s.getMinimum().doubleValue()) / (getMaxMinusMinNoZero())) - thumbWidth / 2D);
|
||||||
double ly = lowThumbPos;
|
double ly = lowThumbPos;
|
||||||
|
|
||||||
if (thumbRange != null) {
|
if (thumbRange != null) {
|
||||||
|
|
|
@ -50,9 +50,7 @@ import javafx.scene.control.ScrollPane;
|
||||||
import javafx.scene.control.SelectionMode;
|
import javafx.scene.control.SelectionMode;
|
||||||
import javafx.scene.control.Separator;
|
import javafx.scene.control.Separator;
|
||||||
import javafx.scene.control.TextField;
|
import javafx.scene.control.TextField;
|
||||||
import javafx.scene.control.TitledPane;
|
|
||||||
import javafx.scene.image.Image;
|
import javafx.scene.image.Image;
|
||||||
import javafx.scene.layout.BorderPane;
|
|
||||||
import javafx.scene.layout.GridPane;
|
import javafx.scene.layout.GridPane;
|
||||||
import javafx.scene.layout.HBox;
|
import javafx.scene.layout.HBox;
|
||||||
import javafx.scene.layout.Pane;
|
import javafx.scene.layout.Pane;
|
||||||
|
@ -61,8 +59,8 @@ import javafx.stage.Modality;
|
||||||
import javafx.stage.Stage;
|
import javafx.stage.Stage;
|
||||||
import javafx.stage.Window;
|
import javafx.stage.Window;
|
||||||
|
|
||||||
public class ActionSettingsPanel extends TitledPane {
|
public class ActionSettingsPanel extends Pane {
|
||||||
private static final transient Logger LOG = LoggerFactory.getLogger(ActionSettingsPanel.class);
|
private static final Logger LOG = LoggerFactory.getLogger(ActionSettingsPanel.class);
|
||||||
private ListView<EventHandlerConfiguration> actionTable;
|
private ListView<EventHandlerConfiguration> actionTable;
|
||||||
|
|
||||||
private TextField name = new TextField();
|
private TextField name = new TextField();
|
||||||
|
@ -80,11 +78,8 @@ public class ActionSettingsPanel extends TitledPane {
|
||||||
|
|
||||||
private Recorder recorder;
|
private Recorder recorder;
|
||||||
|
|
||||||
public ActionSettingsPanel(SettingsTab settingsTab, Recorder recorder) {
|
public ActionSettingsPanel(Recorder recorder) {
|
||||||
this.recorder = recorder;
|
this.recorder = recorder;
|
||||||
setText("Events & Actions");
|
|
||||||
setExpanded(true);
|
|
||||||
setCollapsible(false);
|
|
||||||
createGui();
|
createGui();
|
||||||
loadEventHandlers();
|
loadEventHandlers();
|
||||||
}
|
}
|
||||||
|
@ -94,15 +89,22 @@ public class ActionSettingsPanel extends TitledPane {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void createGui() {
|
private void createGui() {
|
||||||
BorderPane mainLayout = new BorderPane();
|
GridPane grid = new GridPane();
|
||||||
setContent(mainLayout);
|
grid.setHgap(10);
|
||||||
|
grid.setVgap(10);
|
||||||
|
grid.setPadding(new Insets(20, 150, 10, 10));
|
||||||
|
getChildren().add(grid);
|
||||||
|
|
||||||
|
Label headline = new Label("Events & Actions");
|
||||||
|
headline.getStyleClass().add("settings-group-label");
|
||||||
|
grid.add(headline, 0, 0);
|
||||||
|
|
||||||
actionTable = createActionTable();
|
actionTable = createActionTable();
|
||||||
ScrollPane scrollPane = new ScrollPane(actionTable);
|
ScrollPane scrollPane = new ScrollPane(actionTable);
|
||||||
scrollPane.setFitToHeight(true);
|
scrollPane.setFitToHeight(true);
|
||||||
scrollPane.setFitToWidth(true);
|
scrollPane.setFitToWidth(true);
|
||||||
scrollPane.setStyle("-fx-background-color: -fx-background");
|
scrollPane.setStyle("-fx-background-color: -fx-background");
|
||||||
mainLayout.setCenter(scrollPane);
|
grid.add(scrollPane, 0, 1);
|
||||||
|
|
||||||
Button add = new Button("Add");
|
Button add = new Button("Add");
|
||||||
add.setOnAction(this::add);
|
add.setOnAction(this::add);
|
||||||
|
@ -110,15 +112,10 @@ public class ActionSettingsPanel extends TitledPane {
|
||||||
delete.setOnAction(this::delete);
|
delete.setOnAction(this::delete);
|
||||||
delete.setDisable(true);
|
delete.setDisable(true);
|
||||||
HBox buttons = new HBox(5, add, delete);
|
HBox buttons = new HBox(5, add, delete);
|
||||||
mainLayout.setBottom(buttons);
|
buttons.setStyle("-fx-background-color: -fx-background"); // workaround so that the buttons don't shrink
|
||||||
BorderPane.setMargin(buttons, new Insets(5, 0, 0, 0));
|
grid.add(buttons, 0, 2);
|
||||||
|
|
||||||
actionTable.getSelectionModel().getSelectedItems().addListener(new ListChangeListener<EventHandlerConfiguration>() {
|
actionTable.getSelectionModel().getSelectedItems().addListener((ListChangeListener<EventHandlerConfiguration>) change -> delete.setDisable(change.getList().isEmpty()));
|
||||||
@Override
|
|
||||||
public void onChanged(Change<? extends EventHandlerConfiguration> change) {
|
|
||||||
delete.setDisable(change.getList().isEmpty());
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void add(ActionEvent evt) {
|
private void add(ActionEvent evt) {
|
||||||
|
@ -235,9 +232,7 @@ public class ActionSettingsPanel extends TitledPane {
|
||||||
event.getItems().clear();
|
event.getItems().clear();
|
||||||
event.getItems().add(Event.Type.MODEL_STATUS_CHANGED);
|
event.getItems().add(Event.Type.MODEL_STATUS_CHANGED);
|
||||||
event.getItems().add(Event.Type.RECORDING_STATUS_CHANGED);
|
event.getItems().add(Event.Type.RECORDING_STATUS_CHANGED);
|
||||||
event.setOnAction(evt -> {
|
event.setOnAction(evt -> modelState.setVisible(event.getSelectionModel().getSelectedItem() == Event.Type.MODEL_STATUS_CHANGED));
|
||||||
modelState.setVisible(event.getSelectionModel().getSelectedItem() == Event.Type.MODEL_STATUS_CHANGED);
|
|
||||||
});
|
|
||||||
event.getSelectionModel().select(Event.Type.MODEL_STATUS_CHANGED);
|
event.getSelectionModel().select(Event.Type.MODEL_STATUS_CHANGED);
|
||||||
layout.add(event, 1, row++);
|
layout.add(event, 1, row++);
|
||||||
|
|
||||||
|
@ -254,7 +249,7 @@ public class ActionSettingsPanel extends TitledPane {
|
||||||
|
|
||||||
Label l = new Label("Models");
|
Label l = new Label("Models");
|
||||||
layout.add(l, 0, row);
|
layout.add(l, 0, row);
|
||||||
modelSelectionPane = new ListSelectionPane<Model>(recorder.getModels(), Collections.emptyList());
|
modelSelectionPane = new ListSelectionPane<>(recorder.getModels(), Collections.emptyList());
|
||||||
layout.add(modelSelectionPane, 1, row++);
|
layout.add(modelSelectionPane, 1, row++);
|
||||||
GridPane.setValignment(l, VPos.TOP);
|
GridPane.setValignment(l, VPos.TOP);
|
||||||
GridPane.setHgrow(modelSelectionPane, Priority.ALWAYS);
|
GridPane.setHgrow(modelSelectionPane, Priority.ALWAYS);
|
||||||
|
|
|
@ -2,7 +2,8 @@ package ctbrec.ui.settings;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
import java.util.Arrays;
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
@ -10,16 +11,33 @@ import org.slf4j.LoggerFactory;
|
||||||
import ctbrec.Config;
|
import ctbrec.Config;
|
||||||
import ctbrec.Settings;
|
import ctbrec.Settings;
|
||||||
import ctbrec.StringUtil;
|
import ctbrec.StringUtil;
|
||||||
|
import ctbrec.ui.controls.DirectorySelectionBox;
|
||||||
|
import ctbrec.ui.controls.ProgramSelectionBox;
|
||||||
|
import ctbrec.ui.controls.range.DiscreteRange;
|
||||||
|
import ctbrec.ui.controls.range.RangeSlider;
|
||||||
|
import ctbrec.ui.settings.api.ExclusiveSelectionProperty;
|
||||||
import ctbrec.ui.settings.api.Preferences;
|
import ctbrec.ui.settings.api.Preferences;
|
||||||
import ctbrec.ui.settings.api.PreferencesStorage;
|
import ctbrec.ui.settings.api.PreferencesStorage;
|
||||||
import ctbrec.ui.settings.api.SelectionSetting;
|
|
||||||
import ctbrec.ui.settings.api.Setting;
|
import ctbrec.ui.settings.api.Setting;
|
||||||
import javafx.collections.FXCollections;
|
import ctbrec.ui.settings.api.SimpleDirectoryProperty;
|
||||||
|
import ctbrec.ui.settings.api.SimpleFileProperty;
|
||||||
|
import ctbrec.ui.settings.api.SimpleRangeProperty;
|
||||||
|
import javafx.beans.property.BooleanProperty;
|
||||||
|
import javafx.beans.property.IntegerProperty;
|
||||||
|
import javafx.beans.property.ListProperty;
|
||||||
|
import javafx.beans.property.LongProperty;
|
||||||
|
import javafx.beans.property.Property;
|
||||||
|
import javafx.beans.property.StringProperty;
|
||||||
|
import javafx.geometry.Insets;
|
||||||
import javafx.scene.Node;
|
import javafx.scene.Node;
|
||||||
import javafx.scene.control.CheckBox;
|
import javafx.scene.control.CheckBox;
|
||||||
import javafx.scene.control.ComboBox;
|
import javafx.scene.control.ComboBox;
|
||||||
import javafx.scene.control.Label;
|
import javafx.scene.control.Label;
|
||||||
|
import javafx.scene.control.RadioButton;
|
||||||
import javafx.scene.control.TextField;
|
import javafx.scene.control.TextField;
|
||||||
|
import javafx.scene.control.ToggleGroup;
|
||||||
|
import javafx.scene.layout.HBox;
|
||||||
|
import javafx.util.converter.NumberStringConverter;
|
||||||
|
|
||||||
public class CtbrecPreferencesStorage implements PreferencesStorage {
|
public class CtbrecPreferencesStorage implements PreferencesStorage {
|
||||||
|
|
||||||
|
@ -37,7 +55,7 @@ public class CtbrecPreferencesStorage implements PreferencesStorage {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void save(Preferences preferences) throws IOException {
|
public void save(Preferences preferences) throws IOException {
|
||||||
config.save();
|
throw new RuntimeException("not implemented");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -47,69 +65,206 @@ public class CtbrecPreferencesStorage implements PreferencesStorage {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Node createGui(Setting setting) throws Exception {
|
public Node createGui(Setting setting) throws Exception {
|
||||||
String key = setting.getKey();
|
Property<?> prop = setting.getProperty();
|
||||||
Field field = Settings.class.getField(key);
|
if (prop instanceof ExclusiveSelectionProperty) {
|
||||||
Class<?> t = field.getType();
|
return createRadioGroup(setting);
|
||||||
Object value = field.get(settings);
|
} else if (prop instanceof SimpleRangeProperty) {
|
||||||
if(setting instanceof SelectionSetting) {
|
return createRangeSlider(setting);
|
||||||
return createComboBox(key, ((SelectionSetting) setting).getOptions());
|
} else if (prop instanceof SimpleDirectoryProperty) {
|
||||||
} else if (t == String.class) {
|
return createDirectorySelector(setting);
|
||||||
return createStringProperty(key, (String) value);
|
} else if (prop instanceof SimpleFileProperty) {
|
||||||
} else if (t == int.class || t == Integer.class) {
|
return createFileSelector(setting);
|
||||||
return createIntegerProperty(key, (Integer) value);
|
} else if (prop instanceof IntegerProperty) {
|
||||||
} else if (t == boolean.class || t == Boolean.class) {
|
return createIntegerProperty(setting);
|
||||||
return createBooleanProperty(key, (Boolean) value);
|
} else if (prop instanceof LongProperty) {
|
||||||
|
return createLongProperty(setting);
|
||||||
|
} else if (prop instanceof BooleanProperty) {
|
||||||
|
return createBooleanProperty(setting);
|
||||||
|
} else if (prop instanceof ListProperty) {
|
||||||
|
return createComboBox(setting);
|
||||||
|
} else if (prop instanceof StringProperty) {
|
||||||
|
return createStringProperty(setting);
|
||||||
} else {
|
} else {
|
||||||
return new Label("Unsupported Type for key " + key + ": " + t);
|
return new Label("Unsupported Type for key " + setting.getKey() + ": " + setting.getProperty());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private Node createStringProperty(String fieldName, String value) {
|
private Node createRadioGroup(Setting setting) {
|
||||||
TextField ctrl = new TextField(value);
|
ExclusiveSelectionProperty prop = (ExclusiveSelectionProperty) setting.getProperty();
|
||||||
ctrl.textProperty().addListener((obs, oldV, newV) -> saveValue(() -> {
|
ToggleGroup toggleGroup = new ToggleGroup();
|
||||||
Field field = Settings.class.getField(fieldName);
|
RadioButton optionA = new RadioButton(prop.getOptionA());
|
||||||
|
optionA.setSelected(prop.getValue());
|
||||||
|
optionA.setToggleGroup(toggleGroup);
|
||||||
|
RadioButton optionB = new RadioButton(prop.getOptionB());
|
||||||
|
optionB.setSelected(!optionA.isSelected());
|
||||||
|
optionB.setToggleGroup(toggleGroup);
|
||||||
|
optionA.selectedProperty().bindBidirectional(prop);
|
||||||
|
prop.addListener((obs, oldV, newV) -> saveValue(() -> {
|
||||||
|
Field field = Settings.class.getField(setting.getKey());
|
||||||
field.set(settings, newV);
|
field.set(settings, newV);
|
||||||
config.save();
|
config.save();
|
||||||
}));
|
}));
|
||||||
|
HBox row = new HBox();
|
||||||
|
row.getChildren().addAll(optionA, optionB);
|
||||||
|
HBox.setMargin(optionA, new Insets(5));
|
||||||
|
HBox.setMargin(optionB, new Insets(5));
|
||||||
|
return row;
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
private Node createRangeSlider(Setting setting) {
|
||||||
|
SimpleRangeProperty<Integer> rangeProperty = (SimpleRangeProperty<Integer>) setting.getProperty();
|
||||||
|
DiscreteRange<Integer> range = (DiscreteRange<Integer>) rangeProperty.getRange();
|
||||||
|
List<Integer> labels = (List<Integer>) range.getLabels();
|
||||||
|
List<Integer> values = range.getTicks();
|
||||||
|
RangeSlider<Integer> resolutionRange = new RangeSlider<>(rangeProperty.getRange());
|
||||||
|
resolutionRange.setShowTickMarks(true);
|
||||||
|
resolutionRange.setShowTickLabels(true);
|
||||||
|
int lowValue = getRangeSliderValue(values, labels, Config.getInstance().getSettings().minimumResolution);
|
||||||
|
resolutionRange.setLow(lowValue >= 0 ? lowValue : values.get(0));
|
||||||
|
int highValue = getRangeSliderValue(values, labels, Config.getInstance().getSettings().maximumResolution);
|
||||||
|
resolutionRange.setHigh(highValue >= 0 ? highValue : values.get(values.size() - 1));
|
||||||
|
resolutionRange.getLow().addListener((obs, o, n) -> saveValue(() -> {
|
||||||
|
int newV = labels.get(n.intValue());
|
||||||
|
Field field = Settings.class.getField(rangeProperty.getLowKey());
|
||||||
|
field.set(settings, newV);
|
||||||
|
config.save();
|
||||||
|
}));
|
||||||
|
resolutionRange.getHigh().addListener((obs, o, n) -> saveValue(() -> {
|
||||||
|
int newV = labels.get(n.intValue());
|
||||||
|
Field field = Settings.class.getField(rangeProperty.getHighKey());
|
||||||
|
field.set(settings, newV);
|
||||||
|
config.save();
|
||||||
|
}));
|
||||||
|
return resolutionRange;
|
||||||
|
}
|
||||||
|
|
||||||
|
private int getRangeSliderValue(List<Integer> values, List<Integer> labels, int value) {
|
||||||
|
for (int i = 0; i < labels.size(); i++) {
|
||||||
|
int label = labels.get(i).intValue();
|
||||||
|
if(label == value) {
|
||||||
|
return values.get(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Node createFileSelector(Setting setting) {
|
||||||
|
ProgramSelectionBox programSelector = new ProgramSelectionBox("");
|
||||||
|
programSelector.fileProperty().addListener((obs, o, n) -> saveValue(() -> {
|
||||||
|
String path = n;
|
||||||
|
Field field = Settings.class.getField(setting.getKey());
|
||||||
|
String oldValue = (String) field.get(settings);
|
||||||
|
if (!Objects.equals(path, oldValue)) {
|
||||||
|
field.set(settings, path);
|
||||||
|
config.save();
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
StringProperty property = (StringProperty) setting.getProperty();
|
||||||
|
programSelector.fileProperty().bindBidirectional(property);
|
||||||
|
return programSelector;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Node createDirectorySelector(Setting setting) {
|
||||||
|
DirectorySelectionBox directorySelector = new DirectorySelectionBox("");
|
||||||
|
directorySelector.prefWidth(400);
|
||||||
|
directorySelector.fileProperty().addListener((obs, o, n) -> saveValue(() -> {
|
||||||
|
String path = n;
|
||||||
|
Field field = Settings.class.getField(setting.getKey());
|
||||||
|
String oldValue = (String) field.get(settings);
|
||||||
|
if (!Objects.equals(path, oldValue)) {
|
||||||
|
field.set(settings, path);
|
||||||
|
config.save();
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
StringProperty property = (StringProperty) setting.getProperty();
|
||||||
|
directorySelector.fileProperty().bindBidirectional(property);
|
||||||
|
return directorySelector;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Node createStringProperty(Setting setting) {
|
||||||
|
TextField ctrl = new TextField();
|
||||||
|
ctrl.textProperty().addListener((obs, oldV, newV) -> saveValue(() -> {
|
||||||
|
Field field = Settings.class.getField(setting.getKey());
|
||||||
|
field.set(settings, newV);
|
||||||
|
config.save();
|
||||||
|
}));
|
||||||
|
StringProperty prop = (StringProperty) setting.getProperty();
|
||||||
|
ctrl.textProperty().bindBidirectional(prop);
|
||||||
return ctrl;
|
return ctrl;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Node createIntegerProperty(String fieldName, Integer value) {
|
@SuppressWarnings("unchecked")
|
||||||
TextField ctrl = new TextField(value.toString());
|
private Node createIntegerProperty(Setting setting) {
|
||||||
|
TextField ctrl = new TextField();
|
||||||
ctrl.textProperty().addListener((obs, oldV, newV) -> saveValue(() -> {
|
ctrl.textProperty().addListener((obs, oldV, newV) -> saveValue(() -> {
|
||||||
if (!newV.matches("\\d*")) {
|
if (!newV.matches("\\d*")) {
|
||||||
ctrl.setText(newV.replaceAll(PATTERN_NOT_A_DIGIT, ""));
|
ctrl.setText(newV.replaceAll(PATTERN_NOT_A_DIGIT, ""));
|
||||||
}
|
}
|
||||||
if (!ctrl.getText().isEmpty()) {
|
if (!ctrl.getText().isEmpty()) {
|
||||||
Field field = Settings.class.getField(fieldName);
|
Field field = Settings.class.getField(setting.getKey());
|
||||||
field.set(settings, Integer.parseInt(ctrl.getText()));
|
field.set(settings, Integer.parseInt(ctrl.getText()));
|
||||||
config.save();
|
config.save();
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
|
Property<Number> prop = setting.getProperty();
|
||||||
|
ctrl.textProperty().bindBidirectional(prop, new NumberStringConverter());
|
||||||
return ctrl;
|
return ctrl;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Node createBooleanProperty(String fieldName, Boolean value) {
|
@SuppressWarnings("unchecked")
|
||||||
|
private Node createLongProperty(Setting setting) {
|
||||||
|
TextField ctrl = new TextField();
|
||||||
|
ctrl.textProperty().addListener((obs, oldV, newV) -> saveValue(() -> {
|
||||||
|
if (!newV.matches("\\d*")) {
|
||||||
|
ctrl.setText(newV.replaceAll(PATTERN_NOT_A_DIGIT, ""));
|
||||||
|
}
|
||||||
|
if (!ctrl.getText().isEmpty()) {
|
||||||
|
long value = Long.parseLong(ctrl.getText());
|
||||||
|
if (setting.getConverter() != null) {
|
||||||
|
value = (long) setting.getConverter().convertFrom(value);
|
||||||
|
}
|
||||||
|
Field field = Settings.class.getField(setting.getKey());
|
||||||
|
field.set(settings, value);
|
||||||
|
config.save();
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
Property<Number> prop = setting.getProperty();
|
||||||
|
ctrl.textProperty().bindBidirectional(prop, new NumberStringConverter());
|
||||||
|
return ctrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Node createBooleanProperty(Setting setting) {
|
||||||
CheckBox ctrl = new CheckBox();
|
CheckBox ctrl = new CheckBox();
|
||||||
ctrl.setSelected(value);
|
|
||||||
ctrl.selectedProperty().addListener((obs, oldV, newV) -> saveValue(() -> {
|
ctrl.selectedProperty().addListener((obs, oldV, newV) -> saveValue(() -> {
|
||||||
Field field = Settings.class.getField(fieldName);
|
Field field = Settings.class.getField(setting.getKey());
|
||||||
field.set(settings, newV);
|
field.set(settings, newV);
|
||||||
config.save();
|
config.save();
|
||||||
}));
|
}));
|
||||||
|
BooleanProperty prop = (BooleanProperty) setting.getProperty();
|
||||||
|
ctrl.selectedProperty().bindBidirectional(prop);
|
||||||
return ctrl;
|
return ctrl;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Node createComboBox(String key, Object[] options) throws NoSuchFieldException, IllegalAccessException {
|
@SuppressWarnings({ "rawtypes", "unchecked" })
|
||||||
@SuppressWarnings({ "rawtypes", "unchecked" })
|
private Node createComboBox(Setting setting) throws NoSuchFieldException, IllegalAccessException {
|
||||||
ComboBox<Object> comboBox = new ComboBox(FXCollections.observableList(Arrays.asList(options)));
|
ListProperty<?> listProp = (ListProperty<?>) setting.getProperty();
|
||||||
Field field = Settings.class.getField(key);
|
ComboBox<Object> comboBox = new ComboBox(listProp);
|
||||||
|
Field field = Settings.class.getField(setting.getKey());
|
||||||
Object value = field.get(settings);
|
Object value = field.get(settings);
|
||||||
if(StringUtil.isNotBlank(value.toString())) {
|
if (StringUtil.isNotBlank(value.toString())) {
|
||||||
comboBox.getSelectionModel().select(value);
|
if (setting.getConverter() != null) {
|
||||||
|
comboBox.getSelectionModel().select(setting.getConverter().convertTo(value));
|
||||||
|
} else {
|
||||||
|
comboBox.getSelectionModel().select(value);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
comboBox.valueProperty().addListener((obs, oldV, newV) -> saveValue(() -> {
|
comboBox.valueProperty().addListener((obs, oldV, newV) -> saveValue(() -> {
|
||||||
field.set(settings, newV);
|
if (setting.getConverter() != null) {
|
||||||
|
field.set(settings, setting.getConverter().convertFrom(newV));
|
||||||
|
} else {
|
||||||
|
field.set(settings, newV);
|
||||||
|
}
|
||||||
config.save();
|
config.save();
|
||||||
}));
|
}));
|
||||||
return comboBox;
|
return comboBox;
|
||||||
|
|
|
@ -0,0 +1,162 @@
|
||||||
|
package ctbrec.ui.settings;
|
||||||
|
|
||||||
|
import static javafx.scene.control.ButtonType.*;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.lang.reflect.Type;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import com.squareup.moshi.JsonAdapter;
|
||||||
|
import com.squareup.moshi.Moshi;
|
||||||
|
import com.squareup.moshi.Types;
|
||||||
|
|
||||||
|
import ctbrec.Config;
|
||||||
|
import ctbrec.Model;
|
||||||
|
import ctbrec.io.ModelJsonAdapter;
|
||||||
|
import ctbrec.sites.Site;
|
||||||
|
import ctbrec.ui.AutosizeAlert;
|
||||||
|
import ctbrec.ui.controls.Dialogs;
|
||||||
|
import javafx.geometry.Insets;
|
||||||
|
import javafx.scene.control.Alert.AlertType;
|
||||||
|
import javafx.scene.control.Button;
|
||||||
|
import javafx.scene.control.ButtonType;
|
||||||
|
import javafx.scene.control.Label;
|
||||||
|
import javafx.scene.control.ListView;
|
||||||
|
import javafx.scene.control.SelectionMode;
|
||||||
|
import javafx.scene.input.KeyCode;
|
||||||
|
import javafx.scene.input.KeyEvent;
|
||||||
|
import javafx.scene.layout.GridPane;
|
||||||
|
import javafx.scene.layout.HBox;
|
||||||
|
import javafx.scene.layout.Pane;
|
||||||
|
import javafx.stage.FileChooser;
|
||||||
|
|
||||||
|
public class IgnoreList extends Pane {
|
||||||
|
|
||||||
|
private static final Logger LOG = LoggerFactory.getLogger(IgnoreList.class);
|
||||||
|
|
||||||
|
private ListView<Model> ignoreListView;
|
||||||
|
|
||||||
|
private List<Site> sites;
|
||||||
|
|
||||||
|
public IgnoreList(List<Site> sites) {
|
||||||
|
this.sites = sites;
|
||||||
|
createGui();
|
||||||
|
loadIgnoredModels();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void createGui() {
|
||||||
|
GridPane grid = new GridPane();
|
||||||
|
grid.setHgap(10);
|
||||||
|
grid.setVgap(10);
|
||||||
|
grid.setPadding(new Insets(20, 150, 10, 10));
|
||||||
|
|
||||||
|
Label headline = new Label("Ignore List");
|
||||||
|
headline.getStyleClass().add("settings-group-label");
|
||||||
|
grid.add(headline, 0, 0);
|
||||||
|
|
||||||
|
ignoreListView = new ListView<>();
|
||||||
|
ignoreListView.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE);
|
||||||
|
ignoreListView.addEventHandler(KeyEvent.KEY_PRESSED, event -> {
|
||||||
|
if (event.getCode() == KeyCode.DELETE) {
|
||||||
|
removeSelectedModels();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
grid.add(ignoreListView, 0, 1);
|
||||||
|
|
||||||
|
Button remove = new Button("Remove");
|
||||||
|
remove.setOnAction(evt -> removeSelectedModels());
|
||||||
|
Button exportIgnoreList = new Button("Export");
|
||||||
|
exportIgnoreList.setOnAction(e -> exportIgnoreList());
|
||||||
|
Button importIgnoreList = new Button("Import");
|
||||||
|
importIgnoreList.setOnAction(e -> importIgnoreList());
|
||||||
|
HBox buttons = new HBox(10, remove, exportIgnoreList, importIgnoreList);
|
||||||
|
grid.add(buttons, 0, 2);
|
||||||
|
buttons.setStyle("-fx-background-color: -fx-background"); // workaround so that the buttons don't shrink
|
||||||
|
|
||||||
|
getChildren().add(grid);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void removeSelectedModels() {
|
||||||
|
List<Model> selectedModels = ignoreListView.getSelectionModel().getSelectedItems();
|
||||||
|
if (selectedModels.isEmpty()) {
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
Config.getInstance().getSettings().modelsIgnored.removeAll(selectedModels);
|
||||||
|
ignoreListView.getItems().removeAll(selectedModels);
|
||||||
|
LOG.debug(Config.getInstance().getSettings().modelsIgnored.toString());
|
||||||
|
try {
|
||||||
|
Config.getInstance().save();
|
||||||
|
} catch (IOException e) {
|
||||||
|
LOG.warn("Couldn't save config", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void loadIgnoredModels() {
|
||||||
|
List<Model> ignored = Config.getInstance().getSettings().modelsIgnored;
|
||||||
|
ignoreListView.getItems().clear();
|
||||||
|
ignoreListView.getItems().addAll(ignored);
|
||||||
|
Collections.sort(ignoreListView.getItems());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void refresh() {
|
||||||
|
loadIgnoredModels();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void exportIgnoreList() {
|
||||||
|
FileChooser chooser = new FileChooser();
|
||||||
|
chooser.setTitle("Export ignore list");
|
||||||
|
chooser.setInitialFileName("ctbrec-ignorelist.json");
|
||||||
|
File file = chooser.showSaveDialog(null);
|
||||||
|
if (file != null) {
|
||||||
|
Moshi moshi = new Moshi.Builder().add(Model.class, new ModelJsonAdapter(sites)).build();
|
||||||
|
Type modelListType = Types.newParameterizedType(List.class, Model.class);
|
||||||
|
JsonAdapter<List<Model>> adapter = moshi.adapter(modelListType);
|
||||||
|
adapter = adapter.indent(" ");
|
||||||
|
try (FileOutputStream out = new FileOutputStream(file)) {
|
||||||
|
String json = adapter.toJson(Config.getInstance().getSettings().modelsIgnored);
|
||||||
|
out.write(json.getBytes(StandardCharsets.UTF_8));
|
||||||
|
} catch (IOException e) {
|
||||||
|
Dialogs.showError(getScene(), "Couldn't export ignore list", e.getLocalizedMessage(), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void importIgnoreList() {
|
||||||
|
FileChooser chooser = new FileChooser();
|
||||||
|
chooser.setTitle("Import ignore list");
|
||||||
|
File file = chooser.showOpenDialog(null);
|
||||||
|
if (file != null) {
|
||||||
|
Moshi moshi = new Moshi.Builder().add(Model.class, new ModelJsonAdapter(sites)).build();
|
||||||
|
Type modelListType = Types.newParameterizedType(List.class, Model.class);
|
||||||
|
JsonAdapter<List<Model>> adapter = moshi.adapter(modelListType);
|
||||||
|
try {
|
||||||
|
byte[] fileContent = Files.readAllBytes(file.toPath());
|
||||||
|
List<Model> ignoredModels = adapter.fromJson(new String(fileContent, StandardCharsets.UTF_8));
|
||||||
|
boolean confirmed = true;
|
||||||
|
if (!Config.getInstance().getSettings().modelsIgnored.isEmpty()) {
|
||||||
|
String msg = "This will replace the existing ignore list! Continue?";
|
||||||
|
AutosizeAlert confirm = new AutosizeAlert(AlertType.CONFIRMATION, msg, getScene(), YES, NO);
|
||||||
|
confirm.setTitle("Import ignore list");
|
||||||
|
confirm.setHeaderText("Overwrite ignore list");
|
||||||
|
confirm.showAndWait();
|
||||||
|
confirmed = confirm.getResult() == ButtonType.YES;
|
||||||
|
}
|
||||||
|
if (confirmed) {
|
||||||
|
Config.getInstance().getSettings().modelsIgnored = ignoredModels;
|
||||||
|
refresh();
|
||||||
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
Dialogs.showError(getScene(), "Couldn't import ignore list", e.getLocalizedMessage(), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,96 +0,0 @@
|
||||||
package ctbrec.ui.settings;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
|
|
||||||
import ctbrec.Config;
|
|
||||||
import ctbrec.Model;
|
|
||||||
import ctbrec.ui.controls.Dialogs;
|
|
||||||
import javafx.geometry.Insets;
|
|
||||||
import javafx.scene.Scene;
|
|
||||||
import javafx.scene.control.Button;
|
|
||||||
import javafx.scene.control.ButtonType;
|
|
||||||
import javafx.scene.control.Dialog;
|
|
||||||
import javafx.scene.control.ListView;
|
|
||||||
import javafx.scene.control.SelectionMode;
|
|
||||||
import javafx.scene.image.Image;
|
|
||||||
import javafx.scene.input.KeyCode;
|
|
||||||
import javafx.scene.input.KeyEvent;
|
|
||||||
import javafx.scene.layout.GridPane;
|
|
||||||
import javafx.stage.Modality;
|
|
||||||
import javafx.stage.Stage;
|
|
||||||
|
|
||||||
public class IgnoreListDialog extends Dialog<Void> {
|
|
||||||
|
|
||||||
private static final Logger LOG = LoggerFactory.getLogger(IgnoreListDialog.class);
|
|
||||||
|
|
||||||
private Scene parent;
|
|
||||||
private ListView<Model> ignoreList;
|
|
||||||
|
|
||||||
public IgnoreListDialog(Scene parent) {
|
|
||||||
this.parent = parent;
|
|
||||||
createGui();
|
|
||||||
loadIgnoredModels();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void createGui() {
|
|
||||||
setTitle("Ignore List");
|
|
||||||
getDialogPane().getButtonTypes().addAll(ButtonType.OK);
|
|
||||||
initModality(Modality.APPLICATION_MODAL);
|
|
||||||
setResizable(true);
|
|
||||||
InputStream icon = Dialogs.class.getResourceAsStream("/icon.png");
|
|
||||||
Stage stage = (Stage) getDialogPane().getScene().getWindow();
|
|
||||||
stage.getIcons().add(new Image(icon));
|
|
||||||
if (parent != null) {
|
|
||||||
stage.getScene().getStylesheets().addAll(parent.getStylesheets());
|
|
||||||
}
|
|
||||||
|
|
||||||
GridPane grid = new GridPane();
|
|
||||||
grid.setHgap(10);
|
|
||||||
grid.setVgap(10);
|
|
||||||
grid.setPadding(new Insets(20, 150, 10, 10));
|
|
||||||
|
|
||||||
ignoreList = new ListView<>();
|
|
||||||
ignoreList.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE);
|
|
||||||
ignoreList.addEventHandler(KeyEvent.KEY_PRESSED, event -> {
|
|
||||||
if (event.getCode() == KeyCode.DELETE) {
|
|
||||||
removeSelectedModels();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
grid.add(ignoreList, 0, 0);
|
|
||||||
|
|
||||||
Button remove = new Button("Remove");
|
|
||||||
remove.setOnAction(evt -> removeSelectedModels());
|
|
||||||
grid.add(remove, 0, 1);
|
|
||||||
|
|
||||||
getDialogPane().setContent(grid);
|
|
||||||
setResizable(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void removeSelectedModels() {
|
|
||||||
List<Model> selectedModels = ignoreList.getSelectionModel().getSelectedItems();
|
|
||||||
if (selectedModels.isEmpty()) {
|
|
||||||
return;
|
|
||||||
} else {
|
|
||||||
Config.getInstance().getSettings().modelsIgnored.removeAll(selectedModels);
|
|
||||||
ignoreList.getItems().removeAll(selectedModels);
|
|
||||||
LOG.debug(Config.getInstance().getSettings().modelsIgnored.toString());
|
|
||||||
try {
|
|
||||||
Config.getInstance().save();
|
|
||||||
} catch (IOException e) {
|
|
||||||
LOG.warn("Couldn't save config", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void loadIgnoredModels() {
|
|
||||||
List<Model> ignored = Config.getInstance().getSettings().modelsIgnored;
|
|
||||||
ignoreList.getItems().addAll(ignored);
|
|
||||||
Collections.sort(ignoreList.getItems());
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -38,6 +38,7 @@ import ctbrec.ui.controls.DirectorySelectionBox;
|
||||||
import ctbrec.ui.controls.ProgramSelectionBox;
|
import ctbrec.ui.controls.ProgramSelectionBox;
|
||||||
import ctbrec.ui.controls.range.DiscreteRange;
|
import ctbrec.ui.controls.range.DiscreteRange;
|
||||||
import ctbrec.ui.controls.range.RangeSlider;
|
import ctbrec.ui.controls.range.RangeSlider;
|
||||||
|
import ctbrec.ui.settings.api.ValueConverter;
|
||||||
import ctbrec.ui.sites.ConfigUI;
|
import ctbrec.ui.sites.ConfigUI;
|
||||||
import ctbrec.ui.tabs.TabSelectionListener;
|
import ctbrec.ui.tabs.TabSelectionListener;
|
||||||
import javafx.collections.FXCollections;
|
import javafx.collections.FXCollections;
|
||||||
|
@ -159,7 +160,7 @@ public class SettingsTab extends Tab implements TabSelectionListener {
|
||||||
|
|
||||||
//right side
|
//right side
|
||||||
rightSide.getChildren().add(siteConfigAccordion);
|
rightSide.getChildren().add(siteConfigAccordion);
|
||||||
ActionSettingsPanel actions = new ActionSettingsPanel(this, recorder);
|
ActionSettingsPanel actions = new ActionSettingsPanel(recorder);
|
||||||
rightSide.getChildren().add(actions);
|
rightSide.getChildren().add(actions);
|
||||||
proxySettingsPane = new ProxySettingsPane(this);
|
proxySettingsPane = new ProxySettingsPane(this);
|
||||||
rightSide.getChildren().add(createIgnoreListPanel());
|
rightSide.getChildren().add(createIgnoreListPanel());
|
||||||
|
@ -561,7 +562,7 @@ public class SettingsTab extends Tab implements TabSelectionListener {
|
||||||
private Node createIgnoreListPanel() {
|
private Node createIgnoreListPanel() {
|
||||||
GridPane layout = createGridLayout();
|
GridPane layout = createGridLayout();
|
||||||
Button editIgnoreList = new Button("Edit");
|
Button editIgnoreList = new Button("Edit");
|
||||||
editIgnoreList.setOnAction(e -> new IgnoreListDialog(editIgnoreList.getScene()).showAndWait());
|
//editIgnoreList.setOnAction(e -> new IgnoreList(editIgnoreList.getScene()).showAndWait());
|
||||||
layout.add(editIgnoreList, 0, 0);
|
layout.add(editIgnoreList, 0, 0);
|
||||||
Button exportIgnoreList = new Button("Export");
|
Button exportIgnoreList = new Button("Export");
|
||||||
exportIgnoreList.setOnAction(e -> exportIgnoreList());
|
exportIgnoreList.setOnAction(e -> exportIgnoreList());
|
||||||
|
@ -850,7 +851,7 @@ public class SettingsTab extends Tab implements TabSelectionListener {
|
||||||
|
|
||||||
public void saveConfig() {
|
public void saveConfig() {
|
||||||
if(proxySettingsPane != null) {
|
if(proxySettingsPane != null) {
|
||||||
proxySettingsPane.saveConfig();
|
//proxySettingsPane.saveConfig();
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
Config.getInstance().save();
|
Config.getInstance().save();
|
||||||
|
@ -877,5 +878,39 @@ public class SettingsTab extends Tab implements TabSelectionListener {
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return label;
|
return label;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
final int prime = 31;
|
||||||
|
int result = 1;
|
||||||
|
result = prime * result + value;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
if (this == obj)
|
||||||
|
return true;
|
||||||
|
if (obj == null)
|
||||||
|
return false;
|
||||||
|
if (getClass() != obj.getClass())
|
||||||
|
return false;
|
||||||
|
SplitAfterOption other = (SplitAfterOption) obj;
|
||||||
|
return value == other.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ValueConverter converter() {
|
||||||
|
return new ValueConverter() {
|
||||||
|
@Override
|
||||||
|
public Integer convertFrom(Object splitAfterOption) {
|
||||||
|
return ((SplitAfterOption) splitAfterOption).getValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SplitAfterOption convertTo(Object integer) {
|
||||||
|
return new SplitAfterOption(integer.toString(), (Integer) integer);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,91 +1,284 @@
|
||||||
package ctbrec.ui.settings;
|
package ctbrec.ui.settings;
|
||||||
|
|
||||||
|
import static ctbrec.Settings.DirectoryStructure.*;
|
||||||
import static ctbrec.Settings.ProxyType.*;
|
import static ctbrec.Settings.ProxyType.*;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import ctbrec.Config;
|
import ctbrec.Config;
|
||||||
|
import ctbrec.Hmac;
|
||||||
|
import ctbrec.Settings;
|
||||||
|
import ctbrec.Settings.DirectoryStructure;
|
||||||
|
import ctbrec.Settings.ProxyType;
|
||||||
import ctbrec.recorder.Recorder;
|
import ctbrec.recorder.Recorder;
|
||||||
import ctbrec.sites.Site;
|
import ctbrec.sites.Site;
|
||||||
import ctbrec.ui.SiteUiFactory;
|
import ctbrec.ui.SiteUiFactory;
|
||||||
|
import ctbrec.ui.controls.range.DiscreteRange;
|
||||||
|
import ctbrec.ui.settings.SettingsTab.SplitAfterOption;
|
||||||
import ctbrec.ui.settings.api.Category;
|
import ctbrec.ui.settings.api.Category;
|
||||||
|
import ctbrec.ui.settings.api.ExclusiveSelectionProperty;
|
||||||
|
import ctbrec.ui.settings.api.GigabytesConverter;
|
||||||
import ctbrec.ui.settings.api.Group;
|
import ctbrec.ui.settings.api.Group;
|
||||||
import ctbrec.ui.settings.api.Preferences;
|
import ctbrec.ui.settings.api.Preferences;
|
||||||
import ctbrec.ui.settings.api.Setting;
|
import ctbrec.ui.settings.api.Setting;
|
||||||
|
import ctbrec.ui.settings.api.SimpleDirectoryProperty;
|
||||||
|
import ctbrec.ui.settings.api.SimpleFileProperty;
|
||||||
|
import ctbrec.ui.settings.api.SimpleRangeProperty;
|
||||||
import ctbrec.ui.tabs.TabSelectionListener;
|
import ctbrec.ui.tabs.TabSelectionListener;
|
||||||
|
import javafx.beans.property.SimpleBooleanProperty;
|
||||||
|
import javafx.beans.property.SimpleIntegerProperty;
|
||||||
|
import javafx.beans.property.SimpleListProperty;
|
||||||
|
import javafx.beans.property.SimpleLongProperty;
|
||||||
|
import javafx.beans.property.SimpleStringProperty;
|
||||||
|
import javafx.beans.value.ObservableValue;
|
||||||
|
import javafx.collections.FXCollections;
|
||||||
import javafx.scene.control.Tab;
|
import javafx.scene.control.Tab;
|
||||||
|
import javafx.scene.control.TextInputDialog;
|
||||||
|
|
||||||
public class SettingsTab2 extends Tab implements TabSelectionListener {
|
public class SettingsTab2 extends Tab implements TabSelectionListener {
|
||||||
|
|
||||||
|
private static final Logger LOG = LoggerFactory.getLogger(SettingsTab2.class);
|
||||||
|
|
||||||
private List<Site> sites;
|
private List<Site> sites;
|
||||||
private Recorder recorder;
|
private Recorder recorder;
|
||||||
private Preferences prefs;
|
private Preferences prefs;
|
||||||
private boolean initialized = false;
|
private boolean initialized = false;
|
||||||
|
private Config config;
|
||||||
|
private Settings settings;
|
||||||
|
|
||||||
|
private SimpleStringProperty httpUserAgent;
|
||||||
|
private SimpleStringProperty httpUserAgentMobile;
|
||||||
|
private SimpleIntegerProperty overviewUpdateIntervalInSecs;
|
||||||
|
private SimpleBooleanProperty updateThumbnails;
|
||||||
|
private SimpleBooleanProperty determineResolution;
|
||||||
|
private SimpleBooleanProperty chooseStreamQuality;
|
||||||
|
private SimpleBooleanProperty livePreviews;
|
||||||
|
private SimpleListProperty<String> startTab;
|
||||||
|
private SimpleFileProperty mediaPlayer;
|
||||||
|
private SimpleStringProperty mediaPlayerParams;
|
||||||
|
private SimpleIntegerProperty maximumResolutionPlayer;
|
||||||
|
private SimpleBooleanProperty showPlayerStarting;
|
||||||
|
private SimpleBooleanProperty singlePlayer;
|
||||||
|
private SimpleListProperty<ProxyType> proxyType;
|
||||||
|
private SimpleStringProperty proxyHost;
|
||||||
|
private SimpleStringProperty proxyPort;
|
||||||
|
private SimpleStringProperty proxyUser;
|
||||||
|
private SimpleStringProperty proxyPassword;
|
||||||
|
private SimpleDirectoryProperty recordingsDir;
|
||||||
|
private SimpleListProperty<DirectoryStructure> directoryStructure;
|
||||||
|
private SimpleListProperty<SplitAfterOption> splitAfter;
|
||||||
|
private SimpleRangeProperty<Integer> resolutionRange;
|
||||||
|
private List<Integer> labels = Arrays.asList(0, 240, 360, 480, 600, 720, 960, 1080, 1440, 2160, 4320, 8640);
|
||||||
|
private List<Integer> values = Arrays.asList(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11);
|
||||||
|
private DiscreteRange<Integer> rangeValues = new DiscreteRange<>(values, labels);
|
||||||
|
private SimpleIntegerProperty concurrentRecordings;
|
||||||
|
private SimpleIntegerProperty onlineCheckIntervalInSecs;
|
||||||
|
private SimpleLongProperty leaveSpaceOnDevice;
|
||||||
|
private SimpleIntegerProperty minimumLengthInSecs;
|
||||||
|
private SimpleStringProperty ffmpegParameters;
|
||||||
|
private SimpleStringProperty fileExtension;
|
||||||
|
private SimpleStringProperty server;
|
||||||
|
private SimpleIntegerProperty port;
|
||||||
|
private SimpleStringProperty path;
|
||||||
|
private SimpleBooleanProperty requireAuthentication;
|
||||||
|
private SimpleBooleanProperty transportLayerSecurity;
|
||||||
|
private ExclusiveSelectionProperty recordLocal;
|
||||||
|
private SimpleFileProperty postProcessing;
|
||||||
|
private SimpleIntegerProperty postProcessingThreads;
|
||||||
|
private SimpleBooleanProperty removeRecordingAfterPp;
|
||||||
|
private IgnoreList ignoreList;
|
||||||
|
|
||||||
public SettingsTab2(List<Site> sites, Recorder recorder) {
|
public SettingsTab2(List<Site> sites, Recorder recorder) {
|
||||||
this.sites = sites;
|
this.sites = sites;
|
||||||
this.recorder = recorder;
|
this.recorder = recorder;
|
||||||
setText("Settings");
|
setText("Settings");
|
||||||
setClosable(false);
|
setClosable(false);
|
||||||
|
config = Config.getInstance();
|
||||||
|
settings = config.getSettings();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initializeProperties() {
|
||||||
|
httpUserAgent = new SimpleStringProperty(null, "httpUserAgent", settings.httpUserAgent);
|
||||||
|
httpUserAgentMobile = new SimpleStringProperty(null, "httpUserAgentMobile", settings.httpUserAgentMobile);
|
||||||
|
overviewUpdateIntervalInSecs = new SimpleIntegerProperty(null, "overviewUpdateIntervalInSecs", settings.overviewUpdateIntervalInSecs);
|
||||||
|
updateThumbnails = new SimpleBooleanProperty(null, "updateThumbnails", settings.updateThumbnails);
|
||||||
|
determineResolution = new SimpleBooleanProperty(null, "determineResolution", settings.determineResolution);
|
||||||
|
chooseStreamQuality = new SimpleBooleanProperty(null, "chooseStreamQuality", settings.chooseStreamQuality);
|
||||||
|
livePreviews = new SimpleBooleanProperty(null, "livePreviews", settings.livePreviews);
|
||||||
|
startTab = new SimpleListProperty<>(null, "startTab", FXCollections.observableList(getTabNames()));
|
||||||
|
mediaPlayer = new SimpleFileProperty(null, "mediaPlayer", settings.mediaPlayer);
|
||||||
|
mediaPlayerParams = new SimpleStringProperty(null, "mediaPlayerParams", settings.mediaPlayerParams);
|
||||||
|
maximumResolutionPlayer = new SimpleIntegerProperty(null, "maximumResolutionPlayer", settings.maximumResolutionPlayer);
|
||||||
|
showPlayerStarting = new SimpleBooleanProperty(null, "showPlayerStarting", settings.showPlayerStarting);
|
||||||
|
singlePlayer = new SimpleBooleanProperty(null, "singlePlayer", settings.singlePlayer);
|
||||||
|
proxyType = new SimpleListProperty<>(null, "proxyType", FXCollections.observableList(List.of(DIRECT, HTTP, SOCKS4, SOCKS5)));
|
||||||
|
proxyHost = new SimpleStringProperty(null, "proxyHost", settings.proxyHost);
|
||||||
|
proxyPort = new SimpleStringProperty(null, "proxyPort", settings.proxyPort);
|
||||||
|
proxyUser = new SimpleStringProperty(null, "proxyUser", settings.proxyUser);
|
||||||
|
proxyPassword = new SimpleStringProperty(null, "proxyPassword", settings.proxyPassword);
|
||||||
|
recordingsDir = new SimpleDirectoryProperty(null, "recordingsDir", settings.recordingsDir);
|
||||||
|
directoryStructure = new SimpleListProperty<>(null, "recordingsDirStructure", FXCollections.observableList(List.of(FLAT, ONE_PER_MODEL, ONE_PER_RECORDING)));
|
||||||
|
splitAfter = new SimpleListProperty<>(null, "splitRecordings", FXCollections.observableList(getSplitOptions()));
|
||||||
|
resolutionRange = new SimpleRangeProperty<>(rangeValues, "minimumResolution", "maximumResolution", settings.minimumResolution, settings.maximumResolution);
|
||||||
|
concurrentRecordings = new SimpleIntegerProperty(null, "concurrentRecordings", settings.concurrentRecordings);
|
||||||
|
onlineCheckIntervalInSecs = new SimpleIntegerProperty(null, "onlineCheckIntervalInSecs", settings.onlineCheckIntervalInSecs);
|
||||||
|
leaveSpaceOnDevice = new SimpleLongProperty(null, "minimumSpaceLeftInBytes", (long) new GigabytesConverter().convertTo(settings.minimumSpaceLeftInBytes));
|
||||||
|
minimumLengthInSecs = new SimpleIntegerProperty(null, "minimumLengthInSeconds", settings.minimumLengthInSeconds);
|
||||||
|
ffmpegParameters = new SimpleStringProperty(null, "ffmpegMergedDownloadArgs", settings.ffmpegMergedDownloadArgs);
|
||||||
|
fileExtension = new SimpleStringProperty(null, "ffmpegFileSuffix", settings.ffmpegFileSuffix);
|
||||||
|
server = new SimpleStringProperty(null, "httpServer", settings.httpServer);
|
||||||
|
port = new SimpleIntegerProperty(null, "httpPort", settings.httpPort);
|
||||||
|
path = new SimpleStringProperty(null, "servletContext", settings.servletContext);
|
||||||
|
requireAuthentication = new SimpleBooleanProperty(null, "requireAuthentication", settings.requireAuthentication);
|
||||||
|
requireAuthentication.addListener(this::requireAuthenticationChanged);
|
||||||
|
transportLayerSecurity = new SimpleBooleanProperty(null, "transportLayerSecurity", settings.transportLayerSecurity);
|
||||||
|
recordLocal = new ExclusiveSelectionProperty(null, "localRecording", settings.localRecording, "Local", "Remote");
|
||||||
|
postProcessing = new SimpleFileProperty(null, "postProcessing", settings.postProcessing);
|
||||||
|
postProcessingThreads = new SimpleIntegerProperty(null, "postProcessingThreads", settings.postProcessingThreads);
|
||||||
|
removeRecordingAfterPp = new SimpleBooleanProperty(null, "removeRecordingAfterPostProcessing", settings.removeRecordingAfterPostProcessing);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void createGui() {
|
private void createGui() {
|
||||||
|
ignoreList = new IgnoreList(sites);
|
||||||
List<Category> siteCategories = new ArrayList<>();
|
List<Category> siteCategories = new ArrayList<>();
|
||||||
for (Site site : sites) {
|
for (Site site : sites) {
|
||||||
siteCategories.add(Category.of(site.getName(), SiteUiFactory.getUi(site).getConfigUI().createConfigPanel()));
|
siteCategories.add(Category.of(site.getName(), SiteUiFactory.getUi(site).getConfigUI().createConfigPanel()));
|
||||||
}
|
}
|
||||||
|
|
||||||
prefs = Preferences.of(new CtbrecPreferencesStorage(Config.getInstance()),
|
prefs = Preferences.of(new CtbrecPreferencesStorage(config),
|
||||||
Category.of("General",
|
Category.of("General",
|
||||||
Group.of("General",
|
Group.of("General",
|
||||||
Setting.of("User-Agent", "httpUserAgent"),
|
Setting.of("User-Agent", httpUserAgent),
|
||||||
Setting.of("User-Agent mobile", "httpUserAgentMobile"),
|
Setting.of("User-Agent mobile", httpUserAgentMobile),
|
||||||
Setting.of("Update overview interval (seconds)", "overviewUpdateIntervalInSecs", "Update the thumbnail overviews every x seconds"),
|
Setting.of("Update overview interval (seconds)", overviewUpdateIntervalInSecs, "Update the thumbnail overviews every x seconds"),
|
||||||
Setting.of("Update thumbnails", "updateThumbnails", "The overviews will still be updated, but the thumbnails won't be changed. This is useful for less powerful systems."),
|
Setting.of("Update thumbnails", updateThumbnails, "The overviews will still be updated, but the thumbnails won't be changed. This is useful for less powerful systems."),
|
||||||
Setting.of("Display stream resolution in overview", "determineResolution"),
|
Setting.of("Display stream resolution in overview", determineResolution),
|
||||||
Setting.of("Manually select stream quality", "chooseStreamQuality", "Opens a dialog to select the video resolution before recording"),
|
Setting.of("Enable live previews (experimental)", livePreviews),
|
||||||
Setting.of("Enable live previews (experimental)", "livePreviews"),
|
Setting.of("Start Tab", startTab),
|
||||||
Setting.of("Start Tab", "startTab", getTabNames()),
|
Setting.of("Colors (Base / Accent)", new ColorSettingsPane(Config.getInstance()))
|
||||||
Setting.of("Colors (Base / Accent)", null, new ColorSettingsPane(Config.getInstance()))
|
|
||||||
),
|
),
|
||||||
Group.of("Player",
|
Group.of("Player",
|
||||||
Setting.of("Player", "mediaPlayer"),
|
Setting.of("Player", mediaPlayer),
|
||||||
Setting.of("Start parameters", "mediaPlayerParams"),
|
Setting.of("Start parameters", mediaPlayerParams),
|
||||||
Setting.of("Maximum resolution (0 = unlimited)", "maximumResolutionPlayer", "video height, e.g. 720 or 1080"),
|
Setting.of("Maximum resolution (0 = unlimited)", maximumResolutionPlayer, "video height, e.g. 720 or 1080"),
|
||||||
Setting.of("Show \"Player Starting\" Message", "showPlayerStarting"),
|
Setting.of("Show \"Player Starting\" Message", showPlayerStarting),
|
||||||
Setting.of("Start only one player at a time", "singlePlayer")
|
Setting.of("Start only one player at a time", singlePlayer)
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
|
Category.of("Recorder",
|
||||||
|
Group.of("Settings",
|
||||||
|
Setting.of("Recordings Directory", recordingsDir),
|
||||||
|
Setting.of("Directory Structure", directoryStructure),
|
||||||
|
Setting.of("Split recordings after (minutes)", splitAfter).converter(SplitAfterOption.converter()),
|
||||||
|
Setting.of("Manually select stream quality", chooseStreamQuality, "Opens a dialog to select the video resolution before recording"),
|
||||||
|
Setting.of("Restrict Resolution", resolutionRange, "Only record streams with resolution within the given range"),
|
||||||
|
Setting.of("Concurrent Recordings (0 = unlimited)", concurrentRecordings),
|
||||||
|
Setting.of("Check online state every (seconds)", onlineCheckIntervalInSecs, "Check every x seconds, if a model came online"),
|
||||||
|
Setting.of("Leave space on device (GiB)", leaveSpaceOnDevice, "Stop recording, if the free space on the device gets below this threshold").converter(new GigabytesConverter()),
|
||||||
|
Setting.of("FFmpeg parameters", ffmpegParameters, "FFmpeg parameters to use when merging stream segments"),
|
||||||
|
Setting.of("File Extension", fileExtension, "File extension to use for recordings")
|
||||||
|
),
|
||||||
|
Group.of("Location",
|
||||||
|
Setting.of("Record Location", recordLocal),
|
||||||
|
Setting.of("Server", server),
|
||||||
|
Setting.of("Port", port),
|
||||||
|
Setting.of("Path", path, "Leave empty, if you didn't change the servletContext in the server config"),
|
||||||
|
Setting.of("Require authentication", requireAuthentication),
|
||||||
|
Setting.of("Use Secure Communication (TLS)", transportLayerSecurity)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
Category.of("Post-Processing",
|
||||||
|
Group.of("Post-Processing",
|
||||||
|
Setting.of("Post-Processing", postProcessing),
|
||||||
|
Setting.of("Threads", postProcessingThreads),
|
||||||
|
Setting.of("Delete recordings shorter than (secs)", minimumLengthInSecs, "Delete recordings, which are shorter than x seconds. 0 to disable"),
|
||||||
|
Setting.of("Remove recording after post-processing", removeRecordingAfterPp)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
Category.of("Events & Actions", new ActionSettingsPanel(recorder)),
|
||||||
|
Category.of("Ignore List", ignoreList),
|
||||||
Category.of("Sites", siteCategories.toArray(new Category[0])),
|
Category.of("Sites", siteCategories.toArray(new Category[0])),
|
||||||
Category.of("Proxy",
|
Category.of("Proxy",
|
||||||
Group.of("Proxy",
|
Group.of("Proxy",
|
||||||
Setting.of("Type", "proxyType", DIRECT, HTTP, SOCKS4, SOCKS5),
|
Setting.of("Type", proxyType),
|
||||||
Setting.of("Host", "proxyHost"),
|
Setting.of("Host", proxyHost),
|
||||||
Setting.of("Port", "proxyPort"),
|
Setting.of("Port", proxyPort),
|
||||||
Setting.of("Username", "proxyUser"),
|
Setting.of("Username", proxyUser),
|
||||||
Setting.of("Password", "proxyPassword")
|
Setting.of("Password", proxyPassword)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
setContent(prefs.getView());
|
setContent(prefs.getView());
|
||||||
}
|
}
|
||||||
|
|
||||||
private Object[] getTabNames() {
|
private List<String> getTabNames() {
|
||||||
List<String> tabNames = new ArrayList<>();
|
return getTabPane().getTabs().stream()
|
||||||
for (Tab tab : getTabPane().getTabs()) {
|
.map(Tab::getText)
|
||||||
tabNames.add(tab.getText());
|
.collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<SplitAfterOption> getSplitOptions() {
|
||||||
|
List<SplitAfterOption> splitOptions = new ArrayList<>();
|
||||||
|
splitOptions.add(new SplitAfterOption("disabled", 0));
|
||||||
|
if (Config.isDevMode()) {
|
||||||
|
splitOptions.add(new SplitAfterOption("1 min", 1 * 60));
|
||||||
|
splitOptions.add(new SplitAfterOption("3 min", 3 * 60));
|
||||||
|
}
|
||||||
|
splitOptions.add(new SplitAfterOption("5 min", 5 * 60));
|
||||||
|
splitOptions.add(new SplitAfterOption("10 min", 10 * 60));
|
||||||
|
splitOptions.add(new SplitAfterOption("15 min", 15 * 60));
|
||||||
|
splitOptions.add(new SplitAfterOption("20 min", 20 * 60));
|
||||||
|
splitOptions.add(new SplitAfterOption("30 min", 30 * 60));
|
||||||
|
splitOptions.add(new SplitAfterOption("60 min", 60 * 60));
|
||||||
|
return splitOptions;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void requireAuthenticationChanged(ObservableValue<?> obs, Boolean oldV, Boolean newV) { // NOSONAR
|
||||||
|
boolean requiresAuthentication = newV;
|
||||||
|
Config.getInstance().getSettings().requireAuthentication = requiresAuthentication;
|
||||||
|
if (requiresAuthentication) {
|
||||||
|
byte[] key = Config.getInstance().getSettings().key;
|
||||||
|
if (key == null) {
|
||||||
|
key = Hmac.generateKey();
|
||||||
|
Config.getInstance().getSettings().key = key;
|
||||||
|
saveConfig();
|
||||||
|
}
|
||||||
|
TextInputDialog keyDialog = new TextInputDialog();
|
||||||
|
keyDialog.setResizable(true);
|
||||||
|
keyDialog.setTitle("Server Authentication");
|
||||||
|
keyDialog.setHeaderText("A key has been generated");
|
||||||
|
keyDialog.setContentText("Add this setting to your server's config.json:\n");
|
||||||
|
keyDialog.getEditor().setText("\"key\": " + Arrays.toString(key));
|
||||||
|
keyDialog.getEditor().setEditable(false);
|
||||||
|
keyDialog.setWidth(800);
|
||||||
|
keyDialog.setHeight(200);
|
||||||
|
keyDialog.show();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void saveConfig() {
|
||||||
|
try {
|
||||||
|
Config.getInstance().save();
|
||||||
|
} catch (IOException e) {
|
||||||
|
LOG.error("Couldn't save config", e);
|
||||||
}
|
}
|
||||||
return tabNames.toArray(new String[0]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void selected() {
|
public void selected() {
|
||||||
if (!initialized) {
|
if (!initialized) {
|
||||||
|
initializeProperties();
|
||||||
createGui();
|
createGui();
|
||||||
initialized = true;
|
initialized = true;
|
||||||
}
|
}
|
||||||
|
ignoreList.refresh();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -0,0 +1,23 @@
|
||||||
|
package ctbrec.ui.settings.api;
|
||||||
|
|
||||||
|
import javafx.beans.property.SimpleBooleanProperty;
|
||||||
|
|
||||||
|
public class ExclusiveSelectionProperty extends SimpleBooleanProperty {
|
||||||
|
|
||||||
|
private String optionA;
|
||||||
|
private String optionB;
|
||||||
|
|
||||||
|
public ExclusiveSelectionProperty(Object bean, String name, boolean value, String optionA, String optionB) {
|
||||||
|
super(bean, name, value);
|
||||||
|
this.optionA = optionA;
|
||||||
|
this.optionB = optionB;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getOptionA() {
|
||||||
|
return optionA;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getOptionB() {
|
||||||
|
return optionB;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,19 @@
|
||||||
|
package ctbrec.ui.settings.api;
|
||||||
|
|
||||||
|
public class GigabytesConverter implements ValueConverter {
|
||||||
|
|
||||||
|
private static final int ONE_GIB_IN_BYTES = 1024 * 1024 * 1024;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object convertTo(Object a) {
|
||||||
|
long input = (long) a;
|
||||||
|
return input / ONE_GIB_IN_BYTES;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object convertFrom(Object b) {
|
||||||
|
long spaceLeftInGiB = (long) b;
|
||||||
|
return spaceLeftInGiB * ONE_GIB_IN_BYTES;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -15,12 +15,12 @@ class HighlightingSupport {
|
||||||
|
|
||||||
private HighlightingSupport() {}
|
private HighlightingSupport() {}
|
||||||
|
|
||||||
static void highlightMatchess(Category cat, String filter) {
|
static void highlightMatches(Category cat, String filter) {
|
||||||
Node node = cat.getGuiOrElse(Label::new);
|
Node node = cat.getGuiOrElse(Label::new);
|
||||||
highlightMatchess(node, filter);
|
highlightMatchess(node, filter);
|
||||||
if(cat.hasSubCategories()) {
|
if(cat.hasSubCategories()) {
|
||||||
for (Category sub : cat.getSubCategories()) {
|
for (Category sub : cat.getSubCategories()) {
|
||||||
highlightMatchess(sub, filter);
|
highlightMatches(sub, filter);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -59,7 +59,7 @@ class HighlightingSupport {
|
||||||
contains |= ofNullable(((Control) labeledNode).getTooltip()).map(Tooltip::getText).orElse("").toLowerCase().contains(filter);
|
contains |= ofNullable(((Control) labeledNode).getTooltip()).map(Tooltip::getText).orElse("").toLowerCase().contains(filter);
|
||||||
}
|
}
|
||||||
if (labeledNode instanceof TextInputControl) {
|
if (labeledNode instanceof TextInputControl) {
|
||||||
contains |= ((TextInputControl) labeledNode).getText().toLowerCase().contains(filter);
|
contains |= ofNullable(((TextInputControl) labeledNode).getText()).orElse("").toLowerCase().contains(filter);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return contains;
|
return contains;
|
||||||
|
|
|
@ -102,7 +102,7 @@ public class Preferences {
|
||||||
|
|
||||||
for (Category category : categories) {
|
for (Category category : categories) {
|
||||||
if (q.length() > 2) {
|
if (q.length() > 2) {
|
||||||
HighlightingSupport.highlightMatchess(category, q);
|
HighlightingSupport.highlightMatches(category, q);
|
||||||
} else {
|
} else {
|
||||||
HighlightingSupport.removeHighlights(category);
|
HighlightingSupport.removeHighlights(category);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,15 +0,0 @@
|
||||||
package ctbrec.ui.settings.api;
|
|
||||||
|
|
||||||
public class SelectionSetting extends Setting {
|
|
||||||
|
|
||||||
private Object[] options;
|
|
||||||
|
|
||||||
public SelectionSetting(String name, String key, Object[] options) {
|
|
||||||
super(name, key);
|
|
||||||
this.options = options;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Object[] getOptions() {
|
|
||||||
return options;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -11,35 +11,35 @@ import javafx.scene.control.Tooltip;
|
||||||
public class Setting {
|
public class Setting {
|
||||||
|
|
||||||
private String name;
|
private String name;
|
||||||
private String key;
|
|
||||||
private String tooltip;
|
private String tooltip;
|
||||||
private Property<?> property;
|
private Property<?> property;
|
||||||
private Node gui;
|
private Node gui;
|
||||||
private PreferencesStorage preferencesStorage;
|
private PreferencesStorage preferencesStorage;
|
||||||
private boolean needsRestart = false;
|
private boolean needsRestart = false;
|
||||||
|
private ValueConverter converter;
|
||||||
|
|
||||||
protected Setting(String name, String key) {
|
protected Setting(String name, Property<?> property) {
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.key = key;
|
this.property = property;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Setting of(String name, String key) {
|
protected Setting(String name, Node gui) {
|
||||||
return new Setting(name, key);
|
this.name = name;
|
||||||
|
this.gui = gui;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Setting of(String name, String key, String tooltip) {
|
public static Setting of(String name, Property<?> property) {
|
||||||
Setting setting = new Setting(name, key);
|
return new Setting(name, property);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Setting of(String name, Property<?> property, String tooltip) {
|
||||||
|
Setting setting = new Setting(name, property);
|
||||||
setting.tooltip = tooltip;
|
setting.tooltip = tooltip;
|
||||||
return setting;
|
return setting;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Setting of(String name, String key, Object...options) {
|
public static Setting of(String name, Node gui) {
|
||||||
return new SelectionSetting(name, key, options);
|
Setting setting = new Setting(name, gui);
|
||||||
}
|
|
||||||
|
|
||||||
public static Setting of(String name, String key, Node gui) {
|
|
||||||
Setting setting = new Setting(name, key);
|
|
||||||
setting.gui = gui;
|
|
||||||
return setting;
|
return setting;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -48,7 +48,15 @@ public class Setting {
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getKey() {
|
public String getKey() {
|
||||||
return key;
|
if (getProperty() == null) {
|
||||||
|
return "";
|
||||||
|
} else {
|
||||||
|
String key = getProperty().getName();
|
||||||
|
if (StringUtil.isBlank(key)) {
|
||||||
|
throw new IllegalStateException("Name for property of setting [" + name + "] is null");
|
||||||
|
}
|
||||||
|
return key;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getTooltip() {
|
public String getTooltip() {
|
||||||
|
@ -65,7 +73,7 @@ public class Setting {
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("rawtypes")
|
@SuppressWarnings("rawtypes")
|
||||||
Property getProperty() {
|
public Property getProperty() {
|
||||||
return property;
|
return property;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -87,7 +95,16 @@ public class Setting {
|
||||||
public boolean contains(String filter) {
|
public boolean contains(String filter) {
|
||||||
boolean contains = name.toLowerCase().contains(filter)
|
boolean contains = name.toLowerCase().contains(filter)
|
||||||
|| ofNullable(tooltip).orElse("").toLowerCase().contains(filter)
|
|| ofNullable(tooltip).orElse("").toLowerCase().contains(filter)
|
||||||
|| property != null && property.getValue().toString().toLowerCase().contains(filter);
|
|| ofNullable(property).map(Property::getValue).map(Object::toString).orElse("").toLowerCase().contains(filter);
|
||||||
return contains;
|
return contains;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Setting converter(ValueConverter converter) {
|
||||||
|
this.converter = converter;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ValueConverter getConverter() {
|
||||||
|
return converter;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,11 @@
|
||||||
|
package ctbrec.ui.settings.api;
|
||||||
|
|
||||||
|
import javafx.beans.property.SimpleStringProperty;
|
||||||
|
|
||||||
|
public class SimpleDirectoryProperty extends SimpleStringProperty {
|
||||||
|
|
||||||
|
public SimpleDirectoryProperty(Object bean, String name, String initialValue) {
|
||||||
|
super(bean, name, initialValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
package ctbrec.ui.settings.api;
|
||||||
|
|
||||||
|
import javafx.beans.property.SimpleStringProperty;
|
||||||
|
|
||||||
|
public class SimpleFileProperty extends SimpleStringProperty {
|
||||||
|
|
||||||
|
public SimpleFileProperty(Object bean, String name, String initialValue) {
|
||||||
|
super(bean, name, initialValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,42 @@
|
||||||
|
package ctbrec.ui.settings.api;
|
||||||
|
|
||||||
|
import ctbrec.ui.controls.range.Range;
|
||||||
|
import javafx.beans.property.ObjectProperty;
|
||||||
|
import javafx.beans.property.SimpleObjectProperty;
|
||||||
|
|
||||||
|
public class SimpleRangeProperty<T> extends SimpleObjectProperty<T> {
|
||||||
|
|
||||||
|
private Range<T> range;
|
||||||
|
private SimpleObjectProperty<T> lowProperty;
|
||||||
|
private SimpleObjectProperty<T> highProperty;
|
||||||
|
private String lowKey;
|
||||||
|
private String highKey;
|
||||||
|
|
||||||
|
public SimpleRangeProperty(Range<T> range, String lowKey, String highKey, T low, T high) {
|
||||||
|
this.range = range;
|
||||||
|
this.lowKey = lowKey;
|
||||||
|
this.highKey = highKey;
|
||||||
|
lowProperty = new SimpleObjectProperty<>(low);
|
||||||
|
highProperty = new SimpleObjectProperty<>(high);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Range<T> getRange() {
|
||||||
|
return range;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ObjectProperty<T> lowProperty() {
|
||||||
|
return lowProperty;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ObjectProperty<T> highProperty() {
|
||||||
|
return highProperty;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getLowKey() {
|
||||||
|
return lowKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getHighKey() {
|
||||||
|
return highKey;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
package ctbrec.ui.settings.api;
|
||||||
|
|
||||||
|
public interface ValueConverter {
|
||||||
|
|
||||||
|
Object convertTo(Object a);
|
||||||
|
Object convertFrom(Object b);
|
||||||
|
}
|
Loading…
Reference in New Issue