Add minimize to tray
This commit is contained in:
parent
ce98919499
commit
6e9b92effa
|
@ -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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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"),
|
||||||
|
|
|
@ -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<>();
|
||||||
|
|
Loading…
Reference in New Issue