forked from j62/ctbrec
1
0
Fork 0

Add all settings to the new settings panel

This commit is contained in:
0xboobface 2020-07-05 14:02:15 +02:00
parent e7349b2118
commit 83775c805a
21 changed files with 806 additions and 240 deletions

View File

@ -54,6 +54,7 @@ import ctbrec.sites.stripchat.Stripchat;
import ctbrec.ui.controls.Dialogs;
import ctbrec.ui.news.NewsTab;
import ctbrec.ui.settings.SettingsTab;
import ctbrec.ui.settings.SettingsTab2;
import ctbrec.ui.tabs.DonateTabFx;
import ctbrec.ui.tabs.HelpTab;
import ctbrec.ui.tabs.RecordedModelsTab;
@ -189,7 +190,7 @@ public class CamrecApplication extends Application {
recordingsTab = new RecordingsTab("Recordings", recorder, config, sites);
tabPane.getTabs().add(recordingsTab);
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(new NewsTab());
tabPane.getTabs().add(new DonateTabFx());

View File

@ -46,9 +46,10 @@ public abstract class AbstractFileSelectionBox extends HBox {
}
});
Node browse = createBrowseButton();
getChildren().addAll(fileInput, browse);
fileInput.disableProperty().bind(disableProperty());
browse.disableProperty().bind(disableProperty());
fileInput.disableProperty().bind(disableProperty());
fileInput.textProperty().bindBidirectional(fileProperty);
getChildren().addAll(fileInput, browse);
HBox.setHgrow(fileInput, Priority.ALWAYS);
disabledProperty().addListener((obs, oldV, newV) -> {
@ -70,10 +71,12 @@ public abstract class AbstractFileSelectionBox extends HBox {
private ChangeListener<? super String> textListener() {
return (obs, o, n) -> {
String input = fileInput.getText();
if (StringUtil.isBlank(input) && allowEmptyValue) {
fileProperty.set("");
hideValidationHints();
return;
if (StringUtil.isBlank(input)) {
if (allowEmptyValue) {
fileProperty.set("");
hideValidationHints();
return;
}
} else {
File program = new File(input);
setFile(program);
@ -122,6 +125,8 @@ public abstract class AbstractFileSelectionBox extends HBox {
private Button createBrowseButton() {
Button button = new Button("Select");
button.setOnAction(e -> choose());
button.prefHeightProperty().bind(this.heightProperty());
button.prefWidthProperty().set(70);
return button;
}

View File

@ -1,7 +1,7 @@
package ctbrec.ui.controls.range;
import javafx.beans.property.DoubleProperty;
import javafx.beans.property.SimpleDoubleProperty;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.geometry.Orientation;
import javafx.scene.control.Control;
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 Range<T> range;
private DoubleProperty low;
private DoubleProperty high;
private ObjectProperty<T> low;
private ObjectProperty<T> high;
private boolean showTickMarks = false;
private boolean showTickLabels = false;
private Orientation orientation = Orientation.HORIZONTAL;
@SuppressWarnings({ "unchecked", "rawtypes" })
public RangeSlider(Range<T> range) {
this.range = range;
low = new SimpleDoubleProperty(getMinimum().doubleValue());
high = new SimpleDoubleProperty(getMaximum().doubleValue());
low = new SimpleObjectProperty(getMinimum());
high = new SimpleObjectProperty(getMaximum());
getStyleClass().setAll(DEFAULT_STYLE_CLASS);
}
@ -34,20 +35,20 @@ public class RangeSlider<T extends Number> extends Control {
return RangeSlider.class.getResource("rangeslider.css").toExternalForm();
}
public DoubleProperty getLow() {
public ObjectProperty<T> getLow() {
return low;
}
public void setLow(T newPosition) {
low.set(newPosition.doubleValue());
low.set(newPosition);
}
public DoubleProperty getHigh() {
public ObjectProperty<T> getHigh() {
return high;
}
public void setHigh(T newPosition) {
this.high.set(newPosition.doubleValue());
this.high.set(newPosition);
}
public T getMinimum() {

View File

@ -70,11 +70,11 @@ public class RangeSliderBehavior<T extends Number> extends BehaviorBase<RangeSli
}
private T getLow() {
return rangeSlider.getRange().getTicks().get(rangeSlider.getLow().intValue());
return rangeSlider.getRange().getTicks().get(rangeSlider.getLow().get().intValue());
}
private T getHigh() {
return rangeSlider.getRange().getTicks().get(rangeSlider.getHigh().intValue());
return rangeSlider.getRange().getTicks().get(rangeSlider.getHigh().get().intValue());
}
/**

View File

@ -42,7 +42,7 @@ public class RangeSliderSkin extends SkinBase<RangeSlider<?>> {
t.low.setOnMousePressed(me -> {
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 -> {
Point2D cur = t.low.localToParent(me.getX(), me.getY());
@ -52,7 +52,7 @@ public class RangeSliderSkin extends SkinBase<RangeSlider<?>> {
t.high.setOnMousePressed(me -> {
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 -> {
boolean orientation = getSkinnable().getOrientation() == Orientation.HORIZONTAL;
@ -160,8 +160,8 @@ public class RangeSliderSkin extends SkinBase<RangeSlider<?>> {
private void positionThumbs() {
RangeSlider<?> s = getSkinnable();
double lxl = trackStart + (trackLength * ((s.getLow().doubleValue() - s.getMinimum().doubleValue()) / (getMaxMinusMinNoZero())) - thumbWidth / 2D);
double lxh = trackStart + (trackLength * ((s.getHigh().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().get().doubleValue() - s.getMinimum().doubleValue()) / (getMaxMinusMinNoZero())) - thumbWidth / 2D);
double ly = lowThumbPos;
if (thumbRange != null) {

View File

@ -50,9 +50,7 @@ import javafx.scene.control.ScrollPane;
import javafx.scene.control.SelectionMode;
import javafx.scene.control.Separator;
import javafx.scene.control.TextField;
import javafx.scene.control.TitledPane;
import javafx.scene.image.Image;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Pane;
@ -61,8 +59,8 @@ import javafx.stage.Modality;
import javafx.stage.Stage;
import javafx.stage.Window;
public class ActionSettingsPanel extends TitledPane {
private static final transient Logger LOG = LoggerFactory.getLogger(ActionSettingsPanel.class);
public class ActionSettingsPanel extends Pane {
private static final Logger LOG = LoggerFactory.getLogger(ActionSettingsPanel.class);
private ListView<EventHandlerConfiguration> actionTable;
private TextField name = new TextField();
@ -80,11 +78,8 @@ public class ActionSettingsPanel extends TitledPane {
private Recorder recorder;
public ActionSettingsPanel(SettingsTab settingsTab, Recorder recorder) {
public ActionSettingsPanel(Recorder recorder) {
this.recorder = recorder;
setText("Events & Actions");
setExpanded(true);
setCollapsible(false);
createGui();
loadEventHandlers();
}
@ -94,15 +89,22 @@ public class ActionSettingsPanel extends TitledPane {
}
private void createGui() {
BorderPane mainLayout = new BorderPane();
setContent(mainLayout);
GridPane grid = new GridPane();
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();
ScrollPane scrollPane = new ScrollPane(actionTable);
scrollPane.setFitToHeight(true);
scrollPane.setFitToWidth(true);
scrollPane.setStyle("-fx-background-color: -fx-background");
mainLayout.setCenter(scrollPane);
grid.add(scrollPane, 0, 1);
Button add = new Button("Add");
add.setOnAction(this::add);
@ -110,15 +112,10 @@ public class ActionSettingsPanel extends TitledPane {
delete.setOnAction(this::delete);
delete.setDisable(true);
HBox buttons = new HBox(5, add, delete);
mainLayout.setBottom(buttons);
BorderPane.setMargin(buttons, new Insets(5, 0, 0, 0));
buttons.setStyle("-fx-background-color: -fx-background"); // workaround so that the buttons don't shrink
grid.add(buttons, 0, 2);
actionTable.getSelectionModel().getSelectedItems().addListener(new ListChangeListener<EventHandlerConfiguration>() {
@Override
public void onChanged(Change<? extends EventHandlerConfiguration> change) {
delete.setDisable(change.getList().isEmpty());
}
});
actionTable.getSelectionModel().getSelectedItems().addListener((ListChangeListener<EventHandlerConfiguration>) change -> delete.setDisable(change.getList().isEmpty()));
}
private void add(ActionEvent evt) {
@ -235,9 +232,7 @@ public class ActionSettingsPanel extends TitledPane {
event.getItems().clear();
event.getItems().add(Event.Type.MODEL_STATUS_CHANGED);
event.getItems().add(Event.Type.RECORDING_STATUS_CHANGED);
event.setOnAction(evt -> {
modelState.setVisible(event.getSelectionModel().getSelectedItem() == Event.Type.MODEL_STATUS_CHANGED);
});
event.setOnAction(evt -> modelState.setVisible(event.getSelectionModel().getSelectedItem() == Event.Type.MODEL_STATUS_CHANGED));
event.getSelectionModel().select(Event.Type.MODEL_STATUS_CHANGED);
layout.add(event, 1, row++);
@ -254,7 +249,7 @@ public class ActionSettingsPanel extends TitledPane {
Label l = new Label("Models");
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++);
GridPane.setValignment(l, VPos.TOP);
GridPane.setHgrow(modelSelectionPane, Priority.ALWAYS);

View File

@ -2,7 +2,8 @@ package ctbrec.ui.settings;
import java.io.IOException;
import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -10,16 +11,33 @@ import org.slf4j.LoggerFactory;
import ctbrec.Config;
import ctbrec.Settings;
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.PreferencesStorage;
import ctbrec.ui.settings.api.SelectionSetting;
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.control.CheckBox;
import javafx.scene.control.ComboBox;
import javafx.scene.control.Label;
import javafx.scene.control.RadioButton;
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 {
@ -37,7 +55,7 @@ public class CtbrecPreferencesStorage implements PreferencesStorage {
@Override
public void save(Preferences preferences) throws IOException {
config.save();
throw new RuntimeException("not implemented");
}
@Override
@ -47,69 +65,206 @@ public class CtbrecPreferencesStorage implements PreferencesStorage {
@Override
public Node createGui(Setting setting) throws Exception {
String key = setting.getKey();
Field field = Settings.class.getField(key);
Class<?> t = field.getType();
Object value = field.get(settings);
if(setting instanceof SelectionSetting) {
return createComboBox(key, ((SelectionSetting) setting).getOptions());
} else if (t == String.class) {
return createStringProperty(key, (String) value);
} else if (t == int.class || t == Integer.class) {
return createIntegerProperty(key, (Integer) value);
} else if (t == boolean.class || t == Boolean.class) {
return createBooleanProperty(key, (Boolean) value);
Property<?> prop = setting.getProperty();
if (prop instanceof ExclusiveSelectionProperty) {
return createRadioGroup(setting);
} else if (prop instanceof SimpleRangeProperty) {
return createRangeSlider(setting);
} else if (prop instanceof SimpleDirectoryProperty) {
return createDirectorySelector(setting);
} else if (prop instanceof SimpleFileProperty) {
return createFileSelector(setting);
} else if (prop instanceof IntegerProperty) {
return createIntegerProperty(setting);
} 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 {
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) {
TextField ctrl = new TextField(value);
ctrl.textProperty().addListener((obs, oldV, newV) -> saveValue(() -> {
Field field = Settings.class.getField(fieldName);
private Node createRadioGroup(Setting setting) {
ExclusiveSelectionProperty prop = (ExclusiveSelectionProperty) setting.getProperty();
ToggleGroup toggleGroup = new ToggleGroup();
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);
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;
}
private Node createIntegerProperty(String fieldName, Integer value) {
TextField ctrl = new TextField(value.toString());
@SuppressWarnings("unchecked")
private Node createIntegerProperty(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()) {
Field field = Settings.class.getField(fieldName);
Field field = Settings.class.getField(setting.getKey());
field.set(settings, Integer.parseInt(ctrl.getText()));
config.save();
}
}));
Property<Number> prop = setting.getProperty();
ctrl.textProperty().bindBidirectional(prop, new NumberStringConverter());
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();
ctrl.setSelected(value);
ctrl.selectedProperty().addListener((obs, oldV, newV) -> saveValue(() -> {
Field field = Settings.class.getField(fieldName);
Field field = Settings.class.getField(setting.getKey());
field.set(settings, newV);
config.save();
}));
BooleanProperty prop = (BooleanProperty) setting.getProperty();
ctrl.selectedProperty().bindBidirectional(prop);
return ctrl;
}
private Node createComboBox(String key, Object[] options) throws NoSuchFieldException, IllegalAccessException {
@SuppressWarnings({ "rawtypes", "unchecked" })
ComboBox<Object> comboBox = new ComboBox(FXCollections.observableList(Arrays.asList(options)));
Field field = Settings.class.getField(key);
@SuppressWarnings({ "rawtypes", "unchecked" })
private Node createComboBox(Setting setting) throws NoSuchFieldException, IllegalAccessException {
ListProperty<?> listProp = (ListProperty<?>) setting.getProperty();
ComboBox<Object> comboBox = new ComboBox(listProp);
Field field = Settings.class.getField(setting.getKey());
Object value = field.get(settings);
if(StringUtil.isNotBlank(value.toString())) {
comboBox.getSelectionModel().select(value);
if (StringUtil.isNotBlank(value.toString())) {
if (setting.getConverter() != null) {
comboBox.getSelectionModel().select(setting.getConverter().convertTo(value));
} else {
comboBox.getSelectionModel().select(value);
}
}
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();
}));
return comboBox;

View File

@ -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);
}
}
}
}

View File

@ -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());
}
}

View File

@ -38,6 +38,7 @@ 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.ValueConverter;
import ctbrec.ui.sites.ConfigUI;
import ctbrec.ui.tabs.TabSelectionListener;
import javafx.collections.FXCollections;
@ -159,7 +160,7 @@ public class SettingsTab extends Tab implements TabSelectionListener {
//right side
rightSide.getChildren().add(siteConfigAccordion);
ActionSettingsPanel actions = new ActionSettingsPanel(this, recorder);
ActionSettingsPanel actions = new ActionSettingsPanel(recorder);
rightSide.getChildren().add(actions);
proxySettingsPane = new ProxySettingsPane(this);
rightSide.getChildren().add(createIgnoreListPanel());
@ -561,7 +562,7 @@ public class SettingsTab extends Tab implements TabSelectionListener {
private Node createIgnoreListPanel() {
GridPane layout = createGridLayout();
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);
Button exportIgnoreList = new Button("Export");
exportIgnoreList.setOnAction(e -> exportIgnoreList());
@ -850,7 +851,7 @@ public class SettingsTab extends Tab implements TabSelectionListener {
public void saveConfig() {
if(proxySettingsPane != null) {
proxySettingsPane.saveConfig();
//proxySettingsPane.saveConfig();
}
try {
Config.getInstance().save();
@ -877,5 +878,39 @@ public class SettingsTab extends Tab implements TabSelectionListener {
public String toString() {
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);
}
};
}
}
}

View File

@ -1,91 +1,284 @@
package ctbrec.ui.settings;
import static ctbrec.Settings.DirectoryStructure.*;
import static ctbrec.Settings.ProxyType.*;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import ctbrec.Config;
import ctbrec.Hmac;
import ctbrec.Settings;
import ctbrec.Settings.DirectoryStructure;
import ctbrec.Settings.ProxyType;
import ctbrec.recorder.Recorder;
import ctbrec.sites.Site;
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.ExclusiveSelectionProperty;
import ctbrec.ui.settings.api.GigabytesConverter;
import ctbrec.ui.settings.api.Group;
import ctbrec.ui.settings.api.Preferences;
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 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.TextInputDialog;
public class SettingsTab2 extends Tab implements TabSelectionListener {
private static final Logger LOG = LoggerFactory.getLogger(SettingsTab2.class);
private List<Site> sites;
private Recorder recorder;
private Preferences prefs;
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) {
this.sites = sites;
this.recorder = recorder;
setText("Settings");
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() {
ignoreList = new IgnoreList(sites);
List<Category> siteCategories = new ArrayList<>();
for (Site site : sites) {
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",
Group.of("General",
Setting.of("User-Agent", "httpUserAgent"),
Setting.of("User-Agent mobile", "httpUserAgentMobile"),
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("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("Start Tab", "startTab", getTabNames()),
Setting.of("Colors (Base / Accent)", null, new ColorSettingsPane(Config.getInstance()))
Setting.of("User-Agent", httpUserAgent),
Setting.of("User-Agent mobile", httpUserAgentMobile),
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("Display stream resolution in overview", determineResolution),
Setting.of("Enable live previews (experimental)", livePreviews),
Setting.of("Start Tab", startTab),
Setting.of("Colors (Base / Accent)", new ColorSettingsPane(Config.getInstance()))
),
Group.of("Player",
Setting.of("Player", "mediaPlayer"),
Setting.of("Start parameters", "mediaPlayerParams"),
Setting.of("Maximum resolution (0 = unlimited)", "maximumResolutionPlayer", "video height, e.g. 720 or 1080"),
Setting.of("Show \"Player Starting\" Message", "showPlayerStarting"),
Setting.of("Start only one player at a time", "singlePlayer")
Setting.of("Player", mediaPlayer),
Setting.of("Start parameters", mediaPlayerParams),
Setting.of("Maximum resolution (0 = unlimited)", maximumResolutionPlayer, "video height, e.g. 720 or 1080"),
Setting.of("Show \"Player Starting\" Message", showPlayerStarting),
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("Proxy",
Group.of("Proxy",
Setting.of("Type", "proxyType", DIRECT, HTTP, SOCKS4, SOCKS5),
Setting.of("Host", "proxyHost"),
Setting.of("Port", "proxyPort"),
Setting.of("Username", "proxyUser"),
Setting.of("Password", "proxyPassword")
Setting.of("Type", proxyType),
Setting.of("Host", proxyHost),
Setting.of("Port", proxyPort),
Setting.of("Username", proxyUser),
Setting.of("Password", proxyPassword)
)
)
);
setContent(prefs.getView());
}
private Object[] getTabNames() {
List<String> tabNames = new ArrayList<>();
for (Tab tab : getTabPane().getTabs()) {
tabNames.add(tab.getText());
private List<String> getTabNames() {
return getTabPane().getTabs().stream()
.map(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
public void selected() {
if (!initialized) {
initializeProperties();
createGui();
initialized = true;
}
ignoreList.refresh();
}
@Override

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -15,12 +15,12 @@ class HighlightingSupport {
private HighlightingSupport() {}
static void highlightMatchess(Category cat, String filter) {
static void highlightMatches(Category cat, String filter) {
Node node = cat.getGuiOrElse(Label::new);
highlightMatchess(node, filter);
if(cat.hasSubCategories()) {
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);
}
if (labeledNode instanceof TextInputControl) {
contains |= ((TextInputControl) labeledNode).getText().toLowerCase().contains(filter);
contains |= ofNullable(((TextInputControl) labeledNode).getText()).orElse("").toLowerCase().contains(filter);
}
}
return contains;

View File

@ -102,7 +102,7 @@ public class Preferences {
for (Category category : categories) {
if (q.length() > 2) {
HighlightingSupport.highlightMatchess(category, q);
HighlightingSupport.highlightMatches(category, q);
} else {
HighlightingSupport.removeHighlights(category);
}

View File

@ -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;
}
}

View File

@ -11,35 +11,35 @@ import javafx.scene.control.Tooltip;
public class Setting {
private String name;
private String key;
private String tooltip;
private Property<?> property;
private Node gui;
private PreferencesStorage preferencesStorage;
private boolean needsRestart = false;
private ValueConverter converter;
protected Setting(String name, String key) {
protected Setting(String name, Property<?> property) {
this.name = name;
this.key = key;
this.property = property;
}
public static Setting of(String name, String key) {
return new Setting(name, key);
protected Setting(String name, Node gui) {
this.name = name;
this.gui = gui;
}
public static Setting of(String name, String key, String tooltip) {
Setting setting = new Setting(name, key);
public static Setting of(String name, Property<?> property) {
return new Setting(name, property);
}
public static Setting of(String name, Property<?> property, String tooltip) {
Setting setting = new Setting(name, property);
setting.tooltip = tooltip;
return setting;
}
public static Setting of(String name, String key, Object...options) {
return new SelectionSetting(name, key, options);
}
public static Setting of(String name, String key, Node gui) {
Setting setting = new Setting(name, key);
setting.gui = gui;
public static Setting of(String name, Node gui) {
Setting setting = new Setting(name, gui);
return setting;
}
@ -48,7 +48,15 @@ public class Setting {
}
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() {
@ -65,7 +73,7 @@ public class Setting {
}
@SuppressWarnings("rawtypes")
Property getProperty() {
public Property getProperty() {
return property;
}
@ -87,7 +95,16 @@ public class Setting {
public boolean contains(String filter) {
boolean contains = name.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;
}
public Setting converter(ValueConverter converter) {
this.converter = converter;
return this;
}
public ValueConverter getConverter() {
return converter;
}
}

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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;
}
}

View File

@ -0,0 +1,7 @@
package ctbrec.ui.settings.api;
public interface ValueConverter {
Object convertTo(Object a);
Object convertFrom(Object b);
}