Add minimize to tray

This commit is contained in:
0xb00bface 2021-04-18 12:17:02 +02:00
parent ce98919499
commit 6e9b92effa
5 changed files with 246 additions and 111 deletions

View File

@ -1,6 +1,5 @@
package ctbrec.ui; package ctbrec.ui;
import static ctbrec.event.Event.Type.*; import static ctbrec.event.Event.Type.*;
import java.io.BufferedReader; import java.io.BufferedReader;
@ -15,6 +14,7 @@ import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException; import java.security.NoSuchAlgorithmException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.Objects; import java.util.Objects;
import java.util.concurrent.Executors; import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledExecutorService;
@ -110,7 +110,6 @@ public class CamrecApplication extends Application {
private int activeRecordings = 0; private int activeRecordings = 0;
private double bytesPerSecond = 0; private double bytesPerSecond = 0;
@Override @Override
public void start(Stage primaryStage) throws Exception { public void start(Stage primaryStage) throws Exception {
this.primaryStage = primaryStage; this.primaryStage = primaryStage;
@ -134,6 +133,26 @@ public class CamrecApplication extends Application {
createGui(primaryStage); createGui(primaryStage);
checkForUpdates(); checkForUpdates();
registerClipboardListener(); registerClipboardListener();
registerTrayIconListener();
}
private void registerTrayIconListener() {
EventBusHolder.BUS.register(new Object() {
@Subscribe
public void trayActionRequest(Map<String, Object> evt) {
if (Objects.equals("shutdown", evt.get("event"))) {
LOG.debug("Shutdown request from tray icon");
try {
Platform.runLater(() -> {
primaryStage.show();
shutdown();
});
} catch (Exception ex) {
LOG.error(ex.getMessage(), ex);
}
}
}
});
} }
private void initSites() { private void initSites() {
@ -152,7 +171,7 @@ public class CamrecApplication extends Application {
} }
private void registerClipboardListener() { private void registerClipboardListener() {
if(config.getSettings().monitorClipboard) { if (config.getSettings().monitorClipboard) {
ClipboardListener clipboardListener = new ClipboardListener(recorder, sites); ClipboardListener clipboardListener = new ClipboardListener(recorder, sites);
scheduler.scheduleAtFixedRate(clipboardListener, 0, 1, TimeUnit.SECONDS); scheduler.scheduleAtFixedRate(clipboardListener, 0, 1, TimeUnit.SECONDS);
} }
@ -160,11 +179,11 @@ public class CamrecApplication extends Application {
private void startOnlineMonitor() { private void startOnlineMonitor() {
for (Site site : sites) { for (Site site : sites) {
if(site.isEnabled()) { if (site.isEnabled()) {
try { try {
site.setRecorder(recorder); site.setRecorder(recorder);
site.init(); site.init();
} catch(Exception e) { } catch (Exception e) {
LOG.error("Error while initializing site {}", site.getName(), e); LOG.error("Error while initializing site {}", site.getName(), e);
} }
} }
@ -181,6 +200,8 @@ public class CamrecApplication extends Application {
private void createGui(Stage primaryStage) throws IOException { private void createGui(Stage primaryStage) throws IOException {
LOG.debug("Creating GUI"); LOG.debug("Creating GUI");
DesktopIntegration.setRecorder(recorder);
DesktopIntegration.setPrimaryStage(primaryStage);
CamrecApplication.title = "CTB Recorder " + getVersion(); CamrecApplication.title = "CTB Recorder " + getVersion();
primaryStage.setTitle(title); primaryStage.setTitle(title);
InputStream icon = getClass().getResourceAsStream("/icon.png"); InputStream icon = getClass().getResourceAsStream("/icon.png");
@ -188,7 +209,6 @@ public class CamrecApplication extends Application {
int windowWidth = Config.getInstance().getSettings().windowWidth; int windowWidth = Config.getInstance().getSettings().windowWidth;
int windowHeight = Config.getInstance().getSettings().windowHeight; int windowHeight = Config.getInstance().getSettings().windowHeight;
Scene scene = new Scene(rootPane, windowWidth, windowHeight); Scene scene = new Scene(rootPane, windowWidth, windowHeight);
primaryStage.setScene(scene); primaryStage.setScene(scene);
Dialogs.setScene(scene); Dialogs.setScene(scene);
@ -217,7 +237,7 @@ public class CamrecApplication extends Application {
switchToStartTab(); switchToStartTab();
writeColorSchemeStyleSheet(); writeColorSchemeStyleSheet();
Color base = Color.web(Config.getInstance().getSettings().colorBase); Color base = Color.web(Config.getInstance().getSettings().colorBase);
if(!base.equals(Color.WHITE)) { if (!base.equals(Color.WHITE)) {
loadStyleSheet(primaryStage, "color.css"); loadStyleSheet(primaryStage, "color.css");
} }
loadStyleSheet(primaryStage, "style.css"); loadStyleSheet(primaryStage, "style.css");
@ -230,8 +250,7 @@ public class CamrecApplication extends Application {
primaryStage.getScene().heightProperty() primaryStage.getScene().heightProperty()
.addListener((observable, oldVal, newVal) -> Config.getInstance().getSettings().windowHeight = newVal.intValue()); .addListener((observable, oldVal, newVal) -> Config.getInstance().getSettings().windowHeight = newVal.intValue());
primaryStage.setMaximized(Config.getInstance().getSettings().windowMaximized); primaryStage.setMaximized(Config.getInstance().getSettings().windowMaximized);
primaryStage.maximizedProperty() primaryStage.maximizedProperty().addListener((observable, oldVal, newVal) -> Config.getInstance().getSettings().windowMaximized = newVal);
.addListener((observable, oldVal, newVal) -> Config.getInstance().getSettings().windowMaximized = newVal);
Player.scene = primaryStage.getScene(); Player.scene = primaryStage.getScene();
primaryStage.setX(Config.getInstance().getSettings().windowX); primaryStage.setX(Config.getInstance().getSettings().windowX);
primaryStage.setY(Config.getInstance().getSettings().windowY); primaryStage.setY(Config.getInstance().getSettings().windowY);
@ -239,6 +258,8 @@ public class CamrecApplication extends Application {
primaryStage.yProperty().addListener((observable, oldVal, newVal) -> Config.getInstance().getSettings().windowY = newVal.intValue()); primaryStage.yProperty().addListener((observable, oldVal, newVal) -> Config.getInstance().getSettings().windowY = newVal.intValue());
primaryStage.show(); primaryStage.show();
primaryStage.setOnCloseRequest(createShutdownHandler()); primaryStage.setOnCloseRequest(createShutdownHandler());
Runtime.getRuntime().addShutdownHook(new Thread(() -> Platform.runLater(this::shutdown)));
setWindowMinimizeListener(primaryStage);
// register changelistener to activate / deactivate tabs, when the user switches between them // register changelistener to activate / deactivate tabs, when the user switches between them
tabPane.getSelectionModel().selectedItemProperty().addListener((ov, from, to) -> { tabPane.getSelectionModel().selectedItemProperty().addListener((ov, from, to) -> {
@ -254,84 +275,95 @@ public class CamrecApplication extends Application {
HBox.setMargin(statusLabel, new Insets(10, 10, 10, 10)); HBox.setMargin(statusLabel, new Insets(10, 10, 10, 10));
} }
private void setWindowMinimizeListener(Stage primaryStage) {
primaryStage.iconifiedProperty().addListener((obs, oldV, newV) -> {
if (newV.booleanValue() && Config.getInstance().getSettings().minimizeToTray && primaryStage.isShowing()) {
DesktopIntegration.minimizeToTray(primaryStage);
}
});
}
private javafx.event.EventHandler<WindowEvent> createShutdownHandler() { private javafx.event.EventHandler<WindowEvent> createShutdownHandler() {
return e -> { return e -> {
e.consume(); e.consume();
shutdown();
// check for active downloads
if (recordingsTab.isDownloadRunning()) {
boolean exitAnyway = Dialogs.showConfirmDialog("Shutdown", "Do you want to exit anyway?", "There are downloads running",
primaryStage.getScene());
if (!exitAnyway) {
return;
}
}
// check for active recordings
boolean shutdownNow = false;
if (config.getSettings().localRecording) {
try {
if (!recorder.getCurrentlyRecording().isEmpty()) {
ButtonType result = Dialogs.showShutdownDialog(primaryStage.getScene());
if (result == ButtonType.NO) {
return;
} else if (result == ButtonType.YES) {
shutdownNow = true;
}
}
} catch (InvalidKeyException | NoSuchAlgorithmException | IOException ex) {
LOG.warn("Couldn't check, if recordings are running");
}
}
Alert shutdownInfo = new AutosizeAlert(Alert.AlertType.INFORMATION, primaryStage.getScene());
shutdownInfo.setTitle("Shutdown");
shutdownInfo.setContentText("Shutting down. Please wait while recordings are finished...");
shutdownInfo.show();
final boolean immediately = shutdownNow;
new Thread(() -> {
for (Tab tab : tabPane.getTabs()) {
if (tab instanceof ShutdownListener) {
((ShutdownListener) tab).onShutdown();
}
}
onlineMonitor.shutdown();
recorder.shutdown(immediately);
for (Site site : sites) {
if (site.isEnabled()) {
site.shutdown();
}
}
try {
Config.getInstance().save();
LOG.info("Shutdown complete. Goodbye!");
Platform.runLater(() -> {
primaryStage.close();
shutdownInfo.close();
Platform.exit();
// This is needed, because OkHttp?! seems to block the shutdown with its writer threads. They are not daemon threads :(
System.exit(0);
});
} catch (IOException e1) {
Platform.runLater(() -> {
Alert alert = new AutosizeAlert(Alert.AlertType.ERROR, primaryStage.getScene());
alert.setTitle("Error saving settings");
alert.setContentText("Couldn't save settings: " + e1.getLocalizedMessage());
alert.showAndWait();
System.exit(1);
});
}
try {
ExternalBrowser.getInstance().close();
} catch (IOException e12) {
// noop
}
scheduler.shutdownNow();
}).start();
}; };
} }
private void shutdown() {
// check for active downloads
if (recordingsTab.isDownloadRunning()) {
boolean exitAnyway = Dialogs.showConfirmDialog("Shutdown", "Do you want to exit anyway?", "There are downloads running", primaryStage.getScene());
if (!exitAnyway) {
return;
}
}
// check for active recordings
boolean shutdownNow = false;
if (config.getSettings().localRecording) {
try {
if (!recorder.getCurrentlyRecording().isEmpty()) {
ButtonType result = Dialogs.showShutdownDialog(primaryStage.getScene());
if (result == ButtonType.NO) {
return;
} else if (result == ButtonType.YES) {
shutdownNow = true;
}
}
} catch (InvalidKeyException | NoSuchAlgorithmException | IOException ex) {
LOG.warn("Couldn't check, if recordings are running");
}
}
Alert shutdownInfo = new AutosizeAlert(Alert.AlertType.INFORMATION, primaryStage.getScene());
shutdownInfo.setTitle("Shutdown");
shutdownInfo.setContentText("Shutting down. Please wait while recordings are finished...");
shutdownInfo.show();
final boolean immediately = shutdownNow;
new Thread(() -> {
for (Tab tab : tabPane.getTabs()) {
if (tab instanceof ShutdownListener) {
((ShutdownListener) tab).onShutdown();
}
}
onlineMonitor.shutdown();
recorder.shutdown(immediately);
for (Site site : sites) {
if (site.isEnabled()) {
site.shutdown();
}
}
try {
Config.getInstance().save();
LOG.info("Shutdown complete. Goodbye!");
Platform.runLater(() -> {
primaryStage.close();
shutdownInfo.close();
Platform.exit();
// This is needed, because OkHttp?! seems to block the shutdown with its writer threads. They are not daemon threads :(
System.exit(0);
});
} catch (IOException e1) {
Platform.runLater(() -> {
Alert alert = new AutosizeAlert(Alert.AlertType.ERROR, primaryStage.getScene());
alert.setTitle("Error saving settings");
alert.setContentText("Couldn't save settings: " + e1.getLocalizedMessage());
alert.showAndWait();
System.exit(1);
});
}
try {
ExternalBrowser.getInstance().close();
} catch (IOException e12) {
// noop
}
scheduler.shutdownNow();
}).start();
}
private void registerAlertSystem() { private void registerAlertSystem() {
for (EventHandlerConfiguration eventHandlerConfig : Config.getInstance().getSettings().eventHandlers) { for (EventHandlerConfiguration eventHandlerConfig : Config.getInstance().getSettings().eventHandlers) {
EventHandler handler = new EventHandler(eventHandlerConfig); EventHandler handler = new EventHandler(eventHandlerConfig);
@ -377,7 +409,7 @@ public class CamrecApplication extends Application {
private void registerBandwidthMeterListener() { private void registerBandwidthMeterListener() {
BandwidthMeter.addListener((bytes, dur) -> { BandwidthMeter.addListener((bytes, dur) -> {
long millis = dur.toMillis(); long millis = dur.toMillis();
double bytesPerMilli = bytes / (double)millis; double bytesPerMilli = bytes / (double) millis;
bytesPerSecond = bytesPerMilli * 1000; bytesPerSecond = bytesPerMilli * 1000;
updateStatus(); updateStatus();
}); });
@ -394,39 +426,35 @@ public class CamrecApplication extends Application {
private void writeColorSchemeStyleSheet() { private void writeColorSchemeStyleSheet() {
File colorCss = new File(Config.getInstance().getConfigDir(), "color.css"); File colorCss = new File(Config.getInstance().getConfigDir(), "color.css");
try(FileOutputStream fos = new FileOutputStream(colorCss)) { try (FileOutputStream fos = new FileOutputStream(colorCss)) {
String content = ".root {\n" + String content = ".root {\n" + " -fx-base: " + Config.getInstance().getSettings().colorBase + ";\n" + " -fx-accent: "
" -fx-base: "+Config.getInstance().getSettings().colorBase+";\n" + + Config.getInstance().getSettings().colorAccent + ";\n" + " -fx-default-button: -fx-accent;\n" + " -fx-focus-color: -fx-accent;\n"
" -fx-accent: "+Config.getInstance().getSettings().colorAccent+";\n" + + " -fx-control-inner-background-alt: derive(-fx-base, 95%);\n" + "}";
" -fx-default-button: -fx-accent;\n" +
" -fx-focus-color: -fx-accent;\n" +
" -fx-control-inner-background-alt: derive(-fx-base, 95%);\n" +
"}";
fos.write(content.getBytes(StandardCharsets.UTF_8)); fos.write(content.getBytes(StandardCharsets.UTF_8));
} catch(Exception e) { } catch (Exception e) {
LOG.error("Couldn't write stylesheet for user defined color theme"); LOG.error("Couldn't write stylesheet for user defined color theme");
} }
} }
private void loadStyleSheet(Stage primaryStage, String filename) { public static void loadStyleSheet(Stage primaryStage, String filename) {
File css = new File(Config.getInstance().getConfigDir(), filename); File css = new File(Config.getInstance().getConfigDir(), filename);
if(css.exists() && css.isFile()) { if (css.exists() && css.isFile()) {
primaryStage.getScene().getStylesheets().add(css.toURI().toString()); primaryStage.getScene().getStylesheets().add(css.toURI().toString());
} }
} }
private void switchToStartTab() { private void switchToStartTab() {
String startTab = Config.getInstance().getSettings().startTab; String startTab = Config.getInstance().getSettings().startTab;
if(StringUtil.isNotBlank(startTab)) { if (StringUtil.isNotBlank(startTab)) {
for (Tab tab : tabPane.getTabs()) { for (Tab tab : tabPane.getTabs()) {
if(Objects.equals(startTab, tab.getText())) { if (Objects.equals(startTab, tab.getText())) {
tabPane.getSelectionModel().select(tab); tabPane.getSelectionModel().select(tab);
break; break;
} }
} }
} }
if(tabPane.getSelectionModel().getSelectedItem() instanceof TabSelectionListener) { if (tabPane.getSelectionModel().getSelectedItem() instanceof TabSelectionListener) {
((TabSelectionListener)tabPane.getSelectionModel().getSelectedItem()).selected(); ((TabSelectionListener) tabPane.getSelectionModel().getSelectedItem()).selected();
} }
} }

View File

@ -3,24 +3,38 @@ package ctbrec.ui;
import java.awt.AWTException; import java.awt.AWTException;
import java.awt.Desktop; import java.awt.Desktop;
import java.awt.Image; import java.awt.Image;
import java.awt.MenuItem;
import java.awt.PopupMenu;
import java.awt.SystemTray; import java.awt.SystemTray;
import java.awt.Toolkit; import java.awt.Toolkit;
import java.awt.TrayIcon; import java.awt.TrayIcon;
import java.awt.TrayIcon.MessageType; import java.awt.TrayIcon.MessageType;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.net.URI; import java.net.URI;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.Map;
import javax.swing.SwingUtilities;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import ctbrec.OS; import ctbrec.OS;
import ctbrec.event.EventBusHolder;
import ctbrec.io.StreamRedirector; import ctbrec.io.StreamRedirector;
import ctbrec.recorder.Recorder;
import ctbrec.ui.controls.Dialogs;
import javafx.application.Platform;
import javafx.geometry.Insets; import javafx.geometry.Insets;
import javafx.scene.control.Alert; import javafx.scene.control.Alert;
import javafx.scene.control.Label; import javafx.scene.control.Label;
import javafx.scene.control.TextField; import javafx.scene.control.TextField;
import javafx.scene.layout.BorderPane; import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;
public class DesktopIntegration { public class DesktopIntegration {
@ -30,6 +44,8 @@ public class DesktopIntegration {
private static SystemTray tray; private static SystemTray tray;
private static TrayIcon trayIcon; private static TrayIcon trayIcon;
private static Recorder recorder;
private static Stage primaryStage;
public static void open(String uri) { public static void open(String uri) {
try { try {
@ -152,23 +168,109 @@ public class DesktopIntegration {
private static synchronized void notifySystemTray(String title, String header, String msg) { private static synchronized void notifySystemTray(String title, String header, String msg) {
if (SystemTray.isSupported()) { if (SystemTray.isSupported()) {
if (tray == null) { createTrayIcon(primaryStage);
LOG.debug("Creating tray icon");
tray = SystemTray.getSystemTray();
Image image = Toolkit.getDefaultToolkit().createImage(DesktopIntegration.class.getResource("/icon64.png"));
trayIcon = new TrayIcon(image, title);
trayIcon.setImageAutoSize(true);
trayIcon.setToolTip(title);
try {
tray.add(trayIcon);
} catch (AWTException e) {
LOG.error("Coulnd't add tray icon", e);
}
}
LOG.debug("Display tray message");
trayIcon.displayMessage(header, msg, MessageType.INFO); trayIcon.displayMessage(header, msg, MessageType.INFO);
} else { } else {
LOG.error("SystemTray notifications not supported by this OS"); LOG.error("SystemTray notifications not supported by this OS");
} }
} }
public static void minimizeToTray(Stage primaryStage) {
Platform.setImplicitExit(false);
boolean supported = createTrayIcon(primaryStage);
if (supported) {
primaryStage.hide();
}
}
private static boolean createTrayIcon(Stage stage) {
if (SystemTray.isSupported()) {
if (tray == null) {
String title = CamrecApplication.title;
tray = SystemTray.getSystemTray();
Image image = Toolkit.getDefaultToolkit().createImage(DesktopIntegration.class.getResource("/icon64.png"));
PopupMenu menu = createTrayContextMenu(stage);
trayIcon = new TrayIcon(image, title, menu);
trayIcon.setImageAutoSize(true);
trayIcon.setToolTip(title);
try {
tray.add(trayIcon);
} catch (AWTException e) {
LOG.error("Couldn't add tray icon", e);
}
trayIcon.addMouseListener(new MouseAdapter() {
@Override
public void mousePressed(MouseEvent e) {
if (SwingUtilities.isLeftMouseButton(e)) {
toggleVisibility(stage);
}
}
});
}
return true;
} else {
LOG.error("SystemTray notifications not supported by this OS");
return false;
}
}
private static PopupMenu createTrayContextMenu(Stage stage) {
PopupMenu menu = new PopupMenu();
MenuItem show = new MenuItem("Show");
show.addActionListener(evt -> restoreStage(stage));
menu.add(show);
menu.addSeparator();
MenuItem pauseRecording = new MenuItem("Pause recording");
pauseRecording.addActionListener(evt -> {
try {
recorder.pause();
} catch (InvalidKeyException | NoSuchAlgorithmException | IOException e) {
Dialogs.showError(stage.getScene(), "Pausing recording", "Pausing of the recorder failed", e);
}
});
menu.add(pauseRecording);
MenuItem resumeRecording = new MenuItem("Resume recording");
resumeRecording.addActionListener(evt -> {
try {
recorder.resume();
} catch (InvalidKeyException | NoSuchAlgorithmException | IOException e) {
Dialogs.showError(stage.getScene(), "Resuming recording", "Resuming of the recorder failed", e);
}
});
menu.add(resumeRecording);
menu.addSeparator();
MenuItem exit = new MenuItem("Exit");
exit.addActionListener(evt -> exit(stage));
menu.add(exit);
return menu;
}
private static void toggleVisibility(Stage stage) {
if (stage.isShowing()) {
Platform.runLater(stage::hide);
} else {
restoreStage(stage);
}
}
private static void restoreStage(Stage stage) {
Platform.runLater(() -> {
stage.setIconified(false);
stage.show();
stage.toFront();
});
}
private static void exit(Stage stage) {
EventBusHolder.BUS.post(Map.of("event", "shutdown"));
}
public static void setRecorder(Recorder recorder) {
DesktopIntegration.recorder = recorder;
}
public static void setPrimaryStage(Stage primaryStage) {
DesktopIntegration.primaryStage = primaryStage;
}
} }

View File

@ -17,6 +17,7 @@ public class Launcher {
LOG.error("Your Java version ({}) is too old. Please update to Java 10 or newer", javaVersion); LOG.error("Your Java version ({}) is too old. Please update to Java 10 or newer", javaVersion);
System.exit(1); System.exit(1);
} }
System.setProperty("awt.useSystemAAFontSettings","lcd");
CamrecApplication.main(args); CamrecApplication.main(args);
} }
} }

View File

@ -138,6 +138,7 @@ public class SettingsTab extends Tab implements TabSelectionListener {
private IgnoreList ignoreList; private IgnoreList ignoreList;
private Label restartNotification; private Label restartNotification;
private SimpleIntegerProperty playlistRequestTimeout; private SimpleIntegerProperty playlistRequestTimeout;
private SimpleBooleanProperty minimizeToTray;
public SettingsTab(List<Site> sites, Recorder recorder) { public SettingsTab(List<Site> sites, Recorder recorder) {
this.sites = sites; this.sites = sites;
@ -198,6 +199,7 @@ public class SettingsTab extends Tab implements TabSelectionListener {
hlsdlExecutable = new SimpleFileProperty(null, "hlsdlExecutable", settings.hlsdlExecutable); hlsdlExecutable = new SimpleFileProperty(null, "hlsdlExecutable", settings.hlsdlExecutable);
recentlyWatched = new SimpleBooleanProperty(null, "recentlyWatched", settings.recentlyWatched); recentlyWatched = new SimpleBooleanProperty(null, "recentlyWatched", settings.recentlyWatched);
playlistRequestTimeout = new SimpleIntegerProperty(null, "playlistRequestTimeout", settings.playlistRequestTimeout); playlistRequestTimeout = new SimpleIntegerProperty(null, "playlistRequestTimeout", settings.playlistRequestTimeout);
minimizeToTray = new SimpleBooleanProperty(null, "minimizeToTray", settings.minimizeToTray);
} }
private void createGui() { private void createGui() {
@ -223,6 +225,7 @@ public class SettingsTab extends Tab implements TabSelectionListener {
Setting.of("Enable live previews (experimental)", livePreviews), Setting.of("Enable live previews (experimental)", livePreviews),
Setting.of("Enable recently watched tab", recentlyWatched).needsRestart(), Setting.of("Enable recently watched tab", recentlyWatched).needsRestart(),
Setting.of("Add models from clipboard", monitorClipboard, "Monitor clipboard for model URLs and automatically add them to the recorder").needsRestart(), Setting.of("Add models from clipboard", monitorClipboard, "Monitor clipboard for model URLs and automatically add them to the recorder").needsRestart(),
Setting.of("Minimize to tray", minimizeToTray, "Removes the app from the task bar, if minimized"),
Setting.of("Fast scroll speed", fastScrollSpeed, "Makes the thumbnail overviews scroll faster with the mouse wheel").needsRestart(), Setting.of("Fast scroll speed", fastScrollSpeed, "Makes the thumbnail overviews scroll faster with the mouse wheel").needsRestart(),
Setting.of("Show confirmation dialogs", confirmationDialogs, "Show confirmation dialogs for irreversible actions"), Setting.of("Show confirmation dialogs", confirmationDialogs, "Show confirmation dialogs for irreversible actions"),
Setting.of("Total model count in title", totalModelCountInTitle, "Show the total number of models in the title bar"), Setting.of("Total model count in title", totalModelCountInTitle, "Show the total number of models in the title bar"),

View File

@ -103,6 +103,7 @@ public class Settings {
public String mfcModelsTableSortType = ""; public String mfcModelsTableSortType = "";
public String mfcPassword = ""; public String mfcPassword = "";
public String mfcUsername = ""; public String mfcUsername = "";
public boolean minimizeToTray = true;
public int minimumLengthInSeconds = 0; public int minimumLengthInSeconds = 0;
public long minimumSpaceLeftInBytes = 0; public long minimumSpaceLeftInBytes = 0;
public Map<String, String> modelNotes = new HashMap<>(); public Map<String, String> modelNotes = new HashMap<>();