diff --git a/CHANGELOG.md b/CHANGELOG.md
index 6d5899ef..ffea0876 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,9 @@
+3.8.1
+========================
+* Fixed recent MFC error
+* Added log file rotation
+* Fixed a bug with the resolution slider
+
3.8.0
========================
* Server Settings are now editable in the web-interface
diff --git a/client/pom.xml b/client/pom.xml
index 96c8a191..76f36cf6 100644
--- a/client/pom.xml
+++ b/client/pom.xml
@@ -8,7 +8,7 @@
ctbrec
master
- 3.8.0
+ 3.8.1
../master
diff --git a/client/src/main/java/ctbrec/ui/CamrecApplication.java b/client/src/main/java/ctbrec/ui/CamrecApplication.java
index 4143697c..9eb41adf 100644
--- a/client/src/main/java/ctbrec/ui/CamrecApplication.java
+++ b/client/src/main/java/ctbrec/ui/CamrecApplication.java
@@ -89,7 +89,6 @@ public class CamrecApplication extends Application {
private Recorder recorder;
private OnlineMonitor onlineMonitor;
static HostServices hostServices;
- private SettingsTab settingsTab;
private BorderPane rootPane = new BorderPane();
private HBox statusBar = new HBox();
private Label statusLabel = new Label();
@@ -190,8 +189,7 @@ public class CamrecApplication extends Application {
tabPane.getTabs().add(modelsTab);
recordingsTab = new RecordingsTab("Recordings", recorder, config, sites);
tabPane.getTabs().add(recordingsTab);
- settingsTab = new SettingsTab(sites, recorder);
- tabPane.getTabs().add(settingsTab);
+ tabPane.getTabs().add(new SettingsTab(sites, recorder));
tabPane.getTabs().add(new NewsTab());
tabPane.getTabs().add(new DonateTabFx());
tabPane.getTabs().add(new HelpTab());
@@ -207,6 +205,7 @@ public class CamrecApplication extends Application {
primaryStage.getScene().getStylesheets().add("/ctbrec/ui/ThumbCell.css");
primaryStage.getScene().getStylesheets().add("/ctbrec/ui/controls/SearchBox.css");
primaryStage.getScene().getStylesheets().add("/ctbrec/ui/controls/Popover.css");
+ primaryStage.getScene().getStylesheets().add("/ctbrec/ui/settings/api/Preferences.css");
primaryStage.getScene().widthProperty().addListener((observable, oldVal, newVal) -> Config.getInstance().getSettings().windowWidth = newVal.intValue());
primaryStage.getScene().heightProperty()
.addListener((observable, oldVal, newVal) -> Config.getInstance().getSettings().windowHeight = newVal.intValue());
@@ -258,7 +257,6 @@ public class CamrecApplication extends Application {
public void run() {
modelsTab.saveState();
recordingsTab.saveState();
- settingsTab.saveConfig();
onlineMonitor.shutdown();
recorder.shutdown();
for (Site site : sites) {
diff --git a/client/src/main/java/ctbrec/ui/Launcher.java b/client/src/main/java/ctbrec/ui/Launcher.java
index d42d769b..0cd88767 100644
--- a/client/src/main/java/ctbrec/ui/Launcher.java
+++ b/client/src/main/java/ctbrec/ui/Launcher.java
@@ -7,7 +7,7 @@ import ctbrec.Java;
public class Launcher {
- private static final transient Logger LOG = LoggerFactory.getLogger(Launcher.class);
+ private static final Logger LOG = LoggerFactory.getLogger(Launcher.class);
public static void main(String[] args) {
int javaVersion = Java.version();
diff --git a/client/src/main/java/ctbrec/ui/controls/AbstractFileSelectionBox.java b/client/src/main/java/ctbrec/ui/controls/AbstractFileSelectionBox.java
index 5fc0ce2c..0f3820b8 100644
--- a/client/src/main/java/ctbrec/ui/controls/AbstractFileSelectionBox.java
+++ b/client/src/main/java/ctbrec/ui/controls/AbstractFileSelectionBox.java
@@ -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;
}
diff --git a/client/src/main/java/ctbrec/ui/controls/AutoFillTextField.java b/client/src/main/java/ctbrec/ui/controls/autocomplete/AutoFillTextField.java
similarity index 66%
rename from client/src/main/java/ctbrec/ui/controls/AutoFillTextField.java
rename to client/src/main/java/ctbrec/ui/controls/autocomplete/AutoFillTextField.java
index ca772778..6b12b354 100644
--- a/client/src/main/java/ctbrec/ui/controls/AutoFillTextField.java
+++ b/client/src/main/java/ctbrec/ui/controls/autocomplete/AutoFillTextField.java
@@ -1,7 +1,8 @@
-package ctbrec.ui.controls;
+package ctbrec.ui.controls.autocomplete;
-import javafx.collections.ObservableList;
+import java.util.Optional;
+
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.control.TextField;
@@ -10,12 +11,12 @@ import javafx.scene.input.KeyEvent;
public class AutoFillTextField extends TextField {
- private ObservableList suggestions;
private EventHandler handler;
+ private Suggester suggester;
- public AutoFillTextField(ObservableList suggestions) {
- this.suggestions = suggestions;
- addEventHandler(KeyEvent.KEY_RELEASED, (evt) -> {
+ public AutoFillTextField(Suggester suggester) {
+ this.suggester = suggester;
+ addEventHandler(KeyEvent.KEY_RELEASED, evt -> {
if (evt.getCode().isLetterKey() || evt.getCode().isDigitKey()) {
autocomplete(false);
} else if (evt.getCode() == KeyCode.ENTER) {
@@ -38,16 +39,19 @@ public class AutoFillTextField extends TextField {
if(oldtext.isEmpty()) {
return;
}
- for (String sug : suggestions) {
- boolean startsWith = sug.toLowerCase().startsWith(oldtext.toLowerCase());
- boolean textMatch = fulltextSearch && sug.toLowerCase().contains(oldtext.toLowerCase());
- if(startsWith || textMatch) {
- setText(sug);
- int pos = oldtext.length();
- positionCaret(pos);
- selectRange(pos, sug.length());
- break;
- }
+
+ Optional match = null;
+ if(fulltextSearch) {
+ match = suggester.fulltext(oldtext);
+ } else {
+ match = suggester.startsWith(oldtext);
+ }
+
+ if(match.isPresent()) {
+ setText(match.get());
+ int pos = oldtext.length();
+ positionCaret(pos);
+ selectRange(pos, match.get().length());
}
}
diff --git a/client/src/main/java/ctbrec/ui/controls/autocomplete/ObservableListSuggester.java b/client/src/main/java/ctbrec/ui/controls/autocomplete/ObservableListSuggester.java
new file mode 100644
index 00000000..a4970b32
--- /dev/null
+++ b/client/src/main/java/ctbrec/ui/controls/autocomplete/ObservableListSuggester.java
@@ -0,0 +1,37 @@
+package ctbrec.ui.controls.autocomplete;
+
+import java.util.Optional;
+
+import javafx.collections.ObservableList;
+
+public class ObservableListSuggester implements Suggester {
+
+ private ObservableList> suggestions;
+
+ public ObservableListSuggester(ObservableList> suggestions) {
+ this.suggestions = suggestions;
+ }
+
+ @Override
+ public Optional startsWith(String search) {
+ for (Object sug : suggestions) {
+ boolean startsWith = sug.toString().toLowerCase().startsWith(search.toLowerCase());
+ if (startsWith) {
+ return Optional.of(sug.toString());
+ }
+ }
+ return Optional.empty();
+ }
+
+ @Override
+ public Optional fulltext(String search) {
+ for (Object sug : suggestions) {
+ boolean startsWith = sug.toString().toLowerCase().contains(search.toLowerCase());
+ if (startsWith) {
+ return Optional.of(sug.toString());
+ }
+ }
+ return Optional.empty();
+ }
+
+}
diff --git a/client/src/main/java/ctbrec/ui/controls/autocomplete/Suggester.java b/client/src/main/java/ctbrec/ui/controls/autocomplete/Suggester.java
new file mode 100644
index 00000000..f3e1e8c2
--- /dev/null
+++ b/client/src/main/java/ctbrec/ui/controls/autocomplete/Suggester.java
@@ -0,0 +1,9 @@
+package ctbrec.ui.controls.autocomplete;
+
+import java.util.Optional;
+
+public interface Suggester {
+
+ public Optional startsWith(String search);
+ public Optional fulltext(String search);
+}
\ No newline at end of file
diff --git a/client/src/main/java/ctbrec/ui/controls/autocomplete/Suggestion.java b/client/src/main/java/ctbrec/ui/controls/autocomplete/Suggestion.java
new file mode 100644
index 00000000..82378795
--- /dev/null
+++ b/client/src/main/java/ctbrec/ui/controls/autocomplete/Suggestion.java
@@ -0,0 +1,5 @@
+package ctbrec.ui.controls.autocomplete;
+
+public class Suggestion {
+
+}
diff --git a/client/src/main/java/ctbrec/ui/controls/range/RangeSlider.java b/client/src/main/java/ctbrec/ui/controls/range/RangeSlider.java
index 78d98b21..b1c27951 100644
--- a/client/src/main/java/ctbrec/ui/controls/range/RangeSlider.java
+++ b/client/src/main/java/ctbrec/ui/controls/range/RangeSlider.java
@@ -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 extends Control {
private static final String DEFAULT_STYLE_CLASS = "rangeslider";
private Range range;
- private DoubleProperty low;
- private DoubleProperty high;
+ private ObjectProperty low;
+ private ObjectProperty high;
private boolean showTickMarks = false;
private boolean showTickLabels = false;
private Orientation orientation = Orientation.HORIZONTAL;
+ @SuppressWarnings({ "unchecked", "rawtypes" })
public RangeSlider(Range 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 extends Control {
return RangeSlider.class.getResource("rangeslider.css").toExternalForm();
}
- public DoubleProperty getLow() {
+ public ObjectProperty getLow() {
return low;
}
public void setLow(T newPosition) {
- low.set(newPosition.doubleValue());
+ low.set(newPosition);
}
- public DoubleProperty getHigh() {
+ public ObjectProperty getHigh() {
return high;
}
public void setHigh(T newPosition) {
- this.high.set(newPosition.doubleValue());
+ this.high.set(newPosition);
}
public T getMinimum() {
diff --git a/client/src/main/java/ctbrec/ui/controls/range/RangeSliderBehavior.java b/client/src/main/java/ctbrec/ui/controls/range/RangeSliderBehavior.java
index f2fe2484..b2496f65 100644
--- a/client/src/main/java/ctbrec/ui/controls/range/RangeSliderBehavior.java
+++ b/client/src/main/java/ctbrec/ui/controls/range/RangeSliderBehavior.java
@@ -5,6 +5,9 @@ import java.util.List;
import com.sun.javafx.scene.control.behavior.BehaviorBase; // NOSONAR
import com.sun.javafx.scene.control.inputmap.InputMap; // NOSONAR
+import javafx.scene.Node;
+import javafx.scene.input.MouseEvent;
+
public class RangeSliderBehavior extends BehaviorBase> {
private RangeSlider rangeSlider;
@@ -12,6 +15,13 @@ public class RangeSliderBehavior extends BehaviorBase rangeSlider) {
super(rangeSlider);
this.rangeSlider = rangeSlider;
+ rangeSlider.addEventFilter(MouseEvent.MOUSE_CLICKED, this::sliderClicked);
+ }
+
+ private void sliderClicked(MouseEvent me) {
+ Node source = (Node) me.getSource();
+ double positionPercentage = me.getX() / source.getBoundsInParent().getWidth();
+ moveClosestThumbTo(positionPercentage);
}
@Override
@@ -25,7 +35,12 @@ public class RangeSliderBehavior extends BehaviorBase= high.doubleValue()) {
+ newPosition = getLow();
+ }
+ rangeSlider.setLow(newPosition);
}
/**
@@ -33,7 +48,33 @@ public class RangeSliderBehavior extends BehaviorBase extends BehaviorBase> {
this.behavior = behavior;
initTrack();
initThumbs(thumbRange);
- registerChangeListener(control.getLow(), (obsVal) -> getSkinnable().requestLayout());
- registerChangeListener(control.getHigh(), (obsVal) -> getSkinnable().requestLayout());
+ registerChangeListener(control.getLow(), obsVal -> getSkinnable().requestLayout());
+ registerChangeListener(control.getHigh(), obsVal -> getSkinnable().requestLayout());
}
private void initThumbs(ThumbRange t) {
@@ -42,10 +42,8 @@ public class RangeSliderSkin extends SkinBase> {
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());
double dragPos = (isHorizontal()) ? cur.getX() - preDragThumbPoint.getX() : -(cur.getY() - preDragThumbPoint.getY());
@@ -54,17 +52,14 @@ public class RangeSliderSkin extends SkinBase> {
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;
double trackLen = orientation ? track.getWidth() : track.getHeight();
Point2D cur = t.high.localToParent(me.getX(), me.getY());
double dragPos = getSkinnable().getOrientation() != Orientation.HORIZONTAL ? -(cur.getY() - preDragThumbPoint.getY()) : cur.getX() - preDragThumbPoint.getX();
behavior.highThumbDragged(preDragPos + dragPos / trackLen);
-
});
}
@@ -165,8 +160,8 @@ public class RangeSliderSkin extends SkinBase> {
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) {
@@ -208,7 +203,6 @@ public class RangeSliderSkin extends SkinBase> {
if (isHorizontal()) {
if (showTickMarks) {
double w = Math.max(140, tickLine.prefWidth(-1));
- System.err.println("computePrefWidth " + w);
return w;
} else {
return 140;
diff --git a/client/src/main/java/ctbrec/ui/settings/ActionSettingsPanel.java b/client/src/main/java/ctbrec/ui/settings/ActionSettingsPanel.java
index 80f88e62..6e945d46 100644
--- a/client/src/main/java/ctbrec/ui/settings/ActionSettingsPanel.java
+++ b/client/src/main/java/ctbrec/ui/settings/ActionSettingsPanel.java
@@ -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 GridPane {
+ private static final Logger LOG = LoggerFactory.getLogger(ActionSettingsPanel.class);
private ListView 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,21 @@ public class ActionSettingsPanel extends TitledPane {
}
private void createGui() {
- BorderPane mainLayout = new BorderPane();
- setContent(mainLayout);
+ setHgap(10);
+ setVgap(10);
+ setPadding(new Insets(20, 10, 10, 10));
+
+ Label headline = new Label("Events & Actions");
+ headline.getStyleClass().add("settings-group-label");
+ 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);
+ add(scrollPane, 0, 1);
+ GridPane.setHgrow(scrollPane, Priority.ALWAYS);
Button add = new Button("Add");
add.setOnAction(this::add);
@@ -110,15 +111,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
+ add(buttons, 0, 2);
- actionTable.getSelectionModel().getSelectedItems().addListener(new ListChangeListener() {
- @Override
- public void onChanged(Change extends EventHandlerConfiguration> change) {
- delete.setDisable(change.getList().isEmpty());
- }
- });
+ actionTable.getSelectionModel().getSelectedItems().addListener((ListChangeListener) change -> delete.setDisable(change.getList().isEmpty()));
}
private void add(ActionEvent evt) {
@@ -235,9 +231,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 +248,7 @@ public class ActionSettingsPanel extends TitledPane {
Label l = new Label("Models");
layout.add(l, 0, row);
- modelSelectionPane = new ListSelectionPane(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);
@@ -289,7 +283,6 @@ public class ActionSettingsPanel extends TitledPane {
private ListView createActionTable() {
ListView view = new ListView<>();
view.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE);
- view.setPrefSize(300, 200);
return view;
}
diff --git a/client/src/main/java/ctbrec/ui/settings/ColorSettingsPane.java b/client/src/main/java/ctbrec/ui/settings/ColorSettingsPane.java
index b91765d1..0725b779 100644
--- a/client/src/main/java/ctbrec/ui/settings/ColorSettingsPane.java
+++ b/client/src/main/java/ctbrec/ui/settings/ColorSettingsPane.java
@@ -1,6 +1,9 @@
package ctbrec.ui.settings;
+import java.io.IOException;
+
import ctbrec.Config;
+import ctbrec.ui.controls.Dialogs;
import javafx.scene.control.Button;
import javafx.scene.control.ColorPicker;
import javafx.scene.control.Tooltip;
@@ -14,41 +17,49 @@ public class ColorSettingsPane extends HBox {
ColorPicker accentColor = new ColorPicker();
Button reset = new Button("Reset");
Pane foobar = new Pane();
+ private Config config;
- public ColorSettingsPane(SettingsTab settingsTab) {
+ public ColorSettingsPane(Config config) {
super(5);
+ this.config = config;
getChildren().add(baseColor);
getChildren().add(accentColor);
getChildren().add(reset);
- baseColor.setValue(Color.web(Config.getInstance().getSettings().colorBase));
+ baseColor.setValue(Color.web(config.getSettings().colorBase));
baseColor.setTooltip(new Tooltip("Base Color"));
baseColor.setMaxSize(44, 25);
- accentColor.setValue(Color.web(Config.getInstance().getSettings().colorAccent));
+ accentColor.setValue(Color.web(config.getSettings().colorAccent));
accentColor.setTooltip(new Tooltip("Accent Color"));
accentColor.setMaxSize(44, 25);
+ reset.setMinSize(60, 25);
reset.setMaxSize(60, 25);
baseColor.setOnAction(evt -> {
- Config.getInstance().getSettings().colorBase = toWeb(baseColor.getValue());
- settingsTab.showRestartRequired();
- settingsTab.saveConfig();
+ config.getSettings().colorBase = toWeb(baseColor.getValue());
+ save();
});
accentColor.setOnAction(evt -> {
- Config.getInstance().getSettings().colorAccent = toWeb(accentColor.getValue());
- settingsTab.showRestartRequired();
- settingsTab.saveConfig();
+ config.getSettings().colorAccent = toWeb(accentColor.getValue());
+ save();
});
reset.setOnAction(evt -> {
baseColor.setValue(Color.WHITE);
- Config.getInstance().getSettings().colorBase = toWeb(Color.WHITE);
+ config.getSettings().colorBase = toWeb(Color.WHITE);
accentColor.setValue(Color.WHITE);
- Config.getInstance().getSettings().colorAccent = toWeb(Color.WHITE);
- settingsTab.showRestartRequired();
- settingsTab.saveConfig();
+ config.getSettings().colorAccent = toWeb(Color.WHITE);
+ save();
});
}
+ private void save() {
+ try {
+ config.save();
+ } catch (IOException e) {
+ Dialogs.showError(getScene(), "Save Settings", "Couldn't save color settings", e);
+ }
+ }
+
private String toWeb(Color value) {
StringBuilder sb = new StringBuilder("#");
sb.append(toHex((int) (value.getRed() * 255)));
diff --git a/client/src/main/java/ctbrec/ui/settings/CtbrecPreferencesStorage.java b/client/src/main/java/ctbrec/ui/settings/CtbrecPreferencesStorage.java
new file mode 100644
index 00000000..b3bb8180
--- /dev/null
+++ b/client/src/main/java/ctbrec/ui/settings/CtbrecPreferencesStorage.java
@@ -0,0 +1,285 @@
+package ctbrec.ui.settings;
+
+import java.io.IOException;
+import java.lang.reflect.Field;
+import java.util.List;
+import java.util.Objects;
+
+import org.slf4j.Logger;
+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.Setting;
+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 {
+
+ private static final Logger LOG = LoggerFactory.getLogger(CtbrecPreferencesStorage.class);
+ public static final String PATTERN_NOT_A_DIGIT = "[^\\d]";
+ public static final String COULDNT_SAVE_MSG = "Couldn't save config setting";
+
+ private Config config;
+ private Settings settings;
+
+ public CtbrecPreferencesStorage(Config config) {
+ this.config = config;
+ this.settings = config.getSettings();
+ }
+
+ @Override
+ public void save(Preferences preferences) throws IOException {
+ throw new RuntimeException("not implemented");
+ }
+
+ @Override
+ public void load(Preferences preferences) {
+ throw new RuntimeException("not implemented");
+ }
+
+ @Override
+ public Node createGui(Setting setting) throws Exception {
+ 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 " + setting.getKey() + ": " + setting.getProperty());
+ }
+ }
+
+ 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 rangeProperty = (SimpleRangeProperty) setting.getProperty();
+ DiscreteRange range = (DiscreteRange) rangeProperty.getRange();
+ List labels = (List) range.getLabels();
+ List values = range.getTicks();
+ RangeSlider 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 values, List 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;
+ }
+
+ @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(setting.getKey());
+ field.set(settings, Integer.parseInt(ctrl.getText()));
+ config.save();
+ }
+ }));
+ Property prop = setting.getProperty();
+ ctrl.textProperty().bindBidirectional(prop, new NumberStringConverter());
+ return ctrl;
+ }
+
+ @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 prop = setting.getProperty();
+ ctrl.textProperty().bindBidirectional(prop, new NumberStringConverter());
+ return ctrl;
+ }
+
+ private Node createBooleanProperty(Setting setting) {
+ CheckBox ctrl = new CheckBox();
+ ctrl.selectedProperty().addListener((obs, oldV, newV) -> saveValue(() -> {
+ Field field = Settings.class.getField(setting.getKey());
+ field.set(settings, newV);
+ config.save();
+ }));
+ BooleanProperty prop = (BooleanProperty) setting.getProperty();
+ ctrl.selectedProperty().bindBidirectional(prop);
+ return ctrl;
+ }
+
+ @SuppressWarnings({ "rawtypes", "unchecked" })
+ private Node createComboBox(Setting setting) throws NoSuchFieldException, IllegalAccessException {
+ ListProperty> listProp = (ListProperty>) setting.getProperty();
+ ComboBox