From 45755c342dee28e6f6133c4caa9b20af7f769301 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 3 Apr 2026 13:24:23 +0000 Subject: [PATCH 01/58] Phase 1: Migrate from Spring Boot to Quarkus - Replace Spring Boot parent POM with Quarkus BOM 3.19.1 - Add Quarkus extensions: arc, rest, rest-jackson, scheduler, cache, websockets-next, smallrye-health, vertx, quinoa 2.3.10 - Remove Spring Boot, JavaFX, Jetty WebSocket, OBS client dependencies - Rewrite application.properties with Quarkus configuration - Replace Main.java: remove @SpringBootApplication, implement QuarkusApplication with @QuarkusMain - Delete MainFX.java (JavaFX entry point no longer needed) - Add AppEvents.java: static CDI event accessor for non-CDI contexts - Add ShowMainEvent.java: record replacing HomePage.ShowMainEvent - Update FileChecker.java: replace MainFX/Spring event publishing with AppEvents.fire(new ShowMainEvent()) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Co-authored-by: nvdweem <830783+nvdweem@users.noreply.github.com> --- pom.xml | 374 ++++++------------ src/main/java/com/getpcpanel/Main.java | 24 +- src/main/java/com/getpcpanel/MainFX.java | 63 --- .../java/com/getpcpanel/util/AppEvents.java | 29 ++ .../java/com/getpcpanel/util/FileChecker.java | 8 +- .../com/getpcpanel/util/ShowMainEvent.java | 7 + src/main/resources/application.properties | 40 +- 7 files changed, 201 insertions(+), 344 deletions(-) delete mode 100644 src/main/java/com/getpcpanel/MainFX.java create mode 100644 src/main/java/com/getpcpanel/util/AppEvents.java create mode 100644 src/main/java/com/getpcpanel/util/ShowMainEvent.java diff --git a/pom.xml b/pom.xml index ba74235c..0555c5b1 100644 --- a/pom.xml +++ b/pom.xml @@ -3,13 +3,6 @@ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> 4.0.0 - - org.springframework.boot - spring-boot-starter-parent - 3.5.9 - - - com.getpcpanel pcpanel ${project.baseversion}${project.snapshot} @@ -24,52 +17,102 @@ ${env.GITHUB_REPOSITORY} - 25 - 9.4.49.v20220914 + 21 ${java.version} ${java.version} + ${java.version} com.getpcpanel.Main + quarkus-bom + io.quarkus.platform + 3.19.1 - PCPanel yy.ww.WWkkmm ${project.baseversion}.${github.build} - javafx.controls,javafx.fxml,javafx.swing,java.desktop,java.management,java.logging,jdk.crypto.cryptoki,java.net.http 9421bff0-3840-414c-8563-407fbcd1d04d PCPanel UTF-8 - 18.0.2 5.13.0 9.5 5.2.0 - - ALL-UNNAMED + + + + ${quarkus.platform.group-id} + ${quarkus.platform.artifact-id} + ${quarkus.platform.version} + pom + import + + + + + + + io.quarkus + quarkus-arc + + + io.quarkus + quarkus-rest + - org.springframework.boot - spring-boot-starter + io.quarkus + quarkus-rest-jackson - org.springframework.boot - spring-boot-starter-json + io.quarkus + quarkus-scheduler + + io.quarkus + quarkus-cache + + + io.quarkus + quarkus-websockets-next + + + io.quarkus + quarkus-smallrye-health + + + io.quarkus + quarkus-vertx + + + + + io.quarkiverse.quinoa + quarkus-quinoa + 2.3.10 + + + com.fasterxml.jackson.core jackson-databind + + org.projectlombok lombok provided + + com.google.code.findbugs jsr305 3.0.2 + + org.apache.commons commons-lang3 @@ -79,7 +122,13 @@ commons-collections4 4.4 + + commons-io + commons-io + 2.11.0 + + net.java.dev.jna jna @@ -90,36 +139,22 @@ jna-platform ${jna.version} - - org.openjfx - javafx-controls - ${javafx.version} - - - org.openjfx - javafx-swing - ${javafx.version} - - - org.openjfx - javafx-fxml - ${javafx.version} - - - commons-io - commons-io - 2.11.0 - + + org.hid4java hid4java 0.7.0 + + one.util streamex 0.8.1 + + org.ow2.asm asm @@ -135,53 +170,15 @@ asm-util ${asm.version} - - javax.activation - activation - 1.1.1 - + + com.github.kwhat jnativehook 2.2.2 - - io.obs-websocket.community - client - 2.0.0 - - - slf4j-simple - org.slf4j - - - - - org.eclipse.jetty.websocket - websocket-client - ${jetty.websocket.client.version} - - - org.eclipse.jetty - jetty-client - ${jetty.websocket.client.version} - - - org.eclipse.jetty - jetty-io - ${jetty.websocket.client.version} - - - org.eclipse.jetty - jetty-http - ${jetty.websocket.client.version} - - - org.eclipse.jetty - jetty-util - ${jetty.websocket.client.version} - + com.illposed.osc javaosc-core @@ -197,21 +194,29 @@ + + org.jetbrains annotations 24.0.1 + + org.apache.xmlgraphics batik-transcoder 1.16 + + com.hivemq hivemq-mqtt-client 1.3.3 + + io.reactivex.rxjava3 rxjava @@ -230,6 +235,12 @@ ${dbus-java.version} + + + io.quarkus + quarkus-junit5 + test + org.junit.jupiter junit-jupiter-engine @@ -251,11 +262,6 @@ - - ${project.basedir}/src/packaging - true - ${project.build.directory}/packaging - ${project.basedir}/src/main/resources true @@ -273,105 +279,59 @@ - org.springframework.boot - spring-boot-maven-plugin - - - org.apache.maven.plugins - maven-install-plugin - - true - - - - org.apache.maven.plugins - maven-dependency-plugin + ${quarkus.platform.group-id} + quarkus-maven-plugin + ${quarkus.platform.version} + true - copy-dependencies - package - copy-dependencies + build + generate-code + generate-code-tests - - runtime - org.openjfx - org.apache.maven.plugins - maven-jar-plugin + maven-compiler-plugin - ${project.build.directory}/dependency + + + org.projectlombok + lombok + + - org.openjfx - javafx-maven-plugin - 0.0.8 + org.apache.maven.plugins + maven-surefire-plugin - ${start-class} - - - - - true + + org.jboss.logmanager.LogManager + ${maven.home} + - io.github.wiverson - jtoolprovider-plugin - 1.0.34 + org.apache.maven.plugins + maven-failsafe-plugin - jlink - package - java-tool + integration-test + verify - - jlink - ${project.build.directory}/jvm-image/ - ${jvm.modules} - ${project.build.directory}/jvm-image - - --strip-native-commands - --no-header-files - --strip-debug - --no-man-pages - --compress=2 - - - - - jpackage - install - - java-tool - - - jpackage - true - true - true - ${project.build.directory}/installer-work - @${project.build.directory}/packaging/${os.detected.name}-jpackage.txt - - - - org.apache.maven.plugins - maven-compiler-plugin - - - org.projectlombok - lombok - - + + ${project.build.directory}/${project.build.finalName}-runner + org.jboss.logmanager.LogManager + ${maven.home} + @@ -390,26 +350,7 @@ - - - - org.codehaus.mojo - versions-maven-plugin - - - - dependency-updates-report - plugin-updates-report - property-updates-report - - - - - false - - - - + not-github-build @@ -424,77 +365,10 @@ - windows-active - - - windows - - - - - - com.coderplus.maven.plugins - copy-rename-maven-plugin - 1.0.1 - - - rename-file - install - - rename - - - ${project.build.directory}/${app.name}-${app.version}.msi - ${project.build.directory}/${app.name}-${project.version}.msi - - - - - - org.codehaus.mojo - exec-maven-plugin - 3.0.0 - - - install - add-launch-to-msi - - exec - - - - - cscript - ${project.build.directory}/msi-result.log - ${project.build.directory} - - ${project.build.directory}/packaging/add-launch-to-msi.js - - - - - + native + + true + - - - - spring-milestones - Spring Milestones - https://repo.spring.io/milestone - - false - - - - - - spring-milestones - Spring Milestones - https://repo.spring.io/milestone - - false - - - diff --git a/src/main/java/com/getpcpanel/Main.java b/src/main/java/com/getpcpanel/Main.java index f8799bf2..baa71469 100644 --- a/src/main/java/com/getpcpanel/Main.java +++ b/src/main/java/com/getpcpanel/Main.java @@ -2,30 +2,30 @@ import java.util.Set; -import org.springframework.boot.autoconfigure.SpringBootApplication; -import org.springframework.cache.annotation.EnableCaching; -import org.springframework.scheduling.annotation.EnableScheduling; - import com.getpcpanel.hid.HidDebug; import com.getpcpanel.util.FileChecker; -import javafx.application.Application; +import io.quarkus.runtime.Quarkus; +import io.quarkus.runtime.QuarkusApplication; +import io.quarkus.runtime.annotations.QuarkusMain; -@EnableCaching -@EnableScheduling -@SpringBootApplication -public class Main { - public static void main(String[] args) { +@QuarkusMain +public class Main implements QuarkusApplication { + public static void main(String... args) { var argSet = Set.of(args); if (!argSet.contains("skipfilecheck")) { FileChecker.createAndStart(); } - if (argSet.contains("hiddebug")) { new HidDebug().execute(); return; } + Quarkus.run(Main.class, args); + } - Application.launch(MainFX.class, args); + @Override + public int run(String... args) throws Exception { + Quarkus.waitForExit(); + return 0; } } diff --git a/src/main/java/com/getpcpanel/MainFX.java b/src/main/java/com/getpcpanel/MainFX.java deleted file mode 100644 index ff8be95e..00000000 --- a/src/main/java/com/getpcpanel/MainFX.java +++ /dev/null @@ -1,63 +0,0 @@ -package com.getpcpanel; - -import java.util.Map; -import java.util.Optional; -import java.util.concurrent.ConcurrentHashMap; - -import javax.annotation.Nullable; - -import org.springframework.boot.builder.SpringApplicationBuilder; -import org.springframework.context.ConfigurableApplicationContext; - -import com.getpcpanel.ui.HomePage; - -import javafx.application.Application; -import javafx.application.Platform; -import javafx.stage.Stage; -import lombok.Getter; -import lombok.extern.log4j.Log4j2; - -@Log4j2 -public class MainFX extends Application { - @Getter @SuppressWarnings("StaticNonFinalField") private static ConfigurableApplicationContext context; - private static final Map, CacheObject> beanCache = new ConcurrentHashMap<>(); - - @Override - @SuppressWarnings("AssignmentToStaticFieldFromInstanceMethod") - public void init() throws Exception { - context = new SpringApplicationBuilder(Main.class) - .headless(false) - .run(getParameters().getRaw().toArray(new String[0])); - } - - @Override - public void start(Stage primaryStage) throws Exception { - log.info("Starting v{}", context.getEnvironment().getProperty("application.version")); - context.getBean(HomePage.class).start(primaryStage, getParameters().getRaw().contains("quiet")); - } - - @Override - public void stop() throws Exception { - context.close(); - Platform.exit(); - } - - @SuppressWarnings("unchecked") - public static Optional getOptionalBean(Class clazz) { - return Optional.ofNullable((T) beanCache.computeIfAbsent(clazz, cls -> { - try { - return new CacheObject(context.getBean(clazz)); - } catch (Exception e) { - return CacheObject.NULL; - } - }).o); - } - - public static T getBean(Class clazz) { - return context.getBean(clazz); - } - - record CacheObject(@Nullable Object o) { - private static final CacheObject NULL = new CacheObject(null); - } -} diff --git a/src/main/java/com/getpcpanel/util/AppEvents.java b/src/main/java/com/getpcpanel/util/AppEvents.java new file mode 100644 index 00000000..80d5629f --- /dev/null +++ b/src/main/java/com/getpcpanel/util/AppEvents.java @@ -0,0 +1,29 @@ +package com.getpcpanel.util; + +import jakarta.annotation.PostConstruct; +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.enterprise.event.Event; +import jakarta.inject.Inject; + +/** + * Static accessor for CDI events from non-CDI contexts (e.g., plain Thread subclasses). + */ +@ApplicationScoped +public class AppEvents { + @SuppressWarnings("StaticNonFinalField") + private static AppEvents instance; + + @Inject + Event eventBus; + + @PostConstruct + void init() { + instance = this; + } + + public static void fire(Object event) { + if (instance != null) { + instance.eventBus.fire(event); + } + } +} diff --git a/src/main/java/com/getpcpanel/util/FileChecker.java b/src/main/java/com/getpcpanel/util/FileChecker.java index 61da79f8..5cb3ff6c 100644 --- a/src/main/java/com/getpcpanel/util/FileChecker.java +++ b/src/main/java/com/getpcpanel/util/FileChecker.java @@ -10,9 +10,6 @@ import java.nio.file.WatchService; import java.util.concurrent.atomic.AtomicBoolean; -import com.getpcpanel.MainFX; -import com.getpcpanel.ui.HomePage; - import lombok.extern.log4j.Log4j2; @Log4j2 @@ -110,10 +107,7 @@ public void run() { log.trace("Unable to delete {}", file); } log.debug("Showing window because another process was started"); - var eventPublisher = MainFX.getContext(); - if (eventPublisher != null) { - eventPublisher.publishEvent(new HomePage.ShowMainEvent()); - } + AppEvents.fire(new ShowMainEvent()); } } } catch (Exception e) { diff --git a/src/main/java/com/getpcpanel/util/ShowMainEvent.java b/src/main/java/com/getpcpanel/util/ShowMainEvent.java new file mode 100644 index 00000000..7db5801c --- /dev/null +++ b/src/main/java/com/getpcpanel/util/ShowMainEvent.java @@ -0,0 +1,7 @@ +package com.getpcpanel.util; + +/** + * Event fired when another instance of the application requests the main window to be shown. + */ +public record ShowMainEvent() { +} diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index aabed692..21dd9bf6 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -1,16 +1,32 @@ -application.version=@project.version@ -application.build=@github.build@ -application.github.user-and-repo=@github.repo@ -application.root=${user.home}/.pcpanel/ +# Version info (filtered at build time) +pcpanel.version=@project.version@ +pcpanel.build=@github.build@ +pcpanel.github.user-and-repo=@github.repo@ +pcpanel.root=${user.home}/.pcpanel/ -spring.main.web-application-type=none -spring.output.ansi.enabled=ALWAYS -logging.file.name=${application.root}/logs/logging.log -logging.logback.rollingpolicy.max-file-size=5MB -logging.logback.rollingpolicy.total-size-cap=30MB -logging.level.org.jnativehook=off -spring.main.banner-mode=off -spring.jackson.deserialization.fail-on-unknown-properties=false +# Quarkus HTTP server (localhost only for desktop app) +quarkus.http.port=7654 +quarkus.http.host=localhost +# Quinoa (Angular frontend) +quarkus.quinoa.package-manager-install=false +quarkus.quinoa.build-dir=dist/pcpanel + +# Jackson +quarkus.jackson.fail-on-unknown-properties=false + +# Logging +quarkus.log.file.enable=true +quarkus.log.file.path=${pcpanel.root}/logs/logging.log +quarkus.log.file.rotation.max-file-size=5M +quarkus.log.file.rotation.max-backup-index=6 +quarkus.log.category."org.jnativehook".level=OFF +quarkus.banner.enabled=false + +# Linux commands linux.commands.xdotool=xdotool linux.commands.kdotool=kdotool + +# OBS integration (disabled - javax vs jakarta conflict, re-enable when Jakarta-compatible client available) +pcpanel.obs.enabled=false +pcpanel.obs.rate=2500 From 48ae3f85c64387782886cfede799bb3d6b2fffce Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 3 Apr 2026 13:26:15 +0000 Subject: [PATCH 02/58] fix: make AppEvents.instance volatile for thread visibility Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Co-authored-by: nvdweem <830783+nvdweem@users.noreply.github.com> --- src/main/java/com/getpcpanel/util/AppEvents.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/getpcpanel/util/AppEvents.java b/src/main/java/com/getpcpanel/util/AppEvents.java index 80d5629f..cebb80cb 100644 --- a/src/main/java/com/getpcpanel/util/AppEvents.java +++ b/src/main/java/com/getpcpanel/util/AppEvents.java @@ -11,7 +11,7 @@ @ApplicationScoped public class AppEvents { @SuppressWarnings("StaticNonFinalField") - private static AppEvents instance; + private static volatile AppEvents instance; @Inject Event eventBus; From 66ebb1b92930624d4264d323b34c71e42fb40c4e Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 3 Apr 2026 13:59:22 +0000 Subject: [PATCH 03/58] Migrate from Spring to Quarkus CDI - Delete JavaFX UI files (ui/, wavelink/ui/, PCPanelMiniUI, PCPanelProUI, PCPanelRGBUI) - Replace Spring @Conditional annotations with CDI @Qualifier annotations - Create PlatformProducer for CDI-based OS-conditional bean selection - Create ITrayService interface; TrayServiceAwt/Wayland implement it - Replace @Service/@Component with @ApplicationScoped - Replace @Autowired with @Inject; fix @RequiredArgsConstructor for CDI - Replace ApplicationEventPublisher with CDI Event - Replace @EventListener with @Observes parameter pattern - Replace @Value with @ConfigProperty - Replace @Scheduled with Quarkus @Scheduled - Replace @Cacheable/@CacheEvict with Quarkus cache annotations - Rewrite VersionChecker to use java.net.http.HttpClient - Stub OBS integration (javax/jakarta conflict) - Create stub device implementations (PCPanelRGBDevice, Mini, Pro) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Co-authored-by: nvdweem <830783+nvdweem@users.noreply.github.com> --- .../java/com/getpcpanel/CachingConfig.java | 46 +- src/main/java/com/getpcpanel/Json.java | 41 +- .../com/getpcpanel/RestTemplateConfig.java | 13 - .../commands/AbstractNewXVolumeService.java | 20 +- .../commands/CommandDispatcher.java | 13 +- .../com/getpcpanel/commands/IconService.java | 20 +- .../com/getpcpanel/commands/KeyMacro.java | 4 +- .../commands/SetMuteOverrideService.java | 46 +- .../getpcpanel/commands/command/Command.java | 4 +- .../commands/command/CommandBrightness.java | 4 +- .../commands/command/CommandConverter.java | 4 +- .../commands/command/CommandEndProgram.java | 4 +- .../commands/command/CommandKeystroke.java | 4 +- .../commands/command/CommandRun.java | 4 +- .../commands/command/CommandShortcut.java | 4 +- .../CommandVolumeApplicationDeviceToggle.java | 4 +- .../CommandVolumeDefaultDeviceToggle.java | 4 +- ...mandVolumeDefaultDeviceToggleAdvanced.java | 4 +- .../java/com/getpcpanel/cpp/AudioDevice.java | 62 +- .../java/com/getpcpanel/cpp/AudioSession.java | 101 +++- .../java/com/getpcpanel/cpp/ISndCtrl.java | 20 + .../cpp/SetNewSessionVolumeService.java | 37 +- .../cpp/linux/LinuxProcessHelper.java | 15 +- .../pulseaudio/ConditionalOnPulseAudio.java | 41 -- .../pulseaudio/PulseAudioAudioDevice.java | 36 +- .../pulseaudio/PulseAudioAudioSession.java | 30 +- .../pulseaudio/PulseAudioEventListener.java | 24 +- .../cpp/linux/pulseaudio/PulseAudioImpl.java | 20 + .../linux/pulseaudio/PulseAudioWrapper.java | 18 +- .../linux/pulseaudio/SndCtrlPulseAudio.java | 34 +- .../pulseaudio/SndCtrlPulseAudioDebug.java | 18 +- .../cpp/windows/SndCtrlWindows.java | 26 +- .../cpp/windows/WindowsAudioDevice.java | 83 ++- .../cpp/windows/WindowsAudioSession.java | 31 +- .../java/com/getpcpanel/device/Device.java | 308 +--------- .../com/getpcpanel/device/DeviceFactory.java | 66 ++- .../getpcpanel/device/PCPanelMiniDevice.java | 25 + .../com/getpcpanel/device/PCPanelMiniUI.java | 274 --------- .../getpcpanel/device/PCPanelProDevice.java | 25 + .../com/getpcpanel/device/PCPanelProUI.java | 387 ------------ .../getpcpanel/device/PCPanelRGBDevice.java | 25 + .../com/getpcpanel/device/PCPanelRGBUI.java | 250 -------- .../hid/DeviceCommunicationHandler.java | 17 +- .../DeviceCommunicationHandlerFactory.java | 23 +- .../java/com/getpcpanel/hid/DeviceHolder.java | 126 +++- .../com/getpcpanel/hid/DeviceScanner.java | 150 ++++- .../com/getpcpanel/hid/InputInterpreter.java | 45 +- .../com/getpcpanel/hid/OutputInterpreter.java | 16 +- .../getpcpanel/iconextract/IIconService.java | 19 +- .../iconextract/IconServiceLinux.java | 9 +- .../iconextract/IconServiceWindows.java | 9 +- .../mqtt/MqttDeviceColorService.java | 27 +- .../getpcpanel/mqtt/MqttDeviceService.java | 55 +- .../mqtt/MqttHomeAssistantHelper.java | 20 +- .../java/com/getpcpanel/mqtt/MqttService.java | 39 +- .../com/getpcpanel/mqtt/MqttTopicHelper.java | 16 +- src/main/java/com/getpcpanel/obs/OBS.java | 82 ++- .../obs/ObsConnectedVolumeService.java | 19 +- .../java/com/getpcpanel/osc/OSCService.java | 24 +- .../profile/CommandMapDeserializer.java | 4 +- .../getpcpanel/profile/LightingConfig.java | 1 - .../profile/ProfileWindowFocusService.java | 14 +- .../java/com/getpcpanel/profile/Save.java | 4 +- .../com/getpcpanel/profile/SaveService.java | 152 ++++- .../profile/SingleKnobLightingConfig.java | 1 - .../profile/SingleLogoLightingConfig.java | 1 - .../SingleSliderLabelLightingConfig.java | 1 - .../profile/SingleSliderLightingConfig.java | 1 - .../sleepdetection/SleepDetector.java | 24 +- .../WindowsSystemEventService.java | 19 +- .../getpcpanel/spring/ConditionalOnLinux.java | 26 - .../spring/ConditionalOnWayland.java | 36 -- .../spring/ConditionalOnWindows.java | 26 - .../java/com/getpcpanel/spring/LinuxImpl.java | 20 + .../java/com/getpcpanel/spring/OsHelper.java | 36 +- .../getpcpanel/spring/PlatformProducer.java | 74 +++ .../java/com/getpcpanel/spring/Prototype.java | 18 - .../com/getpcpanel/spring/WaylandImpl.java | 20 + .../com/getpcpanel/spring/WindowsImpl.java | 20 + .../com/getpcpanel/ui/AdvancedDevice.java | 69 --- .../com/getpcpanel/ui/AdvancedDevices.java | 66 --- .../com/getpcpanel/ui/AppFinderDialog.java | 196 ------- .../java/com/getpcpanel/ui/BasicMacro.java | 195 ------- .../java/com/getpcpanel/ui/DeviceCell.java | 89 --- src/main/java/com/getpcpanel/ui/FxHelper.java | 84 --- src/main/java/com/getpcpanel/ui/HomePage.java | 265 --------- .../ui/ILightingDialogMuteOverrideHelper.java | 179 ------ .../ui/LightingChangedToDefaultEvent.java | 5 - .../com/getpcpanel/ui/LimitedTextField.java | 47 -- .../getpcpanel/ui/MacroControllerService.java | 81 --- .../com/getpcpanel/ui/MiniLightingDialog.java | 373 ------------ .../com/getpcpanel/ui/MqttSettingsDialog.java | 79 --- .../com/getpcpanel/ui/OSCSettingsDialog.java | 84 --- src/main/java/com/getpcpanel/ui/Overlay.java | 201 ------- .../com/getpcpanel/ui/OverlayPosition.java | 7 - .../ui/PickProcessesController.java | 143 ----- .../com/getpcpanel/ui/ProLightingDialog.java | 550 ------------------ .../getpcpanel/ui/ProfileSettingsDialog.java | 221 ------- .../com/getpcpanel/ui/RGBLightingDialog.java | 273 --------- .../java/com/getpcpanel/ui/ResizeHelper.java | 172 ------ .../com/getpcpanel/ui/SettingsDialog.java | 369 ------------ .../ui/SoundDeviceExportFactory.java | 71 --- .../ui/SoundDeviceImportFactory.java | 135 ----- src/main/java/com/getpcpanel/ui/UIHelper.java | 48 -- .../java/com/getpcpanel/ui/UIInitializer.java | 11 - .../ui/colorpicker/ColorDialog.java | 471 --------------- .../getpcpanel/ui/colorpicker/HueSlider.java | 70 --- .../getpcpanel/ui/colorpicker/InputField.java | 135 ----- .../ui/colorpicker/InputFieldSkin.java | 95 --- .../ui/colorpicker/IntegerField.java | 49 -- .../ui/colorpicker/IntegerFieldSkin.java | 64 -- .../ui/colorpicker/WebColorField.java | 31 - .../ui/colorpicker/WebColorFieldSkin.java | 76 --- .../ui/command/ButtonCommandController.java | 7 - .../ui/command/ButtonController.java | 341 ----------- .../java/com/getpcpanel/ui/command/Cmd.java | 29 - .../getpcpanel/ui/command/CommandContext.java | 9 - .../ui/command/CommandController.java | 50 -- .../ui/command/DialCommandController.java | 12 - .../ui/command/DialCutoffOptions.java | 136 ----- .../com/getpcpanel/ui/command/ObsEnabled.java | 12 - .../ui/command/VoiceMeeterEnabled.java | 12 - .../BtnApplicationDeviceToggleController.java | 72 --- .../BtnDefaultDeviceAdvancedController.java | 50 -- .../button/BtnDefaultDeviceController.java | 65 --- .../BtnDefaultDeviceToggleController.java | 87 --- .../button/BtnDeviceMuteController.java | 95 --- .../BtnDeviceToggleAdvancedController.java | 54 -- .../button/BtnEndProgramController.java | 70 --- .../button/BtnFocusMuteController.java | 57 -- .../button/BtnKeystrokeController.java | 105 ---- .../ui/command/button/BtnMediaController.java | 60 -- .../ui/command/button/BtnNoopController.java | 36 -- .../ui/command/button/BtnObsController.java | 108 ---- .../command/button/BtnProfileController.java | 55 -- .../command/button/BtnShortcutController.java | 53 -- .../button/BtnVoiceMeeterController.java | 126 ---- .../BtnVolumeProcessMuteController.java | 63 -- .../dial/DialBrightnessController.java | 38 -- .../ui/command/dial/DialObsController.java | 60 -- .../dial/DialVoiceMeeterController.java | 113 ---- .../dial/DialVolumeDeviceController.java | 89 --- .../dial/DialVolumeFocusController.java | 38 -- .../dial/DialVolumeProcessController.java | 106 ---- .../ui/graphviewer/GraphViewer.java | 59 -- .../java/com/getpcpanel/util/Debouncer.java | 4 +- .../java/com/getpcpanel/util/ExtractUtil.java | 14 +- .../java/com/getpcpanel/util/FileChecker.java | 4 +- .../java/com/getpcpanel/util/FileUtil.java | 47 +- .../com/getpcpanel/util/IPlatformCommand.java | 14 +- src/main/java/com/getpcpanel/util/Images.java | 4 +- .../com/getpcpanel/util/ProcessHelper.java | 4 +- .../com/getpcpanel/util/ShortcutHook.java | 17 +- src/main/java/com/getpcpanel/util/Util.java | 4 +- .../coloroverride/OverrideColorService.java | 10 +- .../getpcpanel/util/tray/ITrayService.java | 11 + .../getpcpanel/util/tray/awt/AwtTrayImpl.java | 20 + .../util/tray/awt/ConditionalOnAwtTray.java | 45 -- .../util/tray/awt/ConditionalOnX11.java | 40 -- .../util/tray/awt/TrayServiceAwt.java | 108 +++- .../tray/wayland/StatusNotifierItemImpl.java | 12 +- .../util/tray/wayland/TrayServiceWayland.java | 100 +++- .../util/version/VersionChecker.java | 115 +++- .../VoiceMeeterConnectedVolumeService.java | 18 +- .../voicemeeter/VoiceMeeterMuteService.java | 23 +- .../getpcpanel/voicemeeter/Voicemeeter.java | 39 +- .../voicemeeter/VoicemeeterAPI.java | 9 +- .../voicemeeter/VoicemeeterException.java | 1 - .../voicemeeter/VoicemeeterVersion.java | 1 - .../wavelink/WaveLinkIconHandler.java | 13 +- .../getpcpanel/wavelink/WaveLinkService.java | 16 +- .../CommandWaveLinkAddFocusToChannel.java | 4 +- .../command/CommandWaveLinkChangeLevel.java | 4 +- .../command/CommandWaveLinkChangeMute.java | 4 +- .../command/CommandWaveLinkChannelEffect.java | 4 +- .../command/CommandWaveLinkMainOutput.java | 4 +- .../wavelink/ui/BaseWaveLinkController.java | 150 ----- .../wavelink/ui/BtnWaveLinkController.java | 205 ------- .../wavelink/ui/DialWaveLinkController.java | 29 - .../wavelink/ui/WaveLinkEnabled.java | 12 - .../dev/niels/wavelink/WaveLinkClient.java | 4 +- .../wavelink/impl/WaveLinkClientImpl.java | 4 +- .../niels/wavelink/impl/WaveLinkListener.java | 4 +- .../wavelink/impl/model/WaveLinkImage.java | 4 +- src/main/resources/assets/1.css | 141 ----- src/main/resources/assets/256x256.png | Bin 15533 -> 0 bytes src/main/resources/assets/32x32.png | Bin 1452 -> 0 bytes src/main/resources/assets/AdvancedDevice.fxml | 54 -- .../resources/assets/AdvancedDevices.fxml | 14 - .../resources/assets/AppFinderDialog.fxml | 23 - src/main/resources/assets/BasicMacro.fxml | 74 --- src/main/resources/assets/ConfigBox.fxml | 75 --- src/main/resources/assets/DefaultExeIcon.ico | Bin 2267 -> 0 bytes .../resources/assets/DialCutoffOptions.fxml | 49 -- src/main/resources/assets/HomePage.fxml | 81 --- .../resources/assets/MiniLightingDialog.fxml | 90 --- .../resources/assets/MqttSettingsDialog.fxml | 101 ---- .../resources/assets/OSCSettingsDialog.fxml | 40 -- src/main/resources/assets/Overlay.fxml | 39 -- .../assets/PCPanelMini/PCPanelMini.css | 8 - .../assets/PCPanelMini/PCPanelMini.fxml | 63 -- .../resources/assets/PCPanelMini/knob.fxml | 36 -- .../resources/assets/PCPanelMini/preview.png | Bin 15996 -> 0 bytes .../assets/PCPanelPro/PCPanelPro.css | 9 - .../assets/PCPanelPro/PCPanelPro.fxml | 129 ---- .../assets/PCPanelPro/Pro_Cutout.png | Bin 13405 -> 0 bytes .../resources/assets/PCPanelPro/knob.fxml | 36 -- .../resources/assets/PCPanelPro/preview.png | Bin 15996 -> 0 bytes .../resources/assets/PCPanelPro/slider.fxml | 41 -- .../assets/PCPanelRGB/PCPanelRGB.css | 9 - .../assets/PCPanelRGB/PCPanelRGB.fxml | 46 -- .../resources/assets/PCPanelRGB/knob.fxml | 34 -- .../resources/assets/PCPanelRGB/preview.png | Bin 38999 -> 0 bytes src/main/resources/assets/PickProcesses.fxml | 6 - .../resources/assets/ProLightingDialog.fxml | 123 ---- .../assets/ProfileSettingsDialog.fxml | 96 --- .../resources/assets/RGBLightingDialog.fxml | 81 --- src/main/resources/assets/SettingsDialog.fxml | 289 --------- .../resources/assets/apex-mk2.regular.otf | Bin 7932 -> 0 bytes .../resources/assets/background_image.png | Bin 170568 -> 0 bytes src/main/resources/assets/command/Button.fxml | 29 - .../button/ApplicationDeviceToggle.fxml | 30 - .../assets/command/button/DefaultDevice.fxml | 16 - .../command/button/DefaultDeviceAdvanced.fxml | 6 - .../command/button/DefaultDeviceToggle.fxml | 33 -- .../assets/command/button/DeviceMute.fxml | 34 -- .../command/button/DeviceToggleAdvanced.fxml | 14 - .../assets/command/button/EndProgram.fxml | 26 - .../assets/command/button/FocusMute.fxml | 25 - .../assets/command/button/Keystroke.fxml | 20 - .../assets/command/button/Media.fxml | 36 -- .../resources/assets/command/button/Noop.fxml | 14 - .../resources/assets/command/button/Obs.fxml | 50 -- .../assets/command/button/Profile.fxml | 16 - .../assets/command/button/Shortcut.fxml | 20 - .../assets/command/button/VoiceMeeter.fxml | 54 -- .../command/button/VolumeProcessMute.fxml | 25 - .../assets/command/button/WaveLink.fxml | 59 -- .../assets/command/dial/Brightness.fxml | 14 - .../resources/assets/command/dial/Obs.fxml | 16 - .../assets/command/dial/VoiceMeeter.fxml | 52 -- .../assets/command/dial/VolumeDevice.fxml | 31 - .../assets/command/dial/VolumeFocus.fxml | 15 - .../assets/command/dial/VolumeProcess.fxml | 46 -- .../assets/command/dial/WaveLink.fxml | 36 -- src/main/resources/assets/dark_theme.css | 83 --- src/main/resources/assets/device.png | Bin 3134 -> 0 bytes src/main/resources/assets/dots.png | Bin 52186 -> 0 bytes .../resources/assets/icons/chevron-down.svg | 3 - .../resources/assets/icons/chevron-up.svg | 3 - .../assets/icons/close-circle-outline.svg | 3 - .../resources/assets/icons/content-copy.svg | 3 - .../assets/icons/lightbulb-on-outline.svg | 3 - src/main/resources/assets/icons/magnify.svg | 3 - src/main/resources/assets/lighting.png | Bin 17049 -> 0 bytes src/main/resources/assets/obs.png | Bin 2616 -> 0 bytes src/main/resources/assets/systemsounds.ico | Bin 47689 -> 0 bytes src/main/resources/assets/voicemeeter.png | Bin 5379 -> 0 bytes 258 files changed, 2009 insertions(+), 12385 deletions(-) delete mode 100644 src/main/java/com/getpcpanel/RestTemplateConfig.java delete mode 100644 src/main/java/com/getpcpanel/cpp/linux/pulseaudio/ConditionalOnPulseAudio.java create mode 100644 src/main/java/com/getpcpanel/cpp/linux/pulseaudio/PulseAudioImpl.java create mode 100644 src/main/java/com/getpcpanel/device/PCPanelMiniDevice.java delete mode 100644 src/main/java/com/getpcpanel/device/PCPanelMiniUI.java create mode 100644 src/main/java/com/getpcpanel/device/PCPanelProDevice.java delete mode 100644 src/main/java/com/getpcpanel/device/PCPanelProUI.java create mode 100644 src/main/java/com/getpcpanel/device/PCPanelRGBDevice.java delete mode 100644 src/main/java/com/getpcpanel/device/PCPanelRGBUI.java delete mode 100644 src/main/java/com/getpcpanel/spring/ConditionalOnLinux.java delete mode 100644 src/main/java/com/getpcpanel/spring/ConditionalOnWayland.java delete mode 100644 src/main/java/com/getpcpanel/spring/ConditionalOnWindows.java create mode 100644 src/main/java/com/getpcpanel/spring/LinuxImpl.java create mode 100644 src/main/java/com/getpcpanel/spring/PlatformProducer.java delete mode 100644 src/main/java/com/getpcpanel/spring/Prototype.java create mode 100644 src/main/java/com/getpcpanel/spring/WaylandImpl.java create mode 100644 src/main/java/com/getpcpanel/spring/WindowsImpl.java delete mode 100644 src/main/java/com/getpcpanel/ui/AdvancedDevice.java delete mode 100644 src/main/java/com/getpcpanel/ui/AdvancedDevices.java delete mode 100644 src/main/java/com/getpcpanel/ui/AppFinderDialog.java delete mode 100644 src/main/java/com/getpcpanel/ui/BasicMacro.java delete mode 100644 src/main/java/com/getpcpanel/ui/DeviceCell.java delete mode 100644 src/main/java/com/getpcpanel/ui/FxHelper.java delete mode 100644 src/main/java/com/getpcpanel/ui/HomePage.java delete mode 100644 src/main/java/com/getpcpanel/ui/ILightingDialogMuteOverrideHelper.java delete mode 100644 src/main/java/com/getpcpanel/ui/LightingChangedToDefaultEvent.java delete mode 100644 src/main/java/com/getpcpanel/ui/LimitedTextField.java delete mode 100644 src/main/java/com/getpcpanel/ui/MacroControllerService.java delete mode 100644 src/main/java/com/getpcpanel/ui/MiniLightingDialog.java delete mode 100644 src/main/java/com/getpcpanel/ui/MqttSettingsDialog.java delete mode 100644 src/main/java/com/getpcpanel/ui/OSCSettingsDialog.java delete mode 100644 src/main/java/com/getpcpanel/ui/Overlay.java delete mode 100644 src/main/java/com/getpcpanel/ui/OverlayPosition.java delete mode 100644 src/main/java/com/getpcpanel/ui/PickProcessesController.java delete mode 100644 src/main/java/com/getpcpanel/ui/ProLightingDialog.java delete mode 100644 src/main/java/com/getpcpanel/ui/ProfileSettingsDialog.java delete mode 100644 src/main/java/com/getpcpanel/ui/RGBLightingDialog.java delete mode 100644 src/main/java/com/getpcpanel/ui/ResizeHelper.java delete mode 100644 src/main/java/com/getpcpanel/ui/SettingsDialog.java delete mode 100644 src/main/java/com/getpcpanel/ui/SoundDeviceExportFactory.java delete mode 100644 src/main/java/com/getpcpanel/ui/SoundDeviceImportFactory.java delete mode 100644 src/main/java/com/getpcpanel/ui/UIHelper.java delete mode 100644 src/main/java/com/getpcpanel/ui/UIInitializer.java delete mode 100644 src/main/java/com/getpcpanel/ui/colorpicker/ColorDialog.java delete mode 100644 src/main/java/com/getpcpanel/ui/colorpicker/HueSlider.java delete mode 100644 src/main/java/com/getpcpanel/ui/colorpicker/InputField.java delete mode 100644 src/main/java/com/getpcpanel/ui/colorpicker/InputFieldSkin.java delete mode 100644 src/main/java/com/getpcpanel/ui/colorpicker/IntegerField.java delete mode 100644 src/main/java/com/getpcpanel/ui/colorpicker/IntegerFieldSkin.java delete mode 100644 src/main/java/com/getpcpanel/ui/colorpicker/WebColorField.java delete mode 100644 src/main/java/com/getpcpanel/ui/colorpicker/WebColorFieldSkin.java delete mode 100644 src/main/java/com/getpcpanel/ui/command/ButtonCommandController.java delete mode 100644 src/main/java/com/getpcpanel/ui/command/ButtonController.java delete mode 100644 src/main/java/com/getpcpanel/ui/command/Cmd.java delete mode 100644 src/main/java/com/getpcpanel/ui/command/CommandContext.java delete mode 100644 src/main/java/com/getpcpanel/ui/command/CommandController.java delete mode 100644 src/main/java/com/getpcpanel/ui/command/DialCommandController.java delete mode 100644 src/main/java/com/getpcpanel/ui/command/DialCutoffOptions.java delete mode 100644 src/main/java/com/getpcpanel/ui/command/ObsEnabled.java delete mode 100644 src/main/java/com/getpcpanel/ui/command/VoiceMeeterEnabled.java delete mode 100644 src/main/java/com/getpcpanel/ui/command/button/BtnApplicationDeviceToggleController.java delete mode 100644 src/main/java/com/getpcpanel/ui/command/button/BtnDefaultDeviceAdvancedController.java delete mode 100644 src/main/java/com/getpcpanel/ui/command/button/BtnDefaultDeviceController.java delete mode 100644 src/main/java/com/getpcpanel/ui/command/button/BtnDefaultDeviceToggleController.java delete mode 100644 src/main/java/com/getpcpanel/ui/command/button/BtnDeviceMuteController.java delete mode 100644 src/main/java/com/getpcpanel/ui/command/button/BtnDeviceToggleAdvancedController.java delete mode 100644 src/main/java/com/getpcpanel/ui/command/button/BtnEndProgramController.java delete mode 100644 src/main/java/com/getpcpanel/ui/command/button/BtnFocusMuteController.java delete mode 100644 src/main/java/com/getpcpanel/ui/command/button/BtnKeystrokeController.java delete mode 100644 src/main/java/com/getpcpanel/ui/command/button/BtnMediaController.java delete mode 100644 src/main/java/com/getpcpanel/ui/command/button/BtnNoopController.java delete mode 100644 src/main/java/com/getpcpanel/ui/command/button/BtnObsController.java delete mode 100644 src/main/java/com/getpcpanel/ui/command/button/BtnProfileController.java delete mode 100644 src/main/java/com/getpcpanel/ui/command/button/BtnShortcutController.java delete mode 100644 src/main/java/com/getpcpanel/ui/command/button/BtnVoiceMeeterController.java delete mode 100644 src/main/java/com/getpcpanel/ui/command/button/BtnVolumeProcessMuteController.java delete mode 100644 src/main/java/com/getpcpanel/ui/command/dial/DialBrightnessController.java delete mode 100644 src/main/java/com/getpcpanel/ui/command/dial/DialObsController.java delete mode 100644 src/main/java/com/getpcpanel/ui/command/dial/DialVoiceMeeterController.java delete mode 100644 src/main/java/com/getpcpanel/ui/command/dial/DialVolumeDeviceController.java delete mode 100644 src/main/java/com/getpcpanel/ui/command/dial/DialVolumeFocusController.java delete mode 100644 src/main/java/com/getpcpanel/ui/command/dial/DialVolumeProcessController.java delete mode 100644 src/main/java/com/getpcpanel/ui/graphviewer/GraphViewer.java create mode 100644 src/main/java/com/getpcpanel/util/tray/ITrayService.java create mode 100644 src/main/java/com/getpcpanel/util/tray/awt/AwtTrayImpl.java delete mode 100644 src/main/java/com/getpcpanel/util/tray/awt/ConditionalOnAwtTray.java delete mode 100644 src/main/java/com/getpcpanel/util/tray/awt/ConditionalOnX11.java delete mode 100644 src/main/java/com/getpcpanel/wavelink/ui/BaseWaveLinkController.java delete mode 100644 src/main/java/com/getpcpanel/wavelink/ui/BtnWaveLinkController.java delete mode 100644 src/main/java/com/getpcpanel/wavelink/ui/DialWaveLinkController.java delete mode 100644 src/main/java/com/getpcpanel/wavelink/ui/WaveLinkEnabled.java delete mode 100644 src/main/resources/assets/1.css delete mode 100644 src/main/resources/assets/256x256.png delete mode 100644 src/main/resources/assets/32x32.png delete mode 100644 src/main/resources/assets/AdvancedDevice.fxml delete mode 100644 src/main/resources/assets/AdvancedDevices.fxml delete mode 100644 src/main/resources/assets/AppFinderDialog.fxml delete mode 100644 src/main/resources/assets/BasicMacro.fxml delete mode 100644 src/main/resources/assets/ConfigBox.fxml delete mode 100644 src/main/resources/assets/DefaultExeIcon.ico delete mode 100644 src/main/resources/assets/DialCutoffOptions.fxml delete mode 100644 src/main/resources/assets/HomePage.fxml delete mode 100644 src/main/resources/assets/MiniLightingDialog.fxml delete mode 100644 src/main/resources/assets/MqttSettingsDialog.fxml delete mode 100644 src/main/resources/assets/OSCSettingsDialog.fxml delete mode 100644 src/main/resources/assets/Overlay.fxml delete mode 100644 src/main/resources/assets/PCPanelMini/PCPanelMini.css delete mode 100644 src/main/resources/assets/PCPanelMini/PCPanelMini.fxml delete mode 100644 src/main/resources/assets/PCPanelMini/knob.fxml delete mode 100644 src/main/resources/assets/PCPanelMini/preview.png delete mode 100644 src/main/resources/assets/PCPanelPro/PCPanelPro.css delete mode 100644 src/main/resources/assets/PCPanelPro/PCPanelPro.fxml delete mode 100644 src/main/resources/assets/PCPanelPro/Pro_Cutout.png delete mode 100644 src/main/resources/assets/PCPanelPro/knob.fxml delete mode 100644 src/main/resources/assets/PCPanelPro/preview.png delete mode 100644 src/main/resources/assets/PCPanelPro/slider.fxml delete mode 100644 src/main/resources/assets/PCPanelRGB/PCPanelRGB.css delete mode 100644 src/main/resources/assets/PCPanelRGB/PCPanelRGB.fxml delete mode 100644 src/main/resources/assets/PCPanelRGB/knob.fxml delete mode 100644 src/main/resources/assets/PCPanelRGB/preview.png delete mode 100644 src/main/resources/assets/PickProcesses.fxml delete mode 100644 src/main/resources/assets/ProLightingDialog.fxml delete mode 100644 src/main/resources/assets/ProfileSettingsDialog.fxml delete mode 100644 src/main/resources/assets/RGBLightingDialog.fxml delete mode 100644 src/main/resources/assets/SettingsDialog.fxml delete mode 100644 src/main/resources/assets/apex-mk2.regular.otf delete mode 100644 src/main/resources/assets/background_image.png delete mode 100644 src/main/resources/assets/command/Button.fxml delete mode 100644 src/main/resources/assets/command/button/ApplicationDeviceToggle.fxml delete mode 100644 src/main/resources/assets/command/button/DefaultDevice.fxml delete mode 100644 src/main/resources/assets/command/button/DefaultDeviceAdvanced.fxml delete mode 100644 src/main/resources/assets/command/button/DefaultDeviceToggle.fxml delete mode 100644 src/main/resources/assets/command/button/DeviceMute.fxml delete mode 100644 src/main/resources/assets/command/button/DeviceToggleAdvanced.fxml delete mode 100644 src/main/resources/assets/command/button/EndProgram.fxml delete mode 100644 src/main/resources/assets/command/button/FocusMute.fxml delete mode 100644 src/main/resources/assets/command/button/Keystroke.fxml delete mode 100644 src/main/resources/assets/command/button/Media.fxml delete mode 100644 src/main/resources/assets/command/button/Noop.fxml delete mode 100644 src/main/resources/assets/command/button/Obs.fxml delete mode 100644 src/main/resources/assets/command/button/Profile.fxml delete mode 100644 src/main/resources/assets/command/button/Shortcut.fxml delete mode 100644 src/main/resources/assets/command/button/VoiceMeeter.fxml delete mode 100644 src/main/resources/assets/command/button/VolumeProcessMute.fxml delete mode 100644 src/main/resources/assets/command/button/WaveLink.fxml delete mode 100644 src/main/resources/assets/command/dial/Brightness.fxml delete mode 100644 src/main/resources/assets/command/dial/Obs.fxml delete mode 100644 src/main/resources/assets/command/dial/VoiceMeeter.fxml delete mode 100644 src/main/resources/assets/command/dial/VolumeDevice.fxml delete mode 100644 src/main/resources/assets/command/dial/VolumeFocus.fxml delete mode 100644 src/main/resources/assets/command/dial/VolumeProcess.fxml delete mode 100644 src/main/resources/assets/command/dial/WaveLink.fxml delete mode 100644 src/main/resources/assets/dark_theme.css delete mode 100644 src/main/resources/assets/device.png delete mode 100644 src/main/resources/assets/dots.png delete mode 100644 src/main/resources/assets/icons/chevron-down.svg delete mode 100644 src/main/resources/assets/icons/chevron-up.svg delete mode 100644 src/main/resources/assets/icons/close-circle-outline.svg delete mode 100644 src/main/resources/assets/icons/content-copy.svg delete mode 100644 src/main/resources/assets/icons/lightbulb-on-outline.svg delete mode 100644 src/main/resources/assets/icons/magnify.svg delete mode 100644 src/main/resources/assets/lighting.png delete mode 100644 src/main/resources/assets/obs.png delete mode 100644 src/main/resources/assets/systemsounds.ico delete mode 100644 src/main/resources/assets/voicemeeter.png diff --git a/src/main/java/com/getpcpanel/CachingConfig.java b/src/main/java/com/getpcpanel/CachingConfig.java index c33d06dd..2fb9992e 100644 --- a/src/main/java/com/getpcpanel/CachingConfig.java +++ b/src/main/java/com/getpcpanel/CachingConfig.java @@ -1,28 +1,44 @@ package com.getpcpanel; -import org.springframework.cache.CacheManager; -import org.springframework.cache.annotation.CacheEvict; -import org.springframework.cache.annotation.EnableCaching; -import org.springframework.cache.concurrent.ConcurrentMapCacheManager; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.scheduling.annotation.Scheduled; - -@Configuration -@EnableCaching +import io.quarkus.cache.CacheInvalidateAll; +import io.quarkus.scheduler.Scheduled; +import jakarta.enterprise.context.ApplicationScoped; +import lombok.extern.jbosslog.JBossLog; + +@JBossLog +@ApplicationScoped +public class CachingConfig { + + @CacheInvalidateAll(cacheName = "icon") + @Scheduled(every = "300s") + public void iconEvict() { + log.debug("Evicting icon cache"); + } + + @CacheInvalidateAll(cacheName = "command-icon") + @Scheduled(every = "1s") + public void commandIconEvict() { + } +} + + +import io.quarkus.cache.CacheInvalidateAll; +import jakarta.enterprise.inject.Produces; +import io.quarkus.scheduler.Scheduled; + public class CachingConfig { - @Bean + @Produces public CacheManager cacheManager() { return new ConcurrentMapCacheManager("icon", "command-icon"); } - @CacheEvict(allEntries = true, cacheNames = "icon") - @Scheduled(fixedDelay = 300_000) + @CacheInvalidateAll(cacheName="icon") + @Scheduled(every="300s") public void iconEvict() { } - @CacheEvict(allEntries = true, cacheNames = "command-icon") - @Scheduled(fixedDelay = 1_000) + @CacheInvalidateAll(cacheName="command-icon") + @Scheduled(every="1s") public void commandIconEvict() { } } diff --git a/src/main/java/com/getpcpanel/Json.java b/src/main/java/com/getpcpanel/Json.java index 578d9a5a..487d631d 100644 --- a/src/main/java/com/getpcpanel/Json.java +++ b/src/main/java/com/getpcpanel/Json.java @@ -1,18 +1,51 @@ package com.getpcpanel; -import org.springframework.stereotype.Service; +import com.fasterxml.jackson.databind.ObjectMapper; + +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.inject.Inject; +import lombok.SneakyThrows; +import lombok.extern.jbosslog.JBossLog; + +/** + * Jackson wrapper + */ +@JBossLog +@ApplicationScoped +public final class Json { + @Inject + ObjectMapper mapper; + + @SneakyThrows + public String write(Object o) { + return mapper.writeValueAsString(o); + } + + @SneakyThrows + public T read(String in, Class clazz) { + return mapper.readValue(in, clazz); + } + + @SneakyThrows + public String writePretty(Object o) { + return mapper.writerWithDefaultPrettyPrinter().writeValueAsString(o); + } +} + + +import jakarta.enterprise.context.ApplicationScoped; import com.fasterxml.jackson.databind.ObjectMapper; import lombok.RequiredArgsConstructor; import lombok.SneakyThrows; -import lombok.extern.log4j.Log4j2; +import lombok.extern.jbosslog.JBossLog; /** * Jackson wrapper */ -@Log4j2 -@Service +@JBossLog +@ApplicationScoped @RequiredArgsConstructor public final class Json { private final ObjectMapper mapper; diff --git a/src/main/java/com/getpcpanel/RestTemplateConfig.java b/src/main/java/com/getpcpanel/RestTemplateConfig.java deleted file mode 100644 index 2e943b8d..00000000 --- a/src/main/java/com/getpcpanel/RestTemplateConfig.java +++ /dev/null @@ -1,13 +0,0 @@ -package com.getpcpanel; - -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.web.client.RestTemplate; - -@Configuration -public class RestTemplateConfig { - @Bean - public RestTemplate restTemplate() { - return new RestTemplate(); - } -} diff --git a/src/main/java/com/getpcpanel/commands/AbstractNewXVolumeService.java b/src/main/java/com/getpcpanel/commands/AbstractNewXVolumeService.java index 433d3137..f58c77f8 100644 --- a/src/main/java/com/getpcpanel/commands/AbstractNewXVolumeService.java +++ b/src/main/java/com/getpcpanel/commands/AbstractNewXVolumeService.java @@ -3,8 +3,9 @@ import java.util.Map; import java.util.function.Function; -import org.springframework.context.ApplicationEventPublisher; -import org.springframework.stereotype.Service; +import jakarta.enterprise.event.Event; +import jakarta.inject.Inject; +import jakarta.enterprise.context.ApplicationScoped; import com.getpcpanel.commands.command.Command; import com.getpcpanel.device.Device; @@ -12,19 +13,20 @@ import com.getpcpanel.hid.DeviceHolder; import lombok.RequiredArgsConstructor; -import lombok.extern.log4j.Log4j2; +import lombok.extern.jbosslog.JBossLog; import one.util.streamex.EntryStream; import one.util.streamex.StreamEx; /** * Parent class for services that trigger when a process starts or another tool connects. */ -@Log4j2 -@Service -@RequiredArgsConstructor +@JBossLog +@ApplicationScoped public abstract class AbstractNewXVolumeService { - private final DeviceHolder devices; - private final ApplicationEventPublisher eventPublisher; + @Inject + DeviceHolder devices; + @Inject + Event eventBus; protected void triggerCommandsOf(Class clazz, Function, EntryStream> chain) { StreamEx.of(devices.all()) @@ -37,7 +39,7 @@ protected void triggerCommandsOf(Class clazz, Function devices.getDevice(idAndDial.id()).ifPresent(device -> { var current = device.getKnobRotation(idAndDial.dial()); - eventPublisher.publishEvent(new DeviceCommunicationHandler.KnobRotateEvent(idAndDial.id(), idAndDial.dial(), current, false)); + eventBus.fire(new DeviceCommunicationHandler.KnobRotateEvent(idAndDial.id(), idAndDial.dial(), current, false)); })); } diff --git a/src/main/java/com/getpcpanel/commands/CommandDispatcher.java b/src/main/java/com/getpcpanel/commands/CommandDispatcher.java index 9e573762..69403789 100644 --- a/src/main/java/com/getpcpanel/commands/CommandDispatcher.java +++ b/src/main/java/com/getpcpanel/commands/CommandDispatcher.java @@ -3,14 +3,14 @@ import java.util.Map; import java.util.concurrent.ConcurrentHashMap; -import org.springframework.context.event.EventListener; -import org.springframework.stereotype.Service; +import jakarta.enterprise.event.Observes; +import jakarta.enterprise.context.ApplicationScoped; import jakarta.annotation.PostConstruct; -import lombok.extern.log4j.Log4j2; +import lombok.extern.jbosslog.JBossLog; -@Log4j2 -@Service +@JBossLog +@ApplicationScoped public final class CommandDispatcher { private final Map map = new ConcurrentHashMap<>(); private final HandlerThread handler = new HandlerThread(); @@ -24,8 +24,7 @@ public void init() { private CommandDispatcher() { } - @EventListener - public void onCommand(PCPanelControlEvent event) { + public void onCommand(@Observes PCPanelControlEvent event) { map.put(event.serialNum() + event.knob(), event.buildRunnable()); handler.doNotify(); } diff --git a/src/main/java/com/getpcpanel/commands/IconService.java b/src/main/java/com/getpcpanel/commands/IconService.java index 0d57fb87..705aa9fe 100644 --- a/src/main/java/com/getpcpanel/commands/IconService.java +++ b/src/main/java/com/getpcpanel/commands/IconService.java @@ -12,8 +12,9 @@ import javax.annotation.Nullable; import org.apache.commons.lang3.StringUtils; -import org.springframework.cache.annotation.Cacheable; -import org.springframework.stereotype.Service; +import io.quarkus.cache.CacheResult; +import jakarta.inject.Inject; +import jakarta.enterprise.context.ApplicationScoped; import com.getpcpanel.commands.command.Command; import com.getpcpanel.commands.command.CommandBrightness; @@ -34,12 +35,11 @@ import jakarta.annotation.PostConstruct; import javafx.scene.image.Image; import lombok.RequiredArgsConstructor; -import lombok.extern.log4j.Log4j2; +import lombok.extern.jbosslog.JBossLog; import one.util.streamex.StreamEx; -@Log4j2 -@Service -@RequiredArgsConstructor +@JBossLog +@ApplicationScoped public class IconService { public static final Image DEFAULT = new Image(Objects.requireNonNull(IconService.class.getResource("/assets/32x32.png")).toExternalForm()); private static final Image OBS = new Image(Objects.requireNonNull(IconService.class.getResource("/assets/obs.png")).toExternalForm()); @@ -47,8 +47,10 @@ public class IconService { public static final Image DEVICE = new Image(Objects.requireNonNull(IconService.class.getResource("/assets/device.png")).toExternalForm()); public static final Image SYSTEM_SOUND = new Image(Objects.requireNonNull(IconService.class.getResource("/assets/systemsounds.ico")).toExternalForm()); private final SafeMap imageHandlers = new SafeMap(); - private final ISndCtrl sndCtrl; - private final IIconService iconService; + @Inject + ISndCtrl sndCtrl; + @Inject + IIconService iconService; private final List> iconHandlers; @PostConstruct @@ -76,7 +78,7 @@ public void init() { }); } - @Cacheable("command-icon") + @CacheResult(cacheName="command-icon") @Nonnull public Image getImageFrom(@Nullable Commands commands, @Nullable KnobSetting override) { if (!Commands.hasCommands(commands)) { diff --git a/src/main/java/com/getpcpanel/commands/KeyMacro.java b/src/main/java/com/getpcpanel/commands/KeyMacro.java index 14df003c..43b3390c 100644 --- a/src/main/java/com/getpcpanel/commands/KeyMacro.java +++ b/src/main/java/com/getpcpanel/commands/KeyMacro.java @@ -4,9 +4,9 @@ import java.awt.Robot; import java.awt.event.KeyEvent; -import lombok.extern.log4j.Log4j2; +import lombok.extern.jbosslog.JBossLog; -@Log4j2 +@JBossLog public final class KeyMacro { private static final Robot robot = buildRobot(); diff --git a/src/main/java/com/getpcpanel/commands/SetMuteOverrideService.java b/src/main/java/com/getpcpanel/commands/SetMuteOverrideService.java index a6d72de5..64c1227e 100644 --- a/src/main/java/com/getpcpanel/commands/SetMuteOverrideService.java +++ b/src/main/java/com/getpcpanel/commands/SetMuteOverrideService.java @@ -9,9 +9,10 @@ import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.math.NumberUtils; -import org.springframework.context.event.EventListener; -import org.springframework.core.annotation.Order; -import org.springframework.stereotype.Service; +import jakarta.inject.Inject; +import jakarta.enterprise.event.Observes; +import jakarta.annotation.Priority; +import jakarta.enterprise.context.ApplicationScoped; import com.getpcpanel.commands.command.CommandObsSetSourceVolume; import com.getpcpanel.commands.command.CommandVoiceMeeter; @@ -44,27 +45,29 @@ import com.getpcpanel.voicemeeter.Voicemeeter.ControlType; import lombok.RequiredArgsConstructor; -import lombok.extern.log4j.Log4j2; +import lombok.extern.jbosslog.JBossLog; import one.util.streamex.EntryStream; import one.util.streamex.StreamEx; /** * Triggers a color change when the device or application that is controlled by the dial/slider is muted/unmuted. */ -@Log4j2 -@Service -@Order(0) -@RequiredArgsConstructor +@JBossLog +@ApplicationScoped +@Priority(0) public class SetMuteOverrideService implements IOverrideColorProviderProvider { private static final Pattern voiceMeeterPattern = Pattern.compile("VoiceMeeter: (Input|Output) (\\d+), (.*)"); // 1: In/Out, 2: Idx, 3: ButtonType - private final DeviceHolder devices; - private final ISndCtrl sndCtrl; - private final SaveService saveService; - private final OBS obs; + @Inject + DeviceHolder devices; + @Inject + ISndCtrl sndCtrl; + @Inject + SaveService saveService; + @Inject + OBS obs; private final ColorOverrideHolder colorOverrideHolder = new ColorOverrideHolder(); - @EventListener({ DeviceScanner.DeviceConnectedEvent.class, LightingChangedToDefaultEvent.class }) - public void triggerAll() { + public void triggerAll() { colorOverrideHolder.clearAllOverrides(); for (var device : sndCtrl.getDevices()) { onAudioDevice(new AudioDeviceEvent(device, EventType.CHANGED)); @@ -75,8 +78,7 @@ public void triggerAll() { updateObs(new OBSConnectEvent(obs.isConnected())); } - @EventListener(OBSConnectEvent.class) - public void updateObs(OBSConnectEvent event) { + public void updateObs(@Observes OBSConnectEvent event) { if (!event.connected()) { return; } @@ -84,8 +86,7 @@ public void updateObs(OBSConnectEvent event) { EntryStream.of(obs.getSourcesWithMuteState()).mapKeyValue(OBSMuteEvent::new).forEach(this::onObsSource); } - @EventListener - public void onObsSource(OBSMuteEvent event) { + public void onObsSource(@Observes OBSMuteEvent event) { var lcName = event.input(); handleEvent( dlc -> isFollow(dlc) && @@ -93,8 +94,7 @@ public void onObsSource(OBSMuteEvent event) { event.muted()); } - @EventListener - public void onVoiceMeeterSource(VoiceMeeterMuteEvent event) { + public void onVoiceMeeterSource(@Observes VoiceMeeterMuteEvent event) { var type = event.ct(); var idx = event.idx(); var button = event.button(); @@ -122,8 +122,7 @@ public void onVoiceMeeterSource(VoiceMeeterMuteEvent event) { event.state()); } - @EventListener - public void onAudioSession(AudioSessionEvent event) { + public void onAudioSession(@Observes AudioSessionEvent event) { var lcName = StringUtils.lowerCase(event.session().executable().getName().toLowerCase()); handleEvent( dlc -> isFollow(dlc) && @@ -131,8 +130,7 @@ public void onAudioSession(AudioSessionEvent event) { event.session().muted()); } - @EventListener - public void onAudioDevice(AudioDeviceEvent event) { + public void onAudioDevice(@Observes AudioDeviceEvent event) { handleEvent( dlc -> isDevice(event, dlc) || (isFollow(dlc) && dlc.cmd.getCommand(CommandVolumeDevice.class).filter(vd -> sndCtrl.defaultDeviceOnEmpty(vd.getDeviceId()).equals(event.device().id())).isPresent()), diff --git a/src/main/java/com/getpcpanel/commands/command/Command.java b/src/main/java/com/getpcpanel/commands/command/Command.java index 62342e01..90df351a 100644 --- a/src/main/java/com/getpcpanel/commands/command/Command.java +++ b/src/main/java/com/getpcpanel/commands/command/Command.java @@ -6,9 +6,9 @@ import com.getpcpanel.hid.DialValue; import lombok.ToString; -import lombok.extern.log4j.Log4j2; +import lombok.extern.jbosslog.JBossLog; -@Log4j2 +@JBossLog @ToString @SuppressWarnings("InstanceofThis") @JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, property = "_type") diff --git a/src/main/java/com/getpcpanel/commands/command/CommandBrightness.java b/src/main/java/com/getpcpanel/commands/command/CommandBrightness.java index 99f33a91..535b1ca5 100644 --- a/src/main/java/com/getpcpanel/commands/command/CommandBrightness.java +++ b/src/main/java/com/getpcpanel/commands/command/CommandBrightness.java @@ -8,10 +8,10 @@ import lombok.Getter; import lombok.ToString; -import lombok.extern.log4j.Log4j2; +import lombok.extern.jbosslog.JBossLog; @Getter -@Log4j2 +@JBossLog @ToString(callSuper = true) public class CommandBrightness extends Command implements DialAction { private final DialCommandParams dialParams; diff --git a/src/main/java/com/getpcpanel/commands/command/CommandConverter.java b/src/main/java/com/getpcpanel/commands/command/CommandConverter.java index 7e270f2d..36d445e1 100644 --- a/src/main/java/com/getpcpanel/commands/command/CommandConverter.java +++ b/src/main/java/com/getpcpanel/commands/command/CommandConverter.java @@ -12,10 +12,10 @@ import com.getpcpanel.cpp.MuteType; import com.getpcpanel.voicemeeter.Voicemeeter; -import lombok.extern.log4j.Log4j2; +import lombok.extern.jbosslog.JBossLog; import one.util.streamex.StreamEx; -@Log4j2 +@JBossLog public final class CommandConverter { private CommandConverter() { } diff --git a/src/main/java/com/getpcpanel/commands/command/CommandEndProgram.java b/src/main/java/com/getpcpanel/commands/command/CommandEndProgram.java index f1217ae5..2228bdd6 100644 --- a/src/main/java/com/getpcpanel/commands/command/CommandEndProgram.java +++ b/src/main/java/com/getpcpanel/commands/command/CommandEndProgram.java @@ -7,10 +7,10 @@ import lombok.Getter; import lombok.ToString; -import lombok.extern.log4j.Log4j2; +import lombok.extern.jbosslog.JBossLog; @Getter -@Log4j2 +@JBossLog @ToString(callSuper = true) public class CommandEndProgram extends Command implements ButtonAction { private static final Runtime rt = Runtime.getRuntime(); diff --git a/src/main/java/com/getpcpanel/commands/command/CommandKeystroke.java b/src/main/java/com/getpcpanel/commands/command/CommandKeystroke.java index 4eb38df6..564b725b 100644 --- a/src/main/java/com/getpcpanel/commands/command/CommandKeystroke.java +++ b/src/main/java/com/getpcpanel/commands/command/CommandKeystroke.java @@ -6,10 +6,10 @@ import lombok.Getter; import lombok.ToString; -import lombok.extern.log4j.Log4j2; +import lombok.extern.jbosslog.JBossLog; @Getter -@Log4j2 +@JBossLog @ToString(callSuper = true) public class CommandKeystroke extends Command implements ButtonAction { private final String keystroke; diff --git a/src/main/java/com/getpcpanel/commands/command/CommandRun.java b/src/main/java/com/getpcpanel/commands/command/CommandRun.java index f0eb090b..03326505 100644 --- a/src/main/java/com/getpcpanel/commands/command/CommandRun.java +++ b/src/main/java/com/getpcpanel/commands/command/CommandRun.java @@ -7,10 +7,10 @@ import lombok.Getter; import lombok.ToString; -import lombok.extern.log4j.Log4j2; +import lombok.extern.jbosslog.JBossLog; @Getter -@Log4j2 +@JBossLog @ToString(callSuper = true) public class CommandRun extends Command implements ButtonAction { private final String command; diff --git a/src/main/java/com/getpcpanel/commands/command/CommandShortcut.java b/src/main/java/com/getpcpanel/commands/command/CommandShortcut.java index 13006e46..941c6c9a 100644 --- a/src/main/java/com/getpcpanel/commands/command/CommandShortcut.java +++ b/src/main/java/com/getpcpanel/commands/command/CommandShortcut.java @@ -7,10 +7,10 @@ import lombok.Getter; import lombok.ToString; -import lombok.extern.log4j.Log4j2; +import lombok.extern.jbosslog.JBossLog; @Getter -@Log4j2 +@JBossLog @ToString(callSuper = true) public class CommandShortcut extends Command implements ButtonAction { private static final Runtime rt = Runtime.getRuntime(); diff --git a/src/main/java/com/getpcpanel/commands/command/CommandVolumeApplicationDeviceToggle.java b/src/main/java/com/getpcpanel/commands/command/CommandVolumeApplicationDeviceToggle.java index a9305c42..fdc89030 100644 --- a/src/main/java/com/getpcpanel/commands/command/CommandVolumeApplicationDeviceToggle.java +++ b/src/main/java/com/getpcpanel/commands/command/CommandVolumeApplicationDeviceToggle.java @@ -18,11 +18,11 @@ import lombok.Getter; import lombok.ToString; -import lombok.extern.log4j.Log4j2; +import lombok.extern.jbosslog.JBossLog; import one.util.streamex.StreamEx; @Getter -@Log4j2 +@JBossLog @ToString(callSuper = true) public class CommandVolumeApplicationDeviceToggle extends CommandVolume implements ButtonAction { private final List processes; diff --git a/src/main/java/com/getpcpanel/commands/command/CommandVolumeDefaultDeviceToggle.java b/src/main/java/com/getpcpanel/commands/command/CommandVolumeDefaultDeviceToggle.java index 45e04ff6..42f5d7f5 100644 --- a/src/main/java/com/getpcpanel/commands/command/CommandVolumeDefaultDeviceToggle.java +++ b/src/main/java/com/getpcpanel/commands/command/CommandVolumeDefaultDeviceToggle.java @@ -14,11 +14,11 @@ import lombok.Getter; import lombok.ToString; -import lombok.extern.log4j.Log4j2; +import lombok.extern.jbosslog.JBossLog; import one.util.streamex.StreamEx; @Getter -@Log4j2 +@JBossLog @ToString(callSuper = true) public class CommandVolumeDefaultDeviceToggle extends CommandVolume implements ButtonAction { private final List devices; diff --git a/src/main/java/com/getpcpanel/commands/command/CommandVolumeDefaultDeviceToggleAdvanced.java b/src/main/java/com/getpcpanel/commands/command/CommandVolumeDefaultDeviceToggleAdvanced.java index 17357927..1351a57d 100644 --- a/src/main/java/com/getpcpanel/commands/command/CommandVolumeDefaultDeviceToggleAdvanced.java +++ b/src/main/java/com/getpcpanel/commands/command/CommandVolumeDefaultDeviceToggleAdvanced.java @@ -19,12 +19,12 @@ import lombok.Getter; import lombok.ToString; -import lombok.extern.log4j.Log4j2; +import lombok.extern.jbosslog.JBossLog; import one.util.streamex.EntryStream; import one.util.streamex.StreamEx; @Getter -@Log4j2 +@JBossLog @ToString(callSuper = true) public class CommandVolumeDefaultDeviceToggleAdvanced extends CommandVolume implements ButtonAction { private final List devices; diff --git a/src/main/java/com/getpcpanel/cpp/AudioDevice.java b/src/main/java/com/getpcpanel/cpp/AudioDevice.java index fb585a56..712c8970 100644 --- a/src/main/java/com/getpcpanel/cpp/AudioDevice.java +++ b/src/main/java/com/getpcpanel/cpp/AudioDevice.java @@ -2,34 +2,82 @@ import java.io.Serializable; -import org.springframework.context.ApplicationEventPublisher; +import jakarta.enterprise.event.Event; import lombok.AccessLevel; import lombok.Data; import lombok.Setter; -import lombok.extern.log4j.Log4j2; +import lombok.extern.jbosslog.JBossLog; @Data -@Log4j2 +@JBossLog @Setter(AccessLevel.PROTECTED) @SuppressWarnings("unused") // Methods called from JNI public class AudioDevice implements Serializable { - protected final transient ApplicationEventPublisher eventPublisher; + protected final transient Event eventBus; private final String name; private final String id; private float volume; private boolean muted; private DataFlow dataflow; - public AudioDevice(ApplicationEventPublisher eventPublisher, String name, String id) { - this.eventPublisher = eventPublisher; + public AudioDevice(Event eventBus, String name, String id) { + this.eventBus = eventBus; this.name = name; this.id = id; } private void setState(float volume, boolean muted) { volume(volume).muted(muted); - eventPublisher.publishEvent(new AudioDeviceEvent(this, EventType.CHANGED)); + eventBus.fire(new AudioDeviceEvent(this, EventType.CHANGED)); + log.trace("State changed: {}", this); + } + + public boolean isOutput() { + return dataflow.output(); + } + + public boolean isInput() { + return dataflow.input(); + } + + public String toString() { + return name; + } +} + + +import java.io.Serializable; + +import jakarta.enterprise.event.Event; +import jakarta.inject.Inject; + +import lombok.AccessLevel; +import lombok.Data; +import lombok.Setter; +import lombok.extern.jbosslog.JBossLog; + +@Data +@JBossLog +@Setter(AccessLevel.PROTECTED) +@SuppressWarnings("unused") // Methods called from JNI +public class AudioDevice implements Serializable { + protected final transient Event eventBus; + private final String name; + private final String id; + private float volume; + private boolean muted; + private DataFlow dataflow; + + public AudioDevice(Event eventBus, String name, String id) { + this.eventBus = eventBus; + this.name = name; + this.id = id; + } + + private void setState(float volume, boolean muted) { + volume(volume).muted(muted); + eventBus.fire(new AudioDeviceEvent(this, EventType.CHANGED)); log.trace("State changed: {}", this); } diff --git a/src/main/java/com/getpcpanel/cpp/AudioSession.java b/src/main/java/com/getpcpanel/cpp/AudioSession.java index a47d1d5d..a62ed3e2 100644 --- a/src/main/java/com/getpcpanel/cpp/AudioSession.java +++ b/src/main/java/com/getpcpanel/cpp/AudioSession.java @@ -5,19 +5,20 @@ import javax.annotation.Nullable; import org.apache.commons.lang3.StringUtils; -import org.springframework.context.ApplicationEventPublisher; +import jakarta.enterprise.event.Event; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.ToString; -import lombok.extern.log4j.Log4j2; +import lombok.extern.jbosslog.JBossLog; @Data -@Log4j2 +@JBossLog @SuppressWarnings("unused") // Methods called from JNI public class AudioSession { public static final String SYSTEM = "System Sounds"; - @EqualsAndHashCode.Exclude @ToString.Exclude @Nullable private final ApplicationEventPublisher eventPublisher; + @EqualsAndHashCode.Exclude @ToString.Exclude @Nullable + transient Event eventBus; private int pid; private File executable; @EqualsAndHashCode.Exclude private String title; @@ -25,8 +26,94 @@ public class AudioSession { @EqualsAndHashCode.Exclude private float volume; @EqualsAndHashCode.Exclude private boolean muted; - public AudioSession(@Nullable ApplicationEventPublisher eventPublisher, int pid, File executable, String title, @Nullable String icon, float volume, boolean muted) { - this.eventPublisher = eventPublisher; + public AudioSession(@Nullable Event eventBus, int pid, File executable, String title, @Nullable String icon, float volume, boolean muted) { + this.eventBus = eventBus; + this.pid = pid; + this.executable = executable; + this.icon = icon; + this.volume = volume; + this.muted = muted; + + // Uses pid and icon, so do this late + this.title = isSystemSounds() ? SYSTEM : StringUtils.firstNonBlank(title, executable.getName()); + } + + public AudioSession name(String title) { + this.title = title; + triggerChange(); + return this; + } + + private AudioSession title(String title) { + this.title = title; + triggerChange(); + return this; + } + + private AudioSession icon(String icon) { + this.icon = icon; + triggerChange(); + return this; + } + + private AudioSession volume(float volume) { + this.volume = volume; + triggerChange(); + return this; + } + + private AudioSession muted(boolean muted) { + this.muted = muted; + triggerChange(); + return this; + } + + public boolean isSystemSounds() { + return pid == 0 || StringUtils.containsIgnoreCase(icon, "AudioSrv.Dll"); + } + + protected AudioSession setVolumeNoTrigger(float volume) { + this.volume = volume; + return this; + } + + private void triggerChange() { + if (eventBus != null) { + eventBus.fire(new AudioSessionEvent(this, EventType.CHANGED)); + } + } +} + + +import java.io.File; + +import javax.annotation.Nullable; + +import org.apache.commons.lang3.StringUtils; +import jakarta.enterprise.event.Event; +import jakarta.inject.Inject; + +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; +import lombok.extern.jbosslog.JBossLog; + +@Data +@JBossLog +@SuppressWarnings("unused") // Methods called from JNI +public class AudioSession { + public static final String SYSTEM = "System Sounds"; + @EqualsAndHashCode.Exclude @ToString.Exclude @Nullable @Inject + Event eventBus; + private int pid; + private File executable; + @EqualsAndHashCode.Exclude private String title; + @EqualsAndHashCode.Exclude @Nullable private String icon; + @EqualsAndHashCode.Exclude private float volume; + @EqualsAndHashCode.Exclude private boolean muted; + + public AudioSession(@Nullable Event eventBus, int pid, File executable, String title, @Nullable String icon, float volume, boolean muted) { + this.eventBus = eventBus; this.pid = pid; this.executable = executable; this.icon = icon; @@ -78,7 +165,7 @@ protected AudioSession setVolumeNoTrigger(float volume) { private void triggerChange() { if (eventPublisher != null) { - eventPublisher.publishEvent(new AudioSessionEvent(this, EventType.CHANGED)); + eventBus.fire(new AudioSessionEvent(this, EventType.CHANGED)); } } } diff --git a/src/main/java/com/getpcpanel/cpp/ISndCtrl.java b/src/main/java/com/getpcpanel/cpp/ISndCtrl.java index eb7736c7..fd9393ab 100644 --- a/src/main/java/com/getpcpanel/cpp/ISndCtrl.java +++ b/src/main/java/com/getpcpanel/cpp/ISndCtrl.java @@ -39,4 +39,24 @@ public interface ISndCtrl { record RunningApplication(int pid, File file, String name) { } + + static ISndCtrl noOp() { + return new ISndCtrl() { + @Override public Map getDevicesMap() { return Map.of(); } + @Override public Collection getDevices() { return List.of(); } + @Override public Collection getAllSessions() { return List.of(); } + @Override public AudioDevice getDevice(String id) { return null; } + @Override public void setDeviceVolume(String deviceId, float volume) {} + @Override public void muteDevice(String deviceId, MuteType mute) {} + @Override public void setDefaultDevice(String deviceId) {} + @Override public void setProcessVolume(String fileName, String device, float volume) {} + @Override public void setFocusVolume(float volume) {} + @Override public void muteProcesses(Set fileName, MuteType mute) {} + @Override public String getFocusApplication() { return null; } + @Override public List getRunningApplications() { return List.of(); } + @Override public String defaultDeviceOnEmpty(String deviceId) { return deviceId; } + @Override public String defaultPlayer() { return null; } + @Override public String defaultRecorder() { return null; } + }; + } } diff --git a/src/main/java/com/getpcpanel/cpp/SetNewSessionVolumeService.java b/src/main/java/com/getpcpanel/cpp/SetNewSessionVolumeService.java index 8f5d95d4..e8479b12 100644 --- a/src/main/java/com/getpcpanel/cpp/SetNewSessionVolumeService.java +++ b/src/main/java/com/getpcpanel/cpp/SetNewSessionVolumeService.java @@ -1,9 +1,6 @@ package com.getpcpanel.cpp; import org.apache.commons.lang3.StringUtils; -import org.springframework.context.ApplicationEventPublisher; -import org.springframework.context.event.EventListener; -import org.springframework.stereotype.Service; import com.getpcpanel.commands.AbstractNewXVolumeService; import com.getpcpanel.commands.command.CommandVolumeProcess; @@ -11,25 +8,21 @@ import com.getpcpanel.hid.DeviceHolder; import com.getpcpanel.profile.SaveService; -import lombok.extern.log4j.Log4j2; +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.enterprise.event.Observes; +import jakarta.inject.Inject; +import lombok.extern.jbosslog.JBossLog; /** * Triggers a volume change when a new audio session is started and that session is controlled by the panel. */ -@Log4j2 -@Service +@JBossLog +@ApplicationScoped public class SetNewSessionVolumeService extends AbstractNewXVolumeService { - private final ISndCtrl sndCtrl; - private final SaveService save; + @Inject ISndCtrl sndCtrl; + @Inject SaveService save; - public SetNewSessionVolumeService(DeviceHolder devices, ApplicationEventPublisher eventPublisher, @SuppressWarnings("SpringJavaInjectionPointsAutowiringInspection") ISndCtrl sndCtrl, SaveService save) { - super(devices, eventPublisher); - this.sndCtrl = sndCtrl; - this.save = save; - } - - @EventListener - public void onNewAudioSession(AudioSessionEvent event) { + public void onNewAudioSession(@Observes AudioSessionEvent event) { if (event.eventType() == EventType.ADDED || (save.get().isForceVolume() && event.eventType() == EventType.CHANGED)) { triggerCommandsOf(CommandVolumeProcess.class, s -> s.filterValues(c -> isProcessAndDevice(event, c))); } @@ -37,15 +30,15 @@ public void onNewAudioSession(AudioSessionEvent event) { private boolean isProcessAndDevice(AudioSessionEvent event, CommandVolumeProcess c) { var session = event.session(); + if (session.executable() == null) return false; if (!c.getProcessName().contains(session.executable().getName())) { return false; } - - if (session instanceof WindowsAudioSession wis) { - var device = wis.device(); - return StringUtils.equals("*", c.getDevice()) - || (StringUtils.isBlank(c.getDevice()) && StringUtils.equals(sndCtrl.defaultDeviceOnEmpty(c.getDevice()), device.id())); + var deviceId = c.getDevice(); + if (StringUtils.isBlank(deviceId) || "*".equals(deviceId)) return true; + if (session instanceof WindowsAudioSession was) { + return deviceId.equals(was.device().getId()); } - return true; + return false; } } diff --git a/src/main/java/com/getpcpanel/cpp/linux/LinuxProcessHelper.java b/src/main/java/com/getpcpanel/cpp/linux/LinuxProcessHelper.java index 9cd77f9c..1206e1c7 100644 --- a/src/main/java/com/getpcpanel/cpp/linux/LinuxProcessHelper.java +++ b/src/main/java/com/getpcpanel/cpp/linux/LinuxProcessHelper.java @@ -9,7 +9,8 @@ import org.apache.commons.io.IOUtils; import org.apache.commons.lang3.math.NumberUtils; -import org.springframework.stereotype.Service; +import jakarta.inject.Inject; +import jakarta.enterprise.context.ApplicationScoped; import com.getpcpanel.MainFX; import com.getpcpanel.spring.ConditionalOnLinux; @@ -17,14 +18,14 @@ import lombok.Getter; import lombok.RequiredArgsConstructor; -import lombok.extern.log4j.Log4j2; +import lombok.extern.jbosslog.JBossLog; -@Log4j2 -@Service -@ConditionalOnLinux -@RequiredArgsConstructor +@JBossLog +@ApplicationScoped +@LinuxImpl public class LinuxProcessHelper { - private final ProcessHelper processHelper; + @Inject + ProcessHelper processHelper; public ProcessBuilder builder(String... command) { return processHelper.builder(command); diff --git a/src/main/java/com/getpcpanel/cpp/linux/pulseaudio/ConditionalOnPulseAudio.java b/src/main/java/com/getpcpanel/cpp/linux/pulseaudio/ConditionalOnPulseAudio.java deleted file mode 100644 index 3027aace..00000000 --- a/src/main/java/com/getpcpanel/cpp/linux/pulseaudio/ConditionalOnPulseAudio.java +++ /dev/null @@ -1,41 +0,0 @@ -package com.getpcpanel.cpp.linux.pulseaudio; - -import java.lang.annotation.Documented; -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -import org.apache.commons.lang3.SystemUtils; -import org.springframework.context.annotation.Condition; -import org.springframework.context.annotation.ConditionContext; -import org.springframework.context.annotation.Conditional; -import org.springframework.core.type.AnnotatedTypeMetadata; - -import com.getpcpanel.cpp.linux.ProcessConditionalHelper; - -import lombok.extern.log4j.Log4j2; - -@Documented -@Retention(RetentionPolicy.RUNTIME) -@Target({ ElementType.TYPE, ElementType.METHOD }) -@Conditional(ConditionalOnPulseAudio.OnLinuxCondition.class) -public @interface ConditionalOnPulseAudio { - @Log4j2 - class OnLinuxCondition implements Condition { - private static final String PACTL = "pactl"; - - @Override - public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) { - if (!SystemUtils.IS_OS_LINUX) { - return false; - } - var pactlAvailable = ProcessConditionalHelper.isProcessAvailable(PACTL); - if (!pactlAvailable) { - log.error("Pactl is not available, install it first"); - System.exit(1); - } - return pactlAvailable; - } - } -} diff --git a/src/main/java/com/getpcpanel/cpp/linux/pulseaudio/PulseAudioAudioDevice.java b/src/main/java/com/getpcpanel/cpp/linux/pulseaudio/PulseAudioAudioDevice.java index 6e71d87d..5069280f 100644 --- a/src/main/java/com/getpcpanel/cpp/linux/pulseaudio/PulseAudioAudioDevice.java +++ b/src/main/java/com/getpcpanel/cpp/linux/pulseaudio/PulseAudioAudioDevice.java @@ -1,6 +1,6 @@ package com.getpcpanel.cpp.linux.pulseaudio; -import org.springframework.context.ApplicationEventPublisher; +import jakarta.enterprise.event.Event; import com.getpcpanel.cpp.AudioDevice; import com.getpcpanel.cpp.DataFlow; @@ -13,7 +13,39 @@ public class PulseAudioAudioDevice extends AudioDevice { private final boolean isDefault; private final boolean isOutput; - public PulseAudioAudioDevice(ApplicationEventPublisher eventPublisher, int index, String name, String id, boolean isDefault, boolean isOutput) { + public PulseAudioAudioDevice(Event eventBus, int index, String name, String id, boolean isDefault, boolean isOutput) { + super(eventBus, name, id); + this.index = index; + this.isDefault = isDefault; + this.isOutput = isOutput; + dataflow(DataFlow.dfRender); + } + + public boolean isDefaultOutput() { + return isDefault && isOutput; + } + + @Override + public String toString() { + return super.toString() + " ("+(isOutput ? "out" : "in")+")"; + } +} + + +import jakarta.enterprise.event.Event; + +import com.getpcpanel.cpp.AudioDevice; +import com.getpcpanel.cpp.DataFlow; + +import lombok.Getter; + +@Getter +public class PulseAudioAudioDevice extends AudioDevice { + private final int index; + private final boolean isDefault; + private final boolean isOutput; + + public PulseAudioAudioDevice(Event eventBus, int index, String name, String id, boolean isDefault, boolean isOutput) { super(eventPublisher, name, id); this.index = index; this.isDefault = isDefault; diff --git a/src/main/java/com/getpcpanel/cpp/linux/pulseaudio/PulseAudioAudioSession.java b/src/main/java/com/getpcpanel/cpp/linux/pulseaudio/PulseAudioAudioSession.java index 4eaa624b..4930cfda 100644 --- a/src/main/java/com/getpcpanel/cpp/linux/pulseaudio/PulseAudioAudioSession.java +++ b/src/main/java/com/getpcpanel/cpp/linux/pulseaudio/PulseAudioAudioSession.java @@ -2,7 +2,7 @@ import java.io.File; -import org.springframework.context.ApplicationEventPublisher; +import jakarta.enterprise.event.Event; import com.getpcpanel.cpp.AudioSession; @@ -14,7 +14,33 @@ public class PulseAudioAudioSession extends AudioSession { private final int index; - public PulseAudioAudioSession(ApplicationEventPublisher eventPublisher, int index, int pid, File executable, String title, String icon, float volume, boolean muted) { + public PulseAudioAudioSession(Event eventBus, int index, int pid, File executable, String title, String icon, float volume, boolean muted) { + super(eventBus, pid, executable, title, icon, volume, muted); + this.index = index; + } + + @Override + protected AudioSession setVolumeNoTrigger(float volume) { + return super.setVolumeNoTrigger(volume); + } +} + + +import java.io.File; + +import jakarta.enterprise.event.Event; + +import com.getpcpanel.cpp.AudioSession; + +import lombok.EqualsAndHashCode; +import lombok.Getter; + +@Getter +@EqualsAndHashCode(callSuper = true) +public class PulseAudioAudioSession extends AudioSession { + private final int index; + + public PulseAudioAudioSession(Event eventBus, int index, int pid, File executable, String title, String icon, float volume, boolean muted) { super(eventPublisher, pid, executable, title, icon, volume, muted); this.index = index; } diff --git a/src/main/java/com/getpcpanel/cpp/linux/pulseaudio/PulseAudioEventListener.java b/src/main/java/com/getpcpanel/cpp/linux/pulseaudio/PulseAudioEventListener.java index 2b1b7c71..e3292c60 100644 --- a/src/main/java/com/getpcpanel/cpp/linux/pulseaudio/PulseAudioEventListener.java +++ b/src/main/java/com/getpcpanel/cpp/linux/pulseaudio/PulseAudioEventListener.java @@ -12,23 +12,25 @@ import org.apache.commons.collections4.queue.CircularFifoQueue; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.math.NumberUtils; -import org.springframework.context.ApplicationEventPublisher; -import org.springframework.stereotype.Component; +import jakarta.enterprise.event.Event; +import jakarta.inject.Inject; +import jakarta.enterprise.context.ApplicationScoped; import com.getpcpanel.util.ProcessHelper; import jakarta.annotation.PostConstruct; import jakarta.annotation.PreDestroy; import lombok.RequiredArgsConstructor; -import lombok.extern.log4j.Log4j2; +import lombok.extern.jbosslog.JBossLog; -@Log4j2 -@Component -@ConditionalOnPulseAudio -@RequiredArgsConstructor +@JBossLog +@ApplicationScoped +@PulseAudioImpl public class PulseAudioEventListener extends Thread { - private final ApplicationEventPublisher eventPublisher; - private final ProcessHelper processHelper; + @Inject + Event eventBus; + @Inject + ProcessHelper processHelper; private final CircularFifoQueue latestEvents = new CircularFifoQueue<>(50); private final Pattern numberPattern = Pattern.compile("#(\\d+)"); @@ -76,10 +78,10 @@ private void checkTrigger(String line) { , "Event 'remove' on sink-input" , "Event 'change' on sink-input")) { var m = numberPattern.matcher(line); - eventPublisher.publishEvent(new LinuxSessionChangedEvent(m.find() ? NumberUtils.toInt(m.group(1)) : null)); + eventBus.fire(new LinuxSessionChangedEvent(m.find() ? NumberUtils.toInt(m.group(1)) : null)); } if (StringUtils.containsAnyIgnoreCase(line, "Event 'new' on sink", "Event 'remove' on sink")) { - eventPublisher.publishEvent(new LinuxDeviceChangedEvent()); + eventBus.fire(new LinuxDeviceChangedEvent()); } } diff --git a/src/main/java/com/getpcpanel/cpp/linux/pulseaudio/PulseAudioImpl.java b/src/main/java/com/getpcpanel/cpp/linux/pulseaudio/PulseAudioImpl.java new file mode 100644 index 00000000..5ba5b33f --- /dev/null +++ b/src/main/java/com/getpcpanel/cpp/linux/pulseaudio/PulseAudioImpl.java @@ -0,0 +1,20 @@ +package com.getpcpanel.cpp.linux.pulseaudio; + +import static java.lang.annotation.ElementType.FIELD; +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.ElementType.PARAMETER; +import static java.lang.annotation.ElementType.TYPE; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +import jakarta.inject.Qualifier; + +@Qualifier +@Documented +@Retention(RUNTIME) +@Target({TYPE, METHOD, FIELD, PARAMETER}) +public @interface PulseAudioImpl { +} diff --git a/src/main/java/com/getpcpanel/cpp/linux/pulseaudio/PulseAudioWrapper.java b/src/main/java/com/getpcpanel/cpp/linux/pulseaudio/PulseAudioWrapper.java index f5a4025a..27e14277 100644 --- a/src/main/java/com/getpcpanel/cpp/linux/pulseaudio/PulseAudioWrapper.java +++ b/src/main/java/com/getpcpanel/cpp/linux/pulseaudio/PulseAudioWrapper.java @@ -12,7 +12,8 @@ import org.apache.commons.io.IOUtils; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.math.NumberUtils; -import org.springframework.stereotype.Service; +import jakarta.inject.Inject; +import jakarta.enterprise.context.ApplicationScoped; import com.getpcpanel.cpp.MuteType; import com.getpcpanel.util.ProcessHelper; @@ -20,18 +21,18 @@ import lombok.Builder; import lombok.RequiredArgsConstructor; import lombok.SneakyThrows; -import lombok.extern.log4j.Log4j2; +import lombok.extern.jbosslog.JBossLog; import one.util.streamex.StreamEx; -@Log4j2 -@Service -@ConditionalOnPulseAudio -@RequiredArgsConstructor +@JBossLog +@ApplicationScoped +@PulseAudioImpl public class PulseAudioWrapper { public static final int NO_OP_IDX = -1; public static final int DEFAULT_DEVICE = -2; private static final Pattern pactlFirstLine = Pattern.compile("(.*) #(\\d+)"); - private final ProcessHelper processHelper; + @Inject + ProcessHelper processHelper; public static int volumeFtoI(float volume) { return Math.round(volume * 65536); @@ -165,8 +166,7 @@ List getDebugOutput() { public record PulseAudioTarget(int index, boolean isDefault, Map metas, Map properties, InOutput type) { } - @RequiredArgsConstructor - enum InOutput { + enum InOutput { input("sources"), output("sinks"), session("sink-inputs"); private final String pulseType; diff --git a/src/main/java/com/getpcpanel/cpp/linux/pulseaudio/SndCtrlPulseAudio.java b/src/main/java/com/getpcpanel/cpp/linux/pulseaudio/SndCtrlPulseAudio.java index cc2985d7..74235f49 100644 --- a/src/main/java/com/getpcpanel/cpp/linux/pulseaudio/SndCtrlPulseAudio.java +++ b/src/main/java/com/getpcpanel/cpp/linux/pulseaudio/SndCtrlPulseAudio.java @@ -18,9 +18,10 @@ import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.math.NumberUtils; -import org.springframework.context.ApplicationEventPublisher; -import org.springframework.context.event.EventListener; -import org.springframework.stereotype.Service; +import jakarta.enterprise.event.Event; +import jakarta.inject.Inject; +import jakarta.enterprise.event.Observes; +import jakarta.enterprise.context.ApplicationScoped; import com.getpcpanel.cpp.AudioDevice; import com.getpcpanel.cpp.AudioSession; @@ -33,18 +34,17 @@ import jakarta.annotation.PostConstruct; import lombok.RequiredArgsConstructor; -import lombok.extern.log4j.Log4j2; +import lombok.extern.jbosslog.JBossLog; import one.util.streamex.StreamEx; -@Log4j2 -@Service -@ConditionalOnPulseAudio -@RequiredArgsConstructor +@JBossLog +@ApplicationScoped +@PulseAudioImpl public class SndCtrlPulseAudio implements ISndCtrl { public static final String INPUT_PREFIX = "in_"; - private final PulseAudioWrapper cmd; - private final LinuxProcessHelper processHelper; - private final ApplicationEventPublisher eventPublisher; + @Inject PulseAudioWrapper cmd; + @Inject LinuxProcessHelper processHelper; + @Inject Event eventBus; @GuardedBy("devices") private final Map devices = new HashMap<>(); @GuardedBy("sessions") private final Set sessions = new HashSet<>(); @@ -54,16 +54,14 @@ public void init() { initSessions(null); } - @EventListener(PulseAudioEventListener.LinuxDeviceChangedEvent.class) - public void initDevices() { + public void initDevices() { synchronized (devices) { devices.clear(); StreamEx.of(getDevicesFromCmd()).mapToEntry(AudioDevice::id, Function.identity()).into(devices); } } - @EventListener - public void initSessions(@Nullable LinuxSessionChangedEvent event) { + public void initSessions(@Observes @Nullable LinuxSessionChangedEvent event) { synchronized (sessions) { var prevByIndex = StreamEx.of(sessions).mapToEntry(PulseAudioAudioSession::index).invert().toMap(); sessions.clear(); @@ -78,7 +76,7 @@ public void initSessions(@Nullable LinuxSessionChangedEvent event) { added.map(sess -> new AudioSessionEvent(sess, EventType.ADDED)) .append(removed.map(sess -> new AudioSessionEvent(sess, EventType.REMOVED))) .append(changed.map(sess -> new AudioSessionEvent(sess, EventType.CHANGED))) - .forEach(eventPublisher::publishEvent); + .forEach(e -> eventBus.fire(e)); } } @@ -215,13 +213,13 @@ private Optional toDevice(PulseAudioWrapper.PulseAudioTar if (StringUtils.isBlank(name)) { return Optional.empty(); } - return Optional.of(new PulseAudioAudioDevice(eventPublisher, pa.index(), pa.metas().get("Description"), (isOutput ? "" : INPUT_PREFIX) + pa.metas().get("Name"), pa.isDefault(), isOutput)); + return Optional.of(new PulseAudioAudioDevice(eventBus, pa.index(), pa.metas().get("Description"), (isOutput ? "" : INPUT_PREFIX) + pa.metas().get("Name"), pa.isDefault(), isOutput)); } private Set getSessionsFromCmd() { return StreamEx.of(cmd.getSessions()) .map(pa -> - new PulseAudioAudioSession(eventPublisher, + new PulseAudioAudioSession(eventBus, pa.index(), NumberUtils.toInt(pa.properties().get("application.process.id"), -1), new File(pa.properties().getOrDefault("application.process.binary", "/")), diff --git a/src/main/java/com/getpcpanel/cpp/linux/pulseaudio/SndCtrlPulseAudioDebug.java b/src/main/java/com/getpcpanel/cpp/linux/pulseaudio/SndCtrlPulseAudioDebug.java index f741d897..08633379 100644 --- a/src/main/java/com/getpcpanel/cpp/linux/pulseaudio/SndCtrlPulseAudioDebug.java +++ b/src/main/java/com/getpcpanel/cpp/linux/pulseaudio/SndCtrlPulseAudioDebug.java @@ -3,19 +3,21 @@ import java.awt.Toolkit; import java.awt.datatransfer.StringSelection; -import org.springframework.stereotype.Service; +import jakarta.inject.Inject; +import jakarta.enterprise.context.ApplicationScoped; import lombok.RequiredArgsConstructor; -import lombok.extern.log4j.Log4j2; +import lombok.extern.jbosslog.JBossLog; import one.util.streamex.StreamEx; -@Log4j2 -@Service -@ConditionalOnPulseAudio -@RequiredArgsConstructor +@JBossLog +@ApplicationScoped +@PulseAudioImpl public class SndCtrlPulseAudioDebug { - private final PulseAudioWrapper paWrapper; - private final PulseAudioEventListener paEventListener; + @Inject + PulseAudioWrapper paWrapper; + @Inject + PulseAudioEventListener paEventListener; public void copyDebugOutput() { var output = StreamEx.of(paWrapper.getDebugOutput()) diff --git a/src/main/java/com/getpcpanel/cpp/windows/SndCtrlWindows.java b/src/main/java/com/getpcpanel/cpp/windows/SndCtrlWindows.java index 280fbacf..0a46b3a2 100644 --- a/src/main/java/com/getpcpanel/cpp/windows/SndCtrlWindows.java +++ b/src/main/java/com/getpcpanel/cpp/windows/SndCtrlWindows.java @@ -12,8 +12,9 @@ import javax.annotation.concurrent.GuardedBy; import org.apache.commons.lang3.StringUtils; -import org.springframework.context.ApplicationEventPublisher; -import org.springframework.stereotype.Service; +import jakarta.enterprise.event.Event; +import jakarta.inject.Inject; +import jakarta.enterprise.context.ApplicationScoped; import com.getpcpanel.cpp.AudioDevice; import com.getpcpanel.cpp.AudioDeviceEvent; @@ -28,17 +29,16 @@ import jakarta.annotation.PostConstruct; import lombok.RequiredArgsConstructor; -import lombok.extern.log4j.Log4j2; +import lombok.extern.jbosslog.JBossLog; import one.util.streamex.StreamEx; -@Log4j2 -@Service -@ConditionalOnWindows -@RequiredArgsConstructor +@JBossLog +@ApplicationScoped +@WindowsImpl @SuppressWarnings("unused") // Methods are called from JNI public class SndCtrlWindows implements ISndCtrl { - private final ExtractUtil extractUtil; - private final ApplicationEventPublisher eventPublisher; + @Inject ExtractUtil extractUtil; + @Inject Event eventBus; @GuardedBy("defaults") private final Map defaults = new HashMap<>(); @GuardedBy("devices") private final Map devices = new HashMap<>(); @@ -203,13 +203,13 @@ public String defaultRecorder() { } private AudioDevice deviceAdded(String name, String id, float volume, boolean muted, int dataFlow) { - var result = new WindowsAudioDevice(eventPublisher, name, id).volume(volume).muted(muted).dataflow(DataFlow.from(dataFlow)); + var result = new WindowsAudioDevice(eventBus, name, id).volume(volume).muted(muted).dataflow(DataFlow.from(dataFlow)); synchronized (devices) { devices.put(id, result); } log.trace("Device added: {}", result); - eventPublisher.publishEvent(new AudioDeviceEvent(result, EventType.ADDED)); + eventBus.fire(new AudioDeviceEvent(result, EventType.ADDED)); return result; } @@ -220,7 +220,7 @@ private void deviceRemoved(String id) { removed = devices.remove(id); } if (removed != null) { - eventPublisher.publishEvent(new AudioDeviceEvent(removed, EventType.REMOVED)); + eventBus.fire(new AudioDeviceEvent(removed, EventType.REMOVED)); } } @@ -233,7 +233,7 @@ private void setDefaultDevice(String id, int dataFlow, int role) { private void focusChanged(String to) { log.trace("Focus changed to {}", to); - eventPublisher.publishEvent(new WindowFocusChangedEvent(to)); + eventBus.fire(new WindowFocusChangedEvent(to)); } public Map getDefaults() { diff --git a/src/main/java/com/getpcpanel/cpp/windows/WindowsAudioDevice.java b/src/main/java/com/getpcpanel/cpp/windows/WindowsAudioDevice.java index c9a808c8..c577d2cf 100644 --- a/src/main/java/com/getpcpanel/cpp/windows/WindowsAudioDevice.java +++ b/src/main/java/com/getpcpanel/cpp/windows/WindowsAudioDevice.java @@ -4,7 +4,7 @@ import java.util.HashMap; import java.util.Map; -import org.springframework.context.ApplicationEventPublisher; +import jakarta.enterprise.event.Event; import com.getpcpanel.cpp.AudioDevice; import com.getpcpanel.cpp.AudioSession; @@ -12,14 +12,85 @@ import com.getpcpanel.cpp.DataFlow; import com.getpcpanel.cpp.EventType; -import lombok.extern.log4j.Log4j2; +import lombok.extern.jbosslog.JBossLog; -@Log4j2 +@JBossLog @SuppressWarnings("unused") // Methods called from JNI public class WindowsAudioDevice extends AudioDevice { private final transient Map sessions = new HashMap<>(); // pid -> pointer_addr -> session - public WindowsAudioDevice(ApplicationEventPublisher eventPublisher, String name, String id) { + public WindowsAudioDevice(Event eventBus, String name, String id) { + super(eventBus, name, id); + } + + public Map getSessions() { + return sessions; + } + + public AudioSession addSession(long pointer, int pid, String name, String title, String icon, float volume, boolean muted) { + log.debug("Add device session: {} {} {} {} {} {} {}", pointer, pid, name, title, icon, volume, muted); + var result = sessions.computeIfAbsent(pid, p -> new WindowsAudioSession(this, eventBus, pid, new File(name), title, icon, volume, muted)); + result.pointers().add(pointer); + eventBus.fire(new AudioSessionEvent(result, EventType.ADDED)); + return result; + } + + public void removeSession(long pointer, int pid) { + var session = sessions.get(pid); + if (session == null) { + log.debug("Unknown session was removed: {} ({})", pid, pointer); + return; + } + log.trace("Session pointer removed: {} ({}: {})", pid, pointer, session); + session.pointers().remove(pointer); + if (session.pointers().isEmpty()) { + log.debug("Session removed: {} ({})", pid, pointer); + sessions.remove(pid); + eventBus.fire(new AudioSessionEvent(session, EventType.REMOVED)); + } + } + + @Override + protected WindowsAudioDevice volume(float volume) { + super.volume(volume); + return this; + } + + @Override + protected WindowsAudioDevice muted(boolean muted) { + super.muted(muted); + return this; + } + + @Override + protected WindowsAudioDevice dataflow(DataFlow dataflow) { + super.dataflow(dataflow); + return this; + } +} + + +import java.io.File; +import java.util.HashMap; +import java.util.Map; + +import jakarta.enterprise.event.Event; +import jakarta.inject.Inject; + +import com.getpcpanel.cpp.AudioDevice; +import com.getpcpanel.cpp.AudioSession; +import com.getpcpanel.cpp.AudioSessionEvent; +import com.getpcpanel.cpp.DataFlow; +import com.getpcpanel.cpp.EventType; + +import lombok.extern.jbosslog.JBossLog; + +@JBossLog +@SuppressWarnings("unused") // Methods called from JNI +public class WindowsAudioDevice extends AudioDevice { + private final transient Map sessions = new HashMap<>(); // pid -> pointer_addr -> session + + public WindowsAudioDevice(Event eventBus, String name, String id) { super(eventPublisher, name, id); } @@ -31,7 +102,7 @@ public AudioSession addSession(long pointer, int pid, String name, String title, log.debug("Add device session: {} {} {} {} {} {} {}", pointer, pid, name, title, icon, volume, muted); var result = sessions.computeIfAbsent(pid, p -> new WindowsAudioSession(this, eventPublisher, pid, new File(name), title, icon, volume, muted)); result.pointers().add(pointer); - eventPublisher.publishEvent(new AudioSessionEvent(result, EventType.ADDED)); + eventBus.fire(new AudioSessionEvent(result, EventType.ADDED)); return result; } @@ -46,7 +117,7 @@ public void removeSession(long pointer, int pid) { if (session.pointers().isEmpty()) { log.debug("Session removed: {} ({})", pid, pointer); sessions.remove(pid); - eventPublisher.publishEvent(new AudioSessionEvent(session, EventType.REMOVED)); + eventBus.fire(new AudioSessionEvent(session, EventType.REMOVED)); } } diff --git a/src/main/java/com/getpcpanel/cpp/windows/WindowsAudioSession.java b/src/main/java/com/getpcpanel/cpp/windows/WindowsAudioSession.java index 9cd0d5d9..95fcf27c 100644 --- a/src/main/java/com/getpcpanel/cpp/windows/WindowsAudioSession.java +++ b/src/main/java/com/getpcpanel/cpp/windows/WindowsAudioSession.java @@ -4,7 +4,7 @@ import java.util.HashSet; import java.util.Set; -import org.springframework.context.ApplicationEventPublisher; +import jakarta.enterprise.event.Event; import com.getpcpanel.cpp.AudioDevice; import com.getpcpanel.cpp.AudioSession; @@ -19,7 +19,34 @@ public class WindowsAudioSession extends AudioSession { @ToString.Exclude private final AudioDevice device; private final Set pointers = new HashSet<>(); - public WindowsAudioSession(AudioDevice device, ApplicationEventPublisher eventPublisher, int pid, File executable, String title, String icon, + public WindowsAudioSession(AudioDevice device, Event eventBus, int pid, File executable, String title, String icon, + float volume, boolean muted) { + super(eventBus, pid, executable, title, icon, volume, muted); + this.device = device; + } +} + + +import java.io.File; +import java.util.HashSet; +import java.util.Set; + +import jakarta.enterprise.event.Event; + +import com.getpcpanel.cpp.AudioDevice; +import com.getpcpanel.cpp.AudioSession; + +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.ToString; + +@Getter +@EqualsAndHashCode(callSuper = true) +public class WindowsAudioSession extends AudioSession { + @ToString.Exclude private final AudioDevice device; + private final Set pointers = new HashSet<>(); + + public WindowsAudioSession(AudioDevice device, Event eventBus, int pid, File executable, String title, String icon, float volume, boolean muted) { super(eventPublisher, pid, executable, title, icon, volume, muted); this.device = device; diff --git a/src/main/java/com/getpcpanel/device/Device.java b/src/main/java/com/getpcpanel/device/Device.java index 68e3d940..58fbe935 100644 --- a/src/main/java/com/getpcpanel/device/Device.java +++ b/src/main/java/com/getpcpanel/device/Device.java @@ -1,268 +1,38 @@ package com.getpcpanel.device; -import javax.annotation.Nullable; - import org.apache.commons.lang3.StringUtils; -import org.springframework.context.ApplicationEventPublisher; import com.getpcpanel.commands.IconService; -import com.getpcpanel.commands.command.CommandVolumeFocus; import com.getpcpanel.hid.OutputInterpreter; import com.getpcpanel.profile.DeviceSave; import com.getpcpanel.profile.LightingConfig; import com.getpcpanel.profile.Profile; import com.getpcpanel.profile.SaveService; -import com.getpcpanel.ui.FxHelper; -import com.getpcpanel.ui.LightingChangedToDefaultEvent; -import com.getpcpanel.ui.LimitedTextField; -import com.getpcpanel.util.Images; -import javafx.application.Platform; -import javafx.collections.FXCollections; -import javafx.geometry.Pos; -import javafx.scene.Node; -import javafx.scene.control.Button; -import javafx.scene.control.ComboBox; -import javafx.scene.control.ContentDisplay; -import javafx.scene.control.ContextMenu; -import javafx.scene.control.ListCell; -import javafx.scene.control.MenuItem; -import javafx.scene.image.Image; -import javafx.scene.image.ImageView; -import javafx.scene.input.KeyCode; -import javafx.scene.input.KeyEvent; -import javafx.scene.input.MouseButton; -import javafx.scene.input.MouseEvent; -import javafx.scene.layout.HBox; -import javafx.scene.layout.Pane; -import javafx.scene.shape.SVGPath; -import javafx.stage.Stage; -import lombok.AccessLevel; import lombok.Getter; -import lombok.extern.log4j.Log4j2; -import one.util.streamex.StreamEx; +import lombok.extern.jbosslog.JBossLog; -@Log4j2 +@JBossLog public abstract class Device { - @Getter(AccessLevel.PROTECTED) private final FxHelper fxHelper; private final SaveService saveService; private final OutputInterpreter outputInterpreter; private final IconService iconService; - private final ApplicationEventPublisher eventPublisher; - @Getter private HBox profileMenu; - private ComboBox profiles; @Getter protected String serialNumber; protected DeviceSave save; private LightingConfig lightingConfig; - protected Device(FxHelper fxHelper, SaveService saveService, OutputInterpreter outputInterpreter, IconService iconService, ApplicationEventPublisher eventPublisher, String serialNum, - DeviceSave deviceSave) { - this.fxHelper = fxHelper; + protected Device(SaveService saveService, OutputInterpreter outputInterpreter, IconService iconService, String serialNum, DeviceSave deviceSave) { this.saveService = saveService; this.outputInterpreter = outputInterpreter; this.iconService = iconService; - this.eventPublisher = eventPublisher; serialNumber = serialNum; save = deviceSave; - initProfileMenu(); } protected void postInit() { - updateAllImages(); - } - - private void initProfileMenu() { - profileMenu = new HBox(); - profileMenu.setAlignment(Pos.CENTER_RIGHT); - profiles = new ComboBox<>(FXCollections.observableArrayList(save.getProfiles())); - profiles.setPrefWidth(400.0D); - profiles.getSelectionModel().select(currentProfile()); - var textfield = new LimitedTextField(10); - profiles.getSelectionModel().selectedItemProperty().addListener((observable, oldValue, newValue) -> { - if (newValue == null) - return; - log.debug("change"); - var name = newValue.getName(); - updateCurrentProfileName(name); - }); - var buttonCell = new ListCell() { - @Override - protected void updateItem(Profile item, boolean btl) { - super.updateItem(item, btl); - setGraphic(null); - if (item != null) - setText(item.getName()); - } - }; - textfield.setOnAction(c -> { - var p = buttonCell.getItem(); - var oldName = p.getName(); - var newName = textfield.getText(); - buttonCell.setGraphic(null); - if (save.getProfile(newName).isPresent()) { - buttonCell.setText(oldName); - return; - } - p.setName(newName); - buttonCell.setText(newName); - profiles.getItems().set(profiles.getItems().indexOf(p), p); - saveService.save(); - }); - textfield.focusedProperty().addListener((arg, oldVal, newVal) -> { - if (!newVal) { - buttonCell.setGraphic(null); - buttonCell.setText(buttonCell.getItem().getName()); - } - }); - textfield.addEventFilter(KeyEvent.KEY_PRESSED, event -> { - if (event.getCode() == KeyCode.ESCAPE) { - buttonCell.setGraphic(null); - buttonCell.setText(buttonCell.getItem().getName()); - } - }); - profiles.setButtonCell(buttonCell); - profiles.addEventFilter(MouseEvent.MOUSE_RELEASED, event -> { - if (event.getButton() == MouseButton.SECONDARY) { - event.consume(); - var rename = new MenuItem("Rename"); - var delete = new MenuItem("Delete"); - rename.setOnAction(c -> { - buttonCell.setGraphic(textfield); - textfield.requestFocus(); - textfield.setText(buttonCell.getText()); - textfield.selectAll(); - buttonCell.setText(""); - }); - delete.setOnAction(c -> { - save.getProfiles().remove(buttonCell.getItem()); - profiles.getItems().remove(buttonCell.getItem()); - if (profiles.getValue() == null) - profiles.getSelectionModel().select(0); - }); - if (profiles.getItems().size() <= 1) - delete.setDisable(true); - var cm = new ContextMenu(rename, delete); - profiles.setContextMenu(cm); - } else if (event.getButton() == MouseButton.PRIMARY) { - buttonCell.getGraphic(); - } - }); - - profileMenu.getChildren().add(buildAddButton()); - profileMenu.getChildren().add(buildSettingsButton()); - profileMenu.getChildren().addAll(profiles); - } - - private void updateCurrentProfileName(String name) { - var profile = save.setCurrentProfile(name); - if (profile.isEmpty()) - return; - setLighting(profile.get().getLightingConfig(), true); - saveService.save(); - eventPublisher.publishEvent(LightingChangedToDefaultEvent.INSTANCE); } public void focusChanged(String from, String to) { - if (!StringUtils.equals(from, to) && switchForApplication(to)) - return; - - switchAwayFromApplication(from); - } - - private boolean switchForApplication(String to) { - var result = new boolean[] { false }; - save.getProfiles() - .stream() - .filter(p -> StreamEx.of(p.getActivateApplications()).anyMatch(i -> StringUtils.equalsIgnoreCase(i, to))) - .findFirst() - .ifPresent(p -> { - Platform.runLater(() -> profiles.getSelectionModel().select(p)); - result[0] = true; - }); - return result[0]; - } - - private void switchAwayFromApplication(String from) { - var mainProfile = StreamEx.of(save.getProfiles()).findFirst(Profile::isMainProfile); - var item = profiles.getSelectionModel().getSelectedItem(); - if (item == null || !item.isFocusBackOnLost() || mainProfile.isEmpty()) { - return; - } - if (StreamEx.of(item.getActivateApplications()).anyMatch(a -> StringUtils.equalsIgnoreCase(a, from))) { - Platform.runLater(() -> profiles.getSelectionModel().select(mainProfile.get())); - } - } - - private Button buildAddButton() { - var addButton = new Button(); - var svgCode = "M28,14H18V4c0-1.104-0.896-2-2-2s-2,0.896-2,2v10H4c-1.104,0-2,0.896-2,2s0.896,2,2,2h10v10c0,1.104,0.896,2,2,2 s2-0.896,2-2V18h10c1.104,0,2-0.896,2-2S29.104,14,28,14z"; - var path = new SVGPath(); - path.setStyle("-fx-fill:white;"); - path.setContent(svgCode); - path.setScaleX(0.7D); - path.setScaleY(0.7D); - addButton.setGraphic(path); - addButton.setContentDisplay(ContentDisplay.GRAPHIC_ONLY); - addButton.setOnAction(c -> { - String newName; - //noinspection ForLoopWithMissingComponent - for (var i = 1; ; i++) { - newName = "profile " + i; - if (save.getProfile(newName).isEmpty()) - break; - } - var newProfile = new Profile(newName, getDeviceType()); - save.getProfiles().add(newProfile); - profiles.getItems().add(newProfile); - profiles.getSelectionModel().select(newProfile); - }); - addButton.setPrefSize(44.0D, 44.0D); - return addButton; - } - - private Button buildSettingsButton() { - var settingSvg = "M24.38,10.175l-2.231-0.268c-0.228-0.851-0.562-1.655-0.992-2.401l1.387-1.763c0.212-0.271,0.188-0.69-0.057-0.934" + - "l-2.299-2.3c-0.242-0.243-0.662-0.269-0.934-0.057l-1.766,1.389c-0.743-0.43-1.547-0.764-2.396-0.99L14.825,0.62" + - "C14.784,0.279,14.469,0,14.125,0h-3.252c-0.344,0-0.659,0.279-0.699,0.62L9.906,2.851c-0.85,0.227-1.655,0.562-2.398,0.991" + - "L5.743,2.455c-0.27-0.212-0.69-0.187-0.933,0.056L2.51,4.812C2.268,5.054,2.243,5.474,2.456,5.746L3.842,7.51" + - "c-0.43,0.744-0.764,1.549-0.991,2.4l-2.23,0.267C0.28,10.217,0,10.532,0,10.877v3.252c0,0.344,0.279,0.657,0.621,0.699l2.231,0.268" + - "c0.228,0.848,0.561,1.652,0.991,2.396l-1.386,1.766c-0.211,0.271-0.187,0.69,0.057,0.934l2.296,2.301" + - "c0.243,0.242,0.663,0.269,0.933,0.057l1.766-1.39c0.744,0.43,1.548,0.765,2.398,0.991l0.268,2.23" + - "c0.041,0.342,0.355,0.62,0.699,0.62h3.252c0.345,0,0.659-0.278,0.699-0.62l0.268-2.23c0.851-0.228,1.655-0.562,2.398-0.991" + - "l1.766,1.387c0.271,0.212,0.69,0.187,0.933-0.056l2.299-2.301c0.244-0.242,0.269-0.662,0.056-0.935l-1.388-1.764" + - "c0.431-0.744,0.764-1.548,0.992-2.397l2.23-0.268C24.721,14.785,25,14.473,25,14.127v-3.252" + - "C25.001,10.529,24.723,10.216,24.38,10.175z M12.501,18.75c-3.452,0-6.25-2.798-6.25-6.25s2.798-6.25,6.25-6.25" + - "s6.25,2.798,6.25,6.25S15.954,18.75,12.501,18.75z"; - var setPath = new SVGPath(); - setPath.setStyle("-fx-fill:white;"); - setPath.setContent(settingSvg); - setPath.setScaleX(.9); - setPath.setScaleY(.9); - - var settingsButton = new Button(); - settingsButton.setGraphic(setPath); - settingsButton.setContentDisplay(ContentDisplay.GRAPHIC_ONLY); - settingsButton.setOnAction(c -> { - try { - var stage = new Stage(); - var selection = profiles.getSelectionModel().getSelectedItem(); - fxHelper.buildProfileSettingsDialog(save, selection).start(stage); - stage.setOnHidden(e -> Platform.runLater(() -> { - profiles.getButtonCell().setText(selection.getName()); - if (profiles.getSelectionModel().getSelectedItem().equals(selection)) { - updateCurrentProfileName(selection.getName()); - } - })); - } catch (Exception e) { - log.error("Unable to load profile settings dialog", e); - } - }); - settingsButton.setPrefSize(44.0D, 44.0D); - return settingsButton; - } - - public void setProfile(@Nullable String name) { - save.getProfile(name).ifPresent(profiles::setValue); } public String getDisplayName() { @@ -273,9 +43,6 @@ public void setDisplayName(String name) { save.setDisplayName(name); } - /** - * This will have muted colors for mute-override-controls - */ public LightingConfig getLightingConfig() { if (lightingConfig == null) { lightingConfig = currentProfile().getLightingConfig(); @@ -283,9 +50,6 @@ public LightingConfig getLightingConfig() { return lightingConfig; } - /** - * Will have the original colors, even when mute-override is active. - */ public LightingConfig getSavedLightingConfig() { return currentProfile().getLightingConfig(); } @@ -301,86 +65,24 @@ public void setSavedLighting(LightingConfig config) { private void doSetLighting(LightingConfig config, boolean priority) { lightingConfig = config; - if (config == null) { config = LightingConfig.defaultLightingConfig(getDeviceType()); saveService.save(); } try { - var finalConfig = config; - Platform.runLater(() -> showLightingConfigToUI(finalConfig)); - //noinspection NestedTryStatement - try { - outputInterpreter.sendLightingConfig(getSerialNumber(), getDeviceType(), config, priority); - } catch (Exception e) { - log.error("Unable to send lighting config", e); - } + outputInterpreter.sendLightingConfig(getSerialNumber(), getDeviceType(), config, priority); } catch (Exception e) { - log.error("Unable to set lighting", e); + log.error("Unable to send lighting config", e); setLighting(LightingConfig.defaultLightingConfig(getDeviceType()), priority); } } - protected SVGPath getLightingImage() { - return Images.light(); - } - public void focusApplicationChanged() { - var images = getKnobImages(); - for (var i = 0; i < images.length; i++) { - var idx = i; - var dialData = currentProfile().getDialData(i); - if (dialData != null) { - dialData.getCommand(CommandVolumeFocus.class).ifPresent(c -> determineAndSetImage(idx)); - } - } } public void saveChanged() { - updateAllImages(); } - protected void updateAllImages() { - for (var i = 0; i < getKnobImages().length; i++) { - determineAndSetImage(i); - } - } - - private void determineAndSetImage(int dial) { - var images = getKnobImages(); - if (!isShowIcons()) { - images[dial].setImage(null); - return; - } - - var cmd = currentProfile().getDialData(dial); - var settings = currentProfile().getKnobSettings(dial); - - var image = iconService.getImageFrom(cmd, settings); - images[dial].setImage(iconService.isDefault(image) ? null : image); - } - - protected ImageView buildKnobImageView() { - var result = new ImageView(); - result.setOpacity(.4); - result.setMouseTransparent(true); - return result; - } - - private boolean isShowIcons() { - return saveService.get().isMainUIIcons(); - } - - protected abstract ImageView[] getKnobImages(); - - public abstract Pane getDevicePane(); - - public abstract Node getLabel(); - - public abstract Button getLightingButton(); - - public abstract Image getPreviewImage(); - public abstract DeviceType getDeviceType(); public abstract void setKnobRotation(int paramInt1, int paramInt2); diff --git a/src/main/java/com/getpcpanel/device/DeviceFactory.java b/src/main/java/com/getpcpanel/device/DeviceFactory.java index a28097e7..36cecc1e 100644 --- a/src/main/java/com/getpcpanel/device/DeviceFactory.java +++ b/src/main/java/com/getpcpanel/device/DeviceFactory.java @@ -1,8 +1,42 @@ package com.getpcpanel.device; -import org.springframework.context.ApplicationEventPublisher; -import org.springframework.context.annotation.Configuration; -import org.springframework.stereotype.Service; +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.inject.Inject; + +import com.getpcpanel.commands.IconService; +import com.getpcpanel.hid.InputInterpreter; +import com.getpcpanel.hid.OutputInterpreter; +import com.getpcpanel.profile.DeviceSave; +import com.getpcpanel.profile.SaveService; +import com.getpcpanel.util.coloroverride.OverrideColorService; + +import lombok.RequiredArgsConstructor; + +@ApplicationScoped +public class DeviceFactory { + @Inject InputInterpreter inputInterpreter; + @Inject SaveService saveService; + @Inject OutputInterpreter outputInterpreter; + @Inject IconService iconService; + @Inject OverrideColorService overrideColorService; + + public Device buildRgb(String serialNum, DeviceSave deviceSave) { + return new PCPanelRGBDevice(inputInterpreter, saveService, outputInterpreter, iconService, overrideColorService, deviceSave, serialNum); + } + + public Device buildMini(String serialNum, DeviceSave deviceSave) { + return new PCPanelMiniDevice(inputInterpreter, saveService, outputInterpreter, iconService, overrideColorService, serialNum, deviceSave); + } + + public Device buildPro(String serialNum, DeviceSave deviceSave) { + return new PCPanelProDevice(inputInterpreter, saveService, outputInterpreter, iconService, overrideColorService, serialNum, deviceSave); + } +} + + +import jakarta.enterprise.event.Event; +import jakarta.inject.Inject; +import jakarta.enterprise.context.ApplicationScoped; import com.getpcpanel.commands.IconService; import com.getpcpanel.hid.InputInterpreter; @@ -14,17 +48,23 @@ import lombok.RequiredArgsConstructor; -@Service -@Configuration -@RequiredArgsConstructor +@ApplicationScoped + public class DeviceFactory { - private final FxHelper fxHelper; - private final InputInterpreter inputInterpreter; - private final SaveService saveService; - private final OutputInterpreter outputInterpreter; - private final IconService iconService; - private final ApplicationEventPublisher eventPublisher; - private final OverrideColorService overrideColorService; + @Inject + FxHelper fxHelper; + @Inject + InputInterpreter inputInterpreter; + @Inject + SaveService saveService; + @Inject + OutputInterpreter outputInterpreter; + @Inject + IconService iconService; + @Inject + Event eventBus; + @Inject + OverrideColorService overrideColorService; public PCPanelRGBUI buildRgb(String serialNum, DeviceSave deviceSave) { return new PCPanelRGBUI(fxHelper, inputInterpreter, saveService, outputInterpreter, iconService, eventPublisher, overrideColorService, deviceSave, serialNum); diff --git a/src/main/java/com/getpcpanel/device/PCPanelMiniDevice.java b/src/main/java/com/getpcpanel/device/PCPanelMiniDevice.java new file mode 100644 index 00000000..da62a519 --- /dev/null +++ b/src/main/java/com/getpcpanel/device/PCPanelMiniDevice.java @@ -0,0 +1,25 @@ +package com.getpcpanel.device; + +import com.getpcpanel.commands.IconService; +import com.getpcpanel.hid.InputInterpreter; +import com.getpcpanel.hid.OutputInterpreter; +import com.getpcpanel.profile.DeviceSave; +import com.getpcpanel.profile.LightingConfig; +import com.getpcpanel.profile.SaveService; +import com.getpcpanel.util.coloroverride.OverrideColorService; + +public class PCPanelMiniDevice extends Device { + private final int[] knobRotations = new int[DeviceType.PCPANEL_MINI.getAnalogCount()]; + + public PCPanelMiniDevice(InputInterpreter inputInterpreter, SaveService saveService, OutputInterpreter outputInterpreter, + IconService iconService, OverrideColorService overrideColorService, String serialNum, DeviceSave deviceSave) { + super(saveService, outputInterpreter, iconService, serialNum, deviceSave); + } + + @Override public DeviceType getDeviceType() { return DeviceType.PCPANEL_MINI; } + @Override public void setKnobRotation(int knob, int rotation) { knobRotations[knob] = rotation; } + @Override public int getKnobRotation(int knob) { return knobRotations[knob]; } + @Override public void setButtonPressed(int button, boolean pressed) {} + @Override public void closeDialogs() {} + @Override public void showLightingConfigToUI(LightingConfig config) {} +} diff --git a/src/main/java/com/getpcpanel/device/PCPanelMiniUI.java b/src/main/java/com/getpcpanel/device/PCPanelMiniUI.java deleted file mode 100644 index 73e06ea8..00000000 --- a/src/main/java/com/getpcpanel/device/PCPanelMiniUI.java +++ /dev/null @@ -1,274 +0,0 @@ -package com.getpcpanel.device; - -import java.io.IOException; -import java.util.Objects; - -import org.springframework.context.ApplicationEventPublisher; - -import com.getpcpanel.commands.IconService; -import com.getpcpanel.hid.DeviceCommunicationHandler; -import com.getpcpanel.hid.InputInterpreter; -import com.getpcpanel.hid.OutputInterpreter; -import com.getpcpanel.profile.DeviceSave; -import com.getpcpanel.profile.LightingConfig; -import com.getpcpanel.profile.LightingConfig.LightingMode; -import com.getpcpanel.profile.SaveService; -import com.getpcpanel.profile.SingleKnobLightingConfig; -import com.getpcpanel.profile.SingleKnobLightingConfig.SINGLE_KNOB_MODE; -import com.getpcpanel.ui.FxHelper; -import com.getpcpanel.ui.HomePage; -import com.getpcpanel.util.Util; -import com.getpcpanel.util.coloroverride.OverrideColorService; - -import javafx.application.Platform; -import javafx.fxml.FXML; -import javafx.scene.Node; -import javafx.scene.control.Button; -import javafx.scene.control.ContentDisplay; -import javafx.scene.control.Label; -import javafx.scene.image.Image; -import javafx.scene.image.ImageView; -import javafx.scene.input.MouseButton; -import javafx.scene.layout.Pane; -import javafx.scene.layout.Region; -import javafx.scene.paint.Color; -import javafx.scene.paint.Paint; -import javafx.scene.shape.Shape; -import javafx.scene.text.Font; -import javafx.stage.Stage; -import lombok.extern.log4j.Log4j2; - -@Log4j2 -public class PCPanelMiniUI extends Device { - private final InputInterpreter inputInterpreter; - private final OverrideColorService overrideColorService; - - public static final int KNOB_COUNT = 4; - private static final double MAX_ANALOG_VALUE = 100; - @FXML private Pane lightPanes; - @FXML private Pane panelPane; - private Label label; - private Button lightingButton; - private final Button[] knobs = new Button[KNOB_COUNT]; - private final int[] analogValue = new int[KNOB_COUNT]; - private final ImageView[] images = new ImageView[KNOB_COUNT]; - private static final Image previewImage = new Image(Objects.requireNonNull(PCPanelMiniUI.class.getResource("/assets/PCPanelMini/preview.png")).toExternalForm()); - private Stage childDialogStage; - - public PCPanelMiniUI(FxHelper fxHelper, InputInterpreter inputInterpreter, SaveService saveService, OutputInterpreter outputInterpreter, IconService iconService, ApplicationEventPublisher eventPublisher, OverrideColorService overrideColorService, - String serialNum, DeviceSave deviceSave) { - super(fxHelper, saveService, outputInterpreter, iconService, eventPublisher, serialNum, deviceSave); - this.inputInterpreter = inputInterpreter; - this.overrideColorService = overrideColorService; - var loader = getFxHelper().getLoader(getClass().getResource("/assets/PCPanelMini/PCPanelMini.fxml")); - loader.setController(this); - try { - Pane pane = loader.load(); - initButtons(); - initLabel(); - initLightingButton(); - pane.getStylesheets().addAll(Objects.requireNonNull(getClass().getResource("/assets/PCPanelMini/PCPanelMini.css")).toExternalForm()); - } catch (IOException e) { - log.error("Unable to initialize ui", e); - } - postInit(); - } - - @Override - public Node getLabel() { - return label; - } - - @Override - public Pane getDevicePane() { - return panelPane; - } - - private void rotateKnob(int knob, int val) { - if (knob >= analogValue.length) { - log.error("Getting knob {} value ({}), but the amount of knobs is less: {}", knob, val, analogValue.length); - return; - } - analogValue[knob] = val; - if (getLightingConfig().getLightingMode() == LightingMode.CUSTOM) - showLightingConfigToUI(getLightingConfig()); - ((Region) knobs[knob].getGraphic()).getChildrenUnmodifiable().get(3).setRotate(Util.analogValueToRotation(val)); - } - - @Override - public int getKnobRotation(int knob) { - return analogValue[knob]; - } - - private void initLabel() { - label = new Label("PCPANEL MINI"); - var f = Font.loadFont(getClass().getResourceAsStream("/assets/apex-mk2.regular.otf"), 50.0D); - label.setFont(f); - label.setUnderline(true); - label.setTextFill(Paint.valueOf("white")); - } - - private void initLightingButton() { - lightingButton = new Button("Lighting", getLightingImage()); - lightingButton.setStyle("-fx-background-color: transparent;"); - lightingButton.setContentDisplay(ContentDisplay.TOP); - lightingButton.setMinHeight(100.0D); - lightingButton.setOnAction(e -> { - childDialogStage = new Stage(); - getFxHelper().buildMiniLightingDialog(this).start(childDialogStage); - }); - } - - private void initButtons() throws IOException { - var xPos = 56.3D; - var yPos = 133.4D; - var xDelta = 115.0D; - var buttonSize = 80; - for (var i = 0; i < KNOB_COUNT; i++) { - var loader = getFxHelper().getLoader(getClass().getResource("/assets/PCPanelMini/knob.fxml")); - Node nx = loader.load(); - images[i] = buildKnobImageView(); - knobs[i] = new Button("", nx); - knobs[i].setId("dial_button"); - knobs[i].setContentDisplay(ContentDisplay.CENTER); - knobs[i].setMinSize(buttonSize, buttonSize); - knobs[i].setMaxSize(buttonSize, buttonSize); - knobs[i].setLayoutX(xPos); - knobs[i].setLayoutY(yPos); - knobs[i].setScaleX(1.2D); - knobs[i].setScaleY(1.2D); - - images[i].setLayoutX(xPos + 5); - images[i].setLayoutY(yPos + 5); - images[i].setFitWidth(70); - images[i].setFitHeight(70); - - var knob = i; - knobs[i].setOnAction(e -> { - HomePage.showHint(false); - var bm = getFxHelper().buildBasicMacro(this, knob); - try { - childDialogStage = new Stage(); - bm.start(childDialogStage); - } catch (Exception ex) { - log.error("Unable to init button", ex); - } - }); - var idx = i; - knobs[i].setOnMouseClicked(c -> { - if (c.getButton() == MouseButton.MIDDLE) { - try { - inputInterpreter.onButtonPress(new DeviceCommunicationHandler.ButtonPressEvent(getSerialNumber(), knob, true)); - } catch (IOException e1) { - log.error("Unable to handle button press", e1); - } - try { - inputInterpreter.onButtonPress(new DeviceCommunicationHandler.ButtonPressEvent(getSerialNumber(), knob, false)); - } catch (IOException e1) { - log.error("Unable to handle button up", e1); - } - } else if (c.getButton() == MouseButton.SECONDARY) { - getFxHelper().buildMiniLightingDialog(this).select(idx).start(new Stage()); - } - }); - panelPane.getChildren().add(knobs[i]); - panelPane.getChildren().add(images[i]); - xPos += xDelta; - } - } - - public String toString() { - return getDisplayName(); - } - - @Override - public Image getPreviewImage() { - return previewImage; - } - - private int getKnobCount() { - return KNOB_COUNT; - } - - @Override - public void setKnobRotation(int knob, int value) { - Platform.runLater(() -> rotateKnob(knob, value)); - } - - @Override - public void setButtonPressed(int knob, boolean pressed) { - Platform.runLater(() -> knobs[knob].setOpacity(pressed ? 0.5D : 1.0D)); - } - - private void setKnobUIColorHex(int knob, String color) { - var lightPane = (Shape) lightPanes.getChildren().get(knob); - lightPane.setFill(Paint.valueOf(color)); - } - - private void setKnobUIColor(int knob, Paint color) { - var lightPane = (Shape) lightPanes.getChildren().get(knob); - lightPane.setFill(color); - } - - private void setAllKnobUIColor(Paint color) { - for (var i = 0; i < getKnobCount(); i++) { - setKnobUIColor(i, color); - } - } - - @Override - public void closeDialogs() { - if (childDialogStage != null && childDialogStage.isShowing()) - childDialogStage.close(); - } - - @Override - public Button getLightingButton() { - return lightingButton; - } - - @Override - public DeviceType getDeviceType() { - return DeviceType.PCPANEL_MINI; - } - - @Override - public void showLightingConfigToUI(LightingConfig config) { - var mode = config.getLightingMode(); - if (mode == LightingMode.ALL_COLOR) { - setAllKnobUIColor(Color.valueOf(config.getAllColor())); - } else if (mode == LightingMode.SINGLE_COLOR) { - for (var i = 0; i < getKnobCount(); i++) { - var color = overrideColorService.getDialOverride(serialNumber, i).map(SingleKnobLightingConfig::getColor1).orElse(config.getIndividualColors()[i]); - setKnobUIColorHex(i, color); - } - } else if (mode == LightingMode.ALL_RAINBOW) { - for (var i = 0; i < getKnobCount(); i++) - setKnobUIColor(i, - Color.hsb((360 * (getKnobCount() - i - 1) * (0xFF & config.getRainbowPhaseShift())) / 255.0D * getKnobCount(), 1.0D, (0xFF & config.getRainbowBrightness()) / 255.0D)); - } else if (mode == LightingMode.ALL_WAVE) { - for (var i = 0; i < getKnobCount(); i++) - setKnobUIColor(i, Color.hsb(360.0D * (0xFF & config.getWaveHue()) / 255.0D, 1.0D, (0xFF & config.getWaveBrightness()) / 255.0D)); - } else if (mode == LightingMode.ALL_BREATH) { - for (var i = 0; i < getKnobCount(); i++) - setKnobUIColor(i, Color.hsb(360.0D * (0xFF & config.getBreathHue()) / 255.0D, 1.0D, (0xFF & config.getBreathBrightness()) / 255.0D)); - } else { - var knobConfigs = config.getKnobConfigs(); - for (var i = 0; i < KNOB_COUNT; i++) { - var knobConfig = overrideColorService.getDialOverride(serialNumber, i).orElse(knobConfigs[i]); - if (knobConfig.getMode() == SINGLE_KNOB_MODE.STATIC) { - setKnobUIColorHex(i, knobConfig.getColor1()); - } else if (knobConfig.getMode() == SINGLE_KNOB_MODE.VOLUME_GRADIENT) { - var c1 = Color.web(knobConfig.getColor1()); - var c2 = Color.web(knobConfig.getColor2()); - setKnobUIColor(i, c1.interpolate(c2, analogValue[i] / MAX_ANALOG_VALUE)); - } - } - } - } - - @Override - protected ImageView[] getKnobImages() { - return images; - } -} diff --git a/src/main/java/com/getpcpanel/device/PCPanelProDevice.java b/src/main/java/com/getpcpanel/device/PCPanelProDevice.java new file mode 100644 index 00000000..810062f2 --- /dev/null +++ b/src/main/java/com/getpcpanel/device/PCPanelProDevice.java @@ -0,0 +1,25 @@ +package com.getpcpanel.device; + +import com.getpcpanel.commands.IconService; +import com.getpcpanel.hid.InputInterpreter; +import com.getpcpanel.hid.OutputInterpreter; +import com.getpcpanel.profile.DeviceSave; +import com.getpcpanel.profile.LightingConfig; +import com.getpcpanel.profile.SaveService; +import com.getpcpanel.util.coloroverride.OverrideColorService; + +public class PCPanelProDevice extends Device { + private final int[] knobRotations = new int[DeviceType.PCPANEL_PRO.getAnalogCount()]; + + public PCPanelProDevice(InputInterpreter inputInterpreter, SaveService saveService, OutputInterpreter outputInterpreter, + IconService iconService, OverrideColorService overrideColorService, String serialNum, DeviceSave deviceSave) { + super(saveService, outputInterpreter, iconService, serialNum, deviceSave); + } + + @Override public DeviceType getDeviceType() { return DeviceType.PCPANEL_PRO; } + @Override public void setKnobRotation(int knob, int rotation) { knobRotations[knob] = rotation; } + @Override public int getKnobRotation(int knob) { return knobRotations[knob]; } + @Override public void setButtonPressed(int button, boolean pressed) {} + @Override public void closeDialogs() {} + @Override public void showLightingConfigToUI(LightingConfig config) {} +} diff --git a/src/main/java/com/getpcpanel/device/PCPanelProUI.java b/src/main/java/com/getpcpanel/device/PCPanelProUI.java deleted file mode 100644 index c4e520a2..00000000 --- a/src/main/java/com/getpcpanel/device/PCPanelProUI.java +++ /dev/null @@ -1,387 +0,0 @@ -package com.getpcpanel.device; - -import java.io.IOException; -import java.util.Objects; - -import org.springframework.context.ApplicationEventPublisher; - -import com.getpcpanel.commands.IconService; -import com.getpcpanel.hid.DeviceCommunicationHandler; -import com.getpcpanel.hid.InputInterpreter; -import com.getpcpanel.hid.OutputInterpreter; -import com.getpcpanel.profile.DeviceSave; -import com.getpcpanel.profile.LightingConfig; -import com.getpcpanel.profile.LightingConfig.LightingMode; -import com.getpcpanel.profile.SaveService; -import com.getpcpanel.profile.SingleKnobLightingConfig.SINGLE_KNOB_MODE; -import com.getpcpanel.profile.SingleLogoLightingConfig.SINGLE_LOGO_MODE; -import com.getpcpanel.profile.SingleSliderLabelLightingConfig.SINGLE_SLIDER_LABEL_MODE; -import com.getpcpanel.profile.SingleSliderLightingConfig.SINGLE_SLIDER_MODE; -import com.getpcpanel.ui.FxHelper; -import com.getpcpanel.ui.HomePage; -import com.getpcpanel.util.Util; -import com.getpcpanel.util.coloroverride.OverrideColorService; - -import javafx.application.Platform; -import javafx.fxml.FXML; -import javafx.fxml.FXMLLoader; -import javafx.scene.Node; -import javafx.scene.control.Button; -import javafx.scene.control.ContentDisplay; -import javafx.scene.control.Label; -import javafx.scene.image.Image; -import javafx.scene.image.ImageView; -import javafx.scene.input.MouseButton; -import javafx.scene.layout.Pane; -import javafx.scene.layout.Region; -import javafx.scene.paint.Color; -import javafx.scene.paint.Paint; -import javafx.scene.shape.SVGPath; -import javafx.scene.text.Font; -import javafx.stage.Stage; -import lombok.extern.log4j.Log4j2; - -@Log4j2 -public class PCPanelProUI extends Device { - private final InputInterpreter inputInterpreter; - private final OverrideColorService overrideColorService; - - public static final int KNOB_COUNT = 5; - public static final int SLIDER_COUNT = 4; - private static final int LEDS_PER_SLIDER = 5; - private static final int MAX_ANALOG_VALUE = 100; - @FXML private Pane lightPanes; - @FXML private Pane panelPane; - private Label label; - private Button lightingButton; - private final Button[] knobs = new Button[9]; - private final ImageView[] images = new ImageView[9]; - private static final Image previewImage = new Image(Objects.requireNonNull(PCPanelProUI.class.getResource("/assets/PCPanelPro/Pro_Cutout.png")).toExternalForm()); - private Stage childDialogStage; - @FXML private Pane sliderHolder1; - @FXML private Pane sliderHolder2; - @FXML private Pane sliderHolder3; - @FXML private Pane sliderHolder4; - @FXML private SVGPath sliderLabel1; - @FXML private SVGPath sliderLabel2; - @FXML private SVGPath sliderLabel3; - @FXML private SVGPath sliderLabel4; - @FXML private SVGPath logoLight; - @FXML private SVGPath knobColor1; - @FXML private SVGPath knobColor2; - @FXML private SVGPath knobColor3; - @FXML private SVGPath knobColor4; - @FXML private SVGPath knobColor5; - @FXML private Pane sliderLightPane1; - @FXML private Pane sliderLightPane2; - @FXML private Pane sliderLightPane3; - @FXML private Pane sliderLightPane4; - private final Pane[] sliderLightPanes = new Pane[4]; - private final SVGPath[] knobColors = new SVGPath[5]; - private final SVGPath[] sliderLabels = new SVGPath[4]; - private final int[] analogValue = new int[9]; - private final Pane[] sliderHolders = new Pane[4]; - - public PCPanelProUI(FxHelper fxHelper, InputInterpreter inputInterpreter, SaveService saveService, OutputInterpreter outputInterpreter, IconService iconService, ApplicationEventPublisher eventPublisher, OverrideColorService overrideColorService, - String serialNum, DeviceSave deviceSave) { - super(fxHelper, saveService, outputInterpreter, iconService, eventPublisher, serialNum, deviceSave); - this.inputInterpreter = inputInterpreter; - this.overrideColorService = overrideColorService; - var loader = getFxHelper().getLoader(getClass().getResource("/assets/PCPanelPro/PCPanelPro.fxml")); - loader.setController(this); - try { - Pane pane = loader.load(); - Util.fill(sliderLightPanes, (Object[]) new Pane[] { sliderLightPane1, sliderLightPane2, sliderLightPane3, sliderLightPane4 }); - Util.fill(knobColors, (Object[]) new SVGPath[] { knobColor1, knobColor2, knobColor3, knobColor4, knobColor5 }); - Util.fill(sliderLabels, (Object[]) new SVGPath[] { sliderLabel1, sliderLabel2, sliderLabel3, sliderLabel4 }); - Util.fill(sliderHolders, (Object[]) new Pane[] { sliderHolder1, sliderHolder2, sliderHolder3, sliderHolder4 }); - initButtons(); - initLabel(); - initLightingButton(); - pane.getStylesheets().addAll(Objects.requireNonNull(getClass().getResource("/assets/PCPanelPro/PCPanelPro.css")).toExternalForm()); - } catch (IOException e) { - log.error("Unable to init ui", e); - } - postInit(); - } - - @Override - public Node getLabel() { - return label; - } - - @Override - public Pane getDevicePane() { - return panelPane; - } - - private void rotateKnob(int knob, int val) { - analogValue[knob] = val; - if (getLightingConfig().getLightingMode() == LightingMode.CUSTOM) - showLightingConfigToUI(getLightingConfig()); - if (knob < 5) { - ((Region) knobs[knob].getGraphic()).getChildrenUnmodifiable().get(3).setRotate(Util.analogValueToRotation(val)); - } else { - var x = Util.map(val, 0.0D, 255.0D, sliderHolders[knob - 5].getPrefHeight(), 0.0D) - 40.0D; - knobs[knob].setLayoutY(x); - images[knob].setLayoutY(x); - } - } - - @Override - public int getKnobRotation(int knob) { - return analogValue[knob]; - } - - private void initLabel() { - label = new Label("PCPANEL PRO"); - var f = Font.loadFont(getClass().getResourceAsStream("/assets/apex-mk2.regular.otf"), 50.0D); - label.setFont(f); - label.setUnderline(true); - label.setTextFill(Paint.valueOf("white")); - } - - private void initLightingButton() { - lightingButton = new Button("Lighting", getLightingImage()); - lightingButton.setStyle("-fx-background-color: transparent;"); - lightingButton.setContentDisplay(ContentDisplay.TOP); - lightingButton.setMinHeight(100.0D); - lightingButton.setOnAction(e -> { - childDialogStage = new Stage(); - getFxHelper().buildProLightingDialog(this).start(childDialogStage); - }); - } - - private void initButtons() throws IOException { - var xPos = 121.3D; - var yPos = 66.3D; - var xDelta = 133.0D; - var yDelta = 97.5D; - var buttonSize = 80; - for (var i = 0; i < 9; i++) { - FXMLLoader loader; - if (i < 5) { - loader = getFxHelper().getLoader(getClass().getResource("/assets/PCPanelPro/knob.fxml")); - } else { - loader = getFxHelper().getLoader(getClass().getResource("/assets/PCPanelPro/slider.fxml")); - } - Node nx = loader.load(); - images[i] = buildKnobImageView(); - knobs[i] = new Button("", nx); - knobs[i].setId("dial_button"); - knobs[i].setContentDisplay(ContentDisplay.CENTER); - if (i < 5) { - knobs[i].setMinSize(buttonSize, buttonSize); - knobs[i].setMaxSize(buttonSize, buttonSize); - knobs[i].setLayoutX(xPos); - knobs[i].setLayoutY(yPos); - knobs[i].setScaleX(1.2D); - knobs[i].setScaleY(1.2D); - - images[i].setLayoutX(xPos + 5); - images[i].setLayoutY(yPos + 5); - images[i].setFitWidth(71); - images[i].setFitHeight(71); - } else { - knobs[i].setMinSize(buttonSize, buttonSize); - knobs[i].setMaxSize(buttonSize, buttonSize); - knobs[i].setLayoutX(-26.0D); - knobs[i].setScaleX(0.4D); - knobs[i].setScaleY(0.4D); - - images[i].setLayoutX(-11.0D); - images[i].setFitWidth(50); - images[i].setFitHeight(50); - } - var knob = i; - knobs[i].setOnAction(e -> { - HomePage.showHint(false); - var name = (knob < 5) ? ("Knob " + (knob + 1)) : ("Slider " + (knob - 5 + 1)); - var analogType = (knob < 5) ? "Knob" : "Slider"; - var bm = getFxHelper().buildBasicMacro(this, knob, knob < 5, name, analogType); - try { - childDialogStage = new Stage(); - bm.start(childDialogStage); - } catch (Exception ex) { - log.error("Unable to start dialog", ex); - } - }); - var idx = i; - knobs[i].setOnMouseClicked(c -> { - if (c.getButton() == MouseButton.MIDDLE) { - try { - inputInterpreter.onButtonPress(new DeviceCommunicationHandler.ButtonPressEvent(getSerialNumber(), knob, true)); - } catch (IOException e1) { - log.error("Unable to handle button press", e1); - } - try { - inputInterpreter.onButtonPress(new DeviceCommunicationHandler.ButtonPressEvent(getSerialNumber(), knob, false)); - } catch (IOException e1) { - log.error("Unable to handle button release", e1); - } - } else if (c.getButton() == MouseButton.SECONDARY) { - getFxHelper().buildProLightingDialog(this).select(idx).start(new Stage()); - } - }); - if (i < 5) { - panelPane.getChildren().add(knobs[i]); - panelPane.getChildren().add(images[i]); - } else { - sliderHolders[i - 5].getChildren().add(knobs[i]); - sliderHolders[i - 5].getChildren().add(images[i]); - } - xPos += xDelta; - if (i == 1) { - yPos += yDelta; - xPos -= 332.5D; - } - } - } - - public String toString() { - return getDisplayName(); - } - - @Override - public Image getPreviewImage() { - return previewImage; - } - - @Override - public void setKnobRotation(int knob, int value) { - Platform.runLater(() -> rotateKnob(knob, value)); - } - - @Override - public void setButtonPressed(int knob, boolean pressed) { - Platform.runLater(() -> knobs[knob].setOpacity(pressed ? 0.5D : 1.0D)); - } - - @Override - public void closeDialogs() { - if (childDialogStage != null && childDialogStage.isShowing()) - childDialogStage.close(); - } - - @Override - public Button getLightingButton() { - return lightingButton; - } - - @Override - public DeviceType getDeviceType() { - return DeviceType.PCPANEL_PRO; - } - - private void setAllColor(Paint color) { - for (var p : knobColors) { - p.setFill(color); - } - for (var p : sliderLabels) { - p.setFill(color); - } - for (var pane : sliderLightPanes) { - for (var n : pane.getChildren()) - ((SVGPath) n).setFill(color); - } - logoLight.setFill(color); - } - - @Override - public void showLightingConfigToUI(LightingConfig config) { - var mode = config.getLightingMode(); - if (mode == LightingMode.ALL_COLOR) { - setAllColor(Paint.valueOf(config.getAllColor())); - } else if (mode == LightingMode.ALL_RAINBOW) { - var totalRows = 9; - var row = 0; - knobColor1.setFill(createFill(config, totalRows, row)); - knobColor2.setFill(createFill(config, totalRows, row)); - row++; - knobColor3.setFill(createFill(config, totalRows, row)); - knobColor4.setFill(createFill(config, totalRows, row)); - knobColor5.setFill(createFill(config, totalRows, row)); - row++; - for (var p : sliderLabels) { - p.setFill(createFill(config, totalRows, row)); - } - row++; - for (var i = 4; i >= 0; i--) { - for (var a = 0; a < 4; a++) - ((SVGPath) sliderLightPanes[a].getChildren().get(i)).setFill(createFill(config, totalRows, row)); - row++; - } - logoLight.setFill(createFill(config, totalRows, row)); - } else if (mode == LightingMode.ALL_WAVE) { - setAllColor(Color.hsb(360.0D * (0xFF & config.getWaveHue()) / 255.0D, 1.0D, (0xFF & config.getWaveBrightness()) / 255.0D)); - } else if (mode == LightingMode.ALL_BREATH) { - setAllColor(Color.hsb(360.0D * (0xFF & config.getBreathHue()) / 255.0D, 1.0D, (0xFF & config.getBreathBrightness()) / 255.0D)); - } else if (mode == LightingMode.CUSTOM) { - var knobConfigs = config.getKnobConfigs(); - var sliderLabelConfigs = config.getSliderLabelConfigs(); - var sliderConfigs = config.getSliderConfigs(); - for (var i = 0; i < KNOB_COUNT; i++) { - var knobConfig = overrideColorService.getDialOverride(serialNumber, i).orElse(knobConfigs[i]); - if (knobConfig.getMode() == SINGLE_KNOB_MODE.STATIC) { - knobColors[i].setFill(Paint.valueOf(knobConfig.getColor1())); - } else if (knobConfig.getMode() == SINGLE_KNOB_MODE.VOLUME_GRADIENT) { - var c1 = Color.web(knobConfig.getColor1()); - var c2 = Color.web(knobConfig.getColor2()); - knobColors[i].setFill(c1.interpolate(c2, analogValue[i] / 100.0D)); - } - } - for (var i = 0; i < SLIDER_COUNT; i++) { - var sliderLabelConfig = overrideColorService.getSliderLabelOverride(serialNumber, i).orElse(sliderLabelConfigs[i]); - if (sliderLabelConfig.getMode() == SINGLE_SLIDER_LABEL_MODE.STATIC) - sliderLabels[i].setFill(Paint.valueOf(sliderLabelConfig.getColor())); - } - for (var i = 0; i < SLIDER_COUNT; i++) { - var sliderConfig = overrideColorService.getSliderOverride(serialNumber, i).orElse(sliderConfigs[i]); - if (sliderConfig.getMode() == SINGLE_SLIDER_MODE.STATIC) { - for (var n : sliderLightPanes[i].getChildren()) - ((SVGPath) n).setFill(Paint.valueOf(sliderConfig.getColor1())); - } else if (sliderConfig.getMode() == SINGLE_SLIDER_MODE.STATIC_GRADIENT) { - var c1 = Color.web(sliderConfig.getColor1()); - var c2 = Color.web(sliderConfig.getColor2()); - var f = 0.0D; - var delta = 0.25D; - for (var a = 0; a < LEDS_PER_SLIDER; a++) { - ((SVGPath) sliderLightPanes[i].getChildren().get(a)).setFill(c1.interpolate(c2, f)); - f += delta; - } - } else if (sliderConfig.getMode() == SINGLE_SLIDER_MODE.VOLUME_GRADIENT) { - var c1 = Color.web(sliderConfig.getColor1()); - var c2 = Color.web(sliderConfig.getColor2()); - var f = 0.0D; - var delta = 0.25D; - for (var a = 0; a < 5; a++) { - if (a < (analogValue[i + 5] + 10) * 5 / MAX_ANALOG_VALUE) { - ((SVGPath) sliderLightPanes[i].getChildren().get(a)).setFill(c1.interpolate(c2, f)); - } else { - ((SVGPath) sliderLightPanes[i].getChildren().get(a)).setFill(Paint.valueOf("black")); - } - f += delta; - } - } - } - - var logoConfig = overrideColorService.getLogoOverride(serialNumber).orElse(config.getLogoConfig()); - if (logoConfig.getMode() == SINGLE_LOGO_MODE.STATIC) { - logoLight.setFill(Paint.valueOf(logoConfig.getColor())); - } else if (logoConfig.getMode() == SINGLE_LOGO_MODE.RAINBOW) { - logoLight.setFill(Color.RED); - } else if (logoConfig.getMode() == SINGLE_LOGO_MODE.BREATH) { - logoLight.setFill(Color.hsb(360.0D * (0xFF & logoConfig.getHue()) / 255.0D, 1.0D, (0xFF & logoConfig.getBrightness()) / 255.0D)); - } - } - } - - private static Color createFill(LightingConfig config, int totalRows, int row) { - return Color.hsb((360 * (totalRows - row - 1) * (0xFF & config.getRainbowPhaseShift())) / 255.0D * totalRows, 1.0D, (0xFF & config.getRainbowBrightness()) / 255.0D); - } - - @Override - protected ImageView[] getKnobImages() { - return images; - } -} diff --git a/src/main/java/com/getpcpanel/device/PCPanelRGBDevice.java b/src/main/java/com/getpcpanel/device/PCPanelRGBDevice.java new file mode 100644 index 00000000..665a97ca --- /dev/null +++ b/src/main/java/com/getpcpanel/device/PCPanelRGBDevice.java @@ -0,0 +1,25 @@ +package com.getpcpanel.device; + +import com.getpcpanel.commands.IconService; +import com.getpcpanel.hid.InputInterpreter; +import com.getpcpanel.hid.OutputInterpreter; +import com.getpcpanel.profile.DeviceSave; +import com.getpcpanel.profile.LightingConfig; +import com.getpcpanel.profile.SaveService; +import com.getpcpanel.util.coloroverride.OverrideColorService; + +public class PCPanelRGBDevice extends Device { + private final int[] knobRotations = new int[DeviceType.PCPANEL_RGB.getAnalogCount()]; + + public PCPanelRGBDevice(InputInterpreter inputInterpreter, SaveService saveService, OutputInterpreter outputInterpreter, + IconService iconService, OverrideColorService overrideColorService, DeviceSave deviceSave, String serialNum) { + super(saveService, outputInterpreter, iconService, serialNum, deviceSave); + } + + @Override public DeviceType getDeviceType() { return DeviceType.PCPANEL_RGB; } + @Override public void setKnobRotation(int knob, int rotation) { knobRotations[knob] = rotation; } + @Override public int getKnobRotation(int knob) { return knobRotations[knob]; } + @Override public void setButtonPressed(int button, boolean pressed) {} + @Override public void closeDialogs() {} + @Override public void showLightingConfigToUI(LightingConfig config) {} +} diff --git a/src/main/java/com/getpcpanel/device/PCPanelRGBUI.java b/src/main/java/com/getpcpanel/device/PCPanelRGBUI.java deleted file mode 100644 index 873fd394..00000000 --- a/src/main/java/com/getpcpanel/device/PCPanelRGBUI.java +++ /dev/null @@ -1,250 +0,0 @@ -package com.getpcpanel.device; - -import java.io.IOException; -import java.util.Objects; - -import org.springframework.context.ApplicationEventPublisher; - -import com.getpcpanel.commands.IconService; -import com.getpcpanel.hid.DeviceCommunicationHandler; -import com.getpcpanel.hid.InputInterpreter; -import com.getpcpanel.hid.OutputInterpreter; -import com.getpcpanel.profile.DeviceSave; -import com.getpcpanel.profile.LightingConfig; -import com.getpcpanel.profile.LightingConfig.LightingMode; -import com.getpcpanel.profile.SaveService; -import com.getpcpanel.profile.SingleKnobLightingConfig; -import com.getpcpanel.ui.FxHelper; -import com.getpcpanel.ui.HomePage; -import com.getpcpanel.util.Util; -import com.getpcpanel.util.coloroverride.OverrideColorService; - -import javafx.application.Platform; -import javafx.fxml.FXML; -import javafx.scene.Node; -import javafx.scene.control.Button; -import javafx.scene.control.ContentDisplay; -import javafx.scene.control.Label; -import javafx.scene.image.Image; -import javafx.scene.image.ImageView; -import javafx.scene.input.MouseButton; -import javafx.scene.layout.Pane; -import javafx.scene.layout.Region; -import javafx.scene.paint.Color; -import javafx.scene.paint.Paint; -import javafx.scene.shape.Shape; -import javafx.scene.text.Font; -import javafx.stage.Stage; -import lombok.extern.log4j.Log4j2; - -@Log4j2 -public class PCPanelRGBUI extends Device { - private final InputInterpreter inputInterpreter; - private final OverrideColorService overrideColorService; - - private static final int KNOB_COUNT = 4; - @FXML private Pane lightPanes; - @FXML private Pane panelPane; - private Label label; - private Button lightingButton; - private final Button[] knobs = new Button[KNOB_COUNT]; - private final ImageView[] images = new ImageView[KNOB_COUNT]; - private static final Image previewImage = new Image(Objects.requireNonNull(PCPanelRGBUI.class.getResource("/assets/PCPanelRGB/preview.png")).toExternalForm()); - private Stage childDialogStage; - - public PCPanelRGBUI(FxHelper fxHelper, InputInterpreter inputInterpreter, SaveService saveService, OutputInterpreter outputInterpreter, IconService iconService, ApplicationEventPublisher eventPublisher, OverrideColorService overrideColorService, - DeviceSave deviceSave, String serialNum) { - super(fxHelper, saveService, outputInterpreter, iconService, eventPublisher, serialNum, deviceSave); - this.inputInterpreter = inputInterpreter; - this.overrideColorService = overrideColorService; - var loader = getFxHelper().getLoader(getClass().getResource("/assets/PCPanelRGB/PCPanelRGB.fxml")); - loader.setController(this); - try { - Pane pane = loader.load(); - initButtons(); - initLabel(); - initLightingButton(); - pane.getStylesheets().addAll(Objects.requireNonNull(getClass().getResource("/assets/PCPanelRGB/PCPanelRGB.css")).toExternalForm()); - } catch (IOException e) { - log.error("Unable to init ui", e); - } - postInit(); - } - - @Override - public Node getLabel() { - return label; - } - - @Override - public Pane getDevicePane() { - return panelPane; - } - - private void rotateKnob(int knob, int val) { - ((Region) knobs[knob].getGraphic()).getChildrenUnmodifiable().get(3).setRotate(Util.analogValueToRotation(val)); - } - - @Override - public int getKnobRotation(int knob) { - return Util.rotationToAnalogValue(((Region) knobs[knob].getGraphic()).getChildrenUnmodifiable().get(3).getRotate()); - } - - private void initLabel() { - label = new Label("PCPANEL RGB"); - var f = Font.loadFont(getClass().getResourceAsStream("/assets/apex-mk2.regular.otf"), 50.0D); - label.setFont(f); - label.setUnderline(true); - label.setTextFill(Paint.valueOf("white")); - } - - private void initLightingButton() { - lightingButton = new Button("Lighting", getLightingImage()); - lightingButton.setStyle("-fx-background-color: transparent;"); - lightingButton.setContentDisplay(ContentDisplay.TOP); - lightingButton.setMinHeight(100.0D); - lightingButton.setOnAction(e -> { - childDialogStage = new Stage(); - getFxHelper().buildRGBLightingDialog(this).start(childDialogStage); - }); - } - - private void initButtons() throws IOException { - var xPos = 52.0D; - var yPos = 64.0D; - var xDelta = 107.3D; - var buttonSize = 80; - for (var i = 0; i < KNOB_COUNT; i++) { - var loader = getFxHelper().getLoader(getClass().getResource("/assets/PCPanelRGB/knob.fxml")); - Node nx = loader.load(); - images[i] = buildKnobImageView(); - knobs[i] = new Button("", nx); - knobs[i].setId("dial_button"); - knobs[i].setContentDisplay(ContentDisplay.CENTER); - knobs[i].setMinSize(buttonSize, buttonSize); - knobs[i].setMaxSize(buttonSize, buttonSize); - knobs[i].setLayoutX(xPos); - knobs[i].setLayoutY(yPos); - - images[i].setLayoutX(xPos + 10); - images[i].setLayoutY(yPos + 10); - images[i].setFitWidth(buttonSize - 20); - images[i].setFitHeight(buttonSize - 20); - - var knob = i; - knobs[i].setOnAction(e -> { - HomePage.showHint(false); - var bm = getFxHelper().buildBasicMacro(this, knob); - try { - childDialogStage = new Stage(); - bm.start(childDialogStage); - } catch (Exception ex) { - log.error("Unable to init button", ex); - } - }); - var idx = i; - knobs[i].setOnMouseClicked(c -> { - if (c.getButton() == MouseButton.MIDDLE) { - try { - inputInterpreter.onButtonPress(new DeviceCommunicationHandler.ButtonPressEvent(getSerialNumber(), knob, true)); - } catch (IOException e1) { - log.error("Unable to handle button press", e1); - } - try { - inputInterpreter.onButtonPress(new DeviceCommunicationHandler.ButtonPressEvent(getSerialNumber(), knob, false)); - } catch (IOException e1) { - log.error("Unable to handle button release", e1); - } - } else if (c.getButton() == MouseButton.SECONDARY) { - getFxHelper().buildRGBLightingDialog(this).select(idx).start(new Stage()); - } - }); - panelPane.getChildren().add(knobs[i]); - panelPane.getChildren().add(images[i]); - xPos += xDelta; - } - } - - public String toString() { - return getDisplayName(); - } - - @Override - public Image getPreviewImage() { - return previewImage; - } - - public int getKnobCount() { - return KNOB_COUNT; - } - - @Override - public void setKnobRotation(int knob, int value) { - Platform.runLater(() -> rotateKnob(knob, value)); - } - - @Override - public void setButtonPressed(int knob, boolean pressed) { - Platform.runLater(() -> knobs[knob].setOpacity(pressed ? 0.5D : 1.0D)); - } - - private void setAllKnobUIColor(Color color) { - for (var i = 0; i < getKnobCount(); i++) { - setKnobUIColor(i, color); - } - } - - private void setKnobUIColorHex(int knob, String color) { - var lightPane = (Shape) lightPanes.getChildren().get(knob); - lightPane.setFill(Paint.valueOf(color)); - } - - private void setKnobUIColor(int knob, Color color) { - var lightPane = (Shape) lightPanes.getChildren().get(knob); - lightPane.setFill(color); - } - - @Override - public void closeDialogs() { - if (childDialogStage != null && childDialogStage.isShowing()) - childDialogStage.close(); - } - - @Override - public Button getLightingButton() { - return lightingButton; - } - - @Override - public DeviceType getDeviceType() { - return DeviceType.PCPANEL_RGB; - } - - @Override - public void showLightingConfigToUI(LightingConfig config) { - var mode = config.getLightingMode(); - if (mode == LightingMode.ALL_COLOR) { - setAllKnobUIColor(Color.valueOf(config.getAllColor())); - } else if (mode == LightingMode.SINGLE_COLOR) { - for (var i = 0; i < getKnobCount(); i++) { - var color = overrideColorService.getDialOverride(serialNumber, i).map(SingleKnobLightingConfig::getColor1).orElse(config.getIndividualColors()[i]); - setKnobUIColorHex(i, color); - } - } else if (mode == LightingMode.ALL_RAINBOW) { - for (var i = 0; i < getKnobCount(); i++) - setKnobUIColor(i, - Color.hsb((360 * (getKnobCount() - i - 1) * (0xFF & config.getRainbowPhaseShift())) / 255.0D * getKnobCount(), 1.0D, (0xFF & config.getRainbowBrightness()) / 255.0D)); - } else if (mode == LightingMode.ALL_WAVE) { - for (var i = 0; i < getKnobCount(); i++) - setKnobUIColor(i, Color.hsb(360.0D * (0xFF & config.getWaveHue()) / 255.0D, 1.0D, (0xFF & config.getWaveBrightness()) / 255.0D)); - } else if (mode == LightingMode.ALL_BREATH) { - for (var i = 0; i < getKnobCount(); i++) - setKnobUIColor(i, Color.hsb(360.0D * (0xFF & config.getBreathHue()) / 255.0D, 1.0D, (0xFF & config.getBreathBrightness()) / 255.0D)); - } - } - - @Override - protected ImageView[] getKnobImages() { - return images; - } -} diff --git a/src/main/java/com/getpcpanel/hid/DeviceCommunicationHandler.java b/src/main/java/com/getpcpanel/hid/DeviceCommunicationHandler.java index dc6d47e0..770fcd54 100644 --- a/src/main/java/com/getpcpanel/hid/DeviceCommunicationHandler.java +++ b/src/main/java/com/getpcpanel/hid/DeviceCommunicationHandler.java @@ -17,20 +17,22 @@ import org.apache.commons.lang3.tuple.Pair; import org.hid4java.HidDevice; -import org.springframework.context.ApplicationEventPublisher; +import jakarta.enterprise.event.Event; +import jakarta.inject.Inject; import com.getpcpanel.device.DeviceType; import com.getpcpanel.profile.SaveService; import lombok.Setter; -import lombok.extern.log4j.Log4j2; +import lombok.extern.jbosslog.JBossLog; -@Log4j2 +@JBossLog public class DeviceCommunicationHandler { private static final byte INPUT_CODE_KNOB_CHANGE = 1; private static final byte INPUT_CODE_BUTTON_CHANGE = 2; - private final ApplicationEventPublisher eventPublisher; + @Inject + Event eventBus; private final DeviceScanner deviceScanner; private final SaveService saveService; private final String key; @@ -48,8 +50,7 @@ public class DeviceCommunicationHandler { private final RollingAverageSetter rollingAverageSetter = new RollingAverageSetter(); private final Map prevSent = new ConcurrentHashMap<>(); - public DeviceCommunicationHandler(DeviceScanner deviceScanner, ApplicationEventPublisher eventPublisher, SaveService saveService, String key, HidDevice device, DeviceType deviceType) { - this.eventPublisher = eventPublisher; + public DeviceCommunicationHandler(DeviceScanner deviceScanner, SaveService saveService, String key, HidDevice device, DeviceType deviceType) { this.deviceScanner = deviceScanner; this.saveService = saveService; this.key = key; @@ -164,7 +165,7 @@ private void triggerEvent(KnobRotateEvent o) { } else { prevSent.put(o.knob(), currentSendValue); log.debug("< {}", o); - eventPublisher.publishEvent(o); + eventBus.fire(o); } } @@ -174,7 +175,7 @@ private boolean applyWorkaround(int knob) { private void triggerEvent(ButtonPressEvent o) { log.debug("< {}", o); - eventPublisher.publishEvent(o); + eventBus.fire(o); } private void triggerOrDebounce(KnobRotateEvent event) { diff --git a/src/main/java/com/getpcpanel/hid/DeviceCommunicationHandlerFactory.java b/src/main/java/com/getpcpanel/hid/DeviceCommunicationHandlerFactory.java index ea0dd171..48971a5d 100644 --- a/src/main/java/com/getpcpanel/hid/DeviceCommunicationHandlerFactory.java +++ b/src/main/java/com/getpcpanel/hid/DeviceCommunicationHandlerFactory.java @@ -1,24 +1,27 @@ package com.getpcpanel.hid; import org.hid4java.HidDevice; -import org.springframework.context.ApplicationEventPublisher; -import org.springframework.stereotype.Service; +import jakarta.enterprise.event.Event; +import jakarta.inject.Inject; +import jakarta.enterprise.context.ApplicationScoped; import com.getpcpanel.device.DeviceType; import com.getpcpanel.profile.SaveService; import lombok.RequiredArgsConstructor; -import lombok.extern.log4j.Log4j2; +import lombok.extern.jbosslog.JBossLog; -@Log4j2 -@Service -@RequiredArgsConstructor +@JBossLog +@ApplicationScoped public class DeviceCommunicationHandlerFactory { - private final ApplicationEventPublisher eventPublisher; - private final DeviceScanner deviceScanner; - private final SaveService saveService; + @Inject + Event eventBus; + @Inject + DeviceScanner deviceScanner; + @Inject + SaveService saveService; public DeviceCommunicationHandler build(String key, HidDevice device, DeviceType deviceType) { - return new DeviceCommunicationHandler(deviceScanner, eventPublisher, saveService, key, device, deviceType); + return new DeviceCommunicationHandler(deviceScanner, saveService, key, device, deviceType); } } diff --git a/src/main/java/com/getpcpanel/hid/DeviceHolder.java b/src/main/java/com/getpcpanel/hid/DeviceHolder.java index b5ece766..7558d203 100644 --- a/src/main/java/com/getpcpanel/hid/DeviceHolder.java +++ b/src/main/java/com/getpcpanel/hid/DeviceHolder.java @@ -1,5 +1,88 @@ package com.getpcpanel.hid; +import java.util.Collection; +import java.util.Map; +import java.util.Optional; +import java.util.concurrent.ConcurrentHashMap; + +import jakarta.annotation.Priority; +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.enterprise.event.Event; +import jakarta.enterprise.event.Observes; +import jakarta.inject.Inject; + +import com.getpcpanel.cpp.windows.WindowFocusChangedEvent; +import com.getpcpanel.device.Device; +import com.getpcpanel.device.DeviceFactory; +import com.getpcpanel.device.DeviceType; +import com.getpcpanel.profile.SaveService; + +import lombok.RequiredArgsConstructor; + +@ApplicationScoped +public class DeviceHolder { + private final Map devices = new ConcurrentHashMap<>(); + @Inject SaveService saveService; + @Inject DeviceFactory deviceFactory; + @Inject OutputInterpreter outputInterpreter; + @Inject Event eventBus; + + public Optional getDevice(String key) { + return Optional.ofNullable(devices.get(key)); + } + + public int size() { + return devices.size(); + } + + public Collection values() { + return devices.values(); + } + + @Priority(1) + public void deviceAdded(@Observes DeviceScanner.DeviceConnectedEvent event) { + Device device; + var save = saveService.get(); + if (!save.getDevices().containsKey(event.serialNum())) + save.createSaveForNewDevice(event.serialNum(), event.deviceType()); + if (event.deviceType() == DeviceType.PCPANEL_RGB) { + device = deviceFactory.buildRgb(event.serialNum(), save.getDeviceSave(event.serialNum())); + } else if (event.deviceType() == DeviceType.PCPANEL_MINI) { + device = deviceFactory.buildMini(event.serialNum(), save.getDeviceSave(event.serialNum())); + } else if (event.deviceType() == DeviceType.PCPANEL_PRO) { + device = deviceFactory.buildPro(event.serialNum(), save.getDeviceSave(event.serialNum())); + } else { + throw new IllegalArgumentException("unknown devicetype: " + event.deviceType().name()); + } + devices.put(event.serialNum(), device); + outputInterpreter.sendInit(event.serialNum()); + eventBus.fire(new DeviceFullyConnectedEvent(device)); + } + + public void onDeviceDisconnected(@Observes DeviceScanner.DeviceDisconnectedEvent event) { + var device = devices.remove(event.serialNum()); + if (device != null) { + device.disconnected(); + } + } + + public void focusApplicationChanged(@Observes WindowFocusChangedEvent event) { + devices.values().forEach(Device::focusApplicationChanged); + } + + public void saveChanged(@Observes SaveService.SaveEvent event) { + devices.values().forEach(Device::saveChanged); + } + + public Collection all() { + return devices.values(); + } + + public record DeviceFullyConnectedEvent(Device device) { + } +} + + import static org.springframework.core.Ordered.HIGHEST_PRECEDENCE; import java.util.Collection; @@ -7,12 +90,11 @@ import java.util.Optional; import java.util.concurrent.ConcurrentHashMap; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.context.ApplicationEventPublisher; -import org.springframework.context.annotation.Lazy; -import org.springframework.context.event.EventListener; -import org.springframework.core.annotation.Order; -import org.springframework.stereotype.Service; +import jakarta.inject.Inject; +import jakarta.enterprise.event.Event; +import jakarta.enterprise.event.Observes; +import jakarta.annotation.Priority; +import jakarta.enterprise.context.ApplicationScoped; import com.getpcpanel.cpp.windows.WindowFocusChangedEvent; import com.getpcpanel.device.Device; @@ -24,14 +106,16 @@ import lombok.RequiredArgsConstructor; import lombok.Setter; -@Service -@RequiredArgsConstructor +@ApplicationScoped public class DeviceHolder { private final Map devices = new ConcurrentHashMap<>(); - private final SaveService saveService; - @Autowired @Lazy @Setter private DeviceFactory deviceFactory; - private final OutputInterpreter outputInterpreter; - private final ApplicationEventPublisher eventPublisher; + @Inject + SaveService saveService; + @Inject @Lazy @Setter private DeviceFactory deviceFactory; + @Inject + OutputInterpreter outputInterpreter; + @Inject + Event eventBus; public Optional getDevice(String key) { return Optional.ofNullable(devices.get(key)); @@ -45,9 +129,8 @@ public Collection values() { return devices.values(); } - @EventListener - @Order(HIGHEST_PRECEDENCE) - public void deviceAdded(DeviceScanner.DeviceConnectedEvent event) { + @Priority(1) + public void deviceAdded(@Observes DeviceScanner.DeviceConnectedEvent event) { Device device; var save = saveService.get(); if (!save.getDevices().containsKey(event.serialNum())) @@ -63,25 +146,22 @@ public void deviceAdded(DeviceScanner.DeviceConnectedEvent event) { } devices.put(event.serialNum(), device); outputInterpreter.sendInit(event.serialNum()); - eventPublisher.publishEvent(new DeviceFullyConnectedEvent(device)); + eventBus.fire(new DeviceFullyConnectedEvent(device)); } - @Order - @EventListener - public void onDeviceDisconnected(DeviceScanner.DeviceDisconnectedEvent event) { + @Priority + public void onDeviceDisconnected(@Observes DeviceScanner.DeviceDisconnectedEvent event) { var device = devices.remove(event.serialNum()); if (device != null) { Platform.runLater(device::disconnected); } } - @EventListener(WindowFocusChangedEvent.class) - public void focusApplicationChanged() { + public void focusApplicationChanged() { devices.values().forEach(Device::focusApplicationChanged); } - @EventListener(SaveService.SaveEvent.class) - public void saveChanged() { + public void saveChanged() { devices.values().forEach(Device::saveChanged); } diff --git a/src/main/java/com/getpcpanel/hid/DeviceScanner.java b/src/main/java/com/getpcpanel/hid/DeviceScanner.java index dd0a743d..8839efa4 100644 --- a/src/main/java/com/getpcpanel/hid/DeviceScanner.java +++ b/src/main/java/com/getpcpanel/hid/DeviceScanner.java @@ -10,25 +10,151 @@ import org.hid4java.HidServicesSpecification; import org.hid4java.ScanMode; import org.hid4java.event.HidServicesEvent; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.context.ApplicationEventPublisher; -import org.springframework.context.annotation.Lazy; -import org.springframework.stereotype.Service; + +import com.getpcpanel.device.DeviceType; + +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.enterprise.event.Event; +import jakarta.inject.Inject; +import lombok.NonNull; +import lombok.extern.jbosslog.JBossLog; + +@JBossLog +@ApplicationScoped +public class DeviceScanner implements HidServicesListener { + private final ConcurrentHashMap connectedDeviceMap = new ConcurrentHashMap<>(); + @Inject Event eventBus; + @Inject DeviceCommunicationHandlerFactory deviceCommunicationHandlerFactory; + + private HidServices hidServices; + + public DeviceCommunicationHandler getConnectedDevice(String key) { + return connectedDeviceMap.get(key); + } + + // Not @PostConstruct because the startup sequence needs to control when this runs + public void init() { + hidServices = HidManager.getHidServices(buildSpecification()); + hidServices.addHidServicesListener(this); + log.info("Starting HID services."); + hidServices.start(); + log.info("Enumerating attached devices..."); + } + + static HidServicesSpecification buildSpecification() { + var hidServicesSpecification = new HidServicesSpecification(); + hidServicesSpecification.setAutoShutdown(false); + hidServicesSpecification.setAutoStart(false); + hidServicesSpecification.setScanInterval(3000); + hidServicesSpecification.setPauseInterval(2000); + hidServicesSpecification.setScanMode(ScanMode.SCAN_AT_FIXED_INTERVAL); + return hidServicesSpecification; + } + + public void deviceAdded(@NonNull String key, @NonNull HidDevice device, DeviceType deviceType) { + if (!device.isOpen()) { + if (!device.open()) { + log.error("Unable to open device, it won't be possible to use the panel"); + } + } + var deviceHandler = deviceCommunicationHandlerFactory.build(key, device, deviceType); + connectedDeviceMap.put(key, deviceHandler); + deviceHandler.start(); + eventBus.fire(new DeviceConnectedEvent(key, deviceType)); + } + + public void deviceRemoved(String key, HidDevice device) { + if (key == null || device == null) + throw new IllegalArgumentException("serialNum or device cannot be null serialNum: " + key + " device: " + device); + if (connectedDeviceMap.remove(key) != null) + eventBus.fire(new DeviceDisconnectedEvent(key)); + } + + private void foundPCPanel(HidDevice newPCPanel, DeviceType deviceType) { + log.info("FOUND PCPANEL : {}", newPCPanel); + try { + deviceAdded(newPCPanel.getSerialNumber(), newPCPanel, deviceType); + } catch (Exception e) { + log.error("Unable to handle device added", e); + } + } + + private void lostPCPanel(HidDevice lostPCPanel) { + log.info("LOST PCPANEL : {}", lostPCPanel); + try { + deviceRemoved(lostPCPanel.getSerialNumber(), lostPCPanel); + } catch (Exception e) { + log.error("Unable to handle device disconnect", e); + } + } + + @Override + public void hidDeviceAttached(HidServicesEvent event) { + determineType(event).ifPresent(type -> foundPCPanel(event.getHidDevice(), type)); + } + + @Override + public void hidDeviceDetached(HidServicesEvent event) { + determineType(event).ifPresent(type -> lostPCPanel(event.getHidDevice())); + } + + @Override + public void hidFailure(HidServicesEvent event) { + determineType(event).ifPresent(type -> lostPCPanel(event.getHidDevice())); + } + + private Optional determineType(HidServicesEvent event) { + for (var deviceType : DeviceType.ALL) { + if (event.getHidDevice().isVidPidSerial(deviceType.getVid(), deviceType.getPid(), null)) + return Optional.of(deviceType); + } + return Optional.empty(); + } + + public void close() { + try { + hidServices.shutdown(); + } catch (Exception e) { + log.error("Error occurred when closing device", e); + } + } + + public record DeviceConnectedEvent(String serialNum, DeviceType deviceType) { + } + + public record DeviceDisconnectedEvent(String serialNum) { + } +} + + +import java.util.Optional; +import java.util.concurrent.ConcurrentHashMap; + +import org.hid4java.HidDevice; +import org.hid4java.HidManager; +import org.hid4java.HidServices; +import org.hid4java.HidServicesListener; +import org.hid4java.HidServicesSpecification; +import org.hid4java.ScanMode; +import org.hid4java.event.HidServicesEvent; +import jakarta.inject.Inject; +import jakarta.enterprise.event.Event; +import jakarta.enterprise.context.ApplicationScoped; import com.getpcpanel.device.DeviceType; import lombok.NonNull; import lombok.RequiredArgsConstructor; import lombok.Setter; -import lombok.extern.log4j.Log4j2; +import lombok.extern.jbosslog.JBossLog; -@Log4j2 -@Service -@RequiredArgsConstructor +@JBossLog +@ApplicationScoped public class DeviceScanner implements HidServicesListener { private final ConcurrentHashMap connectedDeviceMap = new ConcurrentHashMap<>(); - private final ApplicationEventPublisher eventPublisher; - @Autowired @Lazy @Setter private DeviceCommunicationHandlerFactory deviceCommunicationHandlerFactory; + @Inject + Event eventBus; + @Inject @Lazy @Setter private DeviceCommunicationHandlerFactory deviceCommunicationHandlerFactory; private HidServices hidServices; @@ -64,14 +190,14 @@ public void deviceAdded(@NonNull String key, @NonNull HidDevice device, DeviceTy var deviceHandler = deviceCommunicationHandlerFactory.build(key, device, deviceType); connectedDeviceMap.put(key, deviceHandler); deviceHandler.start(); - eventPublisher.publishEvent(new DeviceConnectedEvent(key, deviceType)); + eventBus.fire(new DeviceConnectedEvent(key, deviceType)); } public void deviceRemoved(String key, HidDevice device) { if (key == null || device == null) throw new IllegalArgumentException("serialNum or device cannot be null serialNum: " + key + " device: " + device); if (connectedDeviceMap.remove(key) != null) - eventPublisher.publishEvent(new DeviceDisconnectedEvent(key)); + eventBus.fire(new DeviceDisconnectedEvent(key)); } private void foundPCPanel(HidDevice newPCPanel, DeviceType deviceType) { diff --git a/src/main/java/com/getpcpanel/hid/InputInterpreter.java b/src/main/java/com/getpcpanel/hid/InputInterpreter.java index 43bb81d1..263b86ba 100644 --- a/src/main/java/com/getpcpanel/hid/InputInterpreter.java +++ b/src/main/java/com/getpcpanel/hid/InputInterpreter.java @@ -9,9 +9,10 @@ import java.util.Map; import java.util.concurrent.TimeUnit; -import org.springframework.context.ApplicationEventPublisher; -import org.springframework.context.event.EventListener; -import org.springframework.stereotype.Service; +import jakarta.enterprise.event.Event; +import jakarta.inject.Inject; +import jakarta.enterprise.event.Observes; +import jakarta.enterprise.context.ApplicationScoped; import com.getpcpanel.commands.PCPanelControlEvent; import com.getpcpanel.device.DeviceType; @@ -19,20 +20,22 @@ import com.getpcpanel.util.Debouncer; import lombok.RequiredArgsConstructor; -import lombok.extern.log4j.Log4j2; +import lombok.extern.jbosslog.JBossLog; -@Log4j2 -@Service -@RequiredArgsConstructor +@JBossLog +@ApplicationScoped public final class InputInterpreter { - private final SaveService save; - private final DeviceHolder devices; - private final ApplicationEventPublisher eventPublisher; - private final Debouncer debouncer; + @Inject + SaveService save; + @Inject + DeviceHolder devices; + @Inject + Event eventBus; + @Inject + Debouncer debouncer; private final Map lastClicks = new HashMap<>(); - @EventListener - public void onKnobRotate(DeviceCommunicationHandler.KnobRotateEvent event) { + public void onKnobRotate(@Observes DeviceCommunicationHandler.KnobRotateEvent event) { devices.getDevice(event.serialNum()).ifPresent(device -> { var value = event.value(); if (device.getDeviceType() == DeviceType.PCPANEL_RGB) { @@ -44,15 +47,14 @@ public void onKnobRotate(DeviceCommunicationHandler.KnobRotateEvent event) { }); } - @EventListener - public void onButtonPress(DeviceCommunicationHandler.ButtonPressEvent event) throws IOException { + public void onButtonPress(@Observes DeviceCommunicationHandler.ButtonPressEvent event) throws IOException { devices.getDevice(event.serialNum()).ifPresent(device -> device.setButtonPressed(event.button(), event.pressed())); if (event.pressed()) doClickAction(event.serialNum(), event.button()); } private void doDialAction(String serialNum, boolean initial, int knob, DialValue v) { - save.getProfile(serialNum).map(p -> p.getDialData(knob)).ifPresent(data -> eventPublisher.publishEvent(new PCPanelControlEvent(serialNum, knob, data, initial, v))); + save.getProfile(serialNum).map(p -> p.getDialData(knob)).ifPresent(data -> eventBus.fire(new PCPanelControlEvent(serialNum, knob, data, initial, v))); } private void doClickAction(String serialNum, int knob) { @@ -68,13 +70,13 @@ private void determineClick(ClickId clickId, long timeDiff) { if (isDblClick) { debouncer.debounce(clickId, () -> { }, debounceTime, TimeUnit.MILLISECONDS); - eventPublisher.publishEvent(new ButtonClickEvent(clickId.serialNum(), clickId.button(), true)); + eventBus.fire(new ButtonClickEvent(clickId.serialNum(), clickId.button(), true)); lastClicks.remove(clickId); return; } lastClicks.put(clickId, System.currentTimeMillis()); - Runnable trigger = () -> eventPublisher.publishEvent(new ButtonClickEvent(clickId.serialNum(), clickId.button(), false)); + Runnable trigger = () -> eventBus.fire(new ButtonClickEvent(clickId.serialNum(), clickId.button(), false)); if (save.get().isPreventClickWhenDblClick()) { debouncer.debounce(clickId, trigger, debounceTime, TimeUnit.MILLISECONDS); } else { @@ -82,16 +84,15 @@ private void determineClick(ClickId clickId, long timeDiff) { } } - @EventListener - public void onButtonPress(ButtonClickEvent event) { + public void onButtonPress(@Observes ButtonClickEvent event) { save.getProfile(event.serialNum()).ifPresent(profile -> { var click = profile.getButtonData(event.button()); var dblClick = profile.getDblButtonData(event.button()); if (event.dblClick() && hasCommands(dblClick)) { - eventPublisher.publishEvent(new PCPanelControlEvent(event.serialNum(), event.button(), dblClick, false, null)); + eventBus.fire(new PCPanelControlEvent(event.serialNum(), event.button(), dblClick, false, null)); } else if (!event.dblClick() && hasCommands(click)) { - eventPublisher.publishEvent(new PCPanelControlEvent(event.serialNum(), event.button(), click, false, null)); + eventBus.fire(new PCPanelControlEvent(event.serialNum(), event.button(), click, false, null)); } }); } diff --git a/src/main/java/com/getpcpanel/hid/OutputInterpreter.java b/src/main/java/com/getpcpanel/hid/OutputInterpreter.java index 31e5561d..19b8d90f 100644 --- a/src/main/java/com/getpcpanel/hid/OutputInterpreter.java +++ b/src/main/java/com/getpcpanel/hid/OutputInterpreter.java @@ -2,7 +2,8 @@ import java.util.Arrays; -import org.springframework.stereotype.Service; +import jakarta.inject.Inject; +import jakarta.enterprise.context.ApplicationScoped; import com.getpcpanel.device.DeviceType; import com.getpcpanel.profile.LightingConfig; @@ -14,14 +15,15 @@ import javafx.scene.paint.Color; import lombok.RequiredArgsConstructor; -import lombok.extern.log4j.Log4j2; +import lombok.extern.jbosslog.JBossLog; -@Log4j2 -@Service -@RequiredArgsConstructor +@JBossLog +@ApplicationScoped public final class OutputInterpreter { - private final DeviceScanner deviceScanner; - private final OverrideColorService overrideColorService; + @Inject + DeviceScanner deviceScanner; + @Inject + OverrideColorService overrideColorService; private static final byte[] OUTPUT_CODE_INIT = { 1 }; private static final byte ANIMATION_RAINBOW_HORIZONTAL = 1; diff --git a/src/main/java/com/getpcpanel/iconextract/IIconService.java b/src/main/java/com/getpcpanel/iconextract/IIconService.java index 770deafa..6fc7579c 100644 --- a/src/main/java/com/getpcpanel/iconextract/IIconService.java +++ b/src/main/java/com/getpcpanel/iconextract/IIconService.java @@ -3,24 +3,9 @@ import java.awt.image.BufferedImage; import java.io.File; -import javax.annotation.Nullable; - -import org.springframework.cache.annotation.Cacheable; - -import javafx.embed.swing.SwingFXUtils; -import javafx.scene.image.Image; +import jakarta.annotation.Nullable; public interface IIconService { - @Cacheable("icon") - BufferedImage getIconForFile(int width, int height, File file); - @Nullable - @Cacheable("icon") - default Image getIconImageForFile(int width, int height, File file) { - var image = getIconForFile(width, height, file); - if (image != null) { - return SwingFXUtils.toFXImage(image, null); - } - return null; - } + BufferedImage getIconForFile(int width, int height, File file); } diff --git a/src/main/java/com/getpcpanel/iconextract/IconServiceLinux.java b/src/main/java/com/getpcpanel/iconextract/IconServiceLinux.java index c83565c2..93af06e3 100644 --- a/src/main/java/com/getpcpanel/iconextract/IconServiceLinux.java +++ b/src/main/java/com/getpcpanel/iconextract/IconServiceLinux.java @@ -3,15 +3,16 @@ import java.awt.image.BufferedImage; import java.io.File; -import org.springframework.stereotype.Service; +import com.getpcpanel.spring.LinuxImpl; -import com.getpcpanel.spring.ConditionalOnLinux; +import jakarta.enterprise.context.ApplicationScoped; -@Service -@ConditionalOnLinux +@ApplicationScoped +@LinuxImpl public class IconServiceLinux implements IIconService { @Override public BufferedImage getIconForFile(int width, int height, File file) { return null; } } + diff --git a/src/main/java/com/getpcpanel/iconextract/IconServiceWindows.java b/src/main/java/com/getpcpanel/iconextract/IconServiceWindows.java index bf779ecd..c1bef793 100644 --- a/src/main/java/com/getpcpanel/iconextract/IconServiceWindows.java +++ b/src/main/java/com/getpcpanel/iconextract/IconServiceWindows.java @@ -3,15 +3,16 @@ import java.awt.image.BufferedImage; import java.io.File; -import org.springframework.stereotype.Service; +import com.getpcpanel.spring.WindowsImpl; -import com.getpcpanel.spring.ConditionalOnWindows; +import jakarta.enterprise.context.ApplicationScoped; -@Service -@ConditionalOnWindows +@ApplicationScoped +@WindowsImpl public class IconServiceWindows implements IIconService { @Override public BufferedImage getIconForFile(int width, int height, File file) { return JIconExtract.getIconForFile(width, height, file); } } + diff --git a/src/main/java/com/getpcpanel/mqtt/MqttDeviceColorService.java b/src/main/java/com/getpcpanel/mqtt/MqttDeviceColorService.java index 5486f4fd..ca168b4f 100644 --- a/src/main/java/com/getpcpanel/mqtt/MqttDeviceColorService.java +++ b/src/main/java/com/getpcpanel/mqtt/MqttDeviceColorService.java @@ -17,9 +17,10 @@ import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.math.NumberUtils; import org.apache.logging.log4j.util.TriConsumer; -import org.springframework.context.ApplicationEventPublisher; -import org.springframework.core.annotation.Order; -import org.springframework.stereotype.Service; +import jakarta.enterprise.event.Event; +import jakarta.inject.Inject; +import jakarta.annotation.Priority; +import jakarta.enterprise.context.ApplicationScoped; import com.getpcpanel.device.Device; import com.getpcpanel.profile.LightingConfig; @@ -34,22 +35,24 @@ import javafx.scene.paint.Color; import lombok.RequiredArgsConstructor; -import lombok.extern.log4j.Log4j2; +import lombok.extern.jbosslog.JBossLog; import one.util.streamex.EntryStream; import one.util.streamex.StreamEx; -@Log4j2 -@Service -@Order(1) -@RequiredArgsConstructor +@JBossLog +@ApplicationScoped +@Priority(1) public class MqttDeviceColorService implements IOverrideColorProviderProvider { public static final String EFFECT_NONE = "none"; public static final String EFFECT_STOP_OVERRIDE = "stop_override"; - private final MqttService mqtt; - private final MqttTopicHelper mqttTopicHelper; + @Inject + MqttService mqtt; + @Inject + MqttTopicHelper mqttTopicHelper; private final ColorOverrideHolder colorOverrideHolder = new MqttColorOverrideHolder(); - private final ApplicationEventPublisher applicationEventPublisher; + @Inject + Event eventBus; @Override public IOverrideColorProvider getOverrideColorProvider() { @@ -101,7 +104,7 @@ public void buildSubscriptions(Device device, LightingConfig lighting) { var newBrightness = NumberUtils.toInt(payload, 100); lighting.setGlobalBrightness(newBrightness); andThen.run(); - applicationEventPublisher.publishEvent(new HomePage.GlobalBrightnessChangedEvent(this, device.getSerialNumber(), newBrightness)); + eventBus.fire(new HomePage.GlobalBrightnessChangedEvent(this, device.getSerialNumber(), newBrightness)); }); subscribeToColors(lighting.getKnobConfigs(), topicHelper, dial, knobOverride, idx -> device.getLightingConfig().getKnobConfigs()[idx].getColor1()); subscribeToColors(lighting.getSliderConfigs(), topicHelper, slider, sliderOverride, idx -> device.getLightingConfig().getSliderConfigs()[idx].getColor1()); diff --git a/src/main/java/com/getpcpanel/mqtt/MqttDeviceService.java b/src/main/java/com/getpcpanel/mqtt/MqttDeviceService.java index e8024fe2..10b9faee 100644 --- a/src/main/java/com/getpcpanel/mqtt/MqttDeviceService.java +++ b/src/main/java/com/getpcpanel/mqtt/MqttDeviceService.java @@ -16,9 +16,10 @@ import javax.annotation.Nonnull; import javax.annotation.Nullable; -import org.springframework.context.event.EventListener; -import org.springframework.core.annotation.Order; -import org.springframework.stereotype.Service; +import jakarta.inject.Inject; +import jakarta.enterprise.event.Observes; +import jakarta.annotation.Priority; +import jakarta.enterprise.context.ApplicationScoped; import com.getpcpanel.device.Device; import com.getpcpanel.hid.ButtonClickEvent; @@ -35,31 +36,34 @@ import com.getpcpanel.ui.HomePage.GlobalBrightnessChangedEvent; import lombok.RequiredArgsConstructor; -import lombok.extern.log4j.Log4j2; +import lombok.extern.jbosslog.JBossLog; import one.util.streamex.EntryStream; -@Log4j2 -@Service -@RequiredArgsConstructor +@JBossLog +@ApplicationScoped public class MqttDeviceService { - private final MqttService mqtt; - private final SaveService saveService; - private final DeviceHolder deviceHolder; - private final MqttHomeAssistantHelper mqttHomeAssistantHelper; - private final MqttTopicHelper mqttTopicHelper; - private final MqttDeviceColorService deviceColorService; + @Inject + MqttService mqtt; + @Inject + SaveService saveService; + @Inject + DeviceHolder deviceHolder; + @Inject + MqttHomeAssistantHelper mqttHomeAssistantHelper; + @Inject + MqttTopicHelper mqttTopicHelper; + @Inject + MqttDeviceColorService deviceColorService; private final Set initializedDevices = new HashSet<>(); - @Order(ORDER_OF_SAVE + 1) // Ensure we are disconnected if the setting is turned off - @EventListener(SaveService.SaveEvent.class) - public void saveChanged() { + @Priority(ORDER_OF_SAVE + 1) // Ensure we are disconnected if the setting is turned off + public void saveChanged() { if (mqtt.isConnected()) { initialize(); } } - @EventListener - public void mqttConnected(MqttStatusEvent event) { + public void mqttConnected(@Observes MqttStatusEvent event) { if (!event.connected()) { return; } @@ -81,16 +85,14 @@ public boolean clear() { return false; } - @EventListener - public void deviceConnected(DeviceFullyConnectedEvent event) { + public void deviceConnected(@Observes DeviceFullyConnectedEvent event) { if (!mqtt.isConnected()) { return; } initialize(event.device()); } - @EventListener - public void dialAction(DeviceCommunicationHandler.KnobRotateEvent dial) { + public void dialAction(@Observes DeviceCommunicationHandler.KnobRotateEvent dial) { if (!mqtt.isConnected()) { return; } @@ -101,8 +103,7 @@ public void dialAction(DeviceCommunicationHandler.KnobRotateEvent dial) { }); } - @EventListener - public void buttonPress(DeviceCommunicationHandler.ButtonPressEvent btn) { + public void buttonPress(@Observes DeviceCommunicationHandler.ButtonPressEvent btn) { if (!mqtt.isConnected()) { return; } @@ -112,8 +113,7 @@ public void buttonPress(DeviceCommunicationHandler.ButtonPressEvent btn) { }); } - @EventListener - public void buttonPress(ButtonClickEvent btn) { + public void buttonPress(@Observes ButtonClickEvent btn) { if (!mqtt.isConnected()) { return; } @@ -123,8 +123,7 @@ public void buttonPress(ButtonClickEvent btn) { }); } - @EventListener - public void globalBrightnessChange(GlobalBrightnessChangedEvent event) { + public void globalBrightnessChange(@Observes GlobalBrightnessChangedEvent event) { if (!mqtt.isConnected()) { return; } diff --git a/src/main/java/com/getpcpanel/mqtt/MqttHomeAssistantHelper.java b/src/main/java/com/getpcpanel/mqtt/MqttHomeAssistantHelper.java index 83dd4b2a..822eb13d 100644 --- a/src/main/java/com/getpcpanel/mqtt/MqttHomeAssistantHelper.java +++ b/src/main/java/com/getpcpanel/mqtt/MqttHomeAssistantHelper.java @@ -9,22 +9,24 @@ import javax.annotation.Nullable; import org.apache.commons.lang3.StringUtils; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.stereotype.Service; +import org.eclipse.microprofile.config.inject.ConfigProperty; +import jakarta.inject.Inject; +import jakarta.enterprise.context.ApplicationScoped; import com.getpcpanel.device.Device; import com.getpcpanel.profile.MqttSettings; import lombok.RequiredArgsConstructor; -import lombok.extern.log4j.Log4j2; +import lombok.extern.jbosslog.JBossLog; -@Log4j2 -@Service -@RequiredArgsConstructor +@JBossLog +@ApplicationScoped public class MqttHomeAssistantHelper { - private final MqttTopicHelper topicHelper; - private final MqttService mqttService; - @Value("${application.version}") private String version; + @Inject + MqttTopicHelper topicHelper; + @Inject + MqttService mqttService; + @ConfigProperty(name="pcpanel.version") private String version; public void clearAll(MqttSettings settings) { var topic = StringUtils.joinWith("/", settings.homeAssistant().baseTopic(), "+", "pcpanel", "#"); diff --git a/src/main/java/com/getpcpanel/mqtt/MqttService.java b/src/main/java/com/getpcpanel/mqtt/MqttService.java index c31acb8f..a0ee21e3 100644 --- a/src/main/java/com/getpcpanel/mqtt/MqttService.java +++ b/src/main/java/com/getpcpanel/mqtt/MqttService.java @@ -13,10 +13,10 @@ import javax.annotation.Nullable; -import org.springframework.context.ApplicationEventPublisher; -import org.springframework.context.event.EventListener; -import org.springframework.core.annotation.Order; -import org.springframework.stereotype.Service; +import jakarta.enterprise.event.Event; +import jakarta.inject.Inject; +import jakarta.annotation.Priority; +import jakarta.enterprise.context.ApplicationScoped; import com.fasterxml.jackson.databind.ObjectMapper; import com.getpcpanel.profile.MqttSettings; @@ -29,19 +29,23 @@ import jakarta.annotation.PostConstruct; import lombok.RequiredArgsConstructor; -import lombok.extern.log4j.Log4j2; +import lombok.extern.jbosslog.JBossLog; -@Log4j2 -@Service -@RequiredArgsConstructor +@JBossLog +@ApplicationScoped public class MqttService { static final int ORDER_OF_SAVE = 0; public static final String IGNORE_CORRELATION = "pcpanel"; - private final SaveService saveService; - private final ApplicationEventPublisher eventPublisher; - private final ObjectMapper objectMapper; - private final Debouncer debouncer; - private final MqttTopicHelper topicHelper; + @Inject + SaveService saveService; + @Inject + Event eventBus; + @Inject + ObjectMapper objectMapper; + @Inject + Debouncer debouncer; + @Inject + MqttTopicHelper topicHelper; private MqttSettings connectedSettings; @Nullable private Mqtt5Client mqttClient; @@ -140,14 +144,13 @@ private Pattern topicToRegex(String topic) { ); } - @Order(ORDER_OF_SAVE) + @Priority(ORDER_OF_SAVE) @PostConstruct - @EventListener(SaveService.SaveEvent.class) - public void saveChanged() { + public void saveChanged() { var mqttSettings = saveService.get().getMqtt(); if (mqttSettings == null || !mqttSettings.enabled()) { disconnect(); - eventPublisher.publishEvent(new MqttStatusEvent(false)); + eventBus.fire(new MqttStatusEvent(false)); connectedSettings = MqttSettings.DEFAULT; return; } @@ -158,7 +161,7 @@ public void saveChanged() { log.trace("Save changed, starting mqtt"); connect(mqttSettings); connectedSettings = mqttSettings; - eventPublisher.publishEvent(new MqttStatusEvent(true)); + eventBus.fire(new MqttStatusEvent(true)); } private void connect(MqttSettings mqttSettings) { diff --git a/src/main/java/com/getpcpanel/mqtt/MqttTopicHelper.java b/src/main/java/com/getpcpanel/mqtt/MqttTopicHelper.java index ca886132..63928cb1 100644 --- a/src/main/java/com/getpcpanel/mqtt/MqttTopicHelper.java +++ b/src/main/java/com/getpcpanel/mqtt/MqttTopicHelper.java @@ -1,19 +1,20 @@ package com.getpcpanel.mqtt; -import org.springframework.stereotype.Service; +import jakarta.inject.Inject; +import jakarta.enterprise.context.ApplicationScoped; import com.getpcpanel.profile.MqttSettings; import com.getpcpanel.profile.SaveService; import lombok.RequiredArgsConstructor; -import lombok.extern.log4j.Log4j2; +import lombok.extern.jbosslog.JBossLog; import one.util.streamex.StreamEx; -@Log4j2 -@Service -@RequiredArgsConstructor +@JBossLog +@ApplicationScoped class MqttTopicHelper { - private final SaveService saveService; + @Inject + SaveService saveService; public DeviceMqttTopicHelper device(String deviceSerial) { return new DeviceMqttTopicHelper(deviceSerial); @@ -67,8 +68,7 @@ enum ColorType { logo, } - @RequiredArgsConstructor - class DeviceMqttTopicHelper { + class DeviceMqttTopicHelper { private final String deviceSerial; public String valueTopic(ValueType type, int index) { diff --git a/src/main/java/com/getpcpanel/obs/OBS.java b/src/main/java/com/getpcpanel/obs/OBS.java index ade01086..7d83745a 100644 --- a/src/main/java/com/getpcpanel/obs/OBS.java +++ b/src/main/java/com/getpcpanel/obs/OBS.java @@ -1,5 +1,61 @@ package com.getpcpanel.obs; +import java.util.List; +import java.util.Map; + +import com.getpcpanel.profile.SaveService; + +import jakarta.annotation.PostConstruct; +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.enterprise.event.Event; +import jakarta.inject.Inject; +import lombok.extern.jbosslog.JBossLog; + +/** + * OBS integration stub - disabled due to javax/jakarta conflict. + * TODO: Re-enable when obs4j migrates to jakarta. + */ +@JBossLog +@ApplicationScoped +public final class OBS { + @Inject SaveService save; + @Inject Event eventBus; + + @PostConstruct + public void init() { + log.info("OBS integration disabled (javax/jakarta conflict)"); + } + + public List getSourcesWithAudio() { + return List.of(); + } + + public Map getSourcesWithMuteState() { + return Map.of(); + } + + public List getScenes() { + return List.of(); + } + + public void setSourceVolume(String sourceName, int vol) {} + + public void toggleSourceMute(String sourceName) {} + + public void setSourceMute(String sourceName, boolean mute) {} + + public void setCurrentScene(String sceneName) {} + + public boolean isConnected() { + return false; + } + + public String test(String address, int port, String password, long timeout) { + return "OBS integration disabled"; + } +} + + import java.net.ConnectException; import java.net.SocketTimeoutException; import java.util.ArrayList; @@ -19,10 +75,10 @@ import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.math.NumberUtils; -import org.springframework.context.ApplicationEventPublisher; -import org.springframework.context.event.EventListener; -import org.springframework.scheduling.annotation.Scheduled; -import org.springframework.stereotype.Service; +import jakarta.enterprise.event.Event; +import jakarta.inject.Inject; +import io.quarkus.scheduler.Scheduled; +import jakarta.enterprise.context.ApplicationScoped; import com.getpcpanel.profile.SaveService; import com.getpcpanel.util.Util; @@ -40,18 +96,19 @@ import io.obswebsocket.community.client.model.Scene; import jakarta.annotation.PostConstruct; import lombok.RequiredArgsConstructor; -import lombok.extern.log4j.Log4j2; +import lombok.extern.jbosslog.JBossLog; import one.util.streamex.StreamEx; -@Log4j2 -@Service +@JBossLog +@ApplicationScoped @RequiredArgsConstructor public final class OBS { private static final long WAIT_TIME_MS = 1000L; private static final ObsIdHelper OBS_ID_HELPER = new ObsIdHelper(); private final SaveService save; - private final ApplicationEventPublisher eventPublisher; + @Inject + Event eventBus; private List previousSettings = List.of(); private boolean connected; private boolean shuttingDown; @@ -62,7 +119,7 @@ public void init() { Runtime.getRuntime().addShutdownHook(new Thread(this::applicationEnding, "OBS Shutdown hook")); } - @Scheduled(fixedRateString = "${pcpanel.obs.rate:2500}") + @Scheduled(every="${pcpanel.obs.rate:2500}s") public void connect() { if (!connected && !shuttingDown) { buildAndConnectObsController(); @@ -129,7 +186,7 @@ private void doBuildAndConnectObsController() { } private void onMuteChanged(InputMuteStateChangedEvent t) { - eventPublisher.publishEvent(new OBSMuteEvent(t.getMessageData().getEventData().getInputName(), t.getMessageData().getEventData().getInputMuted())); + eventBus.fire(new OBSMuteEvent(t.getMessageData().getEventData().getInputName(), t.getMessageData().getEventData().getInputMuted())); } private void disconnectController() { @@ -220,8 +277,7 @@ private void connected() { connected = true; } - @EventListener(SaveService.SaveEvent.class) - public void saveUpdated() { + public void saveUpdated() { buildAndConnectObsController(); } @@ -303,7 +359,7 @@ public boolean isConnected() { private void doConnected(boolean connected) { new Thread(() -> { - eventPublisher.publishEvent(new OBSConnectEvent(connected)); + eventBus.fire(new OBSConnectEvent(connected)); if (connected) { getSourcesWithMuteState(); } diff --git a/src/main/java/com/getpcpanel/obs/ObsConnectedVolumeService.java b/src/main/java/com/getpcpanel/obs/ObsConnectedVolumeService.java index 00e0f57c..489f08cd 100644 --- a/src/main/java/com/getpcpanel/obs/ObsConnectedVolumeService.java +++ b/src/main/java/com/getpcpanel/obs/ObsConnectedVolumeService.java @@ -2,24 +2,19 @@ import java.util.function.Function; -import org.springframework.context.ApplicationEventPublisher; -import org.springframework.context.event.EventListener; -import org.springframework.stereotype.Service; +import jakarta.enterprise.event.Event; +import jakarta.enterprise.event.Observes; +import jakarta.enterprise.context.ApplicationScoped; import com.getpcpanel.commands.AbstractNewXVolumeService; import com.getpcpanel.commands.command.CommandObsSetSourceVolume; import com.getpcpanel.hid.DeviceHolder; -import com.getpcpanel.spring.ConditionalOnWindows; +import com.getpcpanel.spring.WindowsImpl; -@Service -@ConditionalOnWindows +@ApplicationScoped +@WindowsImpl public class ObsConnectedVolumeService extends AbstractNewXVolumeService { - public ObsConnectedVolumeService(DeviceHolder devices, ApplicationEventPublisher eventPublisher) { - super(devices, eventPublisher); - } - - @EventListener - public void onVoiceMeeterConnected(OBSConnectEvent event) { + public void onVoiceMeeterConnected(@Observes OBSConnectEvent event) { if (event.connected()) { triggerCommandsOf(CommandObsSetSourceVolume.class, Function.identity()); } diff --git a/src/main/java/com/getpcpanel/osc/OSCService.java b/src/main/java/com/getpcpanel/osc/OSCService.java index 0b01a9c6..4d6663f1 100644 --- a/src/main/java/com/getpcpanel/osc/OSCService.java +++ b/src/main/java/com/getpcpanel/osc/OSCService.java @@ -10,8 +10,9 @@ import javax.annotation.Nonnull; -import org.springframework.context.event.EventListener; -import org.springframework.stereotype.Service; +import jakarta.inject.Inject; +import jakarta.enterprise.event.Observes; +import jakarta.enterprise.context.ApplicationScoped; import org.springframework.util.CollectionUtils; import com.getpcpanel.hid.DeviceCommunicationHandler; @@ -31,14 +32,14 @@ import jakarta.annotation.PostConstruct; import lombok.Getter; import lombok.RequiredArgsConstructor; -import lombok.extern.log4j.Log4j2; +import lombok.extern.jbosslog.JBossLog; import one.util.streamex.StreamEx; -@Log4j2 -@Service -@RequiredArgsConstructor +@JBossLog +@ApplicationScoped public class OSCService { - private final SaveService saveService; + @Inject + SaveService saveService; private OSCPortIn portIn; private List ports = List.of(); private Integer prevListenPort; @@ -46,8 +47,7 @@ public class OSCService { @Getter private final Set addresses = new HashSet<>(); @PostConstruct - @EventListener(SaveService.SaveEvent.class) - public void saveChanged() { + public void saveChanged() { log.trace("Save changed, restarting OSC"); initSend(); initListen(); @@ -105,8 +105,7 @@ private void stopPortIn() { } } - @EventListener - public void dialAction(DeviceCommunicationHandler.KnobRotateEvent dial) { + public void dialAction(@Observes DeviceCommunicationHandler.KnobRotateEvent dial) { if (dial.initial() || CollectionUtils.isEmpty(ports)) { return; } @@ -122,8 +121,7 @@ public void dialAction(DeviceCommunicationHandler.KnobRotateEvent dial) { }); } - @EventListener - public void dialAction(DeviceCommunicationHandler.ButtonPressEvent button) { + public void dialAction(@Observes DeviceCommunicationHandler.ButtonPressEvent button) { if (CollectionUtils.isEmpty(ports)) { return; } diff --git a/src/main/java/com/getpcpanel/profile/CommandMapDeserializer.java b/src/main/java/com/getpcpanel/profile/CommandMapDeserializer.java index 5fb7e784..0b32a19d 100644 --- a/src/main/java/com/getpcpanel/profile/CommandMapDeserializer.java +++ b/src/main/java/com/getpcpanel/profile/CommandMapDeserializer.java @@ -21,11 +21,11 @@ import com.getpcpanel.commands.command.CommandConverter; import lombok.RequiredArgsConstructor; -import lombok.extern.log4j.Log4j2; +import lombok.extern.jbosslog.JBossLog; import one.util.streamex.EntryStream; import one.util.streamex.IntStreamEx; -@Log4j2 +@JBossLog @RequiredArgsConstructor public class CommandMapDeserializer extends JsonDeserializer> { private final ObjectMapper mapper = new ObjectMapper(); diff --git a/src/main/java/com/getpcpanel/profile/LightingConfig.java b/src/main/java/com/getpcpanel/profile/LightingConfig.java index eb0a8f86..21e53371 100644 --- a/src/main/java/com/getpcpanel/profile/LightingConfig.java +++ b/src/main/java/com/getpcpanel/profile/LightingConfig.java @@ -257,4 +257,3 @@ public SingleLogoLightingConfig getLogoConfig() { return logoConfig; } } - diff --git a/src/main/java/com/getpcpanel/profile/ProfileWindowFocusService.java b/src/main/java/com/getpcpanel/profile/ProfileWindowFocusService.java index ffb4064e..fc95107c 100644 --- a/src/main/java/com/getpcpanel/profile/ProfileWindowFocusService.java +++ b/src/main/java/com/getpcpanel/profile/ProfileWindowFocusService.java @@ -1,21 +1,21 @@ package com.getpcpanel.profile; -import org.springframework.context.event.EventListener; -import org.springframework.stereotype.Service; +import jakarta.inject.Inject; +import jakarta.enterprise.event.Observes; +import jakarta.enterprise.context.ApplicationScoped; import com.getpcpanel.cpp.windows.WindowFocusChangedEvent; import com.getpcpanel.hid.DeviceHolder; import lombok.RequiredArgsConstructor; -@Service -@RequiredArgsConstructor +@ApplicationScoped public class ProfileWindowFocusService { - private final DeviceHolder devices; + @Inject + DeviceHolder devices; private String previousApplication = ""; - @EventListener - public void onFocusChanged(WindowFocusChangedEvent event) { + public void onFocusChanged(@Observes WindowFocusChangedEvent event) { devices.values().forEach(d -> d.focusChanged(previousApplication, event.application())); previousApplication = event.application(); } diff --git a/src/main/java/com/getpcpanel/profile/Save.java b/src/main/java/com/getpcpanel/profile/Save.java index df9bb4a1..7aa08370 100644 --- a/src/main/java/com/getpcpanel/profile/Save.java +++ b/src/main/java/com/getpcpanel/profile/Save.java @@ -12,10 +12,10 @@ import com.getpcpanel.ui.OverlayPosition; import lombok.Data; -import lombok.extern.log4j.Log4j2; +import lombok.extern.jbosslog.JBossLog; @Data -@Log4j2 +@JBossLog public class Save { public static final String DEFAULT_OVERLAY_BG_COLOR = "rgba(255, 255, 255, 0.5)"; public static final String DEFAULT_OVERLAY_TEXT_COLOR = "rgba(0, 0, 0, 1)"; diff --git a/src/main/java/com/getpcpanel/profile/SaveService.java b/src/main/java/com/getpcpanel/profile/SaveService.java index 773029b5..ca360d25 100644 --- a/src/main/java/com/getpcpanel/profile/SaveService.java +++ b/src/main/java/com/getpcpanel/profile/SaveService.java @@ -11,10 +11,136 @@ import org.apache.commons.io.FileUtils; import org.apache.commons.lang3.StringUtils; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.context.ApplicationEventPublisher; -import org.springframework.context.annotation.Lazy; -import org.springframework.stereotype.Service; + +import com.getpcpanel.Json; +import com.getpcpanel.hid.DeviceHolder; +import com.getpcpanel.util.Debouncer; +import com.getpcpanel.util.FileUtil; + +import jakarta.annotation.PostConstruct; +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.enterprise.event.Event; +import jakarta.inject.Inject; +import lombok.extern.jbosslog.JBossLog; +import one.util.streamex.StreamEx; + +@JBossLog +@ApplicationScoped +public class SaveService { + private static final String saveFileName = "profiles.json"; + @Inject Event eventBus; + @Inject FileUtil fileUtil; + @Inject Json json; + @Inject Debouncer debouncer; + @Inject DeviceHolder devices; + @SuppressWarnings("StaticNonFinalField") private static String oldVersionEncountered; + + private Save save; + + public Save get() { + return save; + } + + @PostConstruct + public void load() { + var saveFile = fileUtil.getFile(saveFileName); + if (!saveFile.exists()) { + tryMigrate(saveFile); + } + if (!saveFile.exists()) { + log.info("No save file found, creating new one"); + save = new Save(); + eventBus.fire(new SaveEvent(save, true)); + return; + } + + try { + save = json.read(FileUtils.readFileToString(saveFile, Charset.defaultCharset()), Save.class); + handleOldVersionEncountered(); + StreamEx.ofValues(save.getDevices()).forEach(d -> StreamEx.of(d.getProfiles()).findFirst(Profile::isMainProfile).ifPresent(p -> d.setCurrentProfile(p.getName()))); + eventBus.fire(new SaveEvent(save, false)); + } catch (Exception e) { + log.error("Unable to read file", e); + save = new Save(); + } + } + + private void handleOldVersionEncountered() { + if (StringUtils.isBlank(oldVersionEncountered)) { + return; + } + backup(); + save(); + } + + private void backup() { + try { + FileUtils.copyFile(fileUtil.getFile(saveFileName), fileUtil.getFile(saveFileName + "." + oldVersionEncountered + ".bak")); + } catch (IOException e) { + log.error("Unable to backup profile", e); + } + } + + /** + * Gets called when an older version of a profile is read. + */ + public static void encounterOldVersion(String version) { + oldVersionEncountered = version.replace('.', '_'); + } + + private void tryMigrate(File saveFile) { + @SuppressWarnings("CallToSystemGetenv") var oldFile = new File(System.getenv("LOCALAPPDATA"), "PCPanel Software/save.json"); + if (oldFile.exists()) { + var result = JOptionPane.showConfirmDialog(null, "No save file found, would you like to migrate from original PCPanel software?", "Migrate", JOptionPane.YES_NO_OPTION); + if (result == JOptionPane.YES_OPTION) { + try { + Files.copy(oldFile.toPath(), saveFile.toPath()); + } catch (IOException e) { + log.error("Unable to copy old save file", e); + } + log.info("Migrated old save file to new one"); + } + } + } + + public void save() { + var saveFile = fileUtil.getFile(saveFileName); + try { + FileUtils.writeStringToFile(saveFile, json.writePretty(save), Charset.defaultCharset()); + } catch (IOException e) { + log.error("Unable to save file", e); + } + + eventBus.fire(new SaveEvent(save, false)); + } + + public void debouncedSave() { + debouncer.debounce(this, this::save, 1, TimeUnit.SECONDS); + } + + public Optional getProfile(String serialNum) { + return devices.getDevice(serialNum).map(device -> get().getDeviceSave(serialNum).ensureCurrentProfile(device.getDeviceType())); + } + + public record SaveEvent(Save save, boolean isNew) { + } +} + + +import java.io.File; +import java.io.IOException; +import java.nio.charset.Charset; +import java.nio.file.Files; +import java.util.Optional; +import java.util.concurrent.TimeUnit; + +import javax.swing.JOptionPane; + +import org.apache.commons.io.FileUtils; +import org.apache.commons.lang3.StringUtils; +import jakarta.inject.Inject; +import jakarta.enterprise.event.Event; +import jakarta.enterprise.context.ApplicationScoped; import com.getpcpanel.Json; import com.getpcpanel.hid.DeviceHolder; @@ -24,19 +150,20 @@ import jakarta.annotation.PostConstruct; import lombok.RequiredArgsConstructor; import lombok.Setter; -import lombok.extern.log4j.Log4j2; +import lombok.extern.jbosslog.JBossLog; import one.util.streamex.StreamEx; -@Log4j2 -@Service +@JBossLog +@ApplicationScoped @RequiredArgsConstructor public class SaveService { private static final String saveFileName = "profiles.json"; - private final ApplicationEventPublisher eventPublisher; + @Inject + Event eventBus; private final FileUtil fileUtil; private final Json json; private final Debouncer debouncer; - @Autowired @Lazy @Setter private DeviceHolder devices; + @Inject @Lazy @Setter private DeviceHolder devices; @SuppressWarnings("StaticNonFinalField") private static String oldVersionEncountered; private Save save; @@ -54,7 +181,7 @@ public void load() { if (!saveFile.exists()) { log.info("No save file found, creating new one"); save = new Save(); - eventPublisher.publishEvent(new SaveEvent(save, true)); + eventBus.fire(new SaveEvent(save, true)); return; } @@ -62,7 +189,7 @@ public void load() { save = json.read(FileUtils.readFileToString(saveFile, Charset.defaultCharset()), Save.class); handleOldVersionEncountered(); StreamEx.ofValues(save.getDevices()).forEach(d -> StreamEx.of(d.getProfiles()).findFirst(Profile::isMainProfile).ifPresent(p -> d.setCurrentProfile(p.getName()))); - eventPublisher.publishEvent(new SaveEvent(save, false)); + eventBus.fire(new SaveEvent(save, false)); } catch (Exception e) { log.error("Unable to read file", e); save = new Save(); @@ -115,7 +242,7 @@ public void save() { log.error("Unable to save file", e); } - eventPublisher.publishEvent(new SaveEvent(save, false)); + eventBus.fire(new SaveEvent(save, false)); } public void debouncedSave() { @@ -129,4 +256,3 @@ public Optional getProfile(String serialNum) { public record SaveEvent(Save save, boolean isNew) { } } - diff --git a/src/main/java/com/getpcpanel/profile/SingleKnobLightingConfig.java b/src/main/java/com/getpcpanel/profile/SingleKnobLightingConfig.java index 1234b62b..e72b4205 100644 --- a/src/main/java/com/getpcpanel/profile/SingleKnobLightingConfig.java +++ b/src/main/java/com/getpcpanel/profile/SingleKnobLightingConfig.java @@ -50,4 +50,3 @@ public void set(SingleKnobLightingConfig c) { mode = c.mode; } } - diff --git a/src/main/java/com/getpcpanel/profile/SingleLogoLightingConfig.java b/src/main/java/com/getpcpanel/profile/SingleLogoLightingConfig.java index 105bc463..33b6b9e9 100644 --- a/src/main/java/com/getpcpanel/profile/SingleLogoLightingConfig.java +++ b/src/main/java/com/getpcpanel/profile/SingleLogoLightingConfig.java @@ -34,4 +34,3 @@ public SingleLogoLightingConfig setColor(String color) { return this; } } - diff --git a/src/main/java/com/getpcpanel/profile/SingleSliderLabelLightingConfig.java b/src/main/java/com/getpcpanel/profile/SingleSliderLabelLightingConfig.java index f3ad63cc..a731bdbf 100644 --- a/src/main/java/com/getpcpanel/profile/SingleSliderLabelLightingConfig.java +++ b/src/main/java/com/getpcpanel/profile/SingleSliderLabelLightingConfig.java @@ -37,4 +37,3 @@ public void set(SingleSliderLabelLightingConfig c) { mode = c.mode; } } - diff --git a/src/main/java/com/getpcpanel/profile/SingleSliderLightingConfig.java b/src/main/java/com/getpcpanel/profile/SingleSliderLightingConfig.java index d3f76174..89f7bafa 100644 --- a/src/main/java/com/getpcpanel/profile/SingleSliderLightingConfig.java +++ b/src/main/java/com/getpcpanel/profile/SingleSliderLightingConfig.java @@ -43,4 +43,3 @@ public void set(SingleSliderLightingConfig c) { mode = c.mode; } } - diff --git a/src/main/java/com/getpcpanel/sleepdetection/SleepDetector.java b/src/main/java/com/getpcpanel/sleepdetection/SleepDetector.java index c324cbd9..71ffa63a 100644 --- a/src/main/java/com/getpcpanel/sleepdetection/SleepDetector.java +++ b/src/main/java/com/getpcpanel/sleepdetection/SleepDetector.java @@ -1,7 +1,8 @@ package com.getpcpanel.sleepdetection; -import org.springframework.context.event.EventListener; -import org.springframework.stereotype.Service; +import jakarta.inject.Inject; +import jakarta.enterprise.event.Observes; +import jakarta.enterprise.context.ApplicationScoped; import com.getpcpanel.device.Device; import com.getpcpanel.hid.DeviceHolder; @@ -13,24 +14,25 @@ import javafx.application.Platform; import javafx.scene.paint.Color; import lombok.RequiredArgsConstructor; -import lombok.extern.log4j.Log4j2; +import lombok.extern.jbosslog.JBossLog; -@Log4j2 -@Service -@RequiredArgsConstructor +@JBossLog +@ApplicationScoped public final class SleepDetector { private static final LightingConfig ALL_OFF = LightingConfig.createAllColor(Color.BLACK); - private final DeviceScanner deviceScanner; - private final OutputInterpreter outputInterpreter; - private final DeviceHolder devices; + @Inject + DeviceScanner deviceScanner; + @Inject + OutputInterpreter outputInterpreter; + @Inject + DeviceHolder devices; @PostConstruct public void init() { Runtime.getRuntime().addShutdownHook(new Thread(() -> onSuspended(true), "Shutdown SleepDetector Hook Thread")); } - @EventListener - public void onEvent(WindowsSystemEventService.WindowsSystemEvent event) { + public void onEvent(@Observes WindowsSystemEventService.WindowsSystemEvent event) { switch (event.type()) { case goingToSuspend, locked -> onSuspended(false); case resumedFromSuspend, unlocked -> onResumed(); diff --git a/src/main/java/com/getpcpanel/sleepdetection/WindowsSystemEventService.java b/src/main/java/com/getpcpanel/sleepdetection/WindowsSystemEventService.java index 1be87a66..1a759685 100644 --- a/src/main/java/com/getpcpanel/sleepdetection/WindowsSystemEventService.java +++ b/src/main/java/com/getpcpanel/sleepdetection/WindowsSystemEventService.java @@ -1,7 +1,8 @@ package com.getpcpanel.sleepdetection; -import org.springframework.context.ApplicationEventPublisher; -import org.springframework.stereotype.Service; +import jakarta.enterprise.event.Event; +import jakarta.inject.Inject; +import jakarta.enterprise.context.ApplicationScoped; import com.getpcpanel.spring.ConditionalOnWindows; import com.sun.jna.WString; @@ -19,21 +20,21 @@ import jakarta.annotation.PostConstruct; import lombok.RequiredArgsConstructor; -import lombok.extern.log4j.Log4j2; +import lombok.extern.jbosslog.JBossLog; /** * https://gist.github.com/luanht/88ba957b94f94792a1fd */ -@Log4j2 -@Service -@ConditionalOnWindows -@RequiredArgsConstructor +@JBossLog +@ApplicationScoped +@WindowsImpl public class WindowsSystemEventService implements WindowProc { private static final int WM_POWERBROADCAST = 536; private static final int PBT_APMRESUMESUSPEND = 7; private static final int PBT_APMSUSPEND = 4; - private final ApplicationEventPublisher eventPublisher; + @Inject + Event eventBus; @PostConstruct public void init() { @@ -135,7 +136,7 @@ private void onPowerChange(WPARAM wParam) { } private void triggerEvent(WindowsSystemEventType type) { - eventPublisher.publishEvent(new WindowsSystemEvent(type)); + eventBus.fire(new WindowsSystemEvent(type)); } public record WindowsSystemEvent(WindowsSystemEventType type) { diff --git a/src/main/java/com/getpcpanel/spring/ConditionalOnLinux.java b/src/main/java/com/getpcpanel/spring/ConditionalOnLinux.java deleted file mode 100644 index 6ab92d88..00000000 --- a/src/main/java/com/getpcpanel/spring/ConditionalOnLinux.java +++ /dev/null @@ -1,26 +0,0 @@ -package com.getpcpanel.spring; - -import java.lang.annotation.Documented; -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -import org.apache.commons.lang3.SystemUtils; -import org.springframework.context.annotation.Condition; -import org.springframework.context.annotation.ConditionContext; -import org.springframework.context.annotation.Conditional; -import org.springframework.core.type.AnnotatedTypeMetadata; - -@Documented -@Retention(RetentionPolicy.RUNTIME) -@Target({ ElementType.TYPE, ElementType.METHOD }) -@Conditional(ConditionalOnLinux.OnLinuxCondition.class) -public @interface ConditionalOnLinux { - class OnLinuxCondition implements Condition { - @Override - public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) { - return SystemUtils.IS_OS_LINUX; - } - } -} diff --git a/src/main/java/com/getpcpanel/spring/ConditionalOnWayland.java b/src/main/java/com/getpcpanel/spring/ConditionalOnWayland.java deleted file mode 100644 index 3e57ebc0..00000000 --- a/src/main/java/com/getpcpanel/spring/ConditionalOnWayland.java +++ /dev/null @@ -1,36 +0,0 @@ -package com.getpcpanel.spring; - -import java.lang.annotation.Documented; -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -import javax.annotation.Nonnull; - -import org.apache.commons.lang3.StringUtils; -import org.springframework.context.annotation.Condition; -import org.springframework.context.annotation.ConditionContext; -import org.springframework.context.annotation.Conditional; -import org.springframework.core.type.AnnotatedTypeMetadata; - -import com.getpcpanel.spring.ConditionalOnWayland.OnWaylandCondition; - -@Documented -@Retention(RetentionPolicy.RUNTIME) -@Target(ElementType.TYPE) -@Conditional(OnWaylandCondition.class) -public @interface ConditionalOnWayland { - class OnWaylandCondition implements Condition { - private static final String WAYLAND = "wayland"; - - @Override - public boolean matches(@Nonnull ConditionContext context, @Nonnull AnnotatedTypeMetadata metadata) { - var xdgSessionType = context.getEnvironment().getProperty("XDG_SESSION_TYPE"); - var waylandDisplay = context.getEnvironment().getProperty("WAYLAND_DISPLAY"); - - return StringUtils.isNotBlank(waylandDisplay) - || StringUtils.equalsIgnoreCase(xdgSessionType, WAYLAND); - } - } -} diff --git a/src/main/java/com/getpcpanel/spring/ConditionalOnWindows.java b/src/main/java/com/getpcpanel/spring/ConditionalOnWindows.java deleted file mode 100644 index 1305814a..00000000 --- a/src/main/java/com/getpcpanel/spring/ConditionalOnWindows.java +++ /dev/null @@ -1,26 +0,0 @@ -package com.getpcpanel.spring; - -import java.lang.annotation.Documented; -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -import org.apache.commons.lang3.SystemUtils; -import org.springframework.context.annotation.Condition; -import org.springframework.context.annotation.ConditionContext; -import org.springframework.context.annotation.Conditional; -import org.springframework.core.type.AnnotatedTypeMetadata; - -@Documented -@Retention(RetentionPolicy.RUNTIME) -@Target({ ElementType.TYPE, ElementType.METHOD }) -@Conditional(ConditionalOnWindows.OnLinuxCondition.class) -public @interface ConditionalOnWindows { - class OnLinuxCondition implements Condition { - @Override - public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) { - return SystemUtils.IS_OS_WINDOWS; - } - } -} diff --git a/src/main/java/com/getpcpanel/spring/LinuxImpl.java b/src/main/java/com/getpcpanel/spring/LinuxImpl.java new file mode 100644 index 00000000..d177ed32 --- /dev/null +++ b/src/main/java/com/getpcpanel/spring/LinuxImpl.java @@ -0,0 +1,20 @@ +package com.getpcpanel.spring; + +import static java.lang.annotation.ElementType.FIELD; +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.ElementType.PARAMETER; +import static java.lang.annotation.ElementType.TYPE; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +import jakarta.inject.Qualifier; + +@Qualifier +@Documented +@Retention(RUNTIME) +@Target({TYPE, METHOD, FIELD, PARAMETER}) +public @interface LinuxImpl { +} diff --git a/src/main/java/com/getpcpanel/spring/OsHelper.java b/src/main/java/com/getpcpanel/spring/OsHelper.java index daf84b9b..ecf051a5 100644 --- a/src/main/java/com/getpcpanel/spring/OsHelper.java +++ b/src/main/java/com/getpcpanel/spring/OsHelper.java @@ -1,17 +1,49 @@ package com.getpcpanel.spring; +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.SystemUtils; + +import jakarta.enterprise.context.ApplicationScoped; + +@ApplicationScoped +public class OsHelper { + public static final String WINDOWS = "windows"; + public static final String LINUX = "linux"; + + public boolean notWindows() { + return !SystemUtils.IS_OS_WINDOWS; + } + + public boolean isLinux() { + return SystemUtils.IS_OS_LINUX; + } + + public String osString() { + if (SystemUtils.IS_OS_WINDOWS) { + return WINDOWS; + } + if (SystemUtils.IS_OS_LINUX) { + return LINUX; + } + return "unsupported"; + } + + public boolean isOs(String os) { + return StringUtils.equalsAny(os, "*", osString()); + } +} + import java.util.List; import java.util.Set; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.SystemUtils; -import org.springframework.stereotype.Service; import javafx.css.Styleable; import javafx.scene.Node; import one.util.streamex.StreamEx; -@Service +@ApplicationScoped public class OsHelper { public static final String WINDOWS = "windows"; public static final String LINUX = "linux"; diff --git a/src/main/java/com/getpcpanel/spring/PlatformProducer.java b/src/main/java/com/getpcpanel/spring/PlatformProducer.java new file mode 100644 index 00000000..f96bad87 --- /dev/null +++ b/src/main/java/com/getpcpanel/spring/PlatformProducer.java @@ -0,0 +1,74 @@ +package com.getpcpanel.spring; + +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.SystemUtils; + +import com.getpcpanel.cpp.ISndCtrl; +import com.getpcpanel.cpp.linux.pulseaudio.PulseAudioImpl; +import com.getpcpanel.cpp.linux.pulseaudio.SndCtrlPulseAudio; +import com.getpcpanel.cpp.windows.SndCtrlWindows; +import com.getpcpanel.iconextract.IIconService; +import com.getpcpanel.iconextract.IconServiceLinux; +import com.getpcpanel.iconextract.IconServiceWindows; +import com.getpcpanel.util.tray.ITrayService; +import com.getpcpanel.util.tray.awt.AwtTrayImpl; +import com.getpcpanel.util.tray.awt.TrayServiceAwt; +import com.getpcpanel.util.tray.wayland.TrayServiceWayland; + +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.enterprise.inject.Instance; +import jakarta.enterprise.inject.Produces; +import jakarta.inject.Inject; +import lombok.extern.jbosslog.JBossLog; + +@JBossLog +@ApplicationScoped +public class PlatformProducer { + + @Inject @WindowsImpl Instance windowsSndCtrl; + @Inject @PulseAudioImpl Instance pulseAudioSndCtrl; + @Inject @AwtTrayImpl Instance awtTray; + @Inject @WaylandImpl Instance waylandTray; + @Inject @WindowsImpl Instance windowsIcons; + @Inject @LinuxImpl Instance linuxIcons; + + @Produces + @ApplicationScoped + public ISndCtrl sndCtrl() { + if (SystemUtils.IS_OS_WINDOWS && !windowsSndCtrl.isUnsatisfied()) { + return windowsSndCtrl.get(); + } + if (SystemUtils.IS_OS_LINUX && !pulseAudioSndCtrl.isUnsatisfied()) { + return pulseAudioSndCtrl.get(); + } + log.warn("No ISndCtrl implementation found for current OS, using no-op"); + return ISndCtrl.noOp(); + } + + @Produces + @ApplicationScoped + public ITrayService trayService() { + var waylandDisplay = System.getenv("WAYLAND_DISPLAY"); + var xdgSessionType = System.getenv("XDG_SESSION_TYPE"); + boolean isWayland = StringUtils.isNotBlank(waylandDisplay) || StringUtils.equalsIgnoreCase(xdgSessionType, "wayland"); + if (SystemUtils.IS_OS_LINUX && isWayland && !waylandTray.isUnsatisfied()) { + return waylandTray.get(); + } + if (!awtTray.isUnsatisfied()) { + return awtTray.get(); + } + return new ITrayService.NoOp(); + } + + @Produces + @ApplicationScoped + public IIconService iconService() { + if (SystemUtils.IS_OS_WINDOWS && !windowsIcons.isUnsatisfied()) { + return windowsIcons.get(); + } + if (SystemUtils.IS_OS_LINUX && !linuxIcons.isUnsatisfied()) { + return linuxIcons.get(); + } + return (width, height, file) -> null; + } +} diff --git a/src/main/java/com/getpcpanel/spring/Prototype.java b/src/main/java/com/getpcpanel/spring/Prototype.java deleted file mode 100644 index 3a948f88..00000000 --- a/src/main/java/com/getpcpanel/spring/Prototype.java +++ /dev/null @@ -1,18 +0,0 @@ -package com.getpcpanel.spring; - -import static org.springframework.beans.factory.config.ConfigurableBeanFactory.SCOPE_PROTOTYPE; - -import java.lang.annotation.Documented; -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -import org.springframework.context.annotation.Scope; - -@Documented -@Scope(SCOPE_PROTOTYPE) -@Target(ElementType.TYPE) -@Retention(RetentionPolicy.RUNTIME) -public @interface Prototype { -} diff --git a/src/main/java/com/getpcpanel/spring/WaylandImpl.java b/src/main/java/com/getpcpanel/spring/WaylandImpl.java new file mode 100644 index 00000000..f4a1bcc4 --- /dev/null +++ b/src/main/java/com/getpcpanel/spring/WaylandImpl.java @@ -0,0 +1,20 @@ +package com.getpcpanel.spring; + +import static java.lang.annotation.ElementType.FIELD; +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.ElementType.PARAMETER; +import static java.lang.annotation.ElementType.TYPE; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +import jakarta.inject.Qualifier; + +@Qualifier +@Documented +@Retention(RUNTIME) +@Target({TYPE, METHOD, FIELD, PARAMETER}) +public @interface WaylandImpl { +} diff --git a/src/main/java/com/getpcpanel/spring/WindowsImpl.java b/src/main/java/com/getpcpanel/spring/WindowsImpl.java new file mode 100644 index 00000000..1a73b796 --- /dev/null +++ b/src/main/java/com/getpcpanel/spring/WindowsImpl.java @@ -0,0 +1,20 @@ +package com.getpcpanel.spring; + +import static java.lang.annotation.ElementType.FIELD; +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.ElementType.PARAMETER; +import static java.lang.annotation.ElementType.TYPE; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +import jakarta.inject.Qualifier; + +@Qualifier +@Documented +@Retention(RUNTIME) +@Target({TYPE, METHOD, FIELD, PARAMETER}) +public @interface WindowsImpl { +} diff --git a/src/main/java/com/getpcpanel/ui/AdvancedDevice.java b/src/main/java/com/getpcpanel/ui/AdvancedDevice.java deleted file mode 100644 index 76355ec6..00000000 --- a/src/main/java/com/getpcpanel/ui/AdvancedDevice.java +++ /dev/null @@ -1,69 +0,0 @@ -package com.getpcpanel.ui; - -import org.springframework.stereotype.Component; - -import com.getpcpanel.commands.DeviceSet; -import com.getpcpanel.cpp.AudioDevice; -import com.getpcpanel.cpp.ISndCtrl; -import com.getpcpanel.spring.Prototype; - -import javafx.fxml.FXML; -import javafx.scene.control.Button; -import javafx.scene.control.ComboBox; -import javafx.scene.control.TextField; -import javafx.scene.layout.HBox; -import javafx.scene.layout.VBox; -import lombok.Getter; -import lombok.RequiredArgsConstructor; -import one.util.streamex.StreamEx; - -@Component -@Prototype -@RequiredArgsConstructor -public class AdvancedDevice implements UIInitializer { - private final ISndCtrl sndCtrl; - @FXML private TextField name; - @FXML private ComboBox mediaPlayback; - @FXML private ComboBox mediaRecord; - @FXML private ComboBox communicationPlayback; - @FXML private ComboBox communicationRecord; - @Getter @FXML private VBox root; - @FXML private Button remove; - @FXML private HBox communicationPlaybackBox; - @FXML private HBox communicationRecordBox; - - public void initialize() { - var allSoundDevices = sndCtrl.getDevices().stream().toList(); - var outputDevices = allSoundDevices.stream().filter(AudioDevice::isOutput).toList(); - var inputDevices = allSoundDevices.stream().filter(AudioDevice::isInput).toList(); - - mediaPlayback.getItems().addAll(StreamEx.of(outputDevices).map(Object::toString).prepend("").toList()); - mediaRecord.getItems().addAll(StreamEx.of(inputDevices).map(Object::toString).prepend("").toList()); - communicationPlayback.getItems().addAll(StreamEx.of(outputDevices).map(Object::toString).prepend("").toList()); - communicationRecord.getItems().addAll(StreamEx.of(inputDevices).map(Object::toString).prepend("").toList()); - } - - public void set(String name, String mediaPlayback, String mediaRecord, String communicationPlayback, String communicationRecord) { - this.name.setText(name); - this.mediaPlayback.setValue(mediaPlayback); - this.mediaRecord.setValue(mediaRecord); - this.communicationPlayback.setValue(communicationPlayback); - this.communicationRecord.setValue(communicationRecord); - } - - public void removeCallback(Runnable callback) { - remove.setOnAction(e -> callback.run()); - remove.setVisible(true); - } - - public DeviceSet getEntry() { - return new DeviceSet(name.getText(), mediaPlayback.getValue(), mediaRecord.getValue(), communicationPlayback.getValue(), communicationRecord.getValue()); - } - - public void setOnlyMedia(boolean onlyMedia) { - communicationPlaybackBox.setVisible(!onlyMedia); - communicationPlaybackBox.setManaged(!onlyMedia); - communicationRecordBox.setVisible(!onlyMedia); - communicationRecordBox.setManaged(!onlyMedia); - } -} diff --git a/src/main/java/com/getpcpanel/ui/AdvancedDevices.java b/src/main/java/com/getpcpanel/ui/AdvancedDevices.java deleted file mode 100644 index 51978021..00000000 --- a/src/main/java/com/getpcpanel/ui/AdvancedDevices.java +++ /dev/null @@ -1,66 +0,0 @@ -package com.getpcpanel.ui; - -import java.util.ArrayList; -import java.util.List; - -import org.springframework.stereotype.Component; - -import com.getpcpanel.commands.DeviceSet; -import com.getpcpanel.spring.Prototype; - -import javafx.fxml.FXML; -import javafx.scene.layout.VBox; -import lombok.RequiredArgsConstructor; -import lombok.Setter; -import one.util.streamex.StreamEx; - -@Component -@Prototype -@RequiredArgsConstructor -public class AdvancedDevices { - private final FxHelper loader; - - @FXML private VBox target; - private final List controllers = new ArrayList<>(); - @Setter private boolean allowRemove; - private boolean onlyMedia; - - public void add() { - add("", "", "", "", ""); - } - - public void set(String name, String mediaPlayback, String mediaRecord, String communicationPlayback, String communicationRecord) { - controllers.clear(); - target.getChildren().clear(); - - add(name, mediaPlayback, mediaRecord, communicationPlayback, communicationRecord); - } - - public void add(DeviceSet entry) { - add(entry.name(), entry.mediaPlayback(), entry.mediaRecord(), entry.communicationPlayback(), entry.communicationRecord()); - } - - public void add(String name, String mediaPlayback, String mediaRecord, String communicationPlayback, String communicationRecord) { - var device = loader.open(AdvancedDevice.class, null); - device.set(name, mediaPlayback, mediaRecord, communicationPlayback, communicationRecord); - if (allowRemove) { - device.removeCallback(() -> { - target.getChildren().remove(device.getRoot()); - controllers.remove(device); - }); - } - - target.getChildren().add(device.getRoot()); - device.setOnlyMedia(onlyMedia); - controllers.add(device); - } - - public List getEntries() { - return StreamEx.of(controllers).map(AdvancedDevice::getEntry).toList(); - } - - public void setOnlyMedia(boolean onlyMedia) { - this.onlyMedia = onlyMedia; - controllers.forEach(c -> c.setOnlyMedia(onlyMedia)); - } -} diff --git a/src/main/java/com/getpcpanel/ui/AppFinderDialog.java b/src/main/java/com/getpcpanel/ui/AppFinderDialog.java deleted file mode 100644 index 0e5cfd50..00000000 --- a/src/main/java/com/getpcpanel/ui/AppFinderDialog.java +++ /dev/null @@ -1,196 +0,0 @@ -package com.getpcpanel.ui; - -import java.awt.image.BufferedImage; -import java.io.File; -import java.io.IOException; -import java.util.ArrayList; -import java.util.Comparator; -import java.util.List; -import java.util.Objects; - -import javax.annotation.Nonnull; -import javax.annotation.Nullable; -import javax.imageio.ImageIO; - -import org.apache.commons.lang3.StringUtils; -import org.springframework.stereotype.Component; - -import com.getpcpanel.cpp.AudioSession; -import com.getpcpanel.cpp.ISndCtrl; -import com.getpcpanel.iconextract.IIconService; -import com.getpcpanel.spring.Prototype; - -import javafx.application.Application; -import javafx.embed.swing.SwingFXUtils; -import javafx.fxml.FXML; -import javafx.scene.Scene; -import javafx.scene.control.Button; -import javafx.scene.control.ContentDisplay; -import javafx.scene.control.ScrollPane; -import javafx.scene.control.TextField; -import javafx.scene.image.Image; -import javafx.scene.image.ImageView; -import javafx.scene.input.KeyEvent; -import javafx.scene.layout.FlowPane; -import javafx.scene.text.Font; -import javafx.scene.text.TextAlignment; -import javafx.stage.Modality; -import javafx.stage.Stage; -import lombok.RequiredArgsConstructor; -import lombok.extern.log4j.Log4j2; -import one.util.streamex.StreamEx; - -@Log4j2 -@Component -@Prototype -@RequiredArgsConstructor -public class AppFinderDialog extends Application implements UIInitializer { - private static final int ICON_SIZE = 90; - private final ISndCtrl sndCtrl; - private final IIconService iconService; - @Nullable private Stage parentStage; - private boolean volumeApps; - private Stage stage; - @FXML private FlowPane flowPane; - @FXML private ScrollPane scroll; - @FXML private TextField filterField; - private final List allProgs = new ArrayList<>(); - private String processName; - - @Override - public void initUI(@Nonnull AppFinderParams args) { - parentStage = args.parentStage(); - volumeApps = args.volumeApps; - postInit(); - } - - @Override - public void start(Stage stage) { - start(stage, false); - } - - public void start(Stage stage, boolean andWait) { - this.stage = stage; - UIHelper.closeOnEscape(stage); - var scene = new Scene(scroll); - scene.getStylesheets().add(Objects.requireNonNull(getClass().getResource("/assets/dark_theme.css")).toExternalForm()); - stage.getIcons().add(new Image(Objects.requireNonNull(getClass().getResource("/assets/256x256.png")).toExternalForm())); - stage.setScene(scene); - stage.sizeToScene(); - stage.setTitle("Application Finder"); - if (parentStage != null || andWait) { - stage.initModality(Modality.WINDOW_MODAL); - stage.initOwner(parentStage); - stage.showAndWait(); - } else { - stage.show(); - } - } - - public String getProcessName() { - return processName; - } - - private static BufferedImage resize(BufferedImage img) { - var tmp = img.getScaledInstance(ICON_SIZE, ICON_SIZE, 4); - var dimg = new BufferedImage(ICON_SIZE, ICON_SIZE, 2); - var g2d = dimg.createGraphics(); - g2d.drawImage(tmp, 0, 0, null); - g2d.dispose(); - return dimg; - } - - private BufferedImage toBufferedImage(File f) throws IOException { - BufferedImage bi; - try { - bi = iconService.getIconForFile(ICON_SIZE, ICON_SIZE, f); - } catch (Exception e) { - return getDefaultImage(); - } - if (bi == null) - return getDefaultImage(); - return bi; - } - - private static BufferedImage getDefaultImage() throws IOException { - return resize(ImageIO.read(Objects.requireNonNull(AppFinderDialog.class.getResourceAsStream("/assets/DefaultExeIcon.ico")))); - } - - private ImageView getImage(AudioSession session) throws Exception { - BufferedImage bi; - if (session.isSystemSounds()) { - bi = resize(ImageIO.read(Objects.requireNonNull(getClass().getResourceAsStream("/assets/systemsounds.ico")))); - } else { - bi = toBufferedImage(session.executable()); - } - var writableImage = SwingFXUtils.toFXImage(bi, null); - return new ImageView(writableImage); - } - - private List getProgs() { - if (volumeApps) { - return StreamEx.of(sndCtrl.getAllSessions()) - .remove(as -> StringUtils.isBlank(as.title()) && (as.executable() == null || StringUtils.isBlank(as.executable().toString()))) - .sorted(Comparator.comparing((AudioSession as) -> StringUtils.defaultString(as.title(), "zzz")) - .thenComparing(as -> as.executable() == null ? "zzz" : as.executable().getName())) - .toImmutableList(); - } else { - return StreamEx.of(sndCtrl.getRunningApplications()) - .map(f -> new AudioSession(null, 1, f.file(), f.name(), null, 0, false)) - .distinct(AudioSession::executable) - .toList(); - } - } - - private void postInit() { - scroll.viewportBoundsProperty().addListener((ov, oldBounds, bounds) -> { - flowPane.setPrefWidth(bounds.getWidth()); - flowPane.setPrefHeight(bounds.getHeight()); - }); - var font = new Font(18.0D); - try { - var apps = getProgs(); - for (var app : apps) { - var iv = getImage(app); - var button = new Button(app.title(), iv); - var size = 180; - var ivSize = 64; - iv.minHeight(ivSize); - iv.minWidth(ivSize); - iv.maxHeight(ivSize); - iv.maxWidth(ivSize); - button.setMinSize(size, size); - button.setMaxSize(size, size); - button.setGraphicTextGap(10.0D); - button.setFont(font); - button.setWrapText(true); - button.setTextAlignment(TextAlignment.CENTER); - button.setContentDisplay(ContentDisplay.TOP); - button.setOnAction(a -> { - processName = app.isSystemSounds() ? AudioSession.SYSTEM : StringUtils.firstNonBlank(app.executable().getName(), app.title()); - stage.close(); - }); - allProgs.add(new ButtonTitleExe(button, app.title(), app.executable().getName())); - } - StreamEx.of(allProgs).map(ButtonTitleExe::button).toListAndThen(flowPane.getChildren()::addAll); - } catch (Exception e) { - log.error("Unable to add app-buttons", e); - } - } - - @FXML - private void onFilterChanged(KeyEvent event) { - var filter = filterField.getText(); - flowPane.getChildren().clear(); - StreamEx.of(allProgs).filter(b -> b.matches(filter)).map(ButtonTitleExe::button).toListAndThen(flowPane.getChildren()::addAll); - } - - private record ButtonTitleExe(Button button, String title, String exe) { - boolean matches(String filter) { - return StringUtils.containsIgnoreCase(title, filter) || StringUtils.containsIgnoreCase(exe, filter); - } - } - - record AppFinderParams(@Nullable Stage parentStage, boolean volumeApps) { - } -} diff --git a/src/main/java/com/getpcpanel/ui/BasicMacro.java b/src/main/java/com/getpcpanel/ui/BasicMacro.java deleted file mode 100644 index c9a962c0..00000000 --- a/src/main/java/com/getpcpanel/ui/BasicMacro.java +++ /dev/null @@ -1,195 +0,0 @@ -package com.getpcpanel.ui; - -import java.util.Objects; -import java.util.regex.Pattern; - -import javax.annotation.Nonnull; -import javax.annotation.Nullable; - -import org.apache.commons.lang3.StringUtils; -import org.apache.commons.lang3.math.NumberUtils; -import org.springframework.stereotype.Component; - -import com.getpcpanel.device.Device; -import com.getpcpanel.profile.KnobSetting; -import com.getpcpanel.profile.SaveService; -import com.getpcpanel.spring.Prototype; -import com.getpcpanel.ui.command.ButtonController; -import com.getpcpanel.ui.command.Cmd; -import com.getpcpanel.ui.command.CommandContext; - -import javafx.application.Application; -import javafx.event.ActionEvent; -import javafx.fxml.FXML; -import javafx.scene.Scene; -import javafx.scene.control.Button; -import javafx.scene.control.CheckBox; -import javafx.scene.control.TabPane; -import javafx.scene.control.TextField; -import javafx.scene.image.Image; -import javafx.scene.layout.Pane; -import javafx.stage.Modality; -import javafx.stage.Stage; -import lombok.RequiredArgsConstructor; -import lombok.extern.log4j.Log4j2; - -@Log4j2 -@Component -@Prototype -@RequiredArgsConstructor -public class BasicMacro extends Application implements UIInitializer { - private static final Pattern NUMBER_PATTERN = Pattern.compile("\\d*"); - private static final Pattern NOT_NUMBER_PATTERN = Pattern.compile("\\D"); - private final SaveService saveService; - private CommandContext context; - - @FXML private Pane root; - @FXML private ButtonController singleClickPanelController; - @FXML private ButtonController doubleClickPanelController; - @FXML private ButtonController dialPanelController; - - @FXML private Pane topPane; - @FXML private TabPane mainTabPane; - @FXML private Button scFileButton; - - @FXML private TextField trimMin; - @FXML private TextField trimMax; - @FXML private TextField iconFld; - @FXML private TextField buttonDebounceTime; - @FXML private CheckBox logarithmic; - private Stage stage; - private int dialNum; - private KnobSetting knobSetting; - @Nullable private String name; - - @Override - public void initUI(@Nonnull MacroArgs args) { - var device = args.device(); - dialNum = args.dialNum(); - var hasButton = args.hasButton(); - name = args.name(); - var analogType = args.analogType(); - - var deviceSave = saveService.get().getDeviceSave(device.getSerialNumber()); - var profile = deviceSave.ensureCurrentProfile(device.getDeviceType()); - knobSetting = profile.getKnobSettings(dialNum); - - // Do this before possibly removing the first 2 tabs - if (analogType != null) { - mainTabPane.getTabs().get(2).setText(analogType); - } - context = new CommandContext(stage, deviceSave, profile); - dialPanelController.initController(Cmd.Type.dial, context, profile.getDialData(dialNum)); - dialPanelController.setupGraphRenderer(trimMin, trimMax, logarithmic); - - if (hasButton) { - singleClickPanelController.initController(Cmd.Type.button, context, profile.getButtonData(dialNum)); - doubleClickPanelController.initController(Cmd.Type.button, context, profile.getDblButtonData(dialNum)); - } else { - mainTabPane.getTabs().remove(0); - mainTabPane.getTabs().remove(0); - } - - postInit(); - } - - @Override - public void start(Stage basicmacro) throws Exception { - stage = basicmacro; - dialPanelController.setStage(stage); - UIHelper.closeOnEscape(stage); - var scene = new Scene(root); - scene.getStylesheets().add(Objects.requireNonNull(getClass().getResource("/assets/dark_theme.css")).toExternalForm()); - basicmacro.getIcons().add(new Image(Objects.requireNonNull(getClass().getResource("/assets/256x256.png")).toExternalForm())); - basicmacro.initModality(Modality.APPLICATION_MODAL); - basicmacro.setScene(scene); - basicmacro.sizeToScene(); - basicmacro.setTitle(Objects.requireNonNullElseGet(name, () -> "Knob " + (dialNum + 1))); - basicmacro.show(); - } - - @FXML - private void ok(ActionEvent event) { - var buttonData = singleClickPanelController.determineButtonCommand(); - var dblButtonData = doubleClickPanelController.determineButtonCommand(); - var volData = dialPanelController.determineButtonCommand(); - knobSetting.setMinTrim(NumberUtils.toInt(trimMin.getText(), 0)); - knobSetting.setMaxTrim(NumberUtils.toInt(trimMax.getText(), 100)); - knobSetting.setOverlayIcon(iconFld.getText()); - knobSetting.setButtonDebounce(NumberUtils.toInt(buttonDebounceTime.getText(), 50)); - knobSetting.setLogarithmic(logarithmic.isSelected()); - - var profile = context.profile(); - profile.setButtonData(dialNum, buttonData); - profile.setDblButtonData(dialNum, dblButtonData); - profile.setDialData(dialNum, volData); - if (log.isDebugEnabled()) { - log.debug("-----------------"); - log.debug(buttonData); - log.debug(dblButtonData); - log.debug(volData); - log.debug("-----------------"); - } - saveService.save(); - stage.close(); - } - - @FXML - public void iconFile(ActionEvent event) { - UIHelper.showFilePicker("Pick file", iconFld); - } - - @FXML - private void closeButtonAction() { - stage.close(); - } - - private void postInit() { - trimMin.textProperty().addListener((observable, oldValue, newValue) -> trimMinMax(oldValue, newValue, trimMin)); - trimMax.textProperty().addListener((observable, oldValue, newValue) -> trimMinMax(oldValue, newValue, trimMax)); - buttonDebounceTime.textProperty().addListener((observable, oldValue, newValue) -> { - if (!NUMBER_PATTERN.matcher(newValue).matches() || newValue.contains("-") || StringUtils.isBlank(newValue)) { - buttonDebounceTime.setText(NOT_NUMBER_PATTERN.matcher(newValue.replace("-", "")).replaceAll("")); - } else { - var num = Integer.parseInt(newValue); - if (num < 0 || num > 10000) { - buttonDebounceTime.setText(oldValue); - return; - } - buttonDebounceTime.setText(String.valueOf(num)); - } - }); - - try { - initFields(); - } catch (Exception e) { - log.error("Unable to init fields", e); - } - } - - private void trimMinMax(String oldValue, String newValue, TextField trimMin) { - if (!NUMBER_PATTERN.matcher(newValue).matches() || newValue.contains("-") || StringUtils.isBlank(newValue)) { - trimMin.setText(NOT_NUMBER_PATTERN.matcher(newValue.replace("-", "")).replaceAll("")); - } else { - var num = Integer.parseInt(newValue); - if (num < 0 || num > 100) { - trimMin.setText(oldValue); - return; - } - trimMin.setText(String.valueOf(num)); - } - } - - private void initFields() { - if (knobSetting != null) { - trimMin.setText(String.valueOf(knobSetting.getMinTrim())); - trimMax.setText(String.valueOf(knobSetting.getMaxTrim())); - iconFld.setText(StringUtils.defaultString(knobSetting.getOverlayIcon(), "")); - buttonDebounceTime.setText(String.valueOf(knobSetting.getButtonDebounce())); - logarithmic.setSelected(knobSetting.isLogarithmic()); - } - } - - public record MacroArgs(Device device, Integer dialNum, boolean hasButton, @Nullable String name, @Nullable String analogType) { - } -} diff --git a/src/main/java/com/getpcpanel/ui/DeviceCell.java b/src/main/java/com/getpcpanel/ui/DeviceCell.java deleted file mode 100644 index 1dac2e25..00000000 --- a/src/main/java/com/getpcpanel/ui/DeviceCell.java +++ /dev/null @@ -1,89 +0,0 @@ -package com.getpcpanel.ui; - -import java.util.Objects; - -import com.getpcpanel.device.Device; -import com.getpcpanel.profile.SaveService; - -import javafx.geometry.Pos; -import javafx.scene.control.ContentDisplay; -import javafx.scene.control.ListCell; -import javafx.scene.control.ListView; -import javafx.scene.image.ImageView; -import javafx.scene.input.MouseButton; -import javafx.scene.layout.VBox; -import javafx.util.Callback; - -public class DeviceCell extends ListCell { - private final SaveService saveService; - private final ImageView imageView = new ImageView(); - private final LimitedTextField textField = new LimitedTextField(15); - private final VBox vbox = new VBox(imageView); - private final ListView listView; - - public static Callback, ListCell> buildFactory(SaveService saveService) { - return cell -> new DeviceCell(saveService, cell); - } - - public DeviceCell(SaveService saveService, ListView listView) { - this.saveService = saveService; - this.listView = listView; - setContentDisplay(ContentDisplay.BOTTOM); - vbox.getStylesheets().addAll(Objects.requireNonNull(getClass().getResource("/assets/1.css"), "Unable to find 1.css").toExternalForm()); - vbox.setAlignment(Pos.TOP_CENTER); - textField.setOnAction(e -> { - if (textField.getText().trim().isEmpty()) - return; - commitEdit(getItem()); - }); - textField.setMaxWidth(listView.getWidth() - 30.0D); - textField.setMaxHeight(30.0D); - setOnMouseClicked(mouseClickedEvent -> { - if (mouseClickedEvent.getButton() == MouseButton.PRIMARY && mouseClickedEvent.getClickCount() == 1) - if (isEditing()) - cancelEdit(); - }); - } - - @Override - protected void updateItem(Device device, boolean empty) { - super.updateItem(device, empty); - if (empty || device == null) { - setGraphic(null); - setText(""); - setMaxHeight(-1.0D); - } else { - setGraphic(vbox); - imageView.setImage(device.getPreviewImage()); - setText(device.getDisplayName()); - vbox.setMaxHeight(30.0D + imageView.getFitHeight()); - } - } - - @Override - public void startEdit() { - super.startEdit(); - textField.setText(getText()); - vbox.getChildren().add(0, textField); - setText(""); - textField.requestFocus(); - textField.selectAll(); - } - - @Override - public void cancelEdit() { - super.cancelEdit(); - vbox.getChildren().remove(0); - setText(getItem().getDisplayName()); - } - - @Override - public void commitEdit(Device device) { - var newValue = textField.getText().trim(); - super.commitEdit(device); - listView.getSelectionModel().select(device); - setText(newValue); - device.setDisplayName(newValue); - saveService.save(); - } -} diff --git a/src/main/java/com/getpcpanel/ui/FxHelper.java b/src/main/java/com/getpcpanel/ui/FxHelper.java deleted file mode 100644 index 356b88d8..00000000 --- a/src/main/java/com/getpcpanel/ui/FxHelper.java +++ /dev/null @@ -1,84 +0,0 @@ -package com.getpcpanel.ui; - -import java.io.IOException; -import java.net.URL; - -import javax.annotation.Nonnull; -import javax.annotation.Nullable; - -import org.springframework.stereotype.Service; - -import com.getpcpanel.MainFX; -import com.getpcpanel.device.Device; -import com.getpcpanel.device.PCPanelMiniUI; -import com.getpcpanel.device.PCPanelProUI; -import com.getpcpanel.device.PCPanelRGBUI; -import com.getpcpanel.profile.DeviceSave; -import com.getpcpanel.profile.Profile; -import com.getpcpanel.ui.AppFinderDialog.AppFinderParams; -import com.getpcpanel.ui.BasicMacro.MacroArgs; -import com.getpcpanel.ui.ProfileSettingsDialog.ProfileSettingsArgs; -import com.getpcpanel.ui.UIInitializer.SingleParamInitializer; - -import javafx.fxml.FXMLLoader; -import javafx.stage.Stage; -import lombok.RequiredArgsConstructor; - -/** - * Factory for creating FX dialogs - */ -@Service -@RequiredArgsConstructor -public class FxHelper { - public FXMLLoader getLoader(@Nullable URL location) { - var loader = new FXMLLoader(location); - loader.setControllerFactory(MainFX::getBean); - return loader; - } - - public > @Nonnull T open(@Nonnull Class dialogClass, @Nullable P params) { - var loader = getLoader(getClass().getResource("/assets/%s.fxml".formatted(dialogClass.getSimpleName()))); - try { - loader.load(); - var controller = loader.getController(); - if (params != null) { - controller.initUI(params); - } - return controller; - } catch (IOException e) { - throw new RuntimeException(e); - } - } - - public ProfileSettingsDialog buildProfileSettingsDialog(DeviceSave save, Profile profile) { - return open(ProfileSettingsDialog.class, new ProfileSettingsArgs(save, profile)); - } - - public SettingsDialog buildSettingsDialog(Stage parentStage) { - return open(SettingsDialog.class, new SingleParamInitializer<>(parentStage)); - } - - public RGBLightingDialog buildRGBLightingDialog(PCPanelRGBUI device) { - return open(RGBLightingDialog.class, new SingleParamInitializer<>(device)); - } - - public ProLightingDialog buildProLightingDialog(PCPanelProUI device) { - return open(ProLightingDialog.class, new SingleParamInitializer<>(device)); - } - - public MiniLightingDialog buildMiniLightingDialog(PCPanelMiniUI device) { - return open(MiniLightingDialog.class, new SingleParamInitializer<>(device)); - } - - public BasicMacro buildBasicMacro(Device device, int knob, boolean hasButton, String name, String analogType) { - return open(BasicMacro.class, new MacroArgs(device, knob, hasButton, name, analogType)); - } - - public BasicMacro buildBasicMacro(Device device, int knob) { - return open(BasicMacro.class, new MacroArgs(device, knob, true, null, null)); - } - - public AppFinderDialog buildAppFinderDialog(@Nullable Stage parentStage, boolean volumeApps) { - return open(AppFinderDialog.class, new AppFinderParams(parentStage, volumeApps)); - } -} diff --git a/src/main/java/com/getpcpanel/ui/HomePage.java b/src/main/java/com/getpcpanel/ui/HomePage.java deleted file mode 100644 index c5188c14..00000000 --- a/src/main/java/com/getpcpanel/ui/HomePage.java +++ /dev/null @@ -1,265 +0,0 @@ -package com.getpcpanel.ui; - -import java.util.Objects; - -import org.apache.commons.lang3.StringUtils; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.context.ApplicationEventPublisher; -import org.springframework.context.event.EventListener; -import org.springframework.stereotype.Component; - -import com.getpcpanel.device.Device; -import com.getpcpanel.hid.DeviceHolder; -import com.getpcpanel.hid.DeviceScanner; -import com.getpcpanel.profile.SaveService; -import com.getpcpanel.util.version.VersionChecker.NewVersionAvailableEvent; - -import jakarta.annotation.PostConstruct; -import javafx.application.Application; -import javafx.application.Platform; -import javafx.fxml.FXML; -import javafx.scene.Scene; -import javafx.scene.control.Button; -import javafx.scene.control.Label; -import javafx.scene.control.ListView; -import javafx.scene.control.Slider; -import javafx.scene.image.Image; -import javafx.scene.layout.Pane; -import javafx.scene.layout.Region; -import javafx.scene.layout.VBox; -import javafx.scene.text.Font; -import javafx.stage.Stage; -import javafx.stage.StageStyle; -import lombok.RequiredArgsConstructor; -import lombok.Setter; -import lombok.extern.log4j.Log4j2; -import one.util.streamex.StreamEx; - -@Log4j2 -@Component -@RequiredArgsConstructor -public class HomePage extends Application { - private static final String TITLE_FORMAT = "PCPanel Controller %s"; - @SuppressWarnings("StaticNonFinalField") @Setter private static HomePage window; - @SuppressWarnings("StaticNonFinalField") @Setter public static Stage stage; - private final FxHelper fxHelper; - private final SaveService saveService; - private final DeviceScanner deviceScanner; - private final DeviceHolder devices; - private final ApplicationEventPublisher applicationEventPublisher; - - @Value("${application.version}") private String version; - @Value("${application.build}") private String build; - - @FXML private Pane deviceHolder; - @FXML private Pane titleHolder; - @FXML private Pane hintHolder; - @FXML private Pane lightingButtonHolder; - @FXML private Pane profileHolder; - @FXML private Button close; - @FXML private Button min; - @FXML private Button deviceListToggle; - @FXML private Button settings; - @FXML private Label versionLabel; - @FXML private VBox labelTarget; - @FXML private Label noDevicesLabel; - @FXML private Label hintLabel; - @FXML private ListView connectedDeviceList; - @FXML private Slider globalBrightness; - private Pane pane; - - @Override - @PostConstruct - public void init() { - if (window != null) { - log.error("Error 2 windows"); - return; - } - setWindow(this); - } - - public void start(Stage stage, boolean quiet) throws Exception { - start(stage); - if (!quiet) - stage.show(); - } - - @Override - public void start(Stage stage) throws Exception { - setStage(stage); - var loader = fxHelper.getLoader(getClass().getResource("/assets/HomePage.fxml")); - loader.setController(this); - pane = loader.load(); - pane.setId("pane"); - var scene = new Scene(pane, 1000.0D, 870.0D); - showHint(false); - initWindow(); - scene.getStylesheets().addAll(Objects.requireNonNull(getClass().getResource("/assets/1.css")).toExternalForm()); - stage.getIcons().add(new Image(Objects.requireNonNull(getClass().getResource("/assets/256x256.png")).toExternalForm())); - stage.setScene(scene); - ResizeHelper.addResizeListener(stage, 200.0D, 200.0D); - Platform.setImplicitExit(false); - stage.initStyle(StageStyle.UNDECORATED); - stage.sizeToScene(); - stage.setTitle(TITLE_FORMAT.formatted(version)); - addBrightnessListener(); - - deviceScanner.init(); - } - - @EventListener - public void globalBrightnessChanged(GlobalBrightnessChangedEvent event) { - if (!event.isSource(this)) { - globalBrightness.setValue(connectedDeviceList.getSelectionModel().getSelectedItem().getLightingConfig().getGlobalBrightness()); - } - } - - private void addBrightnessListener() { - globalBrightness.valueProperty().addListener((observable, oldValue, newValue) -> { - var device = connectedDeviceList.getSelectionModel().getSelectedItem(); - if (device == null) { - return; - } - var serialNumber = device.getSerialNumber(); - - // Set saved brightness - saveService.getProfile(serialNumber).ifPresent(profile -> { - var cfg = profile.getLightingConfig(); - cfg.setGlobalBrightness(newValue.byteValue()); - profile.setLightingConfig(cfg); - saveService.debouncedSave(); - }); - - // Set current brightness - var light = device.getLightingConfig(); - light.setGlobalBrightness(newValue.byteValue()); - device.setLighting(light, false); - applicationEventPublisher.publishEvent(new GlobalBrightnessChangedEvent(this, serialNumber, newValue.intValue())); - }); - } - - private String buildVersion() { - return version + (StringUtils.containsIgnoreCase(version, "snapshot") ? " (" + build + ")" : ""); - } - - public static void showHint(boolean show) { - if (show) { - if (!window.hintHolder.getChildren().contains(window.hintLabel)) - window.hintHolder.getChildren().add(window.hintLabel); - } else - window.hintHolder.getChildren().remove(window.hintLabel); - } - - @EventListener(ShowMainEvent.class) - public void reopen() { - Platform.runLater(() -> { - stage.show(); - stage.setIconified(false); - stage.toFront(); - }); - } - - @EventListener - public void onDeviceConnected(DeviceScanner.DeviceConnectedEvent event) { - Platform.runLater(() -> devices.getDevice(event.serialNum()).ifPresent(this::addDeviceToUI)); - } - - @EventListener - public void onDeviceDisconnected(DeviceScanner.DeviceDisconnectedEvent event) { - Platform.runLater(() -> StreamEx.of(connectedDeviceList.getItems()).filterBy(Device::getSerialNumber, event.serialNum()).findFirst().ifPresent(connectedDeviceList.getItems()::remove)); - } - - private void addDeviceToUI(Device device) { - device.setLighting(device.getLightingConfig(), true); - if (devices.size() == 2) - setConnectedDeviceListVisible(true); - connectedDeviceList.getItems().add(device); - connectedDeviceList.getSelectionModel().select(device); - } - - private boolean isConnectedDeviceListVisisble() { - return pane.getChildren().contains(connectedDeviceList); - } - - private void setConnectedDeviceListVisible(boolean vis) { - if (vis) { - if (!pane.getChildren().contains(connectedDeviceList)) - pane.getChildren().add(0, connectedDeviceList); - } else { - pane.getChildren().remove(connectedDeviceList); - } - } - - private void initWindow() { - connectedDeviceList.getSelectionModel().selectedItemProperty().addListener((observable, oldValue, newValue) -> { - if (!deviceHolder.getChildren().isEmpty()) - deviceHolder.getChildren().clear(); - if (!titleHolder.getChildren().isEmpty()) - titleHolder.getChildren().clear(); - if (!lightingButtonHolder.getChildren().isEmpty()) - lightingButtonHolder.getChildren().clear(); - if (!profileHolder.getChildren().isEmpty()) - profileHolder.getChildren().clear(); - if (newValue == null) { - titleHolder.getChildren().add(noDevicesLabel); - } else { - deviceHolder.getChildren().add(newValue.getDevicePane()); - titleHolder.getChildren().add(newValue.getLabel()); - lightingButtonHolder.getChildren().add(newValue.getLightingButton()); - profileHolder.getChildren().add(newValue.getProfileMenu()); - globalBrightness.setValue(newValue.getLightingConfig().getGlobalBrightness()); - } - }); - connectedDeviceList.setCellFactory(DeviceCell.buildFactory(saveService)); - connectedDeviceList.setEditable(true); - setConnectedDeviceListVisible(false); - var apex = Font.loadFont(getClass().getResourceAsStream("/assets/apex-mk2.regular.otf"), 50.0D); - noDevicesLabel.setFont(apex); - close.setOnAction(e -> stage.hide()); - settings.setOnAction(e -> { - try { - var sd = fxHelper.buildSettingsDialog(stage); - var childDialogStage = new Stage(); - sd.start(childDialogStage); - } catch (Exception ex) { - log.error("Unable to open settings dialog", ex); - } - }); - min.setOnAction(e -> stage.setIconified(true)); - var icon = new Region(); - icon.setId("icon"); - min.setGraphic(icon); - deviceListToggle.setOnAction(e -> setConnectedDeviceListVisible(!isConnectedDeviceListVisisble())); - versionLabel.setText(TITLE_FORMAT.formatted(buildVersion())); - } - - @EventListener - public void newVersionAvailable(NewVersionAvailableEvent event) { - var label = new Label("New version available: " + event.version().versionDisplay()); - label.setStyle("-fx-text-fill: #ff8888; -fx-font-weight: bold;"); - label.setOnMouseClicked(e -> getHostServices().showDocument(event.version().html_url())); - Platform.runLater(() -> labelTarget.getChildren().add(label)); - } - - @EventListener - public void onSaveEvent(SaveService.SaveEvent event) { - Platform.runLater(() -> { - showHint(event.isNew()); - - var selectedDevice = connectedDeviceList.getSelectionModel().getSelectedItem(); - if (selectedDevice != null) { - globalBrightness.setValue(selectedDevice.getLightingConfig().getGlobalBrightness()); - } - }); - } - - public record ShowMainEvent() { - } - - public record GlobalBrightnessChangedEvent(Object source, String serialNr, int brightness) { - public boolean isSource(Object other) { - //noinspection ObjectEquality - return source == other; - } - } -} diff --git a/src/main/java/com/getpcpanel/ui/ILightingDialogMuteOverrideHelper.java b/src/main/java/com/getpcpanel/ui/ILightingDialogMuteOverrideHelper.java deleted file mode 100644 index f365102d..00000000 --- a/src/main/java/com/getpcpanel/ui/ILightingDialogMuteOverrideHelper.java +++ /dev/null @@ -1,179 +0,0 @@ -package com.getpcpanel.ui; - -import java.util.Collection; -import java.util.Collections; -import java.util.function.Consumer; - -import javax.annotation.Nullable; - -import org.apache.commons.lang3.StringUtils; - -import com.getpcpanel.MainFX; -import com.getpcpanel.cpp.AudioDevice; -import com.getpcpanel.ui.colorpicker.ColorDialog; -import com.getpcpanel.voicemeeter.Voicemeeter; -import com.getpcpanel.voicemeeter.Voicemeeter.ButtonType; -import com.getpcpanel.voicemeeter.Voicemeeter.ControlType; - -import javafx.geometry.Insets; -import javafx.geometry.Pos; -import javafx.scene.control.CheckBox; -import javafx.scene.control.ComboBox; -import javafx.scene.control.Label; -import javafx.scene.control.Tab; -import javafx.scene.control.TabPane; -import javafx.scene.control.TabPane.TabClosingPolicy; -import javafx.scene.control.Tooltip; -import javafx.scene.layout.HBox; -import javafx.scene.layout.VBox; -import javafx.scene.paint.Color; -import one.util.streamex.EntryStream; -import one.util.streamex.StreamEx; - -public interface ILightingDialogMuteOverrideHelper { - String FOLLOW_PROCESS = "Follow what is controlled by this knob/slider"; - - record OverrideTarget(CheckBox[] cb, ComboBox[] deviceProcess, ColorDialog[] cd) { - } - - enum OverrideTargetType { - KNOB, SLIDER, SLIDER_LABEL - } - - CheckBox[] getMuteOverrideCheckboxesKnobs(); - - ComboBox[] getMuteOverrideComboBoxesKnobs(); - - ColorDialog[] getMuteOverrideColorsKnobs(); - - default CheckBox[] getMuteOverrideCheckboxesSliders() { - return new CheckBox[0]; - } - - default ComboBox[] getMuteOverrideComboBoxesSliders() { - //noinspection unchecked - return new ComboBox[0]; - } - - default ColorDialog[] getMuteOverrideColorsSliders() { - return new ColorDialog[0]; - } - - default CheckBox[] getMuteOverrideCheckboxesSliderLabels() { - return new CheckBox[0]; - } - - default ComboBox[] getMuteOverrideComboBoxesSliderLabels() { - //noinspection unchecked - return new ComboBox[0]; - } - - default ColorDialog[] getMuteOverrideColorsSliderLabels() { - return new ColorDialog[0]; - } - - Collection getDevices(); - - @SuppressWarnings("NestedAssignment") - default TabPane tabWithMuteOverride(ProLightingDialog.OverrideTargetType ott, int button, TabPane tab) { - var target = getOverrideTarget(ott); - var vBox = new VBox(); - var cd = target.cd()[button] = new ColorDialog(); - var deviceProcess = target.deviceProcess()[button] = new ComboBox<>(); - var cb = target.cb()[button] = new CheckBox("Enable mute override"); - var label = new Label( - "Mute override looks at the action on the dial/slider, not the button. If a dial controls the volume of a device or process, " - + "the mute override will set the color when that device or process is muted. If the button action triggers mute but the slider/dial " - + "does not control that device/process, mute override will do nothing."); - var insets = new Insets(15, 15, 0, 15); - label.setPadding(insets); - cb.setPadding(insets); - label.setWrapText(true); - vBox.getChildren().addAll( - label, - cb, - setDeviceProcessOptions(deviceProcess, insets), - cd - ); - - var muteTab = new Tab("Mute override", vBox); - var originalTab = new Tab("Color", tab); - - var result = new TabPane(originalTab, muteTab); - result.setTabClosingPolicy(TabClosingPolicy.UNAVAILABLE); - return result; - } - - private HBox setDeviceProcessOptions(ComboBox deviceProcess, Insets insets) { - deviceProcess.setEditable(true); - deviceProcess.setTooltip(new Tooltip("Can be a partial device name")); - - StreamEx.of(FOLLOW_PROCESS).append(devices()) - .append(voiceMeeterOptions()) - .forEach(deviceProcess.getItems()::add); - - deviceProcess.setPrefWidth(20000); - var label = new Label("Follow: "); - label.setMinWidth(100); - - var deviceProcessBox = new HBox(); - deviceProcessBox.setPadding(insets); - deviceProcessBox.setAlignment(Pos.CENTER); - deviceProcessBox.getChildren().add(label); - deviceProcessBox.getChildren().add(deviceProcess); - return deviceProcessBox; - } - - default StreamEx devices() { - return StreamEx.of(getDevices()).map(AudioDevice::name).sorted(); - } - - default StreamEx voiceMeeterOptions() { - var voiceMeeter = MainFX.getBean(Voicemeeter.class); - var version = voiceMeeter.getVersion(); - if (!voiceMeeter.login() || version == null) { - return StreamEx.of(); - } - - return EntryStream.of(Collections.nCopies(voiceMeeter.getNumStrips(), ControlType.STRIP)).append(EntryStream.of(Collections.nCopies(voiceMeeter.getNumBuses(), ControlType.BUS))) - .flatMapKeyValue((idx, ct) -> StreamEx.of(ButtonType.stateButtonsFor(ct, version)).map(sb -> "VoiceMeeter: " + ct.getDn() + " " + (idx + 1) + ", " + sb)); - } - - default CheckBox[] allOverrideCheckboxes() { - return StreamEx.of(OverrideTargetType.values()).flatMap(t -> StreamEx.of(getOverrideTarget(t).cb())).toArray(CheckBox[]::new); - } - - default ColorDialog[] allOverrideColors() { - return StreamEx.of(OverrideTargetType.values()).flatMap(t -> StreamEx.of(getOverrideTarget(t).cd())).toArray(ColorDialog[]::new); - } - - default ComboBox[] allOverrideComboBoxes() { - return StreamEx.of(OverrideTargetType.values()).flatMap(t -> StreamEx.of(getOverrideTarget(t).deviceProcess())).toArray(ComboBox[]::new); - } - - default OverrideTarget getOverrideTarget(OverrideTargetType ott) { - return switch (ott) { - case KNOB -> new OverrideTarget(getMuteOverrideCheckboxesKnobs(), getMuteOverrideComboBoxesKnobs(), getMuteOverrideColorsKnobs()); - case SLIDER -> new OverrideTarget(getMuteOverrideCheckboxesSliders(), getMuteOverrideComboBoxesSliders(), getMuteOverrideColorsSliders()); - case SLIDER_LABEL -> new OverrideTarget(getMuteOverrideCheckboxesSliderLabels(), getMuteOverrideComboBoxesSliderLabels(), getMuteOverrideColorsSliderLabels()); - }; - } - - default void setOverride(OverrideTargetType type, int typeIndex, @Nullable String deviceOrFollow, @Nullable String muteOverrideColor) { - var target = getOverrideTarget(type); - target.cb()[typeIndex].setSelected(StringUtils.isNotBlank(muteOverrideColor)); - target.deviceProcess()[typeIndex].setValue(deviceOrFollow); - target.cd()[typeIndex].setCustomColor(Color.web(StringUtils.defaultIfBlank(muteOverrideColor, "black"))); - } - - default void setOverrideSetting(OverrideTargetType type, int typeIdx, Consumer deviceFollowSetter, Consumer colorSetter) { - var target = getOverrideTarget(type); - if (target.cb()[typeIdx].isSelected()) { - deviceFollowSetter.accept(target.deviceProcess()[typeIdx].getValue()); - colorSetter.accept(target.cd()[typeIdx].getCustomColor()); - } else { - deviceFollowSetter.accept(null); - colorSetter.accept(null); - } - } -} diff --git a/src/main/java/com/getpcpanel/ui/LightingChangedToDefaultEvent.java b/src/main/java/com/getpcpanel/ui/LightingChangedToDefaultEvent.java deleted file mode 100644 index f4d1fb55..00000000 --- a/src/main/java/com/getpcpanel/ui/LightingChangedToDefaultEvent.java +++ /dev/null @@ -1,5 +0,0 @@ -package com.getpcpanel.ui; - -public enum LightingChangedToDefaultEvent { - INSTANCE -} diff --git a/src/main/java/com/getpcpanel/ui/LimitedTextField.java b/src/main/java/com/getpcpanel/ui/LimitedTextField.java deleted file mode 100644 index a087d16d..00000000 --- a/src/main/java/com/getpcpanel/ui/LimitedTextField.java +++ /dev/null @@ -1,47 +0,0 @@ -package com.getpcpanel.ui; - -import java.util.Objects; - -import javafx.beans.property.IntegerProperty; -import javafx.beans.property.SimpleIntegerProperty; -import javafx.scene.control.TextField; - -public class LimitedTextField extends TextField { - private final IntegerProperty maxLength; - - public LimitedTextField(int limit) { - maxLength = new SimpleIntegerProperty(limit); - } - - public IntegerProperty maxLengthProperty() { - return maxLength; - } - - public final Integer getMaxLength() { - return maxLength.getValue(); - } - - public final void setMaxLength(Integer maxLength) { - Objects.requireNonNull(maxLength, "Max length cannot be null, -1 for no limit"); - this.maxLength.setValue(maxLength); - } - - @Override - public void replaceText(int start, int end, String insertedText) { - if (getMaxLength() <= 0) { - super.replaceText(start, end, insertedText); - } else { - var currentText = (getText() == null) ? "" : getText(); - var finalText = currentText.substring(0, start) + insertedText + currentText.substring(end); - var numberOfexceedingCharacters = finalText.length() - getMaxLength(); - if (numberOfexceedingCharacters <= 0) { - super.replaceText(start, end, insertedText); - } else { - var cutInsertedText = insertedText.substring( - 0, - insertedText.length() - numberOfexceedingCharacters); - super.replaceText(start, end, cutInsertedText); - } - } - } -} diff --git a/src/main/java/com/getpcpanel/ui/MacroControllerService.java b/src/main/java/com/getpcpanel/ui/MacroControllerService.java deleted file mode 100644 index 93e1ae5c..00000000 --- a/src/main/java/com/getpcpanel/ui/MacroControllerService.java +++ /dev/null @@ -1,81 +0,0 @@ -package com.getpcpanel.ui; - -import static java.util.Objects.requireNonNull; - -import java.net.URL; -import java.util.ArrayList; -import java.util.Comparator; -import java.util.EnumMap; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider; -import org.springframework.core.type.filter.AnnotationTypeFilter; -import org.springframework.stereotype.Service; - -import com.getpcpanel.commands.command.Command; -import com.getpcpanel.spring.OsHelper; -import com.getpcpanel.ui.command.Cmd; -import com.getpcpanel.ui.command.Cmd.Type; -import com.getpcpanel.ui.command.DialCommandController; - -import jakarta.annotation.PostConstruct; -import lombok.RequiredArgsConstructor; -import lombok.SneakyThrows; -import lombok.extern.log4j.Log4j2; - -@Log4j2 -@Service -@RequiredArgsConstructor -public class MacroControllerService { - private static final Map> typeToControllers = new EnumMap<>(Type.class); - private static final Map, ControllerInfo> commandToController = new HashMap<>(); - private final OsHelper osHelper; - - @SneakyThrows - @PostConstruct - public void init() { - var provider = new ClassPathScanningCandidateComponentProvider(false); - provider.addIncludeFilter(new AnnotationTypeFilter(Cmd.class)); - - var beanDefs = provider.findCandidateComponents("com.getpcpanel"); - for (var bd : beanDefs) { - var controllerClass = Class.forName(bd.getBeanClassName()); - var cmd = controllerClass.getAnnotation(Cmd.class); - - if (!osHelper.isOs(cmd.os())) { - continue; - } - - var info = new ControllerInfo(controllerClass, cmd); - var type = getTypeForController(controllerClass); - typeToControllers.computeIfAbsent(type, t -> new ArrayList<>()).add(info); - for (var command : cmd.cmds()) { - commandToController.put(command, info); - } - } - - for (var type : Type.values()) { - typeToControllers.computeIfAbsent(type, t -> new ArrayList<>()).sort(Comparator.comparing(a -> a.cmd().name())); - } - } - - public ControllerInfo getControllerForCommand(Class cmd) { - return commandToController.get(cmd); - } - - public List getControllersForType(Type type) { - return typeToControllers.get(type); - } - - private static Type getTypeForController(Class controllerClass) { - return DialCommandController.class.isAssignableFrom(controllerClass) ? Type.dial : Type.button; - } - - public record ControllerInfo(Class controllerClass, Cmd cmd) { - public URL getFxml() { - return requireNonNull(getClass().getResource("/assets/command/%s/%s.fxml".formatted(getTypeForController(controllerClass).name(), cmd.fxml()))); - } - } -} diff --git a/src/main/java/com/getpcpanel/ui/MiniLightingDialog.java b/src/main/java/com/getpcpanel/ui/MiniLightingDialog.java deleted file mode 100644 index ec1f4b1d..00000000 --- a/src/main/java/com/getpcpanel/ui/MiniLightingDialog.java +++ /dev/null @@ -1,373 +0,0 @@ -package com.getpcpanel.ui; - -import java.util.Collection; -import java.util.Objects; - -import javax.annotation.Nonnull; - -import org.springframework.context.ApplicationEventPublisher; -import org.springframework.stereotype.Component; - -import com.getpcpanel.cpp.AudioDevice; -import com.getpcpanel.cpp.ISndCtrl; -import com.getpcpanel.device.Device; -import com.getpcpanel.profile.LightingConfig; -import com.getpcpanel.profile.LightingConfig.LightingMode; -import com.getpcpanel.profile.SaveService; -import com.getpcpanel.profile.SingleKnobLightingConfig.SINGLE_KNOB_MODE; -import com.getpcpanel.spring.Prototype; -import com.getpcpanel.ui.UIInitializer.SingleParamInitializer; -import com.getpcpanel.ui.colorpicker.ColorDialog; -import com.getpcpanel.ui.colorpicker.HueSlider; -import com.getpcpanel.util.Util; - -import javafx.application.Application; -import javafx.event.ActionEvent; -import javafx.fxml.FXML; -import javafx.geometry.Side; -import javafx.scene.Node; -import javafx.scene.Scene; -import javafx.scene.control.Button; -import javafx.scene.control.CheckBox; -import javafx.scene.control.ComboBox; -import javafx.scene.control.Label; -import javafx.scene.control.Slider; -import javafx.scene.control.Tab; -import javafx.scene.control.TabPane; -import javafx.scene.control.TabPane.TabClosingPolicy; -import javafx.scene.image.Image; -import javafx.scene.layout.GridPane; -import javafx.scene.layout.Pane; -import javafx.scene.layout.VBox; -import javafx.scene.paint.Color; -import javafx.stage.Modality; -import javafx.stage.Stage; -import lombok.Getter; -import lombok.RequiredArgsConstructor; -import lombok.extern.log4j.Log4j2; - -@Log4j2 -@Component -@Prototype -@RequiredArgsConstructor -public class MiniLightingDialog extends Application implements UIInitializer>, ILightingDialogMuteOverrideHelper { - private final SaveService saveService; - private final ApplicationEventPublisher eventPublisher; - private final ISndCtrl sndCtrl; - private Device device; - - private Stage stage; - @FXML private TabPane mainPane; - @FXML private TabPane knobsTabbedPane; - @FXML private TabPane fullBodyTabbedPane; - @FXML private Slider rainbowPhaseShift; - @FXML private Slider rainbowBrightness; - @FXML private Slider rainbowSpeed; - @FXML private CheckBox rainbowReverse; - @FXML private CheckBox rainbowVertical; - private HueSlider waveHue; - @FXML private Slider waveBrightness; - @FXML private Slider waveSpeed; - @FXML private CheckBox waveReverse; - @FXML private CheckBox waveBounce; - private HueSlider breathHue; - @FXML private Slider breathBrightness; - @FXML private Slider breathSpeed; - @FXML private VBox wavebox; - @FXML private VBox breathbox; - @FXML private Button applyToAllButton; - private ColorDialog allKnobColor; - private static final int NUM_KNOBS = 4; - private final TabPane[] knobSingleTabPane = new TabPane[NUM_KNOBS]; - private final ColorDialog[] knobStaticCDs = new ColorDialog[NUM_KNOBS]; - private final ColorDialog[] knobVolumeGradientCD1 = new ColorDialog[NUM_KNOBS]; - private final ColorDialog[] knobVolumeGradientCD2 = new ColorDialog[NUM_KNOBS]; - @Getter private final CheckBox[] muteOverrideCheckboxesKnobs = new CheckBox[NUM_KNOBS]; - @SuppressWarnings("unchecked") @Getter private final ComboBox[] muteOverrideComboBoxesKnobs = new ComboBox[NUM_KNOBS]; - @Getter private final ColorDialog[] muteOverrideColorsKnobs = new ColorDialog[NUM_KNOBS]; - - private boolean pressedOk; - @FXML private Pane root; - private LightingConfig lightingConfig; - - @Override - public void initUI(@Nonnull SingleParamInitializer args) { - device = args.param(); - lightingConfig = device.getSavedLightingConfig().deepCopy(); - setDeviceLighting(); - postInit(); - } - - private void setDeviceLighting() { - device.setLighting(lightingConfig.deepCopy(), true); - } - - @Override - public void start(Stage stage) { - this.stage = stage; - UIHelper.closeOnEscape(stage); - var scene = new Scene(Objects.requireNonNull(root)); - scene.getStylesheets().add(Objects.requireNonNull(getClass().getResource("/assets/dark_theme.css")).toExternalForm()); - stage.getIcons().add(new Image(Objects.requireNonNull(getClass().getResource("/assets/256x256.png")).toExternalForm())); - stage.setOnHiding(e -> { - if (!pressedOk) { - device.setLighting(device.getSavedLightingConfig(), true); - } - eventPublisher.publishEvent(LightingChangedToDefaultEvent.INSTANCE); - }); - stage.initModality(Modality.APPLICATION_MODAL); - stage.initOwner(HomePage.stage); - stage.setScene(scene); - stage.sizeToScene(); - stage.setTitle("Lighting Dialog"); - stage.show(); - } - - public MiniLightingDialog select(int idx) { - mainPane.getSelectionModel().select(1); - knobsTabbedPane.getSelectionModel().select(idx); - return this; - } - - @FXML - private void onCancel(ActionEvent event) { - stage.close(); - } - - @FXML - private void ok(ActionEvent event) { - log.debug("{} {}", stage.getWidth(), stage.getHeight()); - pressedOk = true; - device.setSavedLighting(lightingConfig); - saveService.save(); - stage.close(); - } - - @FXML - private void turnOffLights(ActionEvent event) { - allKnobColor.setCustomColor(Color.WHITE); - allKnobColor.setCustomColor(Color.BLACK); - mainPane.getSelectionModel().select(0); - fullBodyTabbedPane.getSelectionModel().select(0); - } - - private void postInit() { - for (var i = 0; i < NUM_KNOBS; i++) { - var knob = i + 1; - var tab = new Tab("Knob " + knob); - var cd = new ColorDialog(Color.BLACK); - knobStaticCDs[i] = cd; - knobVolumeGradientCD1[i] = new ColorDialog(); - knobVolumeGradientCD2[i] = new ColorDialog(); - var volGradientGP = makeFourPanelGridPane( - knobVolumeGradientCD2[i], knobVolumeGradientCD1[i]); - var vbox = new VBox(volGradientGP); - var staticTab = new Tab("Static", cd); - var volGradient = new Tab("Volume Gradient", vbox); - var singleKnobTabPane = new TabPane(staticTab, volGradient); - knobSingleTabPane[i] = singleKnobTabPane; - Util.adjustTabs(singleKnobTabPane, 140, 30); - singleKnobTabPane.setTabClosingPolicy(TabClosingPolicy.UNAVAILABLE); - singleKnobTabPane.setSide(Side.LEFT); - tab.setContent(tabWithMuteOverride(OverrideTargetType.KNOB, i, singleKnobTabPane)); - knobsTabbedPane.getTabs().add(tab); - } - Util.adjustTabs(fullBodyTabbedPane, 120, 30); - allKnobColor = new ColorDialog(); - fullBodyTabbedPane.getTabs().get(0).setContent(allKnobColor); - var allSliders = new Slider[] { rainbowPhaseShift, rainbowBrightness, rainbowSpeed, - waveBrightness, waveSpeed, - breathBrightness, breathSpeed }; - var allCheckBoxes = new CheckBox[] { rainbowReverse, rainbowVertical, - waveReverse, waveBounce }; - waveHue = new HueSlider(); - wavebox.getChildren().add(1, waveHue); - breathHue = new HueSlider(); - breathbox.getChildren().add(1, breathHue); - applyToAllButton.setOnAction(e -> { - if (mainPane.getSelectionModel().getSelectedIndex() == 1) { - var knobIndex = knobsTabbedPane.getSelectionModel().getSelectedIndex(); - for (var i = 0; i < lightingConfig.getKnobConfigs().length; i++) { - if (i != knobIndex) - lightingConfig.getKnobConfigs()[i].set(lightingConfig.getKnobConfigs()[knobIndex]); - } - } - initFields(); - }); - initFields(); - initListeners(allSliders, allCheckBoxes); - } - - private void initFields() { - var mode = lightingConfig.getLightingMode(); - if (mode == LightingMode.ALL_COLOR) { - mainPane.getSelectionModel().select(0); - fullBodyTabbedPane.getSelectionModel().select(0); - allKnobColor.setCustomColor(Color.web(lightingConfig.getAllColor())); - } else if (mode == LightingMode.ALL_RAINBOW) { - mainPane.getSelectionModel().select(0); - fullBodyTabbedPane.getSelectionModel().select(1); - rainbowPhaseShift.setValue(lightingConfig.getRainbowPhaseShift() & 0xFF); - rainbowBrightness.setValue(lightingConfig.getRainbowBrightness() & 0xFF); - rainbowSpeed.setValue(lightingConfig.getRainbowSpeed() & 0xFF); - rainbowReverse.setSelected(lightingConfig.getRainbowReverse() == 1); - rainbowVertical.setSelected(lightingConfig.getRainbowVertical() == 1); - } else if (mode == LightingMode.ALL_WAVE) { - mainPane.getSelectionModel().select(0); - fullBodyTabbedPane.getSelectionModel().select(2); - waveHue.setHue(lightingConfig.getWaveHue() & 0xFF); - waveBrightness.setValue(lightingConfig.getWaveBrightness() & 0xFF); - waveSpeed.setValue(lightingConfig.getWaveSpeed() & 0xFF); - waveReverse.setSelected(lightingConfig.getWaveReverse() == 1); - waveBounce.setSelected(lightingConfig.getWaveBounce() == 1); - } else if (mode == LightingMode.ALL_BREATH) { - mainPane.getSelectionModel().select(0); - fullBodyTabbedPane.getSelectionModel().select(3); - breathHue.setHue(lightingConfig.getBreathHue() & 0xFF); - breathBrightness.setValue(lightingConfig.getBreathBrightness() & 0xFF); - breathSpeed.setValue(lightingConfig.getBreathSpeed() & 0xFF); - } else if (mode == LightingMode.CUSTOM) { - if (mainPane.getSelectionModel().getSelectedIndex() == 0) - mainPane.getSelectionModel().select(1); - var knobConfigs = lightingConfig.getKnobConfigs(); - for (var i = 0; i < NUM_KNOBS; i++) { - var knobConfig = knobConfigs[i]; - if (knobConfig.getMode() == SINGLE_KNOB_MODE.STATIC) { - knobSingleTabPane[i].getSelectionModel().select(0); - knobStaticCDs[i].setCustomColor(Color.web(knobConfig.getColor1())); - } else if (knobConfig.getMode() == SINGLE_KNOB_MODE.VOLUME_GRADIENT) { - knobSingleTabPane[i].getSelectionModel().select(1); - knobVolumeGradientCD1[i].setCustomColor(Color.web(knobConfig.getColor1())); - knobVolumeGradientCD2[i].setCustomColor(Color.web(knobConfig.getColor2())); - } - setOverride(OverrideTargetType.KNOB, i, knobConfig.getMuteOverrideDeviceOrFollow(), knobConfig.getMuteOverrideColor()); - } - } - updateApplyToAllButton(); - } - - private void addListener(ColorDialog[]... xs) { - for (var x : xs) { - for (var cd : x) { - cd.customColorProperty().addListener((a, bb, c) -> updateColors()); - } - } - } - - private void addListener(TabPane... tbs) { - for (var tb : tbs) { - tb.getSelectionModel().selectedItemProperty().addListener((a, bb, c) -> updateColors()); - } - } - - private void addListener(CheckBox[]... checkss) { - for (var checks : checkss) { - for (var check : checks) { - check.setOnAction(a -> updateColors()); - } - } - } - - private void addListener(ComboBox[]... boxes) { - for (var checks : boxes) { - for (var check : checks) { - check.setOnAction(a -> updateColors()); - } - } - } - - private void initListeners(Slider[] allSliders, CheckBox[] allCheckBoxes) { - addListener(knobStaticCDs, knobVolumeGradientCD1, allOverrideColors(), knobVolumeGradientCD2); - addListener(knobSingleTabPane); - allKnobColor.customColorProperty().addListener((observable, oldValue, newValue) -> { - for (var cd : knobStaticCDs) { - cd.setCustomColor(newValue); - } - updateColors(); - }); - addListener(fullBodyTabbedPane); - addListener(allOverrideCheckboxes()); - addListener(allOverrideComboBoxes()); - for (var slider : allSliders) { - slider.valueProperty().addListener((observable, oldValue, newValue) -> updateColors()); - } - for (var cb : allCheckBoxes) { - cb.selectedProperty().addListener((observable, oldValue, newValue) -> updateColors()); - } - - waveHue.getHueProperty().addListener((observable, oldValue, newValue) -> updateColors()); - breathHue.getHueProperty().addListener((observable, oldValue, newValue) -> updateColors()); - mainPane.getSelectionModel().selectedItemProperty().addListener((observable, oldValue, newValue) -> { - updateColors(); - updateApplyToAllButton(); - }); - } - - private void updateApplyToAllButton() { - if (mainPane.getSelectionModel().getSelectedIndex() == 0 || mainPane.getSelectionModel().getSelectedIndex() == 4) { - applyToAllButton.setVisible(false); - return; - } - applyToAllButton.setVisible(true); - if (mainPane.getSelectionModel().getSelectedIndex() == 1) { - applyToAllButton.setText("Apply To All Knobs"); - } else if (mainPane.getSelectionModel().getSelectedIndex() == 2) { - applyToAllButton.setText("Apply To All Sliders"); - } else if (mainPane.getSelectionModel().getSelectedIndex() == 3) { - applyToAllButton.setText("Apply To All Slider Labels"); - } - } - - @SuppressWarnings("NumericCastThatLosesPrecision") - private void updateColors() { - if (mainPane.getSelectionModel().getSelectedIndex() == 0) { - if (fullBodyTabbedPane.getSelectionModel().getSelectedIndex() == 0) { - lightingConfig = LightingConfig.createAllColor(allKnobColor.getCustomColor()); - setDeviceLighting(); - } else if (fullBodyTabbedPane.getSelectionModel().getSelectedIndex() == 1) { - lightingConfig = LightingConfig.createRainbowAnimation((byte) (int) rainbowPhaseShift.getValue(), (byte) (int) rainbowBrightness.getValue(), - (byte) (int) rainbowSpeed.getValue(), rainbowReverse.isSelected(), rainbowVertical.isSelected()); - setDeviceLighting(); - } else if (fullBodyTabbedPane.getSelectionModel().getSelectedIndex() == 2) { - lightingConfig = LightingConfig.createWaveAnimation((byte) waveHue.getHue(), (byte) (int) waveBrightness.getValue(), (byte) (int) waveSpeed.getValue(), - waveReverse.isSelected(), waveBounce.isSelected()); - setDeviceLighting(); - } else if (fullBodyTabbedPane.getSelectionModel().getSelectedIndex() == 3) { - lightingConfig = LightingConfig.createBreathAnimation((byte) breathHue.getHue(), (byte) (int) breathBrightness.getValue(), (byte) (int) breathSpeed.getValue()); - setDeviceLighting(); - } - } else { - lightingConfig = new LightingConfig(NUM_KNOBS, 0); - lightingConfig.setLightingMode(LightingMode.CUSTOM); - for (var knob = 0; knob < NUM_KNOBS; knob++) { - var knobConfig = lightingConfig.getKnobConfigs()[knob]; - if (knobSingleTabPane[knob].getSelectionModel().getSelectedIndex() == 0) { - knobConfig.setMode(SINGLE_KNOB_MODE.STATIC); - knobConfig.setColor1FromColor(knobStaticCDs[knob].getCustomColor()); - } else if (knobSingleTabPane[knob].getSelectionModel().getSelectedIndex() == 1) { - knobConfig.setMode(SINGLE_KNOB_MODE.VOLUME_GRADIENT); - knobConfig.setColor1FromColor(knobVolumeGradientCD1[knob].getCustomColor()); - knobConfig.setColor2FromColor(knobVolumeGradientCD2[knob].getCustomColor()); - } - setOverrideSetting(OverrideTargetType.KNOB, knob, knobConfig::setMuteOverrideDeviceOrFollow, knobConfig::setMuteOverrideColorFromColor); - } - setDeviceLighting(); - } - } - - private static GridPane makeFourPanelGridPane(Node obj1, Node obj2) { - var gp = new GridPane(); - var l1 = new Label("Color when volume is 100"); - var l2 = new Label("Color when volume is 0"); - l1.setWrapText(true); - l2.setWrapText(true); - gp.addColumn(0, l1, l2); - gp.addColumn(1, obj1, obj2); - return gp; - } - - @Override - public Collection getDevices() { - return sndCtrl.getDevices(); - } -} diff --git a/src/main/java/com/getpcpanel/ui/MqttSettingsDialog.java b/src/main/java/com/getpcpanel/ui/MqttSettingsDialog.java deleted file mode 100644 index a45496e6..00000000 --- a/src/main/java/com/getpcpanel/ui/MqttSettingsDialog.java +++ /dev/null @@ -1,79 +0,0 @@ -package com.getpcpanel.ui; - -import static com.getpcpanel.profile.MqttSettings.DEFAULT_MQTT_PORT; - -import javax.annotation.Nonnull; - -import org.apache.commons.lang3.math.NumberUtils; -import org.springframework.stereotype.Component; - -import com.getpcpanel.mqtt.MqttDeviceService; -import com.getpcpanel.profile.MqttSettings; -import com.getpcpanel.profile.Save; -import com.getpcpanel.spring.Prototype; - -import javafx.event.ActionEvent; -import javafx.fxml.FXML; -import javafx.scene.control.Alert; -import javafx.scene.control.ButtonType; -import javafx.scene.control.CheckBox; -import javafx.scene.control.TextField; -import lombok.RequiredArgsConstructor; - -@Component -@Prototype -@RequiredArgsConstructor -public class MqttSettingsDialog { - private final MqttDeviceService mqttDeviceService; - @FXML private CheckBox enabled; - @FXML private TextField host; - @FXML private TextField port; - @FXML private TextField username; - @FXML private TextField password; - @FXML private CheckBox secure; - @FXML private TextField baseTopic; - @FXML private CheckBox enableHomeAssistantDiscovery; - @FXML private TextField homeAssistantBaseTopic; - @FXML private CheckBox homeAssistantAvailability; - - public void save(Save save) { - save.setMqtt(new MqttSettings( - enabled.isSelected(), - host.getText(), - NumberUtils.toInt(port.getText(), DEFAULT_MQTT_PORT), - username.getText(), - password.getText(), - secure.isSelected(), - baseTopic.getText(), - new MqttSettings.HomeAssistantSettings(enableHomeAssistantDiscovery.isSelected(), homeAssistantBaseTopic.getText(), homeAssistantAvailability.isSelected()) - )); - } - - public void populate(Save save) { - if (save.getMqtt() != null) { - populate(save.getMqtt()); - } else { - populate(MqttSettings.DEFAULT); - } - } - - private void populate(@Nonnull MqttSettings settings) { - enabled.setSelected(settings.enabled()); - host.setText(settings.host()); - port.setText(settings.port().toString()); - username.setText(settings.username()); - password.setText(settings.password()); - secure.setSelected(settings.secure()); - baseTopic.setText(settings.baseTopic()); - - enableHomeAssistantDiscovery.setSelected(settings.homeAssistant().enableDiscovery()); - homeAssistantBaseTopic.setText(settings.homeAssistant().baseTopic()); - homeAssistantAvailability.setSelected(settings.homeAssistant().availability()); - } - - public void clearCurrentTopics(ActionEvent actionEvent) { - if (!mqttDeviceService.clear()) { - new Alert(Alert.AlertType.WARNING, "You must be connected to clear the mqtt broker", ButtonType.OK).show(); - } - } -} diff --git a/src/main/java/com/getpcpanel/ui/OSCSettingsDialog.java b/src/main/java/com/getpcpanel/ui/OSCSettingsDialog.java deleted file mode 100644 index 8644e84e..00000000 --- a/src/main/java/com/getpcpanel/ui/OSCSettingsDialog.java +++ /dev/null @@ -1,84 +0,0 @@ -package com.getpcpanel.ui; - -import java.util.List; - -import org.apache.commons.lang3.math.NumberUtils; -import org.springframework.stereotype.Component; - -import com.getpcpanel.profile.OSCConnectionInfo; -import com.getpcpanel.profile.Save; -import com.getpcpanel.spring.Prototype; - -import javafx.fxml.FXML; -import javafx.geometry.Insets; -import javafx.geometry.Pos; -import javafx.scene.control.Button; -import javafx.scene.control.Label; -import javafx.scene.control.TextField; -import javafx.scene.input.MouseEvent; -import javafx.scene.layout.HBox; -import javafx.scene.layout.VBox; -import lombok.RequiredArgsConstructor; -import one.util.streamex.StreamEx; - -@Component -@Prototype -@RequiredArgsConstructor -public class OSCSettingsDialog { - @FXML private VBox connectHostPorts; - @FXML private TextField port; - - public void addConnectHostPort(MouseEvent ignored) { - add("localhost", 8000); - } - - private void add(String host, int port) { - var target = new HBox(); - target.setAlignment(Pos.CENTER_LEFT); - var hostLabel = new Label("Host"); - var hostField = new TextField(host); - var portLabel = new Label("Port"); - var portField = new TextField(String.valueOf(port)); - var deleteBtn = new Button("X"); - - target.getChildren().addAll(hostLabel, hostField, portLabel, portField, deleteBtn); - HBox.setMargin(hostField, new Insets(0, 15, 0, 5)); - HBox.setMargin(portLabel, new Insets(0, 5, 0, 5)); - connectHostPorts.getChildren().add(target); - - deleteBtn.setOnMouseClicked(e -> connectHostPorts.getChildren().remove(target)); - } - - public List getConnections() { - return StreamEx.of(connectHostPorts.getChildren()) - .map(node -> ((HBox) node).getChildren()) - .mapToEntry(c -> ((TextField) c.get(1)).getText(), c -> ((TextField) c.get(3)).getText()) - .mapValues(val -> NumberUtils.toInt(val, -1)) - .filterValues(val -> val != -1) - .mapKeyValue(OSCConnectionInfo::new) - .toList(); - } - - private void setConnections(Integer port, List oscConnections) { - if (port != null) { - this.port.setText(String.valueOf(port)); - } - if (oscConnections != null) { - oscConnections.forEach(c -> add(c.host(), c.port())); - } - } - - public Integer getListenPort() { - var thePort = NumberUtils.toInt(port.getText(), -1); - return thePort == -1 ? null : thePort; - } - - public void save(Save save) { - save.setOscListenPort(getListenPort()); - save.setOscConnections(getConnections()); - } - - public void populate(Save save) { - setConnections(save.getOscListenPort(), save.getOscConnections()); - } -} diff --git a/src/main/java/com/getpcpanel/ui/Overlay.java b/src/main/java/com/getpcpanel/ui/Overlay.java deleted file mode 100644 index 5c43baf7..00000000 --- a/src/main/java/com/getpcpanel/ui/Overlay.java +++ /dev/null @@ -1,201 +0,0 @@ -package com.getpcpanel.ui; - -import java.awt.Toolkit; -import java.io.IOException; -import java.util.List; -import java.util.concurrent.TimeUnit; -import java.util.function.Predicate; -import java.util.function.Supplier; - -import org.springframework.context.event.EventListener; -import org.springframework.stereotype.Component; - -import com.getpcpanel.commands.Commands; -import com.getpcpanel.commands.IconService; -import com.getpcpanel.commands.PCPanelControlEvent; -import com.getpcpanel.commands.command.ButtonAction; -import com.getpcpanel.commands.command.DialAction; -import com.getpcpanel.profile.SaveService; -import com.getpcpanel.profile.SaveService.SaveEvent; -import com.getpcpanel.spring.ConditionalOnWindows; -import com.getpcpanel.util.Debouncer; - -import jakarta.annotation.Nonnull; -import jakarta.annotation.PostConstruct; -import javafx.application.Platform; -import javafx.fxml.FXML; -import javafx.scene.Node; -import javafx.scene.Scene; -import javafx.scene.control.Label; -import javafx.scene.control.ProgressBar; -import javafx.scene.image.Image; -import javafx.scene.image.ImageView; -import javafx.scene.layout.HBox; -import javafx.scene.layout.Pane; -import javafx.scene.paint.Color; -import javafx.stage.Popup; -import javafx.stage.Stage; -import javafx.stage.StageStyle; -import lombok.RequiredArgsConstructor; -import one.util.streamex.StreamEx; - -@Component -@ConditionalOnWindows -@RequiredArgsConstructor -public class Overlay extends Popup { - private final FxHelper fxHelper; - private final SaveService save; - private final IconService iconService; - private final Debouncer debouncer; - - @FXML private Pane panel; - @FXML private HBox volumePanel; - @FXML private HBox textPanel; - @FXML private ProgressBar volume; - @FXML private Label volumeText; - @FXML private Label text; - @FXML private ImageView icon; - private Stage stage; - - @PostConstruct - public void doInit() { - Platform.runLater(() -> prepareStage(new Stage(StageStyle.UTILITY))); - } - - public void prepareStage(Stage helperStage) { - stage = helperStage; - stage.setOpacity(0); - stage.setScene(new Scene(new Pane(), 1, 1)); - helperStage.show(); - var loader = fxHelper.getLoader(getClass().getResource("/assets/Overlay.fxml")); - - loader.setController(this); - HBox panel; - try { - panel = loader.load(); - } catch (IOException e) { - throw new RuntimeException(e); - } - getContent().addAll(panel); - updateSaveValues(); - } - - @EventListener(SaveEvent.class) - public void updateSaveValues() { - updateStyle(); - determinePosition(); - } - - private void determinePosition() { - var window = Toolkit.getDefaultToolkit().getScreenSize(); - var x = window.getWidth(); - var y = window.getHeight(); - var width = getWidth(); - var height = getHeight(); - - switch (save.get().getOverlayPosition()) { - case topLeft, topMiddle, topRight -> setY(save.get().getOverlayPadding()); - case middleLeft, middleMiddle, middleRight -> setY(y / 2 - height / 2); - case bottomLeft, bottomMiddle, bottomRight -> setY(y - getHeight() - save.get().getOverlayPadding()); - } - switch (save.get().getOverlayPosition()) { - case topLeft, middleLeft, bottomLeft -> setX(save.get().getOverlayPadding()); - case topMiddle, middleMiddle, bottomMiddle -> setX(x / 2 - width / 2); - case topRight, middleRight, bottomRight -> setX(x - width - save.get().getOverlayPadding()); - } - } - - @EventListener(SaveEvent.class) - public void updateStyle() { - var save = this.save.get(); - var style = "-fx-background-color: " + save.getOverlayBackgroundColor() + ";"; - if (save.getOverlayWindowCornerRounding() > 0) - style += "-fx-background-radius: " + save.getOverlayWindowCornerRounding() + "px;"; - panel.setStyle(style); - volumeText.setTextFill(Color.web(save.getOverlayTextColor())); - text.setTextFill(Color.web(save.getOverlayTextColor())); - } - - private void initAfterShow() { - determinePosition(); - var save = this.save.get(); - if (save.getOverlayBarCornerRounding() > 0) { - var barStyle = "-fx-background-radius: " + save.getOverlayBarCornerRounding() + "px;"; - volume.setStyle(barStyle); - volume.lookup(".track").setStyle(barStyle + "-fx-background-color: " + save.getOverlayBarBackgroundColor() + ";"); - volume.lookup(".bar").setStyle(barStyle + "-fx-background-color: " + save.getOverlayBarColor() + ";"); - } - volume.setPrefHeight(save.getOverlayBarHeight()); - } - - @EventListener - public void handleControl(PCPanelControlEvent event) { - if (event.initial()) { - return; - } - showDebounced(() -> determineIconImage(event), command -> { - if (event.vol() != null) { - var value = save.get().isOverlayUseLog() ? event.vol().getValue(null, 0, 255) : event.vol().value(); - setVisible(volumePanel); - volume.setProgress(value / 255f); - - if (save.get().isOverlayShowNumber()) { - volumeText.setText(String.valueOf(Math.round(value / 2.55f))); - volumeText.setVisible(true); - volumeText.setManaged(true); - } else { - volumeText.setVisible(false); - volumeText.setManaged(false); - } - return true; - } else { - setVisible(textPanel); - var firstButtonAction = StreamEx.of(Commands.cmds(command)).select(ButtonAction.class).findFirst(); - if (firstButtonAction.isPresent()) { - text.setText(firstButtonAction.get().getOverlayText()); - return true; - } - return false; - } - }); - } - - private void showDebounced(Supplier pre, Predicate pred) { - if (!save.get().isOverlayEnabled()) { - return; - } - Platform.runLater(() -> { - var cai = pre.get(); - if (hasOverlay(cai.command) && pred.test(cai.command)) { - icon.setImage(cai.icon); - show(stage); - initAfterShow(); - } - }); - debouncer.debounce(this, () -> Platform.runLater(this::hide), 2, TimeUnit.SECONDS); - } - - private boolean hasOverlay(Commands commands) { - return Commands.hasCommands(commands) && - StreamEx.of(commands.getCommands()).anyMatch(command -> command instanceof DialAction da && da.hasOverlay() - || command instanceof ButtonAction ba && ba.hasOverlay()); - } - - @Nonnull - private CommandAndIcon determineIconImage(PCPanelControlEvent event) { - return save.getProfile(event.serialNum()).map(profile -> { - var data = event.cmd(); - var setting = event.vol() == null ? null : profile.getKnobSettings(event.knob()); - return new CommandAndIcon(data, iconService.getImageFrom(data, setting)); - }).orElse(CommandAndIcon.DEFAULT); - } - - @SuppressWarnings("ObjectEquality") - private void setVisible(Node param) { - List.of(volumePanel, textPanel).forEach(node -> node.setVisible(node == param)); - } - - private record CommandAndIcon(Commands command, Image icon) { - static final CommandAndIcon DEFAULT = new CommandAndIcon(Commands.EMPTY, IconService.DEFAULT); - } -} diff --git a/src/main/java/com/getpcpanel/ui/OverlayPosition.java b/src/main/java/com/getpcpanel/ui/OverlayPosition.java deleted file mode 100644 index 4cc40965..00000000 --- a/src/main/java/com/getpcpanel/ui/OverlayPosition.java +++ /dev/null @@ -1,7 +0,0 @@ -package com.getpcpanel.ui; - -public enum OverlayPosition { - topLeft, topMiddle, topRight, - middleLeft, middleMiddle, middleRight, - bottomLeft, bottomMiddle, bottomRight -} diff --git a/src/main/java/com/getpcpanel/ui/PickProcessesController.java b/src/main/java/com/getpcpanel/ui/PickProcessesController.java deleted file mode 100644 index 44d8b2a0..00000000 --- a/src/main/java/com/getpcpanel/ui/PickProcessesController.java +++ /dev/null @@ -1,143 +0,0 @@ -package com.getpcpanel.ui; - -import java.util.Collection; -import java.util.List; -import java.util.Optional; - -import org.apache.commons.lang3.StringUtils; -import org.springframework.stereotype.Component; - -import com.getpcpanel.spring.Prototype; -import com.getpcpanel.util.Images; - -import javafx.beans.Observable; -import javafx.beans.binding.Bindings; -import javafx.beans.property.SimpleLongProperty; -import javafx.collections.ListChangeListener; -import javafx.fxml.FXML; -import javafx.scene.control.Button; -import javafx.scene.control.TextField; -import javafx.scene.layout.HBox; -import javafx.scene.layout.Pane; -import javafx.scene.layout.Priority; -import javafx.scene.layout.VBox; -import javafx.stage.Stage; -import lombok.RequiredArgsConstructor; -import lombok.Setter; -import one.util.streamex.StreamEx; - -@Component -@Prototype -@RequiredArgsConstructor -public class PickProcessesController { - public enum PickType { - file, soundSource, process - } - - private final FxHelper fxHelper; - @Setter private boolean single; - - @FXML private VBox pickRows; - @Setter private PickType pickType; - - public void initialize() { - ensureLastEmpty(); - } - - public List getSelection() { - return StreamEx.of(pickRows.getChildren()).select(Pane.class).map(pane -> pane.getChildren().get(0)).select(TextField.class).map(TextField::getText).filter(StringUtils::isNotBlank).toList(); - } - - public void setSelection(Collection volData) { - StreamEx.of(volData).map(StringUtils::trimToNull).nonNull().map(this::createProcessRow).forEach(pickRows.getChildren()::add); - removeEmptyIfNotLast(); - - if (!single) { - pickRows.getChildren().add(createProcessRow("")); - ensureLastEmpty(); - } - } - - public void setDisable(boolean disable) { - StreamEx.of(pickRows.getChildren()).select(Pane.class).flatCollection(Pane::getChildren).forEach(ctrl -> ctrl.setDisable(disable)); - } - - public Observable getObservable() { - var binding = new SimpleLongProperty(); - pickRows.getChildren().addListener((ListChangeListener.Change c) -> { - binding.unbind(); - var values = StreamEx.of(pickRows.getChildren()).select(Pane.class).map(pane -> pane.getChildren().get(0)).select(TextField.class).map(TextField::textProperty).toArray(Observable[]::new); - binding.bind(Bindings.createLongBinding(System::currentTimeMillis, values)); - }); - return binding; - } - - private void removeEmptyIfNotLast() { - var toRemove = StreamEx.of(pickRows.getChildren()) - .select(Pane.class) - .filter(ar -> StringUtils.isBlank(((TextField) ar.getChildren().get(0)).getText())) - .filter(ar -> !ar.equals(pickRows.getChildren().get(pickRows.getChildren().size() - 1))) - .toList(); - toRemove.forEach(pickRows.getChildren()::remove); - } - - private void ensureLastEmpty() { - if (single) - return; - - if (pickRows.getChildren().isEmpty()) { - pickRows.getChildren().add(createProcessRow("")); - } else { - var pane = (Pane) pickRows.getChildren().get(pickRows.getChildren().size() - 1); - if (!StringUtils.isBlank(((TextField) pane.getChildren().get(0)).getText())) { - pickRows.getChildren().add(createProcessRow("")); - } - } - } - - private Pane createProcessRow(String value) { - var pane = new HBox(); - - var textField = new TextField(); - textField.setPromptText("Process"); - textField.setText(value); - - var button = new Button("", Images.magnify()); - button.setPrefSize(34, 31); - button.setOnAction(e -> { - switch (pickType) { - case file -> UIHelper.showFilePicker("Application", textField); - case soundSource -> showAppFinder(true, textField); - case process -> showAppFinder(false, textField); - } - ensureLastEmpty(); - }); - var clear = new Button("", Images.delete()); - clear.setPrefSize(34, 31); - clear.setOnAction(e -> { - textField.setText(""); - removeEmptyIfNotLast(); - }); - - textField.setOnKeyReleased(e -> ensureLastEmpty()); - textField.focusedProperty().addListener((o, oldPropertyValue, newPropertyValue) -> { - if (!newPropertyValue) { - removeEmptyIfNotLast(); - } - }); - - HBox.setHgrow(textField, Priority.ALWAYS); - pane.getChildren().add(textField); - pane.getChildren().add(button); - pane.getChildren().add(clear); - return pane; - } - - private void showAppFinder(boolean sound, TextField textField) { - var afd = fxHelper.buildAppFinderDialog(null, sound); - var afdStage = new Stage(); - afd.start(afdStage, true); - - Optional.ofNullable(StringUtils.trimToNull(afd.getProcessName())).ifPresent(textField::setText); - } -} diff --git a/src/main/java/com/getpcpanel/ui/ProLightingDialog.java b/src/main/java/com/getpcpanel/ui/ProLightingDialog.java deleted file mode 100644 index c3788b13..00000000 --- a/src/main/java/com/getpcpanel/ui/ProLightingDialog.java +++ /dev/null @@ -1,550 +0,0 @@ -package com.getpcpanel.ui; - -import java.util.Collection; -import java.util.Objects; - -import javax.annotation.Nonnull; - -import org.springframework.context.ApplicationEventPublisher; -import org.springframework.stereotype.Component; - -import com.getpcpanel.cpp.AudioDevice; -import com.getpcpanel.cpp.ISndCtrl; -import com.getpcpanel.device.Device; -import com.getpcpanel.profile.LightingConfig; -import com.getpcpanel.profile.LightingConfig.LightingMode; -import com.getpcpanel.profile.SaveService; -import com.getpcpanel.profile.SingleKnobLightingConfig.SINGLE_KNOB_MODE; -import com.getpcpanel.profile.SingleLogoLightingConfig.SINGLE_LOGO_MODE; -import com.getpcpanel.profile.SingleSliderLabelLightingConfig.SINGLE_SLIDER_LABEL_MODE; -import com.getpcpanel.profile.SingleSliderLightingConfig.SINGLE_SLIDER_MODE; -import com.getpcpanel.spring.Prototype; -import com.getpcpanel.ui.UIInitializer.SingleParamInitializer; -import com.getpcpanel.ui.colorpicker.ColorDialog; -import com.getpcpanel.ui.colorpicker.HueSlider; -import com.getpcpanel.util.Util; - -import javafx.application.Application; -import javafx.event.ActionEvent; -import javafx.fxml.FXML; -import javafx.geometry.Side; -import javafx.scene.Node; -import javafx.scene.Scene; -import javafx.scene.control.Button; -import javafx.scene.control.CheckBox; -import javafx.scene.control.ComboBox; -import javafx.scene.control.Label; -import javafx.scene.control.Slider; -import javafx.scene.control.Tab; -import javafx.scene.control.TabPane; -import javafx.scene.control.TabPane.TabClosingPolicy; -import javafx.scene.image.Image; -import javafx.scene.layout.GridPane; -import javafx.scene.layout.Pane; -import javafx.scene.layout.VBox; -import javafx.scene.paint.Color; -import javafx.stage.Modality; -import javafx.stage.Stage; -import lombok.Getter; -import lombok.RequiredArgsConstructor; -import lombok.extern.log4j.Log4j2; - -@Log4j2 -@Component -@Prototype -@RequiredArgsConstructor -public class ProLightingDialog extends Application implements UIInitializer>, ILightingDialogMuteOverrideHelper { - private final SaveService saveService; - private final ApplicationEventPublisher eventPublisher; - private final ISndCtrl sndCtrl; - - private Stage stage; - @FXML private TabPane mainPane; - @FXML private TabPane knobsTabbedPane; - @FXML private TabPane slidersTabbedPane; - @FXML private TabPane sliderLabelsTabbedPane; - @FXML private TabPane logoTabPane; - @FXML private TabPane fullBodyTabbedPane; - @FXML private Slider rainbowPhaseShift; - @FXML private Slider rainbowBrightness; - @FXML private Slider rainbowSpeed; - @FXML private CheckBox rainbowReverse; - private HueSlider waveHue; - @FXML private Slider waveBrightness; - @FXML private Slider waveSpeed; - @FXML private CheckBox waveReverse; - @FXML private CheckBox waveBounce; - private HueSlider breathHue; - @FXML private Slider breathBrightness; - @FXML private Slider breathSpeed; - @FXML private VBox wavebox; - @FXML private VBox breathbox; - @FXML private Button applyToAllButton; - private ColorDialog allKnobColor; - private static final int NUM_KNOBS = 5; - private static final int NUM_SLIDERS = 4; - private final TabPane[] knobSingleTabPane = new TabPane[NUM_KNOBS]; - private final TabPane[] sliderSingleTabPane = new TabPane[NUM_SLIDERS]; - private final TabPane[] sliderLabelSingleTabPane = new TabPane[NUM_SLIDERS]; - private final ColorDialog[] knobStaticCDs = new ColorDialog[NUM_KNOBS]; - private final ColorDialog[] knobVolumeGradientCD1 = new ColorDialog[NUM_KNOBS]; - private final ColorDialog[] knobVolumeGradientCD2 = new ColorDialog[NUM_KNOBS]; - @Getter private final CheckBox[] muteOverrideCheckboxesKnobs = new CheckBox[NUM_KNOBS]; - @SuppressWarnings("unchecked") @Getter private final ComboBox[] muteOverrideComboBoxesKnobs = new ComboBox[NUM_KNOBS]; - @Getter private final ColorDialog[] muteOverrideColorsKnobs = new ColorDialog[NUM_KNOBS]; - @Getter private final CheckBox[] muteOverrideCheckboxesSliders = new CheckBox[NUM_SLIDERS]; - @SuppressWarnings("unchecked") @Getter private final ComboBox[] muteOverrideComboBoxesSliders = new ComboBox[NUM_SLIDERS]; - @Getter private final ColorDialog[] muteOverrideColorsSliders = new ColorDialog[NUM_SLIDERS]; - @Getter private final CheckBox[] muteOverrideCheckboxesSliderLabels = new CheckBox[NUM_SLIDERS]; - @SuppressWarnings("unchecked") @Getter private final ComboBox[] muteOverrideComboBoxesSliderLabels = new ComboBox[NUM_SLIDERS]; - @Getter private final ColorDialog[] muteOverrideColorsSliderLabels = new ColorDialog[NUM_SLIDERS]; - private final ColorDialog[] sliderStaticCDs = new ColorDialog[NUM_SLIDERS]; - private final ColorDialog[] sliderStaticGradientTopCD = new ColorDialog[NUM_SLIDERS]; - private final ColorDialog[] sliderStaticGradientBottomCD = new ColorDialog[NUM_SLIDERS]; - private final ColorDialog[] sliderVolumeGradientCD1 = new ColorDialog[NUM_SLIDERS]; - private final ColorDialog[] sliderVolumeGradientCD2 = new ColorDialog[NUM_SLIDERS]; - private final ColorDialog[] sliderLabelStaticCDs = new ColorDialog[NUM_SLIDERS]; - private ColorDialog logoStaticColor; - @FXML private Slider logoRainbowSpeed; - @FXML private Slider logoRainbowBrightness; - private HueSlider logoBreathHue; - @FXML private VBox logoBreathBox; - @FXML private Slider logoBreathBrightness; - @FXML private Slider logoBreathSpeed; - private Device device; - private LightingConfig lightingConfig; - private boolean pressedOk; - @FXML private Pane root; - - @Override - public void initUI(@Nonnull SingleParamInitializer args) { - device = args.param(); - lightingConfig = device.getSavedLightingConfig().deepCopy(); - setDeviceLighting(); - postInit(); - } - - private void setDeviceLighting() { - device.setLighting(lightingConfig.deepCopy(), true); - } - - public ProLightingDialog select(int button) { - if (button > 4) { - button -= 5; - mainPane.getSelectionModel().select(2); - slidersTabbedPane.getSelectionModel().select(button); - } else { - mainPane.getSelectionModel().select(1); - knobsTabbedPane.getSelectionModel().select(button); - } - - return this; - } - - @Override - public void start(Stage stage) { - this.stage = stage; - UIHelper.closeOnEscape(stage); - var scene = new Scene(root); - scene.getStylesheets().add(Objects.requireNonNull(getClass().getResource("/assets/dark_theme.css")).toExternalForm()); - stage.getIcons().add(new Image(Objects.requireNonNull(getClass().getResource("/assets/256x256.png")).toExternalForm())); - stage.setOnHiding(e -> { - if (!pressedOk) { - device.setLighting(device.getSavedLightingConfig(), true); - } - eventPublisher.publishEvent(LightingChangedToDefaultEvent.INSTANCE); - }); - stage.initModality(Modality.APPLICATION_MODAL); - stage.initOwner(HomePage.stage); - stage.setScene(scene); - stage.sizeToScene(); - stage.setTitle("Lighting Dialog"); - stage.show(); - } - - @FXML - private void onCancel(ActionEvent event) { - stage.close(); - } - - @FXML - private void ok(ActionEvent event) { - log.debug("{} {}", stage.getWidth(), stage.getHeight()); - pressedOk = true; - device.setSavedLighting(lightingConfig); - saveService.save(); - stage.close(); - } - - @FXML - private void turnOffLights(ActionEvent event) { - allKnobColor.setCustomColor(Color.WHITE); - allKnobColor.setCustomColor(Color.BLACK); - mainPane.getSelectionModel().select(0); - fullBodyTabbedPane.getSelectionModel().select(0); - } - - private void postInit() { - int i; - for (i = 0; i < NUM_KNOBS; i++) { - var knob = i + 1; - var tab = new Tab("Knob " + knob); - var cd = new ColorDialog(Color.BLACK); - knobStaticCDs[i] = cd; - knobVolumeGradientCD1[i] = new ColorDialog(); - knobVolumeGradientCD2[i] = new ColorDialog(); - var volGradientGP = makeFourPanelGridPane("Color when volume is 100", "Color when volume is 0", knobVolumeGradientCD2[i], knobVolumeGradientCD1[i]); - var vbox = new VBox(volGradientGP); - var staticTab = new Tab("Static", cd); - var volGradient = new Tab("Volume Gradient", vbox); - var singleKnobTabPane = new TabPane(staticTab, volGradient); - knobSingleTabPane[i] = singleKnobTabPane; - Util.adjustTabs(singleKnobTabPane, 140, 30); - singleKnobTabPane.setTabClosingPolicy(TabClosingPolicy.UNAVAILABLE); - singleKnobTabPane.setSide(Side.LEFT); - tab.setContent(tabWithMuteOverride(OverrideTargetType.KNOB, i, singleKnobTabPane)); - knobsTabbedPane.getTabs().add(tab); - } - for (i = 0; i < NUM_SLIDERS; i++) { - var tab = new Tab("Slider " + (i + 1)); - var cd = new ColorDialog(Color.BLACK); - sliderStaticCDs[i] = cd; - sliderStaticGradientTopCD[i] = new ColorDialog(); - sliderStaticGradientBottomCD[i] = new ColorDialog(); - var staticGradientGP = makeFourPanelGridPane("Top Color", "Bottom Color", sliderStaticGradientTopCD[i], sliderStaticGradientBottomCD[i]); - sliderVolumeGradientCD1[i] = new ColorDialog(); - sliderVolumeGradientCD2[i] = new ColorDialog(); - var volGradientGP = makeFourPanelGridPane("Color when volume is 100", "Color when volume is 0", - sliderVolumeGradientCD2[i], sliderVolumeGradientCD1[i]); - var staticTab = new Tab("Static", cd); - var staticGradient = new Tab("Static Gradient", staticGradientGP); - var volGradient = new Tab("Volume Gradient", volGradientGP); - var singleSliderTabPane = new TabPane(staticTab, staticGradient, volGradient); - sliderSingleTabPane[i] = singleSliderTabPane; - Util.adjustTabs(singleSliderTabPane, 140, 30); - singleSliderTabPane.setTabClosingPolicy(TabClosingPolicy.UNAVAILABLE); - singleSliderTabPane.setSide(Side.LEFT); - tab.setContent(tabWithMuteOverride(OverrideTargetType.SLIDER, i, singleSliderTabPane)); - slidersTabbedPane.getTabs().add(tab); - } - for (i = 0; i < NUM_SLIDERS; i++) { - var tab = new Tab("Slider " + (i + 1)); - sliderLabelStaticCDs[i] = new ColorDialog(); - var staticTab = new Tab("Static", sliderLabelStaticCDs[i]); - var singleSliderLabelTabPane = new TabPane(staticTab); - sliderLabelSingleTabPane[i] = singleSliderLabelTabPane; - Util.adjustTabs(singleSliderLabelTabPane, 140, 30); - singleSliderLabelTabPane.setTabClosingPolicy(TabClosingPolicy.UNAVAILABLE); - singleSliderLabelTabPane.setSide(Side.LEFT); - tab.setContent(tabWithMuteOverride(OverrideTargetType.SLIDER_LABEL, i, singleSliderLabelTabPane)); - sliderLabelsTabbedPane.getTabs().add(tab); - } - Util.adjustTabs(fullBodyTabbedPane, 120, 30); - Util.adjustTabs(logoTabPane, 120, 30); - logoStaticColor = new ColorDialog(); - logoTabPane.getTabs().get(0).setContent(logoStaticColor); - allKnobColor = new ColorDialog(); - fullBodyTabbedPane.getTabs().get(0).setContent(allKnobColor); - var allSliders = new Slider[] { - rainbowPhaseShift, rainbowBrightness, rainbowSpeed, - waveBrightness, waveSpeed, - breathBrightness, breathSpeed, - logoRainbowBrightness, logoRainbowSpeed, - logoBreathBrightness, - logoBreathSpeed }; - var allCheckBoxes = new CheckBox[] { rainbowReverse, - waveReverse, waveBounce }; - waveHue = new HueSlider(); - wavebox.getChildren().add(1, waveHue); - breathHue = new HueSlider(); - breathbox.getChildren().add(1, breathHue); - logoBreathHue = new HueSlider(); - logoBreathBox.getChildren().add(1, logoBreathHue); - applyToAllButton.setOnAction(e -> { - if (mainPane.getSelectionModel().getSelectedIndex() == 1) { - var knobIndex = knobsTabbedPane.getSelectionModel().getSelectedIndex(); - for (var idx = 0; idx < lightingConfig.getKnobConfigs().length; idx++) { - if (idx != knobIndex) - lightingConfig.getKnobConfigs()[idx].set(lightingConfig.getKnobConfigs()[knobIndex]); - } - } else if (mainPane.getSelectionModel().getSelectedIndex() == 2) { - var index = slidersTabbedPane.getSelectionModel().getSelectedIndex(); - for (var idx = 0; idx < lightingConfig.getSliderConfigs().length; idx++) { - if (idx != index) - lightingConfig.getSliderConfigs()[idx].set(lightingConfig.getSliderConfigs()[index]); - } - } else if (mainPane.getSelectionModel().getSelectedIndex() == 3) { - var index = sliderLabelsTabbedPane.getSelectionModel().getSelectedIndex(); - for (var idx = 0; idx < lightingConfig.getSliderLabelConfigs().length; idx++) { - if (idx != index) - lightingConfig.getSliderLabelConfigs()[idx].set(lightingConfig.getSliderLabelConfigs()[index]); - } - } - initFields(); - }); - initFields(); - initListeners(allSliders, allCheckBoxes); - } - - private void initFields() { - var mode = lightingConfig.getLightingMode(); - if (mode == LightingMode.ALL_COLOR) { - mainPane.getSelectionModel().select(0); - fullBodyTabbedPane.getSelectionModel().select(0); - allKnobColor.setCustomColor(Color.web(lightingConfig.getAllColor())); - } else if (mode == LightingMode.ALL_RAINBOW) { - mainPane.getSelectionModel().select(0); - fullBodyTabbedPane.getSelectionModel().select(1); - rainbowPhaseShift.setValue(lightingConfig.getRainbowPhaseShift() & 0xFF); - rainbowBrightness.setValue(lightingConfig.getRainbowBrightness() & 0xFF); - rainbowSpeed.setValue(lightingConfig.getRainbowSpeed() & 0xFF); - rainbowReverse.setSelected(lightingConfig.getRainbowReverse() == 1); - } else if (mode == LightingMode.ALL_WAVE) { - mainPane.getSelectionModel().select(0); - fullBodyTabbedPane.getSelectionModel().select(2); - waveHue.setHue(lightingConfig.getWaveHue() & 0xFF); - waveBrightness.setValue(lightingConfig.getWaveBrightness() & 0xFF); - waveSpeed.setValue(lightingConfig.getWaveSpeed() & 0xFF); - waveReverse.setSelected(lightingConfig.getWaveReverse() == 1); - waveBounce.setSelected(lightingConfig.getWaveBounce() == 1); - } else if (mode == LightingMode.ALL_BREATH) { - mainPane.getSelectionModel().select(0); - fullBodyTabbedPane.getSelectionModel().select(3); - breathHue.setHue(lightingConfig.getBreathHue() & 0xFF); - breathBrightness.setValue(lightingConfig.getBreathBrightness() & 0xFF); - breathSpeed.setValue(lightingConfig.getBreathSpeed() & 0xFF); - } else if (mode == LightingMode.CUSTOM) { - if (mainPane.getSelectionModel().getSelectedIndex() == 0) - mainPane.getSelectionModel().select(1); - var knobConfigs = lightingConfig.getKnobConfigs(); - var sliderLabelConfigs = lightingConfig.getSliderLabelConfigs(); - var sliderConfigs = lightingConfig.getSliderConfigs(); - var logoConfig = lightingConfig.getLogoConfig(); - int i; - for (i = 0; i < NUM_KNOBS; i++) { - var knobConfig = knobConfigs[i]; - if (knobConfig.getMode() == SINGLE_KNOB_MODE.STATIC) { - knobSingleTabPane[i].getSelectionModel().select(0); - knobStaticCDs[i].setCustomColor(Color.web(knobConfig.getColor1())); - } else if (knobConfig.getMode() == SINGLE_KNOB_MODE.VOLUME_GRADIENT) { - knobSingleTabPane[i].getSelectionModel().select(1); - knobVolumeGradientCD1[i].setCustomColor(Color.web(knobConfig.getColor1())); - knobVolumeGradientCD2[i].setCustomColor(Color.web(knobConfig.getColor2())); - } - setOverride(OverrideTargetType.KNOB, i, knobConfig.getMuteOverrideDeviceOrFollow(), knobConfig.getMuteOverrideColor()); - } - for (i = 0; i < NUM_SLIDERS; i++) { - var sliderLabelConfig = sliderLabelConfigs[i]; - if (sliderLabelConfig.getMode() == SINGLE_SLIDER_LABEL_MODE.STATIC) { - sliderLabelSingleTabPane[i].getSelectionModel().select(0); - sliderLabelStaticCDs[i].setCustomColor(Color.web(sliderLabelConfig.getColor())); - } - setOverride(OverrideTargetType.SLIDER_LABEL, i, sliderLabelConfig.getMuteOverrideDeviceOrFollow(), sliderLabelConfig.getMuteOverrideColor()); - } - for (i = 0; i < NUM_SLIDERS; i++) { - var sliderConfig = sliderConfigs[i]; - if (sliderConfig.getMode() == SINGLE_SLIDER_MODE.STATIC) { - sliderSingleTabPane[i].getSelectionModel().select(0); - sliderStaticCDs[i].setCustomColor(Color.web(sliderConfig.getColor1())); - } else if (sliderConfig.getMode() == SINGLE_SLIDER_MODE.STATIC_GRADIENT) { - sliderSingleTabPane[i].getSelectionModel().select(1); - sliderStaticGradientBottomCD[i].setCustomColor(Color.web(sliderConfig.getColor1())); - sliderStaticGradientTopCD[i].setCustomColor(Color.web(sliderConfig.getColor2())); - } else if (sliderConfig.getMode() == SINGLE_SLIDER_MODE.VOLUME_GRADIENT) { - sliderSingleTabPane[i].getSelectionModel().select(2); - sliderVolumeGradientCD1[i].setCustomColor(Color.web(sliderConfig.getColor1())); - sliderVolumeGradientCD2[i].setCustomColor(Color.web(sliderConfig.getColor2())); - } - setOverride(OverrideTargetType.SLIDER, i, sliderConfig.getMuteOverrideDeviceOrFollow(), sliderConfig.getMuteOverrideColor()); - } - if (logoConfig.getMode() == SINGLE_LOGO_MODE.STATIC) { - logoTabPane.getSelectionModel().select(0); - logoStaticColor.setCustomColor(Color.web(logoConfig.getColor())); - } else if (logoConfig.getMode() == SINGLE_LOGO_MODE.RAINBOW) { - logoTabPane.getSelectionModel().select(1); - logoRainbowBrightness.setValue(logoConfig.getBrightness() & 0xFF); - logoRainbowSpeed.setValue(logoConfig.getSpeed() & 0xFF); - } else if (logoConfig.getMode() == SINGLE_LOGO_MODE.BREATH) { - logoTabPane.getSelectionModel().select(2); - logoBreathHue.setHue(logoConfig.getHue() & 0xFF); - logoBreathBrightness.setValue(logoConfig.getBrightness() & 0xFF); - logoBreathSpeed.setValue(logoConfig.getSpeed() & 0xFF); - } - } - updateApplyToAllButton(); - } - - private void addListener(ColorDialog[]... xs) { - for (var x : xs) { - for (var cd : x) { - cd.customColorProperty().addListener((a, bb, c) -> updateColors()); - } - } - } - - private void addListener(TabPane[]... xs) { - for (var x : xs) { - for (var cd : x) { - cd.getSelectionModel().selectedItemProperty().addListener((a, bb, c) -> updateColors()); - } - } - } - - private void addListener(TabPane... tbs) { - for (var tb : tbs) { - tb.getSelectionModel().selectedItemProperty().addListener((a, bb, c) -> updateColors()); - } - } - - private void addListener(CheckBox[]... checkss) { - for (var checks : checkss) { - for (var check : checks) { - check.setOnAction(a -> updateColors()); - } - } - } - - private void addListener(ComboBox[]... boxess) { - for (var boxes : boxess) { - for (var box : boxes) { - box.setOnAction(a -> updateColors()); - } - } - } - - private void initListeners(Slider[] allSliders, CheckBox[] allCheckBoxes) { - addListener(knobStaticCDs, knobVolumeGradientCD1, allOverrideColors(), knobVolumeGradientCD2, - sliderStaticCDs, sliderStaticGradientBottomCD, sliderStaticGradientTopCD, sliderVolumeGradientCD1, sliderVolumeGradientCD2, - sliderLabelStaticCDs); - addListener(knobSingleTabPane, sliderLabelSingleTabPane, sliderSingleTabPane); - addListener(allOverrideCheckboxes()); - addListener(allOverrideComboBoxes()); - logoStaticColor.customColorProperty().addListener((a, b, c) -> updateColors()); - allKnobColor.customColorProperty().addListener((observable, oldValue, newValue) -> { - for (var cd : knobStaticCDs) { - cd.setCustomColor(newValue); - } - updateColors(); - }); - addListener(logoTabPane, fullBodyTabbedPane); - for (var slider : allSliders) { - slider.valueProperty().addListener((observable, oldValue, newValue) -> updateColors()); - } - for (var cb : allCheckBoxes) { - cb.selectedProperty().addListener((observable, oldValue, newValue) -> updateColors()); - } - waveHue.getHueProperty().addListener((observable, oldValue, newValue) -> updateColors()); - breathHue.getHueProperty().addListener((observable, oldValue, newValue) -> updateColors()); - logoBreathHue.getHueProperty().addListener((observable, oldValue, newValue) -> updateColors()); - mainPane.getSelectionModel().selectedItemProperty().addListener((observable, oldValue, newValue) -> { - updateColors(); - updateApplyToAllButton(); - }); - } - - private void updateApplyToAllButton() { - if (mainPane.getSelectionModel().getSelectedIndex() == 0 || mainPane.getSelectionModel().getSelectedIndex() == 4) { - applyToAllButton.setVisible(false); - return; - } - applyToAllButton.setVisible(true); - if (mainPane.getSelectionModel().getSelectedIndex() == 1) { - applyToAllButton.setText("Apply To All Knobs"); - } else if (mainPane.getSelectionModel().getSelectedIndex() == 2) { - applyToAllButton.setText("Apply To All Sliders"); - } else if (mainPane.getSelectionModel().getSelectedIndex() == 3) { - applyToAllButton.setText("Apply To All Slider Labels"); - } - } - - @SuppressWarnings("NumericCastThatLosesPrecision") - private void updateColors() { - if (mainPane.getSelectionModel().getSelectedIndex() == 0) { - if (fullBodyTabbedPane.getSelectionModel().getSelectedIndex() == 0) { - lightingConfig = LightingConfig.createAllColor(allKnobColor.getCustomColor()); - setDeviceLighting(); - } else if (fullBodyTabbedPane.getSelectionModel().getSelectedIndex() == 1) { - lightingConfig = LightingConfig.createRainbowAnimation((byte) (int) rainbowPhaseShift.getValue(), (byte) (int) rainbowBrightness.getValue(), - (byte) (int) rainbowSpeed.getValue(), rainbowReverse.isSelected()); - setDeviceLighting(); - } else if (fullBodyTabbedPane.getSelectionModel().getSelectedIndex() == 2) { - lightingConfig = LightingConfig.createWaveAnimation((byte) waveHue.getHue(), (byte) (int) waveBrightness.getValue(), (byte) (int) waveSpeed.getValue(), - waveReverse.isSelected(), waveBounce.isSelected()); - setDeviceLighting(); - } else if (fullBodyTabbedPane.getSelectionModel().getSelectedIndex() == 3) { - lightingConfig = LightingConfig.createBreathAnimation((byte) breathHue.getHue(), (byte) (int) breathBrightness.getValue(), (byte) (int) breathSpeed.getValue()); - setDeviceLighting(); - } - } else { - lightingConfig = new LightingConfig(NUM_KNOBS, NUM_SLIDERS); - lightingConfig.setLightingMode(LightingMode.CUSTOM); - for (var knob = 0; knob < NUM_KNOBS; knob++) { - var knobConfig = lightingConfig.getKnobConfigs()[knob]; - if (knobSingleTabPane[knob].getSelectionModel().getSelectedIndex() == 0) { - knobConfig.setMode(SINGLE_KNOB_MODE.STATIC); - knobConfig.setColor1FromColor(knobStaticCDs[knob].getCustomColor()); - } else if (knobSingleTabPane[knob].getSelectionModel().getSelectedIndex() == 1) { - knobConfig.setMode(SINGLE_KNOB_MODE.VOLUME_GRADIENT); - knobConfig.setColor1FromColor(knobVolumeGradientCD1[knob].getCustomColor()); - knobConfig.setColor2FromColor(knobVolumeGradientCD2[knob].getCustomColor()); - } - setOverrideSetting(OverrideTargetType.KNOB, knob, knobConfig::setMuteOverrideDeviceOrFollow, knobConfig::setMuteOverrideColorFromColor); - } - int slider; - for (slider = 0; slider < NUM_SLIDERS; slider++) { - var sliderLabelConfig = lightingConfig.getSliderLabelConfigs()[slider]; - if (sliderLabelSingleTabPane[slider].getSelectionModel().getSelectedIndex() == 0) { - sliderLabelConfig.setMode(SINGLE_SLIDER_LABEL_MODE.STATIC); - sliderLabelConfig.setColorFromColor(sliderLabelStaticCDs[slider].getCustomColor()); - } - setOverrideSetting(OverrideTargetType.SLIDER_LABEL, slider, sliderLabelConfig::setMuteOverrideDeviceOrFollow, sliderLabelConfig::setMuteOverrideColorFromColor); - } - for (slider = 0; slider < NUM_SLIDERS; slider++) { - if (sliderSingleTabPane[slider].getSelectionModel().getSelectedIndex() == 0) { - lightingConfig.getSliderConfigs()[slider].setMode(SINGLE_SLIDER_MODE.STATIC); - lightingConfig.getSliderConfigs()[slider].setColor1FromColor(sliderStaticCDs[slider].getCustomColor()); - } else if (sliderSingleTabPane[slider].getSelectionModel().getSelectedIndex() == 1) { - lightingConfig.getSliderConfigs()[slider].setMode(SINGLE_SLIDER_MODE.STATIC_GRADIENT); - lightingConfig.getSliderConfigs()[slider].setColor1FromColor(sliderStaticGradientBottomCD[slider].getCustomColor()); - lightingConfig.getSliderConfigs()[slider].setColor2FromColor(sliderStaticGradientTopCD[slider].getCustomColor()); - } else if (sliderSingleTabPane[slider].getSelectionModel().getSelectedIndex() == 2) { - lightingConfig.getSliderConfigs()[slider].setMode(SINGLE_SLIDER_MODE.VOLUME_GRADIENT); - lightingConfig.getSliderConfigs()[slider].setColor1FromColor(sliderVolumeGradientCD1[slider].getCustomColor()); - lightingConfig.getSliderConfigs()[slider].setColor2FromColor(sliderVolumeGradientCD2[slider].getCustomColor()); - } - var sliderConfig = lightingConfig.getSliderConfigs()[slider]; - setOverrideSetting(OverrideTargetType.SLIDER, slider, sliderConfig::setMuteOverrideDeviceOrFollow, sliderConfig::setMuteOverrideColorFromColor); - } - if (logoTabPane.getSelectionModel().getSelectedIndex() == 0) { - lightingConfig.getLogoConfig().setMode(SINGLE_LOGO_MODE.STATIC); - lightingConfig.getLogoConfig().setColor(logoStaticColor.getCustomColor()); - } else if (logoTabPane.getSelectionModel().getSelectedIndex() == 1) { - lightingConfig.getLogoConfig().setMode(SINGLE_LOGO_MODE.RAINBOW); - lightingConfig.getLogoConfig().setBrightness((byte) (int) logoRainbowBrightness.getValue()); - lightingConfig.getLogoConfig().setSpeed((byte) (int) logoRainbowSpeed.getValue()); - } else if (logoTabPane.getSelectionModel().getSelectedIndex() == 2) { - lightingConfig.getLogoConfig().setMode(SINGLE_LOGO_MODE.BREATH); - lightingConfig.getLogoConfig().setBrightness((byte) (int) logoBreathBrightness.getValue()); - lightingConfig.getLogoConfig().setSpeed((byte) (int) logoBreathSpeed.getValue()); - lightingConfig.getLogoConfig().setHue((byte) logoBreathHue.getHue()); - } - setDeviceLighting(); - } - } - - private static GridPane makeFourPanelGridPane(String str1, String str2, Node obj1, Node obj2) { - var gp = new GridPane(); - var l1 = new Label(str1); - var l2 = new Label(str2); - l1.setWrapText(true); - l2.setWrapText(true); - gp.addColumn(0, l1, l2); - gp.addColumn(1, obj1, obj2); - return gp; - } - - @Override - public Collection getDevices() { - return sndCtrl.getDevices(); - } -} diff --git a/src/main/java/com/getpcpanel/ui/ProfileSettingsDialog.java b/src/main/java/com/getpcpanel/ui/ProfileSettingsDialog.java deleted file mode 100644 index eda7fdf3..00000000 --- a/src/main/java/com/getpcpanel/ui/ProfileSettingsDialog.java +++ /dev/null @@ -1,221 +0,0 @@ -package com.getpcpanel.ui; - -import java.util.HashMap; -import java.util.List; -import java.util.Objects; -import java.util.Optional; - -import javax.annotation.Nonnull; - -import org.apache.commons.lang3.StringUtils; -import org.apache.commons.lang3.math.NumberUtils; -import org.springframework.stereotype.Component; - -import com.getpcpanel.osc.OSCService; -import com.getpcpanel.profile.DeviceSave; -import com.getpcpanel.profile.OSCBinding; -import com.getpcpanel.profile.Profile; -import com.getpcpanel.profile.SaveService; -import com.getpcpanel.spring.OsHelper; -import com.getpcpanel.spring.Prototype; -import com.getpcpanel.util.ShortcutHook; -import com.github.kwhat.jnativehook.keyboard.NativeKeyEvent; - -import javafx.application.Application; -import javafx.collections.FXCollections; -import javafx.collections.transformation.FilteredList; -import javafx.event.ActionEvent; -import javafx.fxml.FXML; -import javafx.geometry.Insets; -import javafx.geometry.Pos; -import javafx.scene.Scene; -import javafx.scene.control.CheckBox; -import javafx.scene.control.ComboBox; -import javafx.scene.control.Label; -import javafx.scene.control.TextField; -import javafx.scene.image.Image; -import javafx.scene.layout.HBox; -import javafx.scene.layout.Pane; -import javafx.scene.layout.VBox; -import javafx.stage.Stage; -import lombok.RequiredArgsConstructor; -import lombok.extern.log4j.Log4j2; -import one.util.streamex.EntryStream; -import one.util.streamex.StreamEx; - -@Log4j2 -@Component -@Prototype -@RequiredArgsConstructor -public class ProfileSettingsDialog extends Application implements UIInitializer { - private final SaveService saveService; - private final Optional shortcutHook; - private final OsHelper osHelper; - private final OSCService oscService; - @FXML private Pane root; - private DeviceSave deviceSave; - private Profile profile; - private Stage stage; - @FXML private TextField profileName; - @FXML private CheckBox mainProfile; - @FXML private CheckBox focusBackOnLost; - @FXML private PickProcessesController focusOnListListController; - @FXML private TextField activationFld; - @FXML private VBox osSpecificHolder; - @FXML private VBox oscBindings; - private List sortedAddresses; - - @Override - public void initUI(@Nonnull ProfileSettingsArgs args) { - deviceSave = args.deviceSave(); - profile = args.profile(); - root.setId("pane"); - } - - @Override - public void start(Stage stage) throws Exception { - this.stage = stage; - UIHelper.closeOnEscape(stage); - var scene = new Scene(root, 800.0D, 400.0D); - scene.getStylesheets().addAll(Objects.requireNonNull(getClass().getResource("/assets/1.css")).toExternalForm()); - stage.getIcons().add(new Image(Objects.requireNonNull(getClass().getResource("/assets/256x256.png")).toExternalForm())); - stage.setScene(scene); - - initWindow(); - ResizeHelper.addResizeListener(stage, 200.0D, 200.0D); - stage.sizeToScene(); - stage.setTitle("Profile: " + profile.getName()); - - var toRemove = StreamEx.of(osSpecificHolder.getChildren()).remove(osHelper::isSupported).toSet(); - osSpecificHolder.getChildren().removeAll(toRemove); - - stage.show(); - } - - private void initWindow() { - profileName.setText(profile.getName()); - mainProfile.setSelected(profile.isMainProfile()); - - focusBackOnLost.setSelected(profile.isFocusBackOnLost()); - focusOnListListController.setPickType(PickProcessesController.PickType.process).setSelection(profile.getActivateApplications()); - - activationFld.focusedProperty().addListener((observable, oldValue, newValue) -> shortcutHook.ifPresent(hook -> { - if (newValue) { - hook.setOverrideListener(this::registerShortcut); - } else { - hook.setOverrideListener(null); - } - } - )); - activationFld.setText(StringUtils.defaultString(profile.getActivationShortcut())); - initOsc(); - } - - private void registerShortcut(NativeKeyEvent event) { - shortcutHook.ifPresent(hook -> { - if (hook.canBeShortcut(event)) { - activationFld.setText(hook.toKeyString(event)); - } - }); - } - - @FXML - private void ok(ActionEvent event) { - profile.setName(profileName.getText()); - profile.setMainProfile(mainProfile.isSelected()); - if (profile.isMainProfile()) { - StreamEx.of(deviceSave.getProfiles()).remove(profile::equals).forEach(p -> p.setMainProfile(false)); - } - - profile.setFocusBackOnLost(focusBackOnLost.isSelected()); - profile.getActivateApplications().clear(); - profile.setActivateApplications(focusOnListListController.getSelection()); - profile.setActivationShortcut(StringUtils.trimToNull(activationFld.getText())); - saveOsc(); - - saveService.save(); - stage.close(); - } - - private void initOsc() { - sortedAddresses = StreamEx.of(oscService.getAddresses()).sorted().prepend("").toList(); - var source = profile.getOscBinding(); - var config = profile.getLightingConfig(); - var knobCount = config.getKnobConfigs().length; - var sliderCount = config.getSliderConfigs().length; - - for (var i = 0; i < knobCount; i++) { - addOscRow("Knob " + (i + 1), source.getOrDefault(i * 2, OSCBinding.EMPTY), false); - addOscRow("Button " + (i + 1), source.getOrDefault(i * 2 + 1, OSCBinding.EMPTY), true); - } - - for (var i = 0; i < sliderCount; i++) { - addOscRow("Slider " + (i + 1), source.getOrDefault((knobCount * 2) + i, OSCBinding.EMPTY), false); - } - } - - private void addOscRow(String controlName, OSCBinding controlValue, boolean button) { - var label = new Label(controlName); - label.setPrefWidth(100); - HBox.setMargin(label, new Insets(0, 10, 0, 0)); - - var address = buildComboBox(); - address.setValue(controlValue.address()); - var target = new HBox(label, address); - target.setAlignment(Pos.CENTER_LEFT); - - if (button) { - // TODO: Add when supporting toggle - // var toggle = new CheckBox("Toggle"); - // toggle.setSelected(controlValue.toggle()); - // target.getChildren().addAll(toggle); - } else { - var min = new TextField(String.valueOf(controlValue.min())); - var max = new TextField(String.valueOf(controlValue.max())); - min.setPromptText("Min value"); - max.setPromptText("Max value"); - target.getChildren().addAll(min, max); - } - - oscBindings.getChildren().add(target); - } - - private ComboBox buildComboBox() { - var field = new ComboBox<>(new FilteredList<>(FXCollections.observableArrayList(sortedAddresses), p -> true)); - field.setPrefHeight(25); - field.setPrefWidth(200); - field.setEditable(true); - return field; - } - - private void saveOsc() { - var target = new HashMap(); - EntryStream.of(oscBindings.getChildren()) - .selectValues(HBox.class) - .mapValues(this::toBinding) - .forKeyValue(target::put); - profile.setOscBinding(target); - } - - private OSCBinding toBinding(HBox row) { - @SuppressWarnings("unchecked") var address = ((ComboBox) row.getChildren().get(1)).getValue(); - var min = NumberUtils.toFloat(row.getChildren().size() > 2 && row.getChildren().get(2) instanceof TextField minField ? minField.getText() : "0", 0); - var max = NumberUtils.toFloat(row.getChildren().size() > 3 && row.getChildren().get(3) instanceof TextField maxField ? maxField.getText() : "1", 1); - var toggle = row.getChildren().size() > 2 && row.getChildren().get(2) instanceof CheckBox cb && cb.isSelected(); - - return new OSCBinding(address, min, max, toggle); - } - - @FXML - private void clearActivationShortcut(ActionEvent event) { - activationFld.setText(""); - } - - @FXML - private void closeButtonAction(ActionEvent event) { - stage.close(); - } - - public record ProfileSettingsArgs(DeviceSave deviceSave, Profile profile) { - } -} diff --git a/src/main/java/com/getpcpanel/ui/RGBLightingDialog.java b/src/main/java/com/getpcpanel/ui/RGBLightingDialog.java deleted file mode 100644 index 9ffb9d74..00000000 --- a/src/main/java/com/getpcpanel/ui/RGBLightingDialog.java +++ /dev/null @@ -1,273 +0,0 @@ -package com.getpcpanel.ui; - -import java.util.ArrayList; -import java.util.List; -import java.util.Objects; -import java.util.stream.IntStream; - -import javax.annotation.Nonnull; - -import org.springframework.context.ApplicationEventPublisher; -import org.springframework.stereotype.Component; - -import com.getpcpanel.device.PCPanelRGBUI; -import com.getpcpanel.profile.LightingConfig; -import com.getpcpanel.profile.LightingConfig.LightingMode; -import com.getpcpanel.profile.SaveService; -import com.getpcpanel.spring.Prototype; -import com.getpcpanel.ui.UIInitializer.SingleParamInitializer; -import com.getpcpanel.ui.colorpicker.ColorDialog; -import com.getpcpanel.ui.colorpicker.HueSlider; -import com.getpcpanel.util.Util; - -import javafx.application.Application; -import javafx.event.ActionEvent; -import javafx.fxml.FXML; -import javafx.scene.Scene; -import javafx.scene.control.CheckBox; -import javafx.scene.control.Slider; -import javafx.scene.control.Tab; -import javafx.scene.control.TabPane; -import javafx.scene.image.Image; -import javafx.scene.layout.HBox; -import javafx.scene.layout.Pane; -import javafx.scene.layout.VBox; -import javafx.scene.paint.Color; -import javafx.stage.Modality; -import javafx.stage.Stage; -import lombok.RequiredArgsConstructor; -import lombok.extern.log4j.Log4j2; - -@Log4j2 -@Component -@Prototype -@RequiredArgsConstructor -public class RGBLightingDialog extends Application implements UIInitializer> { - private final SaveService saveService; - private final ApplicationEventPublisher eventPublisher; - - private Stage stage; - @FXML private TabPane knobsTabbedPane; - @FXML private TabPane allKnobsTabbedPane; - @FXML private Slider rainbowPhaseShift; - @FXML private Slider rainbowBrightness; - @FXML private Slider rainbowSpeed; - @FXML private CheckBox rainbowReverse; - private HueSlider waveHue; - @FXML private Slider waveBrightness; - @FXML private Slider waveSpeed; - @FXML private CheckBox waveReverse; - @FXML private CheckBox waveBounce; - private HueSlider breathHue; - @FXML private Slider breathBrightness; - @FXML private Slider breathSpeed; - @FXML private VBox wavebox; - @FXML private VBox breathbox; - @FXML private HBox volumeFollowBox; - @FXML private HBox volumeFollowCheckboxContainer; - private ColorDialog allKnobColor; - private final List cds = new ArrayList<>(); - private final List volumeFollowingCheckBoxes = new ArrayList<>(); - private PCPanelRGBUI device; - private boolean pressedOk; - @FXML private Pane root; - private LightingConfig lightingConfig; - - @Override - public void initUI(@Nonnull SingleParamInitializer args) { - device = args.param(); - lightingConfig = device.getSavedLightingConfig().deepCopy(); - setDeviceLighting(); - postInit(); - } - - private void setDeviceLighting() { - device.setLighting(lightingConfig.deepCopy(), true); - } - - @Override - public void start(Stage stage) { - this.stage = stage; - UIHelper.closeOnEscape(stage); - var scene = new Scene(root); - scene.getStylesheets().add(Objects.requireNonNull(getClass().getResource("/assets/dark_theme.css")).toExternalForm()); - stage.getIcons().add(new Image(Objects.requireNonNull(getClass().getResource("/assets/256x256.png")).toExternalForm())); - stage.setOnHiding(e -> { - if (!pressedOk) { - device.setLighting(device.getSavedLightingConfig(), true); - } - eventPublisher.publishEvent(LightingChangedToDefaultEvent.INSTANCE); - }); - stage.initModality(Modality.APPLICATION_MODAL); - stage.initOwner(HomePage.stage); - stage.setScene(scene); - stage.sizeToScene(); - stage.setTitle("Lighting Dialog"); - stage.show(); - } - - public RGBLightingDialog select(int idx) { - knobsTabbedPane.getSelectionModel().select(idx + 1); - return this; - } - - @FXML - private void onCancel(ActionEvent event) { - stage.close(); - } - - @FXML - private void ok(ActionEvent event) { - log.debug("{} {}", stage.getWidth(), stage.getHeight()); - pressedOk = true; - device.setSavedLighting(lightingConfig); - saveService.save(); - stage.close(); - } - - @FXML - private void turnOffLights(ActionEvent event) { - allKnobColor.setCustomColor(Color.WHITE); - allKnobColor.setCustomColor(Color.BLACK); - knobsTabbedPane.getSelectionModel().select(0); - allKnobsTabbedPane.getSelectionModel().select(0); - } - - private void postInit() { - for (var i = 0; i < device.getKnobCount(); i++) { - var knob = i + 1; - var tab = new Tab("Knob " + knob); - var cd = new ColorDialog(Color.BLACK); - cds.add(cd); - var cb = new CheckBox("K" + knob); - volumeFollowingCheckBoxes.add(cb); - volumeFollowCheckboxContainer.getChildren().add(cb); - tab.setContent(cd); - knobsTabbedPane.getTabs().add(tab); - } - Util.adjustTabs(allKnobsTabbedPane, 120, 30); - allKnobColor = new ColorDialog(); - allKnobsTabbedPane.getTabs().get(0).setContent(allKnobColor); - var allSliders = new Slider[] { rainbowPhaseShift, rainbowBrightness, rainbowSpeed, - waveBrightness, waveSpeed, - breathBrightness, breathSpeed }; - var allCheckBoxes = new CheckBox[] { rainbowReverse, - waveReverse, waveBounce }; - waveHue = new HueSlider(); - wavebox.getChildren().add(1, waveHue); - breathHue = new HueSlider(); - breathbox.getChildren().add(1, breathHue); - initFields(); - initListeners(allSliders, allCheckBoxes); - } - - private void initFields() { - var mode = lightingConfig.getLightingMode(); - setFollowingControlsVisible(false); - if (mode == LightingMode.ALL_COLOR) { - setFollowingControlsVisible(true); - setVolumeTrackingData(lightingConfig.getVolumeBrightnessTrackingEnabled()); - knobsTabbedPane.getSelectionModel().select(0); - allKnobsTabbedPane.getSelectionModel().select(0); - var color = Color.valueOf(lightingConfig.getAllColor()); - allKnobColor.setCustomColor(color); - for (var cd : cds) - cd.setCustomColor(color); - } else if (mode == LightingMode.SINGLE_COLOR) { - setFollowingControlsVisible(true); - setVolumeTrackingData(lightingConfig.getVolumeBrightnessTrackingEnabled()); - knobsTabbedPane.getSelectionModel().select(1); - for (var i = 0; i < device.getKnobCount(); i++) - cds.get(i).setCustomColor(Color.valueOf(lightingConfig.getIndividualColors()[i])); - } else if (mode == LightingMode.ALL_RAINBOW) { - knobsTabbedPane.getSelectionModel().select(0); - allKnobsTabbedPane.getSelectionModel().select(1); - rainbowPhaseShift.setValue(lightingConfig.getRainbowPhaseShift() & 0xFF); - rainbowBrightness.setValue(lightingConfig.getRainbowBrightness() & 0xFF); - rainbowSpeed.setValue(lightingConfig.getRainbowSpeed() & 0xFF); - rainbowReverse.setSelected(lightingConfig.getRainbowReverse() == 1); - } else if (mode == LightingMode.ALL_WAVE) { - knobsTabbedPane.getSelectionModel().select(0); - allKnobsTabbedPane.getSelectionModel().select(2); - waveHue.setHue(lightingConfig.getWaveHue() & 0xFF); - waveBrightness.setValue(lightingConfig.getWaveBrightness() & 0xFF); - waveSpeed.setValue(lightingConfig.getWaveSpeed() & 0xFF); - waveReverse.setSelected(lightingConfig.getWaveReverse() == 1); - waveBounce.setSelected(lightingConfig.getWaveBounce() == 1); - } else if (mode == LightingMode.ALL_BREATH) { - knobsTabbedPane.getSelectionModel().select(0); - allKnobsTabbedPane.getSelectionModel().select(3); - breathHue.setHue(lightingConfig.getBreathHue() & 0xFF); - breathBrightness.setValue(lightingConfig.getBreathBrightness() & 0xFF); - breathSpeed.setValue(lightingConfig.getBreathSpeed() & 0xFF); - } else { - log.error("unexpected mode in lightingdialog"); - } - } - - private void initListeners(Slider[] allSliders, CheckBox[] allCheckBoxes) { - for (var cd : cds) - cd.customColorProperty().addListener((observable, oldValue, newValue) -> updateColors()); - allKnobColor.customColorProperty().addListener((observable, oldValue, newValue) -> { - for (var cd : cds) - cd.setCustomColor(newValue); - updateColors(); - }); - for (var slider : allSliders) { - slider.valueProperty().addListener((observable, oldValue, newValue) -> updateColors()); - } - for (var cb : allCheckBoxes) { - cb.selectedProperty().addListener((observable, oldValue, newValue) -> updateColors()); - } - for (var cb : volumeFollowingCheckBoxes) - cb.selectedProperty().addListener((observable, oldValue, newValue) -> updateColors()); - waveHue.getHueProperty().addListener((observable, oldValue, newValue) -> updateColors()); - breathHue.getHueProperty().addListener((observable, oldValue, newValue) -> updateColors()); - knobsTabbedPane.getSelectionModel().selectedItemProperty().addListener((observable, oldValue, newValue) -> updateColors()); - allKnobsTabbedPane.getSelectionModel().selectedItemProperty().addListener((observable, oldValue, newValue) -> updateColors()); - } - - @SuppressWarnings("NumericCastThatLosesPrecision") - private void updateColors() { - setFollowingControlsVisible(false); - if (knobsTabbedPane.getSelectionModel().getSelectedIndex() == 0) { - if (allKnobsTabbedPane.getSelectionModel().getSelectedIndex() == 0) { - setFollowingControlsVisible(true); - lightingConfig = LightingConfig.createAllColor(allKnobColor.getCustomColor(), getVolumeTrackingData()); - setDeviceLighting(); - } else if (allKnobsTabbedPane.getSelectionModel().getSelectedIndex() == 1) { - lightingConfig = LightingConfig.createRainbowAnimation((byte) (int) rainbowPhaseShift.getValue(), (byte) (int) rainbowBrightness.getValue(), - (byte) (int) rainbowSpeed.getValue(), rainbowReverse.isSelected()); - setDeviceLighting(); - } else if (allKnobsTabbedPane.getSelectionModel().getSelectedIndex() == 2) { - lightingConfig = LightingConfig.createWaveAnimation((byte) waveHue.getHue(), (byte) (int) waveBrightness.getValue(), (byte) (int) waveSpeed.getValue(), - waveReverse.isSelected(), waveBounce.isSelected()); - setDeviceLighting(); - } else if (allKnobsTabbedPane.getSelectionModel().getSelectedIndex() == 3) { - lightingConfig = LightingConfig.createBreathAnimation((byte) breathHue.getHue(), (byte) (int) breathBrightness.getValue(), (byte) (int) breathSpeed.getValue()); - setDeviceLighting(); - } - } else { - setFollowingControlsVisible(true); - var colors = IntStream.range(0, device.getKnobCount()).mapToObj(i -> cds.get(i).getCustomColor()).toArray(Color[]::new); - lightingConfig = LightingConfig.createSingleColor(colors, getVolumeTrackingData()); - setDeviceLighting(); - } - } - - private void setFollowingControlsVisible(boolean b) { - volumeFollowBox.setVisible(b); - } - - private boolean[] getVolumeTrackingData() { - var ret = new boolean[device.getKnobCount()]; - for (var i = 0; i < ret.length; i++) - ret[i] = volumeFollowingCheckBoxes.get(i).isSelected(); - return ret; - } - - private void setVolumeTrackingData(boolean[] data) { - for (var i = 0; i < data.length; i++) - volumeFollowingCheckBoxes.get(i).setSelected(data[i]); - } -} diff --git a/src/main/java/com/getpcpanel/ui/ResizeHelper.java b/src/main/java/com/getpcpanel/ui/ResizeHelper.java deleted file mode 100644 index 1d10ab7d..00000000 --- a/src/main/java/com/getpcpanel/ui/ResizeHelper.java +++ /dev/null @@ -1,172 +0,0 @@ -package com.getpcpanel.ui; - -import javafx.event.EventHandler; -import javafx.scene.Cursor; -import javafx.scene.Node; -import javafx.scene.Parent; -import javafx.scene.input.MouseEvent; -import javafx.stage.Stage; - -public final class ResizeHelper { - private ResizeHelper() { - } - - public static void addResizeListener(Stage stage, double minWidth, double minHeight) { - addResizeListener(stage, minWidth, minHeight, Double.MAX_VALUE, Double.MAX_VALUE); - } - - public static void addResizeListener(Stage stage, double minWidth, double minHeight, double maxWidth, double maxHeight) { - var resizeListener = new ResizeListener(stage); - stage.getScene().addEventHandler(MouseEvent.MOUSE_MOVED, resizeListener); - stage.getScene().addEventHandler(MouseEvent.MOUSE_PRESSED, resizeListener); - stage.getScene().addEventHandler(MouseEvent.MOUSE_DRAGGED, resizeListener); - stage.getScene().addEventHandler(MouseEvent.MOUSE_EXITED, resizeListener); - stage.getScene().addEventHandler(MouseEvent.MOUSE_EXITED_TARGET, resizeListener); - resizeListener.setMinWidth(minWidth); - resizeListener.setMinHeight(minHeight); - resizeListener.setMaxWidth(maxWidth); - resizeListener.setMaxHeight(maxHeight); - var children = stage.getScene().getRoot().getChildrenUnmodifiable(); - for (var child : children) { - addListenerDeeply(child, resizeListener); - } - } - - private static void addListenerDeeply(Node node, EventHandler listener) { - node.addEventHandler(MouseEvent.MOUSE_MOVED, listener); - node.addEventHandler(MouseEvent.MOUSE_PRESSED, listener); - node.addEventHandler(MouseEvent.MOUSE_DRAGGED, listener); - node.addEventHandler(MouseEvent.MOUSE_EXITED, listener); - node.addEventHandler(MouseEvent.MOUSE_EXITED_TARGET, listener); - if (node instanceof Parent parent) { - var children = parent.getChildrenUnmodifiable(); - for (var child : children) { - if (!child.getStyleClass().contains("no-resize")) { - addListenerDeeply(child, listener); - } - } - } - } - - static class ResizeListener implements EventHandler { - private static final int BORDER = 4; - private final Stage stage; - - private Cursor cursorEvent = Cursor.DEFAULT; - private boolean resizing = true; - private double startX; - private double startY; - private double screenOffsetX; - private double screenOffsetY; - private double minWidth; - private double maxWidth; - private double minHeight; - private double maxHeight; - - public ResizeListener(Stage stage) { - this.stage = stage; - } - - public void setMinWidth(double minWidth) { - this.minWidth = minWidth; - } - - public void setMaxWidth(double maxWidth) { - this.maxWidth = maxWidth; - } - - public void setMinHeight(double minHeight) { - this.minHeight = minHeight; - } - - public void setMaxHeight(double maxHeight) { - this.maxHeight = maxHeight; - } - - @Override - public void handle(MouseEvent mouseEvent) { - var mouseEventType = mouseEvent.getEventType(); - var scene = stage.getScene(); - var mouseEventX = mouseEvent.getSceneX(); - var mouseEventY = mouseEvent.getSceneY(); - var sceneWidth = scene.getWidth(); - var sceneHeight = scene.getHeight(); - if (MouseEvent.MOUSE_MOVED.equals(mouseEventType)) { - if (mouseEventX < BORDER && mouseEventY < BORDER) { - cursorEvent = Cursor.NW_RESIZE; - } else if (mouseEventX < BORDER && mouseEventY > sceneHeight - BORDER) { - cursorEvent = Cursor.SW_RESIZE; - } else if (mouseEventX > sceneWidth - BORDER && mouseEventY < BORDER) { - cursorEvent = Cursor.NE_RESIZE; - } else if (mouseEventX > sceneWidth - BORDER && mouseEventY > sceneHeight - BORDER) { - cursorEvent = Cursor.SE_RESIZE; - } else if (mouseEventX < BORDER) { - cursorEvent = Cursor.W_RESIZE; - } else if (mouseEventX > sceneWidth - BORDER) { - cursorEvent = Cursor.E_RESIZE; - } else if (mouseEventY < BORDER) { - cursorEvent = Cursor.N_RESIZE; - } else if (mouseEventY > sceneHeight - BORDER) { - cursorEvent = Cursor.S_RESIZE; - } else { - cursorEvent = Cursor.DEFAULT; - } - scene.setCursor(cursorEvent); - } else if (MouseEvent.MOUSE_EXITED.equals(mouseEventType) || MouseEvent.MOUSE_EXITED_TARGET.equals(mouseEventType)) { - scene.setCursor(Cursor.DEFAULT); - } else if (MouseEvent.MOUSE_PRESSED.equals(mouseEventType)) { - startX = stage.getWidth() - mouseEventX; - startY = stage.getHeight() - mouseEventY; - } else if (MouseEvent.MOUSE_DRAGGED.equals(mouseEventType) && - !Cursor.DEFAULT.equals(cursorEvent)) { - resizing = true; - if (!Cursor.W_RESIZE.equals(cursorEvent) && !Cursor.E_RESIZE.equals(cursorEvent)) { - var minHeight = (stage.getMinHeight() > (BORDER * 2)) ? stage.getMinHeight() : (BORDER * 2); - if (Cursor.NW_RESIZE.equals(cursorEvent) || Cursor.N_RESIZE.equals(cursorEvent) || - Cursor.NE_RESIZE.equals(cursorEvent)) { - if (stage.getHeight() > minHeight || mouseEventY < 0.0D) { - setStageHeight(stage.getY() - mouseEvent.getScreenY() + stage.getHeight()); - stage.setY(mouseEvent.getScreenY()); - } - } else if (stage.getHeight() > minHeight || mouseEventY + startY - stage.getHeight() > 0.0D) { - setStageHeight(mouseEventY + startY); - } - } - if (!Cursor.N_RESIZE.equals(cursorEvent) && !Cursor.S_RESIZE.equals(cursorEvent)) { - var minWidth = (stage.getMinWidth() > (BORDER * 2)) ? stage.getMinWidth() : (BORDER * 2); - if (Cursor.NW_RESIZE.equals(cursorEvent) || Cursor.W_RESIZE.equals(cursorEvent) || - Cursor.SW_RESIZE.equals(cursorEvent)) { - if (stage.getWidth() > minWidth || mouseEventX < 0.0D) { - setStageWidth(stage.getX() - mouseEvent.getScreenX() + stage.getWidth()); - stage.setX(mouseEvent.getScreenX()); - } - } else if (stage.getWidth() > minWidth || mouseEventX + startX - stage.getWidth() > 0.0D) { - setStageWidth(mouseEventX + startX); - } - } - resizing = false; - } - if (MouseEvent.MOUSE_PRESSED.equals(mouseEventType) && Cursor.DEFAULT.equals(cursorEvent)) { - resizing = false; - screenOffsetX = stage.getX() - mouseEvent.getScreenX(); - screenOffsetY = stage.getY() - mouseEvent.getScreenY(); - } - if (MouseEvent.MOUSE_DRAGGED.equals(mouseEventType) && Cursor.DEFAULT.equals(cursorEvent) && !resizing) { - stage.setX(mouseEvent.getScreenX() + screenOffsetX); - stage.setY(mouseEvent.getScreenY() + screenOffsetY); - } - } - - private void setStageWidth(double width) { - width = Math.min(width, maxWidth); - width = Math.max(width, minWidth); - stage.setWidth(width); - } - - private void setStageHeight(double height) { - height = Math.min(height, maxHeight); - height = Math.max(height, minHeight); - stage.setHeight(height); - } - } -} diff --git a/src/main/java/com/getpcpanel/ui/SettingsDialog.java b/src/main/java/com/getpcpanel/ui/SettingsDialog.java deleted file mode 100644 index 5e687d29..00000000 --- a/src/main/java/com/getpcpanel/ui/SettingsDialog.java +++ /dev/null @@ -1,369 +0,0 @@ -package com.getpcpanel.ui; - -import static com.getpcpanel.profile.Save.DEFAULT_OVERLAY_BAR_BACKGROUND_COLOR; -import static com.getpcpanel.profile.Save.DEFAULT_OVERLAY_BAR_COLOR; -import static com.getpcpanel.profile.Save.DEFAULT_OVERLAY_BAR_HEIGHT; -import static com.getpcpanel.profile.Save.DEFAULT_OVERLAY_BG_COLOR; -import static com.getpcpanel.profile.Save.DEFAULT_OVERLAY_PADDING; -import static com.getpcpanel.profile.Save.DEFAULT_OVERLAY_TEXT_COLOR; - -import java.util.List; -import java.util.Objects; - -import javax.annotation.Nonnull; -import javax.annotation.Nullable; - -import org.apache.commons.lang3.math.NumberUtils; -import org.springframework.stereotype.Component; - -import com.getpcpanel.MainFX; -import com.getpcpanel.cpp.linux.pulseaudio.SndCtrlPulseAudioDebug; -import com.getpcpanel.cpp.windows.SndCtrlWindows; -import com.getpcpanel.obs.OBS; -import com.getpcpanel.profile.Save; -import com.getpcpanel.profile.SaveService; -import com.getpcpanel.profile.WaveLinkSettings; -import com.getpcpanel.spring.OsHelper; -import com.getpcpanel.spring.Prototype; -import com.getpcpanel.ui.UIInitializer.SingleParamInitializer; -import com.getpcpanel.util.FileUtil; -import com.getpcpanel.util.IPlatformCommand; - -import javafx.application.Application; -import javafx.application.Platform; -import javafx.beans.binding.Bindings; -import javafx.event.ActionEvent; -import javafx.fxml.FXML; -import javafx.scene.Scene; -import javafx.scene.control.Button; -import javafx.scene.control.CheckBox; -import javafx.scene.control.ColorPicker; -import javafx.scene.control.Label; -import javafx.scene.control.Tab; -import javafx.scene.control.TextField; -import javafx.scene.control.ToggleButton; -import javafx.scene.image.Image; -import javafx.scene.layout.Pane; -import javafx.scene.layout.VBox; -import javafx.scene.paint.Color; -import javafx.stage.Modality; -import javafx.stage.Stage; -import lombok.RequiredArgsConstructor; -import lombok.extern.log4j.Log4j2; - -@Log4j2 -@Component -@Prototype -@RequiredArgsConstructor -public class SettingsDialog extends Application implements UIInitializer> { - private final SaveService saveService; - private final FileUtil fileUtil; - private final IPlatformCommand platformCommand; - private final OsHelper osHelper; - private final OBS obs; - private Stage parentStage; - - private Stage stage; - @FXML private Pane root; - @FXML private CheckBox mainUiIcons; - @FXML private CheckBox startupVersionCheck; - @FXML private CheckBox forceVolume; - @FXML private CheckBox overlay; - @FXML private CheckBox overlayUseLog; - @FXML private CheckBox overlayShowNumber; - @FXML private ColorPicker overlayTextColor; - @FXML private ColorPicker overlayBackgroundColor; - @FXML private ColorPicker overlayBarColor; - @FXML private ColorPicker overlayBarBackgroundColor; - @FXML private TextField overlayWindowCornerRounding; - @FXML private TextField overlayBarHeight; - @FXML private TextField overlayBarCornerRounding; - @FXML private Label overlayBGTransparency; - @FXML private TextField dblClickInterval; - @FXML private CheckBox preventClickWhenDblClick; - @FXML private CheckBox obsEnable; - @FXML private Pane obsControls; - @FXML private TextField obsAddress; - @FXML private TextField obsPort; - @FXML private TextField obsPassword; - @FXML private Button testBtn; - @FXML private Label obsTestResult; - @FXML private CheckBox vmEnable; - @FXML private Pane vmControls; - @FXML private TextField vmPath; - @FXML private Tab voicemeeterTab; - @FXML private Tab waveLinkTab; - @FXML private CheckBox waveLinkEnable; - @FXML private TextField txtPreventSliderTwitch; - @FXML private TextField txtSliderRollingAverage; - @FXML private TextField txtOnlyIfDelta; - @FXML private CheckBox cbFixOnlySliders; - @FXML private OSCSettingsDialog oscSettingsController; - @FXML private MqttSettingsDialog mqttSettingsController; - @FXML private VBox debug; - @FXML private Label copied; - @FXML private ToggleButton btnTL; - @FXML private ToggleButton btnTM; - @FXML private ToggleButton btnTR; - @FXML private ToggleButton btnML; - @FXML private ToggleButton btnMM; - @FXML private ToggleButton btnMR; - @FXML private ToggleButton btnBL; - @FXML private ToggleButton btnBM; - @FXML private ToggleButton btnBR; - @FXML public TextField overlayPadding; - - @Override - public void initUI(@Nonnull SingleParamInitializer args) { - parentStage = args.param(); - postInit(); - } - - @Override - public void start(Stage stage) { - this.stage = stage; - UIHelper.closeOnEscape(stage); - var scene = new Scene(root); - scene.getStylesheets().add(Objects.requireNonNull(getClass().getResource("/assets/dark_theme.css"), "Unable to find dark_theme.css").toExternalForm()); - scene.getStylesheets().add(Objects.requireNonNull(getClass().getResource("/assets/1.css"), "Unable to find 1.css").toExternalForm()); - stage.getIcons().add(new Image(Objects.requireNonNull(getClass().getResource("/assets/256x256.png")).toExternalForm())); - stage.setScene(scene); - stage.setResizable(false); - stage.sizeToScene(); - stage.setTitle("Settings"); - stage.initModality(Modality.WINDOW_MODAL); - stage.initOwner(parentStage); - - if (osHelper.notWindows()) { - voicemeeterTab.getTabPane().getTabs().remove(voicemeeterTab); - } - if (osHelper.isLinux()) { // MacOS is supported by WaveLink - waveLinkTab.getTabPane().getTabs().remove(waveLinkTab); - } - - osHelper.hideUnsupportedChildren(List.of(forceVolume)); - stage.showAndWait(); - } - - @FXML - private void onOBSEnablePressed(@Nullable ActionEvent ignored) { - obsControls.setDisable(!obsEnable.isSelected()); - } - - @FXML - private void onVMEnablePressed(@Nullable ActionEvent ignored) { - vmControls.setDisable(!vmEnable.isSelected()); - } - - @FXML - private void onVoiceMeeterBrowse(ActionEvent event) { - UIHelper.showFolderPicker("Pick VoiceMeeter directory", vmPath); - } - - @FXML - private void ok(ActionEvent event) { - var save = saveService.get(); - save.setMainUIIcons(mainUiIcons.isSelected()); - save.setStartupVersionCheck(startupVersionCheck.isSelected()); - save.setForceVolume(forceVolume.isSelected()); - save.setOverlayEnabled(overlay.isSelected()); - save.setOverlayUseLog(overlayUseLog.isSelected()); - save.setOverlayShowNumber(overlayShowNumber.isSelected()); - save.setOverlayBackgroundColor(toWebColor(overlayBackgroundColor.getValue())); - save.setOverlayBarColor(toWebColor(overlayBarColor.getValue())); - save.setOverlayBarBackgroundColor(toWebColor(overlayBarBackgroundColor.getValue())); - save.setOverlayTextColor(toWebColor(overlayTextColor.getValue())); - save.setOverlayWindowCornerRounding(NumberUtils.toInt(overlayWindowCornerRounding.getText(), 0)); - save.setOverlayBarHeight(NumberUtils.toInt(overlayBarHeight.getText(), 0)); - save.setOverlayBarCornerRounding(NumberUtils.toInt(overlayBarCornerRounding.getText(), 0)); - save.setOverlayPadding(NumberUtils.toInt(overlayPadding.getText(), 0)); - save.setOverlayPosition(getOverlayPosition()); - save.setDblClickInterval(NumberUtils.toLong(dblClickInterval.getText(), 500)); - save.setPreventClickWhenDblClick(preventClickWhenDblClick.isSelected()); - save.setObsEnabled(obsEnable.isSelected()); - save.setObsAddress(obsAddress.getText()); - save.setObsPort(obsPort.getText()); - save.setObsPassword(obsPassword.getText()); - save.setVoicemeeterEnabled(vmEnable.isSelected()); - save.setVoicemeeterPath(vmPath.getText()); - save.setWaveLink(new WaveLinkSettings(waveLinkEnable.isSelected())); - save.setPreventSliderTwitchDelay(NumberUtils.toInt(txtPreventSliderTwitch.getText(), 0)); - save.setSliderRollingAverage(NumberUtils.toInt(txtSliderRollingAverage.getText(), 0)); - save.setSendOnlyIfDelta(NumberUtils.toInt(txtOnlyIfDelta.getText(), 0)); - save.setWorkaroundsOnlySliders(cbFixOnlySliders.isSelected()); - oscSettingsController.save(save); - mqttSettingsController.save(save); - saveService.save(); - stage.close(); - } - - private String toWebColor(@Nonnull Color value) { - return "rgba(%s, %s, %s, %s)".formatted( - Math.round(value.getRed() * 255), - Math.round(value.getGreen() * 255), - Math.round(value.getBlue() * 255), - Math.round(value.getOpacity() * 100) / 100f - ); - } - - @FXML - private void closeButtonAction(ActionEvent event) { - stage.close(); - } - - @FXML - private void openLogsFolder(ActionEvent event) { - var logFolder = fileUtil.getFile("logs"); - platformCommand.exec(logFolder.getAbsolutePath()); - } - - private void initFields() { - var save = saveService.get(); - mainUiIcons.setSelected(save.isMainUIIcons()); - startupVersionCheck.setSelected(save.isStartupVersionCheck()); - forceVolume.setSelected(save.isForceVolume()); - overlay.setSelected(save.isOverlayEnabled()); - overlayUseLog.setSelected(save.isOverlayUseLog()); - overlayShowNumber.setSelected(save.isOverlayShowNumber()); - overlayWindowCornerRounding.setText("" + save.getOverlayWindowCornerRounding()); - overlayBarHeight.setText("" + save.getOverlayBarHeight()); - overlayBarCornerRounding.setText("" + save.getOverlayBarCornerRounding()); - initOverlayPosition(save); - overlayPadding.setText("" + save.getOverlayPadding()); - dblClickInterval.setText(save.getDblClickInterval() == null ? "500" : save.getDblClickInterval().toString()); - preventClickWhenDblClick.setSelected(save.isPreventClickWhenDblClick()); - obsEnable.setSelected(save.isObsEnabled()); - obsAddress.setText(save.getObsAddress()); - obsPort.setText(save.getObsPort()); - obsPassword.setText(save.getObsPassword()); - onOBSEnablePressed(null); - vmEnable.setSelected(save.isVoicemeeterEnabled()); - vmPath.setText(save.getVoicemeeterPath()); - waveLinkEnable.setSelected(save.getWaveLink().enabled()); - onVMEnablePressed(null); - txtPreventSliderTwitch.setText(save.getPreventSliderTwitchDelay() == null ? "" : save.getPreventSliderTwitchDelay().toString()); - txtSliderRollingAverage.setText(save.getSliderRollingAverage() == null ? "" : save.getSliderRollingAverage().toString()); - txtOnlyIfDelta.setText(save.getSendOnlyIfDelta() == null ? "" : save.getSendOnlyIfDelta().toString()); - cbFixOnlySliders.setSelected(save.isWorkaroundsOnlySliders()); - - oscSettingsController.populate(save); - mqttSettingsController.populate(save); - initOverlayColors(save); - } - - private void initOverlayPosition(Save save) { - var position = save.getOverlayPosition(); - btnTL.setSelected(position == OverlayPosition.topLeft); - btnTM.setSelected(position == OverlayPosition.topMiddle); - btnTR.setSelected(position == OverlayPosition.topRight); - btnML.setSelected(position == OverlayPosition.middleLeft); - btnMM.setSelected(position == OverlayPosition.middleMiddle); - btnMR.setSelected(position == OverlayPosition.middleRight); - btnBL.setSelected(position == OverlayPosition.bottomLeft); - btnBM.setSelected(position == OverlayPosition.bottomMiddle); - btnBR.setSelected(position == OverlayPosition.bottomRight); - } - - private OverlayPosition getOverlayPosition() { - // @formatter:off - if (btnTL.isSelected()) - return OverlayPosition.topLeft; - if (btnTM.isSelected()) - return OverlayPosition.topMiddle; - if (btnTR.isSelected()) - return OverlayPosition.topRight; - if (btnML.isSelected()) - return OverlayPosition.middleLeft; - if (btnMM.isSelected()) - return OverlayPosition.middleMiddle; - if (btnMR.isSelected()) - return OverlayPosition.middleRight; - if (btnBL.isSelected()) - return OverlayPosition.bottomLeft; - if (btnBM.isSelected()) - return OverlayPosition.bottomMiddle; - if (btnBR.isSelected()) - return OverlayPosition.bottomRight; - // @formatter:on - return OverlayPosition.topLeft; - } - - private void initOverlayColors(Save save) { - try { - overlayBackgroundColor.setValue(Color.web(save.getOverlayBackgroundColor())); - } catch (IllegalArgumentException e) { - overlayBackgroundColor.setValue(Color.web(DEFAULT_OVERLAY_BG_COLOR)); - } - try { - overlayTextColor.setValue(Color.web(save.getOverlayTextColor())); - } catch (IllegalArgumentException e) { - overlayTextColor.setValue(Color.web(DEFAULT_OVERLAY_TEXT_COLOR)); - } - try { - overlayBarColor.setValue(Color.web(save.getOverlayBarColor())); - } catch (IllegalArgumentException e) { - overlayBarColor.setValue(Color.web(DEFAULT_OVERLAY_BAR_COLOR)); - } - try { - overlayBarBackgroundColor.setValue(Color.web(save.getOverlayBarBackgroundColor())); - } catch (IllegalArgumentException e) { - overlayBarBackgroundColor.setValue(Color.web(DEFAULT_OVERLAY_BAR_BACKGROUND_COLOR)); - } - - var colorOpacityBinding = Bindings.createObjectBinding(() -> Math.round(overlayBackgroundColor.getValue().getOpacity() * 100) + "%", overlayBackgroundColor.valueProperty()); - overlayBGTransparency.textProperty().bind(colorOpacityBinding); - } - - private void postInit() { - initFields(); - osHelper.hideUnsupportedChildren(debug.getChildrenUnmodifiable()); - } - - public void doTest(ActionEvent ignored) { - obsTestResult.setText("Testing..."); - testBtn.setDisable(true); - new Thread(() -> { - var port = NumberUtils.toInt(obsPort.getText(), -1); - String result; - if (port == -1) { - result = "Invalid port"; - } else { - var message = obs.test(obsAddress.getText(), port, obsPassword.getText(), 2_500L); - if (message == null) { - result = "Success"; - } else { - result = "Failed: " + message; - } - } - Platform.runLater(() -> { - testBtn.setDisable(false); - obsTestResult.setText(result); - }); - }).start(); - } - - @SuppressWarnings("unused") - public void triggerAv(ActionEvent ignored) { - MainFX.getBean(SndCtrlWindows.class).triggerAv(); - } - - public void copyAudioOutput(ActionEvent ignored) { - copied.setText("Preparing output"); - MainFX.getOptionalBean(SndCtrlPulseAudioDebug.class).ifPresent(SndCtrlPulseAudioDebug::copyDebugOutput); - copied.setText("Output was copied to your clipboard"); - } - - public void resetOverlayToDefault(ActionEvent actionEvent) { - overlayUseLog.setSelected(false); - overlayShowNumber.setSelected(false); - btnTL.setSelected(true); - overlayWindowCornerRounding.setText("0"); - overlayBarHeight.setText(DEFAULT_OVERLAY_BAR_HEIGHT + ""); - overlayBarCornerRounding.setText("0"); - overlayPadding.setText(DEFAULT_OVERLAY_PADDING + ""); - overlayBackgroundColor.setValue(Color.web(DEFAULT_OVERLAY_BG_COLOR)); - overlayTextColor.setValue(Color.web(DEFAULT_OVERLAY_TEXT_COLOR)); - overlayBarColor.setValue(Color.web(DEFAULT_OVERLAY_BAR_COLOR)); - overlayBarBackgroundColor.setValue(Color.web(DEFAULT_OVERLAY_BAR_BACKGROUND_COLOR)); - } -} diff --git a/src/main/java/com/getpcpanel/ui/SoundDeviceExportFactory.java b/src/main/java/com/getpcpanel/ui/SoundDeviceExportFactory.java deleted file mode 100644 index 5b3b72da..00000000 --- a/src/main/java/com/getpcpanel/ui/SoundDeviceExportFactory.java +++ /dev/null @@ -1,71 +0,0 @@ -package com.getpcpanel.ui; - -import com.getpcpanel.cpp.AudioDevice; -import com.getpcpanel.cpp.DataFlow; - -import javafx.scene.control.ListCell; -import javafx.scene.control.ListView; -import javafx.scene.input.ClipboardContent; -import javafx.scene.input.DataFormat; -import javafx.scene.input.MouseEvent; -import javafx.scene.input.TransferMode; -import javafx.util.Callback; -import lombok.Setter; - -public class SoundDeviceExportFactory implements Callback, ListCell> { - private static final DataFormat JAVA_FORMAT = SoundDeviceImportFactory.JAVA_FORMAT; - @Setter private DataFlow enabledFlavor; - - public SoundDeviceExportFactory(ListView listView) { - setupListView(listView); - } - - @Override - public ListCell call(ListView listView) { - ListCell cell = new ListCell<>() { - @Override - protected void updateItem(AudioDevice item, boolean empty) { - super.updateItem(item, empty); - if (item == null) { - setText(""); - setDisable(false); - return; - } - setText(item.toString()); - setDisable(enabledFlavor != null && enabledFlavor != item.dataflow()); - } - }; - cell.setOnDragDetected(event -> dragDetected(event, cell)); - return cell; - } - - private void dragDetected(MouseEvent event, ListCell treeCell) { - var draggedItem = treeCell.getItem(); - var db = treeCell.startDragAndDrop(TransferMode.MOVE); - var content = new ClipboardContent(); - content.put(JAVA_FORMAT, draggedItem); - db.setContent(content); - db.setDragView(treeCell.snapshot(null, null)); - event.consume(); - } - - private static void setupListView(ListView listView) { - listView.setOnDragOver(event -> { - if (!event.getDragboard().hasContent(JAVA_FORMAT)) - return; - var dropContent = (AudioDevice) event.getDragboard().getContent(JAVA_FORMAT); - if (listView.getItems().contains(dropContent)) - return; - event.acceptTransferModes(TransferMode.MOVE); - }); - listView.setOnDragDropped(event -> { - var db = event.getDragboard(); - var success = false; - if (!db.hasContent(JAVA_FORMAT)) - return; - var index = 0; - listView.getItems().add(index, (AudioDevice) db.getContent(JAVA_FORMAT)); - event.setDropCompleted(success); - }); - } -} diff --git a/src/main/java/com/getpcpanel/ui/SoundDeviceImportFactory.java b/src/main/java/com/getpcpanel/ui/SoundDeviceImportFactory.java deleted file mode 100644 index ecee2d35..00000000 --- a/src/main/java/com/getpcpanel/ui/SoundDeviceImportFactory.java +++ /dev/null @@ -1,135 +0,0 @@ -package com.getpcpanel.ui; - -import com.getpcpanel.cpp.AudioDevice; - -import javafx.scene.control.ListCell; -import javafx.scene.control.ListView; -import javafx.scene.input.ClipboardContent; -import javafx.scene.input.DataFormat; -import javafx.scene.input.DragEvent; -import javafx.scene.input.MouseEvent; -import javafx.scene.input.TransferMode; -import javafx.util.Callback; - -public class SoundDeviceImportFactory implements Callback, ListCell> { - public static final DataFormat JAVA_FORMAT = new DataFormat("application/x-java-serialized-object"); - - private static final String DROP_HINT_STYLE_ABOVE = "-fx-border-color: #eea82f; -fx-border-width: 2 0 0 0; -fx-padding: 1 7 3 7;"; - - private static final String DROP_HINT_STYLE_BELLOW = "-fx-border-color: #eea82f; -fx-border-width: 0 0 2 0; -fx-padding: 3 7 1 7"; - - private ListCell dropZone; - - private AudioDevice draggedItem; - - private final ListView listView; - - public SoundDeviceImportFactory(ListView listView) { - this.listView = listView; - setupListView(listView); - } - - @Override - public ListCell call(ListView listView) { - var cell = new ListCell() { - @Override - protected void updateItem(AudioDevice item, boolean empty) { - super.updateItem(item, empty); - if (item == null) { - setText(""); - return; - } - setText(item.toString()); - } - }; - cell.setOnDragDetected(event -> dragDetected(event, cell, listView)); - cell.setOnDragOver(event -> dragOver(event, cell, listView)); - cell.setOnDragDropped(event -> drop(event, cell, listView)); - cell.setOnDragDone(event -> clearDropLocation()); - cell.setOnDragExited(event -> clearDropLocation()); - return cell; - } - - private void dragDetected(MouseEvent event, ListCell treeCell, ListView treeView) { - draggedItem = treeCell.getItem(); - var db = treeCell.startDragAndDrop(TransferMode.MOVE); - var content = new ClipboardContent(); - content.put(JAVA_FORMAT, draggedItem); - db.setContent(content); - db.setDragView(treeCell.snapshot(null, null)); - event.consume(); - } - - private void dragOver(DragEvent event, ListCell treeCell, ListView treeView) { - if (!event.getDragboard().hasContent(JAVA_FORMAT)) - return; - var thisItem = treeCell.getItem(); - var dropContent = (AudioDevice) event.getDragboard().getContent(JAVA_FORMAT); - if (event.getGestureSource().getClass().getEnclosingClass().getName().contains("SoundDeviceImportFactory")) { - if (thisItem == draggedItem) - return; - } else if (treeView.getItems().contains(dropContent)) { - return; - } - event.acceptTransferModes(TransferMode.MOVE); - if (thisItem == null) - return; - clearDropLocation(); - dropZone = treeCell; - if (event.getY() < treeCell.getHeight() / 2.0D) { - dropZone.setStyle(DROP_HINT_STYLE_ABOVE); - } else { - dropZone.setStyle(DROP_HINT_STYLE_BELLOW); - } - } - - private void drop(DragEvent event, ListCell treeCell, ListView treeView) { - var db = event.getDragboard(); - var success = false; - if (!db.hasContent(JAVA_FORMAT)) - return; - var thisItem = treeCell.getItem(); - var index = listView.getItems().indexOf(thisItem); - if (DROP_HINT_STYLE_BELLOW.equals(treeCell.getStyle())) - index++; - if (index == -1) - index = listView.getItems().size(); - if (event.getGestureSource().getClass().getEnclosingClass().getName().contains("SoundDeviceImportFactory")) { - var moverLocation = listView.getItems().indexOf(draggedItem); - if (index > moverLocation) - index--; - listView.getItems().remove(draggedItem); - listView.getItems().add(index, draggedItem); - treeView.getSelectionModel().select(draggedItem); - } else { - listView.getItems().add(index, (AudioDevice) db.getContent(JAVA_FORMAT)); - } - event.setDropCompleted(success); - clearDropLocation(); - } - - private void clearDropLocation() { - if (dropZone != null) - dropZone.setStyle(""); - } - - private static void setupListView(ListView listView) { - listView.setOnDragOver(event -> { - if (!event.getDragboard().hasContent(JAVA_FORMAT) || !listView.getItems().isEmpty()) - return; - var dropContent = (AudioDevice) event.getDragboard().getContent(JAVA_FORMAT); - if (listView.getItems().contains(dropContent)) - return; - event.acceptTransferModes(TransferMode.MOVE); - }); - listView.setOnDragDropped(event -> { - var db = event.getDragboard(); - var success = false; - if (!db.hasContent(JAVA_FORMAT) || !listView.getItems().isEmpty()) - return; - var index = listView.getItems().size(); - listView.getItems().add(index, (AudioDevice) db.getContent(JAVA_FORMAT)); - event.setDropCompleted(success); - }); - } -} diff --git a/src/main/java/com/getpcpanel/ui/UIHelper.java b/src/main/java/com/getpcpanel/ui/UIHelper.java deleted file mode 100644 index 4c7fcf7d..00000000 --- a/src/main/java/com/getpcpanel/ui/UIHelper.java +++ /dev/null @@ -1,48 +0,0 @@ -package com.getpcpanel.ui; - -import static javafx.scene.input.KeyEvent.KEY_PRESSED; - -import java.io.File; -import java.util.Optional; - -import javafx.scene.control.TextField; -import javafx.scene.input.KeyCode; -import javafx.stage.FileChooser; -import javafx.stage.Stage; - -public final class UIHelper { - - private UIHelper() { - } - - public static void closeOnEscape(Stage stage) { - stage.addEventHandler(KEY_PRESSED, t -> { - if (t.getCode() == KeyCode.ESCAPE) { - stage.close(); - } - }); - } - - public static void showFolderPicker(String title, TextField target) { - pickFile(title, target) - .map(f -> f.isFile() ? f.getParentFile() : f) - .ifPresent(f -> target.setText(f.getAbsolutePath())); - } - - public static void showFilePicker(String title, TextField target) { - pickFile(title, target) - .ifPresent(f -> target.setText(f.getAbsolutePath())); - } - - private static Optional pickFile(String title, TextField target) { - var stage = (Stage) target.getScene().getWindow(); - var fileChooser = new FileChooser(); - fileChooser.setTitle(title); - - var current = new File(target.getText()); - fileChooser.setInitialFileName(current.isDirectory() ? "" : current.getName()); - fileChooser.setInitialDirectory(current.isDirectory() ? current : current.getParentFile()); - - return Optional.ofNullable(fileChooser.showOpenDialog(stage)); - } -} diff --git a/src/main/java/com/getpcpanel/ui/UIInitializer.java b/src/main/java/com/getpcpanel/ui/UIInitializer.java deleted file mode 100644 index 1f2ded12..00000000 --- a/src/main/java/com/getpcpanel/ui/UIInitializer.java +++ /dev/null @@ -1,11 +0,0 @@ -package com.getpcpanel.ui; - -import javax.annotation.Nonnull; - -public interface UIInitializer { - default void initUI(@Nonnull T args) { - } - - record SingleParamInitializer(TT param) { - } -} diff --git a/src/main/java/com/getpcpanel/ui/colorpicker/ColorDialog.java b/src/main/java/com/getpcpanel/ui/colorpicker/ColorDialog.java deleted file mode 100644 index 89ec06b4..00000000 --- a/src/main/java/com/getpcpanel/ui/colorpicker/ColorDialog.java +++ /dev/null @@ -1,471 +0,0 @@ -package com.getpcpanel.ui.colorpicker; - -import java.util.Objects; - -import com.sun.javafx.scene.control.skin.resources.ControlResources; - -import javafx.beans.Observable; -import javafx.beans.binding.Bindings; -import javafx.beans.binding.ObjectBinding; -import javafx.beans.property.DoubleProperty; -import javafx.beans.property.IntegerProperty; -import javafx.beans.property.ObjectProperty; -import javafx.beans.property.Property; -import javafx.beans.property.SimpleDoubleProperty; -import javafx.beans.property.SimpleIntegerProperty; -import javafx.beans.property.SimpleObjectProperty; -import javafx.event.EventHandler; -import javafx.geometry.Insets; -import javafx.geometry.Orientation; -import javafx.geometry.Pos; -import javafx.scene.control.Label; -import javafx.scene.control.Slider; -import javafx.scene.control.ToggleButton; -import javafx.scene.control.ToggleGroup; -import javafx.scene.input.MouseEvent; -import javafx.scene.layout.Background; -import javafx.scene.layout.BackgroundFill; -import javafx.scene.layout.ColumnConstraints; -import javafx.scene.layout.CornerRadii; -import javafx.scene.layout.GridPane; -import javafx.scene.layout.HBox; -import javafx.scene.layout.Pane; -import javafx.scene.layout.Priority; -import javafx.scene.layout.Region; -import javafx.scene.layout.RowConstraints; -import javafx.scene.layout.StackPane; -import javafx.scene.layout.VBox; -import javafx.scene.paint.Color; -import javafx.scene.paint.CycleMethod; -import javafx.scene.paint.LinearGradient; -import javafx.scene.paint.Stop; - -public class ColorDialog extends HBox { - private ColorRectPane colorRectPane; - private final ObjectProperty customColorProperty = new SimpleObjectProperty<>(Color.TRANSPARENT); - - public ColorDialog(Color color) { - getStyleClass().add("custom-color-dialog"); - buildUI(); - setCustomColor(Objects.requireNonNullElse(color, Color.BLACK)); - } - - public ColorDialog() { - this(null); - } - - private void buildUI() { - colorRectPane = new ColorRectPane(); - var controlsPane = new ControlsPane(); - setHgrow(controlsPane, Priority.ALWAYS); - getChildren().setAll(colorRectPane, controlsPane); - } - - static String getString(String key) { - return ControlResources.getString("ColorPicker." + key); - } - - public ObjectProperty customColorProperty() { - return customColorProperty; - } - - public final void setCustomColor(Color color) { - customColorProperty.set(color); - } - - public Color getCustomColor() { - return customColorProperty.get(); - } - - private class ColorRectPane extends HBox { - private final Pane colorRect; - private final Pane colorBar; - private final Region colorRectIndicator; - private boolean changeIsLocal; - - private final DoubleProperty hue = new SimpleDoubleProperty(-1.0D) { - @Override - protected void invalidated() { - if (!changeIsLocal) { - changeIsLocal = true; - updateHSBColor(); - changeIsLocal = false; - } - } - }; - - private final DoubleProperty sat = new SimpleDoubleProperty(-1.0D) { - @Override - protected void invalidated() { - if (!changeIsLocal) { - changeIsLocal = true; - updateHSBColor(); - changeIsLocal = false; - } - } - }; - - private final DoubleProperty bright = new SimpleDoubleProperty(-1.0D) { - @Override - protected void invalidated() { - if (!changeIsLocal) { - changeIsLocal = true; - updateHSBColor(); - changeIsLocal = false; - } - } - }; - - private final IntegerProperty red = new SimpleIntegerProperty(-1) { - @Override - protected void invalidated() { - if (!changeIsLocal) { - changeIsLocal = true; - updateRGBColor(); - changeIsLocal = false; - } - } - }; - - private final IntegerProperty green = new SimpleIntegerProperty(-1) { - @Override - protected void invalidated() { - if (!changeIsLocal) { - changeIsLocal = true; - updateRGBColor(); - changeIsLocal = false; - } - } - }; - - private final IntegerProperty blue = new SimpleIntegerProperty(-1) { - @Override - protected void invalidated() { - if (!changeIsLocal) { - changeIsLocal = true; - updateRGBColor(); - changeIsLocal = false; - } - } - }; - - private final DoubleProperty alpha = new SimpleDoubleProperty(100.0D) { - @Override - protected void invalidated() { - if (!changeIsLocal) { - changeIsLocal = true; - setCustomColor(new Color( - getCustomColor().getRed(), - getCustomColor().getGreen(), - getCustomColor().getBlue(), - clamp(alpha.get() / 100.0D))); - changeIsLocal = false; - } - } - }; - - private void updateRGBColor() { - var newColor = Color.rgb(red.get(), green.get(), blue.get(), clamp(alpha.get() / 100.0D)); - hue.set(newColor.getHue()); - sat.set(newColor.getSaturation() * 100.0D); - bright.set(newColor.getBrightness() * 100.0D); - setCustomColor(newColor); - } - - private void updateHSBColor() { - var newColor = Color.hsb(hue.get(), clamp(sat.get() / 100.0D), - clamp(bright.get() / 100.0D), clamp(alpha.get() / 100.0D)); - red.set(doubleToInt(newColor.getRed())); - green.set(doubleToInt(newColor.getGreen())); - blue.set(doubleToInt(newColor.getBlue())); - setCustomColor(newColor); - } - - private void colorChanged() { - if (!changeIsLocal) { - changeIsLocal = true; - hue.set(getCustomColor().getHue()); - sat.set(getCustomColor().getSaturation() * 100.0D); - bright.set(getCustomColor().getBrightness() * 100.0D); - red.set(doubleToInt(getCustomColor().getRed())); - green.set(doubleToInt(getCustomColor().getGreen())); - blue.set(doubleToInt(getCustomColor().getBlue())); - changeIsLocal = false; - } - } - - public ColorRectPane() { - getStyleClass().add("color-rect-pane"); - customColorProperty().addListener((ov, t, t1) -> colorChanged()); - colorRectIndicator = new Region(); - colorRectIndicator.setId("color-rect-indicator"); - colorRectIndicator.setManaged(false); - colorRectIndicator.setMouseTransparent(true); - colorRectIndicator.setCache(true); - var stackPane = new StackPane(); - colorRect = new StackPane() { - @Override - public Orientation getContentBias() { - return Orientation.VERTICAL; - } - - @Override - protected double computePrefWidth(double height) { - return height; - } - - @Override - protected double computeMaxWidth(double height) { - return height; - } - }; - colorRect.getStyleClass().addAll("color-rect", "transparent-pattern"); - var colorRectHue = new Pane(); - colorRectHue.backgroundProperty().bind(new BindingObjectBinding<>(hue) { - @Override - protected Background computeValue() { - return new Background(new BackgroundFill( - Color.hsb(hue.getValue(), 1.0D, 1.0D), - CornerRadii.EMPTY, Insets.EMPTY)); - } - }); - var colorRectOverlayOne = new Pane(); - colorRectOverlayOne.getStyleClass().add("color-rect"); - colorRectOverlayOne.setBackground(new Background(new BackgroundFill( - new LinearGradient(0.0D, 0.0D, 1.0D, 0.0D, true, CycleMethod.NO_CYCLE, new Stop(0.0D, Color.rgb(255, 255, 255, 1.0D)), - new Stop(1.0D, Color.rgb(255, 255, 255, 0.0D))), CornerRadii.EMPTY, Insets.EMPTY))); - EventHandler rectMouseHandler = event -> { - var x = event.getX(); - var y = event.getY(); - sat.set(clamp(x / colorRect.getWidth()) * 100.0D); - bright.set(100.0D - clamp(y / colorRect.getHeight()) * 100.0D); - }; - var colorRectOverlayTwo = new Pane(); - colorRectOverlayTwo.getStyleClass().addAll("color-rect"); - colorRectOverlayTwo.setBackground(new Background(new BackgroundFill( - new LinearGradient(0.0D, 0.0D, 0.0D, 1.0D, true, CycleMethod.NO_CYCLE, new Stop(0.0D, Color.rgb(0, 0, 0, 0.0D)), new Stop(1.0D, Color.rgb(0, 0, 0, 1.0D))), - CornerRadii.EMPTY, Insets.EMPTY))); - colorRectOverlayTwo.setOnMouseDragged(rectMouseHandler); - colorRectOverlayTwo.setOnMousePressed(rectMouseHandler); - var colorRectBlackBorder = new Pane(); - colorRectBlackBorder.setMouseTransparent(true); - colorRectBlackBorder.getStyleClass().addAll("color-rect", "color-rect-border"); - colorBar = new Pane(); - colorBar.getStyleClass().add("color-bar"); - colorBar.setBackground(new Background(new BackgroundFill(createHueGradient(), CornerRadii.EMPTY, Insets.EMPTY))); - var colorBarIndicator = new Region(); - colorBarIndicator.setId("color-bar-indicator"); - colorBarIndicator.setMouseTransparent(true); - colorBarIndicator.setCache(true); - colorRectIndicator.layoutXProperty().bind(sat.divide(100).multiply(colorRect.widthProperty())); - colorRectIndicator.layoutYProperty() - .bind(Bindings.subtract(1, bright.divide(100)).multiply(colorRect.heightProperty())); - colorBarIndicator.layoutYProperty().bind(hue.divide(360).multiply(colorBar.heightProperty())); - stackPane.opacityProperty().bind(alpha.divide(100)); - EventHandler barMouseHandler = event -> { - var y = event.getY(); - hue.set(clamp(y / colorRect.getHeight()) * 360.0D); - }; - colorBar.setOnMouseDragged(barMouseHandler); - colorBar.setOnMousePressed(barMouseHandler); - colorBar.getChildren().setAll(colorBarIndicator); - stackPane.getChildren().setAll(colorRectHue, colorRectOverlayOne, colorRectOverlayTwo); - colorRect.getChildren().setAll(stackPane, colorRectBlackBorder, colorRectIndicator); - HBox.setHgrow(colorRect, Priority.SOMETIMES); - getChildren().addAll(colorRect, colorBar); - } - - @Override - protected void layoutChildren() { - super.layoutChildren(); - colorRectIndicator.autosize(); - var size = Math.min(colorRect.getWidth(), colorRect.getHeight()); - colorRect.resize(size, size); - colorBar.resize(colorBar.getWidth(), size); - } - } - - private class ControlsPane extends VBox { - - private final ToggleButton hsbButton; - - private final ToggleButton rgbButton; - - private final Label[] labels = new Label[4]; - - private final Slider[] sliders = new Slider[4]; - - private final IntegerField[] fields = new IntegerField[4]; - - private final Property[] bindedProperties; - - private void showHSBSettings() { - set(0, getString("hue_colon"), 360, colorRectPane.hue); - set(1, getString("saturation_colon"), 100, colorRectPane.sat); - set(2, getString("brightness_colon"), 100, colorRectPane.bright); - } - - private void showRGBSettings() { - set(0, getString("red_colon"), 255, colorRectPane.red); - set(1, getString("green_colon"), 255, colorRectPane.green); - set(2, getString("blue_colon"), 255, colorRectPane.blue); - } - - private void showWebSettings() { - labels[0].setText(getString("web_colon")); - } - - public ControlsPane() { - bindedProperties = new Property[4]; - getStyleClass().add("controls-pane"); - var newColorRect = new Region(); - newColorRect.getStyleClass().add("color-rect"); - newColorRect.setId("new-color"); - newColorRect.backgroundProperty().bind(new BindingObjectBinding<>(customColorProperty) { - @Override - protected Background computeValue() { - return new Background( - new BackgroundFill(customColorProperty.get(), CornerRadii.EMPTY, Insets.EMPTY)); - } - }); - var newColorLabel = new Label("Color"); - var whiteBox = new Region(); - whiteBox.getStyleClass().add("customcolor-controls-background"); - hsbButton = new ToggleButton(getString("colorType.hsb")); - hsbButton.getStyleClass().add("left-pill"); - rgbButton = new ToggleButton(getString("colorType.rgb")); - rgbButton.getStyleClass().add("center-pill"); - var webButton = new ToggleButton(getString("colorType.web")); - webButton.getStyleClass().add("right-pill"); - var group = new ToggleGroup(); - var hBox = new HBox(); - hBox.setAlignment(Pos.CENTER); - hBox.getChildren().addAll(hsbButton, rgbButton, webButton); - var leftSpacer = new Region(); - leftSpacer.setId("spacer-side"); - var rightSpacer = new Region(); - rightSpacer.setId("spacer-side"); - var bottomSpacer = new Region(); - bottomSpacer.setId("spacer-bottom"); - var currentAndNewColor = new GridPane(); - currentAndNewColor.getColumnConstraints().addAll(new ColumnConstraints()); - currentAndNewColor.getColumnConstraints().get(0).setHgrow(Priority.ALWAYS); - currentAndNewColor.getRowConstraints().addAll(new RowConstraints(), new RowConstraints(), new RowConstraints()); - currentAndNewColor.getRowConstraints().get(2).setVgrow(Priority.ALWAYS); - var labelCenterer = new HBox(newColorLabel); - labelCenterer.setAlignment(Pos.CENTER); - currentAndNewColor.getStyleClass().add("current-new-color-grid"); - currentAndNewColor.add(labelCenterer, 0, 0); - currentAndNewColor.add(newColorRect, 0, 2); - currentAndNewColor.setPrefHeight(80.0D); - currentAndNewColor.setMaxHeight(80.0D); - var settingsPane = new GridPane(); - settingsPane.setMaxHeight(Double.MAX_VALUE); - VBox.setVgrow(settingsPane, Priority.ALWAYS); - settingsPane.setId("settings-pane"); - settingsPane.getColumnConstraints() - .addAll(new ColumnConstraints(), new ColumnConstraints(), new ColumnConstraints(), new ColumnConstraints(), new ColumnConstraints(), - new ColumnConstraints()); - settingsPane.getColumnConstraints().get(0).setHgrow(Priority.NEVER); - settingsPane.getColumnConstraints().get(2).setHgrow(Priority.ALWAYS); - settingsPane.getColumnConstraints().get(3).setHgrow(Priority.NEVER); - settingsPane.getColumnConstraints().get(4).setHgrow(Priority.NEVER); - settingsPane.getColumnConstraints().get(5).setHgrow(Priority.NEVER); - settingsPane.add(whiteBox, 0, 0, 6, 5); - settingsPane.add(hBox, 0, 0, 6, 1); - settingsPane.add(leftSpacer, 0, 0); - settingsPane.add(rightSpacer, 5, 0); - settingsPane.add(bottomSpacer, 0, 4); - var webField = new WebColorField(); - webField.getStyleClass().add("web-field"); - webField.setSkin(new WebColorFieldSkin(webField)); - webField.valueProperty().bindBidirectional(customColorProperty); - webField.visibleProperty().bind(group.selectedToggleProperty().isEqualTo(webButton)); - settingsPane.add(webField, 2, 1); - for (var i = 0; i < 4; i++) { - labels[i] = new Label(); - labels[i].getStyleClass().add("settings-label"); - sliders[i] = new Slider(); - fields[i] = new IntegerField(); - fields[i].getStyleClass().add("color-input-field"); - fields[i].setSkin(new IntegerFieldSkin(fields[i])); - var units = new Label[4]; - units[i] = new Label((i == 0) ? "°" : "%"); - units[i].getStyleClass().add("settings-unit"); - if (i > 0 && i < 3) - labels[i].visibleProperty().bind(group.selectedToggleProperty().isNotEqualTo(webButton)); - if (i < 3) { - sliders[i].visibleProperty().bind(group.selectedToggleProperty().isNotEqualTo(webButton)); - fields[i].visibleProperty().bind(group.selectedToggleProperty().isNotEqualTo(webButton)); - units[i].visibleProperty().bind(group.selectedToggleProperty().isEqualTo(hsbButton)); - } - var row = 1 + i; - if (i == 3) - row++; - if (i != 3) { - settingsPane.add(labels[i], 1, row); - settingsPane.add(sliders[i], 2, row); - settingsPane.add(fields[i], 3, row); - settingsPane.add(units[i], 4, row); - } - } - set(3, getString("opacity_colon"), 100, colorRectPane.alpha); - hsbButton.setToggleGroup(group); - rgbButton.setToggleGroup(group); - webButton.setToggleGroup(group); - group.selectedToggleProperty().addListener((observable, oldValue, newValue) -> { - if (newValue == null) { - group.selectToggle(oldValue); - } else if (Objects.equals(newValue, hsbButton)) { - showHSBSettings(); - } else if (Objects.equals(newValue, rgbButton)) { - showRGBSettings(); - } else { - showWebSettings(); - } - }); - group.selectToggle(hsbButton); - var spacer = new VBox(); - VBox.setVgrow(spacer, Priority.ALWAYS); - getChildren().addAll(currentAndNewColor, spacer, settingsPane); - } - - private void set(int row, String caption, int maxValue, Property prop) { - labels[row].setText(caption); - if (bindedProperties[row] != null) { - sliders[row].valueProperty().unbindBidirectional(bindedProperties[row]); - fields[row].valueProperty().unbindBidirectional(bindedProperties[row]); - } - sliders[row].setMax(maxValue); - sliders[row].valueProperty().bindBidirectional(prop); - labels[row].setLabelFor(sliders[row]); - fields[row].setMaxValue(maxValue); - fields[row].valueProperty().bindBidirectional(prop); - bindedProperties[row] = prop; - } - } - - static double clamp(double value) { - return (value < 0.0D) ? 0.0D : Math.min(value, 1.0D); - } - - private static LinearGradient createHueGradient() { - var stops = new Stop[255]; - for (var y = 0; y < 255; y++) { - var offset = 1.0D - 0.00392156862745098D * y; - var h = (int) (y / 255.0D * 360.0D); - stops[y] = new Stop(offset, Color.hsb(h, 1.0D, 1.0D)); - } - return new LinearGradient(0.0D, 1.0D, 0.0D, 0.0D, true, CycleMethod.NO_CYCLE, stops); - } - - private static int doubleToInt(double value) { - return (int) (value * 255.0D + 0.5D); - } - - private abstract static class BindingObjectBinding extends ObjectBinding { - protected BindingObjectBinding(Observable... obs) { - bind(obs); - } - } -} diff --git a/src/main/java/com/getpcpanel/ui/colorpicker/HueSlider.java b/src/main/java/com/getpcpanel/ui/colorpicker/HueSlider.java deleted file mode 100644 index 07c890ab..00000000 --- a/src/main/java/com/getpcpanel/ui/colorpicker/HueSlider.java +++ /dev/null @@ -1,70 +0,0 @@ -package com.getpcpanel.ui.colorpicker; - -import javafx.beans.property.IntegerProperty; -import javafx.beans.property.SimpleIntegerProperty; -import javafx.event.EventHandler; -import javafx.geometry.Insets; -import javafx.scene.input.MouseEvent; -import javafx.scene.layout.Background; -import javafx.scene.layout.BackgroundFill; -import javafx.scene.layout.CornerRadii; -import javafx.scene.layout.Pane; -import javafx.scene.paint.Color; -import javafx.scene.paint.CycleMethod; -import javafx.scene.paint.LinearGradient; -import javafx.scene.paint.Paint; -import javafx.scene.paint.Stop; -import javafx.scene.shape.Circle; - -public class HueSlider extends Pane { - private static final int MAX_HUE = 255; - - private final IntegerProperty hue = new SimpleIntegerProperty(); - - public HueSlider() { - getStyleClass().add("color-bar"); - setBackground(new Background(new BackgroundFill(createHueGradient(), - CornerRadii.EMPTY, Insets.EMPTY))); - setMinHeight(20.0D); - var colorBarIndicator = new Circle(10.0D); - colorBarIndicator.setFill(Paint.valueOf("#25262A")); - colorBarIndicator.setId("color-bar-indicator"); - colorBarIndicator.setMouseTransparent(true); - colorBarIndicator.setCache(true); - colorBarIndicator.layoutXProperty().bind(hue.multiply(widthProperty()).divide(MAX_HUE)); - EventHandler barMouseHandler = event -> { - var x = event.getX(); - hue.set((int) (clamp(x / getWidth()) * 255.0D)); - }; - setOnMouseDragged(barMouseHandler); - setOnMousePressed(barMouseHandler); - getChildren().setAll(colorBarIndicator); - colorBarIndicator.setLayoutY(10.0D); - } - - public IntegerProperty getHueProperty() { - return hue; - } - - public int getHue() { - return hue.get(); - } - - public void setHue(int h) { - hue.set(h); - } - - private static LinearGradient createHueGradient() { - var stops = new Stop[255]; - for (var y = 0; y < 255; y++) { - var offset = 1.0D - 0.00392156862745098D * y; - var h = (int) (y / 255.0D * 360.0D); - stops[y] = new Stop(offset, Color.hsb(h, 1.0D, 1.0D)); - } - return new LinearGradient(1.0D, 0.0D, 0.0D, 0.0D, true, CycleMethod.NO_CYCLE, stops); - } - - static double clamp(double value) { - return (value < 0.0D) ? 0.0D : ((value > 1.0D) ? 1.0D : value); - } -} diff --git a/src/main/java/com/getpcpanel/ui/colorpicker/InputField.java b/src/main/java/com/getpcpanel/ui/colorpicker/InputField.java deleted file mode 100644 index 6d569f37..00000000 --- a/src/main/java/com/getpcpanel/ui/colorpicker/InputField.java +++ /dev/null @@ -1,135 +0,0 @@ -package com.getpcpanel.ui.colorpicker; - -import javafx.beans.property.BooleanProperty; -import javafx.beans.property.IntegerProperty; -import javafx.beans.property.IntegerPropertyBase; -import javafx.beans.property.ObjectProperty; -import javafx.beans.property.ObjectPropertyBase; -import javafx.beans.property.SimpleBooleanProperty; -import javafx.beans.property.StringProperty; -import javafx.beans.property.StringPropertyBase; -import javafx.event.ActionEvent; -import javafx.event.EventHandler; -import javafx.scene.control.Control; - -abstract class InputField extends Control { - public static final int DEFAULT_PREF_COLUMN_COUNT = 12; - - private final BooleanProperty editable = new SimpleBooleanProperty(this, "editable", true); - - public final boolean isEditable() { - return editable.getValue(); - } - - public final void setEditable(boolean value) { - editable.setValue(value); - } - - public final BooleanProperty editableProperty() { - return editable; - } - - private final StringProperty promptText = new StringPropertyBase("") { - @Override - protected void invalidated() { - var txt = get(); - if (txt != null && txt.contains("\n")) { - txt = txt.replace("\n", ""); - set(txt); - } - } - - @Override - public Object getBean() { - return InputField.this; - } - - @Override - public String getName() { - return "promptText"; - } - }; - - public final StringProperty promptTextProperty() { - return promptText; - } - - public final String getPromptText() { - return promptText.get(); - } - - public final void setPromptText(String value) { - promptText.set(value); - } - - private final IntegerProperty prefColumnCount = new IntegerPropertyBase(DEFAULT_PREF_COLUMN_COUNT) { - private int oldValue = get(); - - @Override - protected void invalidated() { - var value = get(); - if (value < 0) { - if (isBound()) - unbind(); - set(oldValue); - throw new IllegalArgumentException("value cannot be negative."); - } - oldValue = value; - } - - @Override - public Object getBean() { - return InputField.this; - } - - @Override - public String getName() { - return "prefColumnCount"; - } - }; - - public final IntegerProperty prefColumnCountProperty() { - return prefColumnCount; - } - - public final int getPrefColumnCount() { - return prefColumnCount.getValue(); - } - - public final void setPrefColumnCount(int value) { - prefColumnCount.setValue(value); - } - - private final ObjectProperty> onAction = new ObjectPropertyBase<>() { - @Override - protected void invalidated() { - setEventHandler(ActionEvent.ACTION, get()); - } - - @Override - public Object getBean() { - return InputField.this; - } - - @Override - public String getName() { - return "onAction"; - } - }; - - public final ObjectProperty> onActionProperty() { - return onAction; - } - - public final EventHandler getOnAction() { - return onActionProperty().get(); - } - - public final void setOnAction(EventHandler value) { - onActionProperty().set(value); - } - - protected InputField() { - getStyleClass().setAll("input-field"); - } -} diff --git a/src/main/java/com/getpcpanel/ui/colorpicker/InputFieldSkin.java b/src/main/java/com/getpcpanel/ui/colorpicker/InputFieldSkin.java deleted file mode 100644 index 7a33c42e..00000000 --- a/src/main/java/com/getpcpanel/ui/colorpicker/InputFieldSkin.java +++ /dev/null @@ -1,95 +0,0 @@ -package com.getpcpanel.ui.colorpicker; - -import com.sun.javafx.event.EventDispatchChainImpl; - -import javafx.beans.InvalidationListener; -import javafx.event.EventDispatchChain; -import javafx.scene.Node; -import javafx.scene.control.Skin; -import javafx.scene.control.TextField; - -abstract class InputFieldSkin implements Skin { - protected InputField control; - - private InnerTextField textField; - - private final InvalidationListener InputFieldFocusListener; - - private final InvalidationListener InputFieldStyleClassListener; - - protected InputFieldSkin(InputField control) { - this.control = control; - textField = new InnerTextField() { - @Override - public void replaceText(int start, int end, String text) { - var t = (textField.getText() == null) ? "" : textField.getText(); - t = t.substring(0, start) + text + t.substring(end); - if (accept(t)) - super.replaceText(start, end, text); - } - - @Override - public void replaceSelection(String text) { - var t = (textField.getText() == null) ? "" : textField.getText(); - var start = Math.min(textField.getAnchor(), textField.getCaretPosition()); - var end = Math.max(textField.getAnchor(), textField.getCaretPosition()); - t = t.substring(0, start) + text + t.substring(end); - if (accept(t)) - super.replaceSelection(text); - } - }; - textField.setId("input-text-field"); - textField.setFocusTraversable(false); - control.getStyleClass().addAll(textField.getStyleClass()); - textField.getStyleClass().setAll(control.getStyleClass()); - control.getStyleClass().addListener(InputFieldStyleClassListener = (observable -> textField.getStyleClass().setAll(control.getStyleClass()))); - textField.promptTextProperty().bind(control.promptTextProperty()); - textField.prefColumnCountProperty().bind(control.prefColumnCountProperty()); - textField.textProperty().addListener(observable -> updateValue()); - control.focusedProperty().addListener(InputFieldFocusListener = (observable -> textField.handleFocus(control.isFocused()))); - updateText(); - } - - @Override - public InputField getSkinnable() { - return control; - } - - @Override - public Node getNode() { - return textField; - } - - @Override - public void dispose() { - control.getStyleClass().removeListener(InputFieldStyleClassListener); - control.focusedProperty().removeListener(InputFieldFocusListener); - textField = null; - } - - protected TextField getTextField() { - return textField; - } - - protected abstract boolean accept(String paramString); - - protected abstract void updateText(); - - protected abstract void updateValue(); - - private class InnerTextField extends TextField { - private InnerTextField() { - } - - public void handleFocus(boolean b) { - setFocused(b); - } - - @Override - public EventDispatchChain buildEventDispatchChain(EventDispatchChain tail) { - var eventDispatchChainImpl = new EventDispatchChainImpl(); - eventDispatchChainImpl.append(textField.getEventDispatcher()); - return eventDispatchChainImpl; - } - } -} diff --git a/src/main/java/com/getpcpanel/ui/colorpicker/IntegerField.java b/src/main/java/com/getpcpanel/ui/colorpicker/IntegerField.java deleted file mode 100644 index 56c60fc4..00000000 --- a/src/main/java/com/getpcpanel/ui/colorpicker/IntegerField.java +++ /dev/null @@ -1,49 +0,0 @@ -package com.getpcpanel.ui.colorpicker; - -import javafx.beans.property.IntegerProperty; -import javafx.beans.property.SimpleIntegerProperty; -import javafx.scene.control.Skin; - -class IntegerField extends InputField { - private final IntegerProperty value = new SimpleIntegerProperty(this, "value"); - - public final int getValue() { - return value.get(); - } - - public final void setValue(int value) { - this.value.set(value); - } - - public final IntegerProperty valueProperty() { - return value; - } - - private final IntegerProperty maxValue = new SimpleIntegerProperty(this, "maxValue", -1); - - public final int getMaxValue() { - return maxValue.get(); - } - - public final void setMaxValue(int maxVal) { - maxValue.set(maxVal); - } - - public final IntegerProperty maxValueProperty() { - return maxValue; - } - - public IntegerField() { - this(-1); - } - - public IntegerField(int maxVal) { - getStyleClass().setAll("integer-field"); - setMaxValue(maxVal); - } - - @Override - protected Skin createDefaultSkin() { - return new IntegerFieldSkin(this); - } -} diff --git a/src/main/java/com/getpcpanel/ui/colorpicker/IntegerFieldSkin.java b/src/main/java/com/getpcpanel/ui/colorpicker/IntegerFieldSkin.java deleted file mode 100644 index 06fe40d8..00000000 --- a/src/main/java/com/getpcpanel/ui/colorpicker/IntegerFieldSkin.java +++ /dev/null @@ -1,64 +0,0 @@ -package com.getpcpanel.ui.colorpicker; - -import javafx.application.Platform; -import javafx.beans.InvalidationListener; -import javafx.scene.Node; - -class IntegerFieldSkin extends InputFieldSkin { - private final InvalidationListener integerFieldValueListener; - - public IntegerFieldSkin(IntegerField control) { - super(control); - control.valueProperty().addListener(integerFieldValueListener = (observable -> updateText())); - } - - @Override - public IntegerField getSkinnable() { - return (IntegerField) control; - } - - @Override - public Node getNode() { - return getTextField(); - } - - @Override - public void dispose() { - ((IntegerField) control).valueProperty().removeListener(integerFieldValueListener); - super.dispose(); - } - - @Override - protected boolean accept(String text) { - if (text.isEmpty()) - return true; - if (text.matches("[0-9]*")) - try { - Integer.parseInt(text); - var value = Integer.parseInt(text); - var maxValue = ((IntegerField) control).getMaxValue(); - return maxValue == -1 || (value <= maxValue); - } catch (NumberFormatException numberFormatException) { - } - return false; - } - - @Override - protected void updateText() { - getTextField().setText(String.valueOf(((IntegerField) control).getValue())); - } - - @Override - protected void updateValue() { - var value = ((IntegerField) control).getValue(); - var text = (getTextField().getText() == null) ? "" : getTextField().getText().trim(); - try { - var newValue = Integer.parseInt(text); - if (newValue != value) - ((IntegerField) control).setValue(newValue); - } catch (NumberFormatException ex) { - ((IntegerField) control).setValue(0); - Platform.runLater(() -> getTextField().positionCaret(1)); - } - } -} diff --git a/src/main/java/com/getpcpanel/ui/colorpicker/WebColorField.java b/src/main/java/com/getpcpanel/ui/colorpicker/WebColorField.java deleted file mode 100644 index 6ff31123..00000000 --- a/src/main/java/com/getpcpanel/ui/colorpicker/WebColorField.java +++ /dev/null @@ -1,31 +0,0 @@ -package com.getpcpanel.ui.colorpicker; - -import javafx.beans.property.ObjectProperty; -import javafx.beans.property.SimpleObjectProperty; -import javafx.scene.control.Skin; -import javafx.scene.paint.Color; - -class WebColorField extends InputField { - private final ObjectProperty value = new SimpleObjectProperty<>(this, "value"); - - public final Color getValue() { - return value.get(); - } - - public final void setValue(Color value) { - this.value.set(value); - } - - public final ObjectProperty valueProperty() { - return value; - } - - public WebColorField() { - getStyleClass().setAll("webcolor-field"); - } - - @Override - protected Skin createDefaultSkin() { - return new WebColorFieldSkin(this); - } -} diff --git a/src/main/java/com/getpcpanel/ui/colorpicker/WebColorFieldSkin.java b/src/main/java/com/getpcpanel/ui/colorpicker/WebColorFieldSkin.java deleted file mode 100644 index c6b31a50..00000000 --- a/src/main/java/com/getpcpanel/ui/colorpicker/WebColorFieldSkin.java +++ /dev/null @@ -1,76 +0,0 @@ -package com.getpcpanel.ui.colorpicker; - -import java.util.Locale; - -import com.getpcpanel.util.Util; - -import javafx.beans.InvalidationListener; -import javafx.geometry.NodeOrientation; -import javafx.scene.Node; -import javafx.scene.paint.Color; -import lombok.extern.log4j.Log4j2; - -@Log4j2 -class WebColorFieldSkin extends InputFieldSkin { - private final InvalidationListener integerFieldValueListener; - - private boolean noChangeInValue; - - public WebColorFieldSkin(WebColorField control) { - super(control); - control.valueProperty().addListener(integerFieldValueListener = (observable -> updateText())); - getTextField().setNodeOrientation(NodeOrientation.LEFT_TO_RIGHT); - } - - @Override - public WebColorField getSkinnable() { - return (WebColorField) control; - } - - @Override - public Node getNode() { - return getTextField(); - } - - @Override - public void dispose() { - ((WebColorField) control).valueProperty().removeListener(integerFieldValueListener); - super.dispose(); - } - - @Override - protected boolean accept(String text) { - if (text.length() == 0) - return true; - return text.matches("#[a-fA-F0-9]{0,6}") || text.matches("[a-fA-F0-9]{0,6}"); - } - - @Override - protected void updateText() { - var color = ((WebColorField) control).getValue(); - if (color == null) - color = Color.BLACK; - getTextField().setText(Util.formatHexString(color)); - } - - @Override - protected void updateValue() { - if (noChangeInValue) - return; - var value = ((WebColorField) control).getValue(); - var text = (getTextField().getText() == null) ? "" : getTextField().getText().trim().toUpperCase(Locale.ROOT); - if (text.matches("#[A-F0-9]{6}") || text.matches("[A-F0-9]{6}")) - try { - var newValue = (text.charAt(0) == '#') ? Color.web(text) : Color.web("#" + text); - if (!newValue.equals(value)) { - ((WebColorField) control).setValue(newValue); - } else { - noChangeInValue = true; - getTextField().setText(Util.formatHexString(newValue)); - noChangeInValue = false; - } - } catch (IllegalArgumentException ex) { - log.error("Failed to parse [{}]", text, ex); - } - } -} diff --git a/src/main/java/com/getpcpanel/ui/command/ButtonCommandController.java b/src/main/java/com/getpcpanel/ui/command/ButtonCommandController.java deleted file mode 100644 index 0b58237b..00000000 --- a/src/main/java/com/getpcpanel/ui/command/ButtonCommandController.java +++ /dev/null @@ -1,7 +0,0 @@ -package com.getpcpanel.ui.command; - -import com.getpcpanel.commands.command.Command; - -public interface ButtonCommandController { - Command buildCommand(); -} diff --git a/src/main/java/com/getpcpanel/ui/command/ButtonController.java b/src/main/java/com/getpcpanel/ui/command/ButtonController.java deleted file mode 100644 index 68da93b5..00000000 --- a/src/main/java/com/getpcpanel/ui/command/ButtonController.java +++ /dev/null @@ -1,341 +0,0 @@ -package com.getpcpanel.ui.command; - -import javax.annotation.Nonnull; -import javax.annotation.Nullable; - -import org.apache.commons.lang3.StringUtils; -import org.apache.commons.lang3.math.NumberUtils; -import org.springframework.stereotype.Component; -import org.springframework.util.ReflectionUtils; - -import com.getpcpanel.commands.Commands; -import com.getpcpanel.commands.CommandsType; -import com.getpcpanel.commands.command.Command; -import com.getpcpanel.commands.command.CommandNoOp; -import com.getpcpanel.commands.command.DialAction; -import com.getpcpanel.commands.command.DialAction.DialCommandParams; -import com.getpcpanel.hid.DialValueCalculator; -import com.getpcpanel.profile.KnobSetting; -import com.getpcpanel.spring.Prototype; -import com.getpcpanel.ui.FxHelper; -import com.getpcpanel.ui.MacroControllerService; -import com.getpcpanel.ui.MacroControllerService.ControllerInfo; -import com.getpcpanel.ui.command.Cmd.Type; -import com.getpcpanel.ui.command.DialCutoffOptions.DialCutoffOptionsParams; -import com.getpcpanel.ui.graphviewer.GraphViewer; -import com.getpcpanel.util.Images; - -import javafx.beans.binding.Bindings; -import javafx.beans.property.StringProperty; -import javafx.beans.value.ObservableValue; -import javafx.collections.ListChangeListener.Change; -import javafx.event.ActionEvent; -import javafx.fxml.FXML; -import javafx.geometry.Insets; -import javafx.geometry.Pos; -import javafx.geometry.Side; -import javafx.scene.Node; -import javafx.scene.control.Accordion; -import javafx.scene.control.Button; -import javafx.scene.control.CheckBox; -import javafx.scene.control.ChoiceBox; -import javafx.scene.control.ContextMenu; -import javafx.scene.control.Label; -import javafx.scene.control.MenuItem; -import javafx.scene.control.TabPane; -import javafx.scene.control.TextField; -import javafx.scene.control.TitledPane; -import javafx.scene.layout.BorderPane; -import javafx.scene.layout.HBox; -import javafx.scene.layout.Priority; -import javafx.scene.shape.SVGPath; -import javafx.stage.Stage; -import lombok.Getter; -import lombok.RequiredArgsConstructor; -import lombok.Setter; -import lombok.SneakyThrows; -import lombok.extern.log4j.Log4j2; -import one.util.streamex.StreamEx; - -@Log4j2 -@Component -@Prototype -@RequiredArgsConstructor -public class ButtonController { - private final MacroControllerService macroControllerService; - private final FxHelper fxHelper; - - private Type cmdType; - private CommandContext context; - private final ContextMenu addMenu = new ContextMenu(); - @FXML @Getter private TabPane root; - - @FXML private Accordion commands; - @FXML private Button addButton; - @FXML private ChoiceBox commandsType; - @Setter private Stage stage; - - public void initController(Type cmdType, CommandContext context, @Nullable Commands buttonData) { - this.cmdType = cmdType; - this.context = context; - Commands.cmds(buttonData).forEach(this::add); - - var panes = commands.getPanes(); - if (!panes.isEmpty()) { - commands.setExpandedPane(panes.get(0)); - } - - macroControllerService.getControllersForType(cmdType).forEach(ctrlr -> { - if (!isEnabled(ctrlr)) { - return; - } - - var menuItem = new MenuItem(ctrlr.cmd().name()); - menuItem.setOnAction(ignored -> { - add(ctrlr, null); - commands.setExpandedPane(panes.get(panes.size() - 1)); - }); - addMenu.getItems().add(menuItem); - }); - addButton.setContextMenu(addMenu); - - commandsType.getItems().addAll(CommandsType.values()); - if (buttonData != null) { - commandsType.getSelectionModel().select(buttonData.getType()); - } - - if (cmdType == Type.button) { - commands.getPanes().addListener(this::determineCommandsTypeVisible); - determineCommandsTypeVisible(null); - } - } - - private void determineCommandsTypeVisible(@Nullable Change change) { - commandsType.setVisible(commands.getPanes().size() > 1); - if (!commandsType.isVisible()) { - commandsType.getSelectionModel().selectFirst(); - } - } - - @SneakyThrows - private static boolean isEnabled(@Nonnull ControllerInfo ctrlr) { - return ReflectionUtils.accessibleConstructor(ctrlr.cmd().enabled()).newInstance().isEnabled(); - } - - private void add(Command cmd) { - var controllerInfo = macroControllerService.getControllerForCommand(cmd.getClass()); - if (controllerInfo == null) { - log.warn("Dial/button with {} found, but that command is not supported", cmd); - return; - } - add(controllerInfo, cmd); - } - - @SneakyThrows - private void add(@Nonnull ControllerInfo info, @Nullable Command cmd) { - var loader = fxHelper.getLoader(info.getFxml()); - var loaded = loader.load(); - var controller = loader.>getController(); - controller.postInit(context); - HBox.setHgrow(loaded, Priority.ALWAYS); - - var pane = new TitledPane(null, loaded); - var panelData = addPanelOptions(controller, pane, info.cmd().name(), cmd); - pane.setUserData(panelData); - commands.getPanes().add(pane); - - if (cmd != null) { - controller.initFromCommand(cmd); - } - } - - private PanelData addPanelOptions(CommandController controller, @Nonnull TitledPane pane, String title, @Nullable Command cmd) { - // Labels - var titleOfTitledPane = new Label(title); - var additionalLabel = buildAdditionalLabel(controller, pane); - var labelsBox = new HBox(titleOfTitledPane, additionalLabel); - labelsBox.setAlignment(Pos.CENTER_LEFT); - - // Buttons - var buttonDelete = deleteButton(pane); - var buttonUp = moveButton(pane, Images.chevronUp(), -1); - var buttonDown = moveButton(pane, Images.chevronDown(), 1); - var buttonCopy = copyButton(pane); - - var hbox = new HBox(buttonCopy, buttonUp, buttonDown, buttonDelete); - var result = new PanelData(controller, cmd instanceof DialAction da ? da.getDialParams() : null); - - if (cmd instanceof DialAction da) { - var invertCheck = addInvertCheck(result, hbox, da); - addGraphViewer(result, controller, cmd, hbox, invertCheck); - } - - var borderPane = new BorderPane(); - borderPane.setLeft(labelsBox); - BorderPane.setAlignment(titleOfTitledPane, Pos.CENTER_LEFT); - borderPane.setRight(hbox); - borderPane.prefWidthProperty().bind(commands.widthProperty().subtract(40)); - pane.setGraphic(borderPane); - - return result; - } - - private CheckBox addInvertCheck(PanelData result, HBox target, DialAction da) { - var invertCheck = new CheckBox("Invert"); - HBox.setMargin(invertCheck, new Insets(3, 20, 0, 0)); - - invertCheck.setSelected(da.isInvert()); - invertCheck.selectedProperty().addListener((obs, old, newValue) -> result.setParams(result.params.withInvert(newValue))); - - target.getChildren().add(0, invertCheck); - return invertCheck; - } - - private void addGraphViewer(PanelData panelData, CommandController controller, @Nonnull Command cmd, HBox hbox, CheckBox invertCheck) { - if (!(cmd instanceof DialAction) || !(controller instanceof DialCommandController dc)) { - return; - } - - var graphViewer = new GraphViewer(new DialValueCalculator(new KnobSetting()), cmd); - graphViewer.setPrefSize(75, 28); - - var graphBox = new HBox(graphViewer); - HBox.setMargin(graphBox, new Insets(0, 10, 0, 0)); - hbox.getChildren().add(0, graphBox); - graphViewer.setOnAction(event -> { - var newParams = DialCutoffOptions.show(new DialCutoffOptionsParams(stage, panelData.params)); - newParams.ifPresent(params -> { - panelData.setParams(params); - invertCheck.setSelected(params.invert()); - graphViewer.setCmd(dc.buildCommand(params)); - graphViewer.redraw(); - }); - }); - - invertCheck.selectedProperty().addListener((obs, old, newValue) -> { - graphViewer.setCmd(dc.buildCommand(panelData.params)); - graphViewer.redraw(); - }); - - panelData.setGraph(graphViewer); - } - - public void setupGraphRenderer(TextField trimMin, TextField trimMax, CheckBox logarithmic) { - trimMin.textProperty().addListener((obs, old, newValue) -> doUpdateGraphRenderer(trimMin.getText(), trimMax.getText(), logarithmic.isSelected())); - trimMax.textProperty().addListener((obs, old, newValue) -> doUpdateGraphRenderer(trimMin.getText(), trimMax.getText(), logarithmic.isSelected())); - logarithmic.selectedProperty().addListener((obs, old, newValue) -> doUpdateGraphRenderer(trimMin.getText(), trimMax.getText(), logarithmic.isSelected())); - } - - private void doUpdateGraphRenderer(String trimMinStr, String trimMaxStr, boolean logarithmic) { - var trimMin = NumberUtils.toInt(trimMinStr, 0); - var trimMax = NumberUtils.toInt(trimMaxStr, 100); - - var knobSettings = new KnobSetting().setMinTrim(trimMin).setMaxTrim(trimMax).setLogarithmic(logarithmic); - StreamEx.of(commands.getPanes()) - .map(Node::getUserData) - .select(PanelData.class) - .map(PanelData::getGraph) - .nonNull() - .forEach(graph -> { - graph.setCalculator(new DialValueCalculator(knobSettings)); - graph.redraw(); - }); - } - - @Nonnull - private Label buildAdditionalLabel(@Nonnull CommandController controller, @Nonnull TitledPane pane) { - var additionalLabel = new Label(); - additionalLabel.setStyle("-fx-text-fill: #999;"); - additionalLabel.textProperty().bind(appendSemiColonBinding(controller.additionalLabelText())); - - // Hide when not needed - var showProperty = Bindings.notEqual(pane, commands.expandedPaneProperty()); - additionalLabel.visibleProperty().bind(showProperty); - additionalLabel.managedProperty().bind(showProperty); - return additionalLabel; - } - - private ObservableValue appendSemiColonBinding(StringProperty stringProperty) { - // TODO: Make this a map call when using JavaFX 19 - // stringProperty.map(v -> StringUtils.isBlank(v) ? "" : ": " + v); - return Bindings.createStringBinding(() -> StringUtils.isBlank(stringProperty.get()) ? "" : ": " + stringProperty.get(), stringProperty); - } - - private Button deleteButton(@Nonnull TitledPane pane) { - var buttonDelete = createButton(Images.delete()); - buttonDelete.setOnAction(event -> commands.getPanes().remove(pane)); - return buttonDelete; - } - - private Button moveButton(@Nonnull TitledPane pane, SVGPath image, int idxChange) { - var buttonMove = createButton(image); - commands.getPanes().addListener((Change c) -> showHideButton(buttonMove, commands.getPanes().size() > 1)); - buttonMove.setOnAction(event -> { - var idx = commands.getPanes().indexOf(pane); - commands.getPanes().remove(pane); - - var newIdx = Math.max(0, Math.min(commands.getPanes().size(), idx + idxChange)); - commands.getPanes().add(newIdx, pane); - }); - return buttonMove; - } - - private Button copyButton(TitledPane pane) { - var buttonCopy = createButton(Images.copy()); - buttonCopy.setOnAction(event -> { - var data = (PanelData) pane.getUserData(); - if (data.controller instanceof DialCommandController dc) { - add(dc.buildCommand(data.params)); - } else if (data.controller instanceof ButtonCommandController bc) { - add(bc.buildCommand()); - } - commands.setExpandedPane(commands.getPanes().get(commands.getPanes().size() - 1)); - - }); - return buttonCopy; - } - - private Button createButton(SVGPath image) { - var result = new Button("", image); - result.setStyle("-fx-background-color: transparent;"); - return result; - } - - private void showHideButton(Button btn, boolean visible) { - btn.setVisible(visible); - btn.setManaged(visible); - } - - public Commands determineButtonCommand() { - var userdata = StreamEx.of(commands.getPanes()).map(Node::getUserData); - - if (cmdType == Type.dial) { - userdata = userdata.select(PanelData.class) - .mapToEntry(PanelData::getController, PanelData::getParams) - .selectKeys(DialCommandController.class) - .mapKeyValue(DialCommandController::buildCommand); - } else { - userdata = userdata.select(PanelData.class).map(PanelData::getController).select(ButtonCommandController.class).map(ButtonCommandController::buildCommand); - } - - var cmds = userdata.select(Command.class).remove(CommandNoOp.class::isInstance).toList(); - return new Commands(cmds, commandsType.getValue()); - } - - public void showActionsMenu(ActionEvent ignored) { - addMenu.show(addButton, Side.TOP, 0, 0); - } - - @Getter - private static class PanelData { - private final CommandController controller; - - @Setter private GraphViewer graph; - @Setter private DialCommandParams params; - - public PanelData(CommandController controller, @Nullable DialCommandParams params) { - this.controller = controller; - this.params = params == null ? DialCommandParams.DEFAULT : params; - } - } -} diff --git a/src/main/java/com/getpcpanel/ui/command/Cmd.java b/src/main/java/com/getpcpanel/ui/command/Cmd.java deleted file mode 100644 index 76279303..00000000 --- a/src/main/java/com/getpcpanel/ui/command/Cmd.java +++ /dev/null @@ -1,29 +0,0 @@ -package com.getpcpanel.ui.command; - -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; - -import com.getpcpanel.commands.command.Command; - -@Retention(RetentionPolicy.RUNTIME) -public @interface Cmd { - String name(); - - String fxml(); - - Class[] cmds(); - - String os() default "*"; - - Class enabled() default CmdEnabled.class; - - enum Type { - button, dial - } - - class CmdEnabled { - public boolean isEnabled() { - return true; - } - } -} diff --git a/src/main/java/com/getpcpanel/ui/command/CommandContext.java b/src/main/java/com/getpcpanel/ui/command/CommandContext.java deleted file mode 100644 index 890d303c..00000000 --- a/src/main/java/com/getpcpanel/ui/command/CommandContext.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.getpcpanel.ui.command; - -import com.getpcpanel.profile.DeviceSave; -import com.getpcpanel.profile.Profile; - -import javafx.stage.Stage; - -public record CommandContext(Stage stage, DeviceSave deviceSave, Profile profile) { -} diff --git a/src/main/java/com/getpcpanel/ui/command/CommandController.java b/src/main/java/com/getpcpanel/ui/command/CommandController.java deleted file mode 100644 index 63e945fd..00000000 --- a/src/main/java/com/getpcpanel/ui/command/CommandController.java +++ /dev/null @@ -1,50 +0,0 @@ -package com.getpcpanel.ui.command; - -import java.util.Arrays; - -import com.getpcpanel.commands.command.Command; - -import javafx.beans.Observable; -import javafx.beans.binding.Bindings; -import javafx.beans.property.BooleanProperty; -import javafx.beans.property.SimpleBooleanProperty; -import javafx.beans.property.SimpleStringProperty; -import javafx.beans.property.StringProperty; -import javafx.scene.Node; - -public abstract class CommandController { - private final BooleanProperty initialized = new SimpleBooleanProperty(false); - - public abstract void postInit(CommandContext context); - - public void initFromCommand(T cmd) { - initialized.set(true); - } - - public StringProperty additionalLabelText() { - var old = determineDependencies(); - var dependencies = Arrays.copyOf(old, old.length + 1); - dependencies[old.length] = initialized; - - var result = new SimpleStringProperty(); - result.bind(Bindings.createStringBinding(() -> buildLabel(), dependencies)); - return result; - } - - protected abstract Observable[] determineDependencies(); - - protected String buildLabel() { - if (this instanceof DialCommandController dc) { - return dc.buildLabelCommand().buildLabel(); - } - if (this instanceof ButtonCommandController bc) { - return bc.buildCommand().buildLabel(); - } - return ""; - } - - protected void setVisible(Node node, boolean visible) { - node.setVisible(visible); - node.setManaged(visible); - } -} diff --git a/src/main/java/com/getpcpanel/ui/command/DialCommandController.java b/src/main/java/com/getpcpanel/ui/command/DialCommandController.java deleted file mode 100644 index e78a255c..00000000 --- a/src/main/java/com/getpcpanel/ui/command/DialCommandController.java +++ /dev/null @@ -1,12 +0,0 @@ -package com.getpcpanel.ui.command; - -import com.getpcpanel.commands.command.Command; -import com.getpcpanel.commands.command.DialAction.DialCommandParams; - -public interface DialCommandController { - Command buildCommand(DialCommandParams params); - - default Command buildLabelCommand() { - return buildCommand(DialCommandParams.DEFAULT); - } -} diff --git a/src/main/java/com/getpcpanel/ui/command/DialCutoffOptions.java b/src/main/java/com/getpcpanel/ui/command/DialCutoffOptions.java deleted file mode 100644 index b7ba22a2..00000000 --- a/src/main/java/com/getpcpanel/ui/command/DialCutoffOptions.java +++ /dev/null @@ -1,136 +0,0 @@ -package com.getpcpanel.ui.command; - -import java.util.Objects; -import java.util.Optional; - -import javax.annotation.Nonnull; - -import org.apache.commons.lang3.math.NumberUtils; -import org.springframework.stereotype.Component; - -import com.getpcpanel.MainFX; -import com.getpcpanel.commands.command.CommandBrightness; -import com.getpcpanel.commands.command.CommandNoOp; -import com.getpcpanel.commands.command.DialAction.DialCommandParams; -import com.getpcpanel.hid.DialValueCalculator; -import com.getpcpanel.profile.KnobSetting; -import com.getpcpanel.spring.Prototype; -import com.getpcpanel.ui.FxHelper; -import com.getpcpanel.ui.UIHelper; -import com.getpcpanel.ui.UIInitializer; -import com.getpcpanel.ui.graphviewer.GraphViewer; - -import javafx.application.Application; -import javafx.event.ActionEvent; -import javafx.fxml.FXML; -import javafx.scene.Scene; -import javafx.scene.control.CheckBox; -import javafx.scene.control.TextField; -import javafx.scene.image.Image; -import javafx.scene.layout.HBox; -import javafx.scene.layout.VBox; -import javafx.stage.Modality; -import javafx.stage.Stage; -import lombok.SneakyThrows; - -@Component -@Prototype -public class DialCutoffOptions extends Application implements UIInitializer { - @FXML private CheckBox invert; - @FXML private VBox panel; - @FXML private TextField moveStart; - @FXML private TextField moveEnd; - @FXML private HBox chartholder; - private Stage stage; - private Stage parentStage; - private boolean okClicked; - private GraphViewer graph; - - @SneakyThrows - public static Optional show(DialCutoffOptionsParams params) { - var res = MainFX.getBean(FxHelper.class).open(DialCutoffOptions.class, params); - var afdStage = new Stage(); - res.start(afdStage); - - if (res.okClicked) { - return Optional.of(res.buildResult()); - } - return Optional.empty(); - } - - @Override - public void initUI(@Nonnull DialCutoffOptionsParams args) { - parentStage = args.parentStage; - if (parentStage == null) { - throw new IllegalStateException("Parent stage is required"); - } - setInitialFieldValues(args.params); - - graph = new GraphViewer(new DialValueCalculator(new KnobSetting()), new CommandNoOp()); - graph.setPrefSize(400, 100); - chartholder.getChildren().add(graph); - keepGraphUpToDate(); - } - - private void keepGraphUpToDate() { - moveStart.textProperty().addListener((observable, oldValue, newValue) -> updateGraph()); - moveEnd.textProperty().addListener((observable, oldValue, newValue) -> updateGraph()); - invert.selectedProperty().addListener((observable, oldValue, newValue) -> updateGraph()); - updateGraph(); - } - - private void updateGraph() { - graph.setCmd(new CommandBrightness(buildResult())); - graph.redraw(); - } - - private void setInitialFieldValues(DialCommandParams originalArgs) { - invert.setSelected(originalArgs.invert()); - - if (originalArgs.moveStart() != null) { - moveStart.setText(String.valueOf(originalArgs.moveStart())); - } - if (originalArgs.moveEnd() != null) { - moveEnd.setText(String.valueOf(originalArgs.moveEnd())); - } - } - - @Override - public void start(Stage stage) throws Exception { - this.stage = stage; - UIHelper.closeOnEscape(stage); - var scene = new Scene(panel); - scene.getStylesheets().add(Objects.requireNonNull(getClass().getResource("/assets/dark_theme.css")).toExternalForm()); - stage.getIcons().add(new Image(Objects.requireNonNull(getClass().getResource("/assets/256x256.png")).toExternalForm())); - stage.setScene(scene); - stage.setTitle("Volume options"); - - stage.initModality(Modality.WINDOW_MODAL); - stage.initOwner(parentStage); - stage.showAndWait(); - } - - @Nonnull - private DialCommandParams buildResult() { - var moveStartVal = NumberUtils.toInt(moveStart.getText(), 0); - var moveEndVal = NumberUtils.toInt(moveEnd.getText(), 0); - return new DialCommandParams(invert.isSelected(), moveStartVal, moveEndVal); - } - - public void ok(ActionEvent actionEvent) { - okClicked = true; - stage.close(); - } - - public void closeButtonAction(ActionEvent actionEvent) { - stage.close(); - } - - public record DialCutoffOptionsParams(Stage parentStage, DialCommandParams params) { - public DialCutoffOptionsParams { - if (params == null) { - params = DialCommandParams.DEFAULT; - } - } - } -} diff --git a/src/main/java/com/getpcpanel/ui/command/ObsEnabled.java b/src/main/java/com/getpcpanel/ui/command/ObsEnabled.java deleted file mode 100644 index 6fb0339b..00000000 --- a/src/main/java/com/getpcpanel/ui/command/ObsEnabled.java +++ /dev/null @@ -1,12 +0,0 @@ -package com.getpcpanel.ui.command; - -import com.getpcpanel.MainFX; -import com.getpcpanel.obs.OBS; -import com.getpcpanel.ui.command.Cmd.CmdEnabled; - -public class ObsEnabled extends CmdEnabled { - @Override - public boolean isEnabled() { - return MainFX.getBean(OBS.class).isConnected(); - } -} diff --git a/src/main/java/com/getpcpanel/ui/command/VoiceMeeterEnabled.java b/src/main/java/com/getpcpanel/ui/command/VoiceMeeterEnabled.java deleted file mode 100644 index 1e1d2ae6..00000000 --- a/src/main/java/com/getpcpanel/ui/command/VoiceMeeterEnabled.java +++ /dev/null @@ -1,12 +0,0 @@ -package com.getpcpanel.ui.command; - -import com.getpcpanel.MainFX; -import com.getpcpanel.ui.command.Cmd.CmdEnabled; -import com.getpcpanel.voicemeeter.Voicemeeter; - -public class VoiceMeeterEnabled extends CmdEnabled { - @Override - public boolean isEnabled() { - return MainFX.getBean(Voicemeeter.class).login(); - } -} diff --git a/src/main/java/com/getpcpanel/ui/command/button/BtnApplicationDeviceToggleController.java b/src/main/java/com/getpcpanel/ui/command/button/BtnApplicationDeviceToggleController.java deleted file mode 100644 index 5612ac4d..00000000 --- a/src/main/java/com/getpcpanel/ui/command/button/BtnApplicationDeviceToggleController.java +++ /dev/null @@ -1,72 +0,0 @@ -package com.getpcpanel.ui.command.button; - -import static com.getpcpanel.spring.OsHelper.WINDOWS; - -import java.util.List; - -import org.springframework.stereotype.Component; - -import com.getpcpanel.commands.command.Command; -import com.getpcpanel.commands.command.CommandVolumeApplicationDeviceToggle; -import com.getpcpanel.spring.Prototype; -import com.getpcpanel.ui.AdvancedDevices; -import com.getpcpanel.ui.PickProcessesController; -import com.getpcpanel.ui.PickProcessesController.PickType; -import com.getpcpanel.ui.command.ButtonCommandController; -import com.getpcpanel.ui.command.Cmd; -import com.getpcpanel.ui.command.CommandContext; -import com.getpcpanel.ui.command.CommandController; - -import javafx.beans.Observable; -import javafx.event.ActionEvent; -import javafx.fxml.FXML; -import javafx.scene.control.RadioButton; -import lombok.RequiredArgsConstructor; -import lombok.extern.log4j.Log4j2; - -@Log4j2 -@Component -@Prototype -@RequiredArgsConstructor -@Cmd(name = "Application Sound Device", fxml = "ApplicationDeviceToggle", cmds = CommandVolumeApplicationDeviceToggle.class, os = WINDOWS) -public class BtnApplicationDeviceToggleController extends CommandController implements ButtonCommandController { - @FXML private AdvancedDevices applicationDeviceDevicesController; - @FXML private PickProcessesController applicationDeviceProcessesController; - @FXML private RadioButton rdioApplicationDeviceFocus; - @FXML private RadioButton rdioApplicationDeviceSpecific; - - @Override - public void postInit(CommandContext context) { - applicationDeviceProcessesController.setPickType(PickType.soundSource); - applicationDeviceDevicesController.setAllowRemove(true); - applicationDeviceDevicesController.setOnlyMedia(true); - } - - @Override - public void initFromCommand(CommandVolumeApplicationDeviceToggle cmd) { - rdioApplicationDeviceSpecific.setSelected(!cmd.isFollowFocus()); - rdioApplicationDeviceFocus.setSelected(cmd.isFollowFocus()); - applicationDeviceProcessesController.setSelection(cmd.getProcesses()); - cmd.getDevices().forEach(applicationDeviceDevicesController::add); - super.initFromCommand(cmd); - } - - @Override - public Command buildCommand() { - var followFocus = rdioApplicationDeviceFocus.isSelected(); - var processes = followFocus ? List.of() : applicationDeviceProcessesController.getSelection(); - return new CommandVolumeApplicationDeviceToggle(processes, followFocus, applicationDeviceDevicesController.getEntries()); - } - - @Override - protected Observable[] determineDependencies() { - return new Observable[] { - applicationDeviceProcessesController.getObservable(), - rdioApplicationDeviceFocus.selectedProperty(), rdioApplicationDeviceSpecific.selectedProperty() - }; - } - - public void addApplicationDevice(ActionEvent ignored) { - applicationDeviceDevicesController.add(); - } -} diff --git a/src/main/java/com/getpcpanel/ui/command/button/BtnDefaultDeviceAdvancedController.java b/src/main/java/com/getpcpanel/ui/command/button/BtnDefaultDeviceAdvancedController.java deleted file mode 100644 index 990ecd32..00000000 --- a/src/main/java/com/getpcpanel/ui/command/button/BtnDefaultDeviceAdvancedController.java +++ /dev/null @@ -1,50 +0,0 @@ -package com.getpcpanel.ui.command.button; - -import static com.getpcpanel.spring.OsHelper.WINDOWS; - -import org.springframework.stereotype.Component; - -import com.getpcpanel.commands.command.Command; -import com.getpcpanel.commands.command.CommandVolumeDefaultDeviceAdvanced; -import com.getpcpanel.spring.Prototype; -import com.getpcpanel.ui.AdvancedDevices; -import com.getpcpanel.ui.command.ButtonCommandController; -import com.getpcpanel.ui.command.Cmd; -import com.getpcpanel.ui.command.CommandContext; -import com.getpcpanel.ui.command.CommandController; - -import javafx.beans.Observable; -import javafx.fxml.FXML; -import lombok.RequiredArgsConstructor; -import lombok.extern.log4j.Log4j2; - -@Log4j2 -@Component -@Prototype -@RequiredArgsConstructor -@Cmd(name = "Default Device Advanced", fxml = "DefaultDeviceAdvanced", cmds = CommandVolumeDefaultDeviceAdvanced.class, os = WINDOWS) -public class BtnDefaultDeviceAdvancedController extends CommandController implements ButtonCommandController { - @FXML private AdvancedDevices defaultDeviceAdvancedController; - - @Override - public void postInit(CommandContext context) { - defaultDeviceAdvancedController.add(); - } - - @Override - public void initFromCommand(CommandVolumeDefaultDeviceAdvanced cmd) { - defaultDeviceAdvancedController.set(cmd.getName(), cmd.getMediaPb(), cmd.getMediaRec(), cmd.getCommunicationPb(), cmd.getCommunicationRec()); - super.initFromCommand(cmd); - } - - @Override - public Command buildCommand() { - var entry = defaultDeviceAdvancedController.getEntries().get(0); - return new CommandVolumeDefaultDeviceAdvanced(entry.name(), entry.mediaPlayback(), entry.mediaRecord(), entry.communicationPlayback(), entry.communicationRecord()); - } - - @Override - protected Observable[] determineDependencies() { - return new Observable[0]; - } -} diff --git a/src/main/java/com/getpcpanel/ui/command/button/BtnDefaultDeviceController.java b/src/main/java/com/getpcpanel/ui/command/button/BtnDefaultDeviceController.java deleted file mode 100644 index 2306ae7b..00000000 --- a/src/main/java/com/getpcpanel/ui/command/button/BtnDefaultDeviceController.java +++ /dev/null @@ -1,65 +0,0 @@ -package com.getpcpanel.ui.command.button; - -import static com.getpcpanel.commands.command.CommandNoOp.NOOP; - -import java.util.Collection; - -import javax.annotation.Nullable; - -import org.springframework.stereotype.Component; - -import com.getpcpanel.commands.command.Command; -import com.getpcpanel.commands.command.CommandVolumeDefaultDevice; -import com.getpcpanel.cpp.AudioDevice; -import com.getpcpanel.cpp.ISndCtrl; -import com.getpcpanel.spring.Prototype; -import com.getpcpanel.ui.command.ButtonCommandController; -import com.getpcpanel.ui.command.Cmd; -import com.getpcpanel.ui.command.CommandContext; -import com.getpcpanel.ui.command.CommandController; - -import javafx.beans.Observable; -import javafx.fxml.FXML; -import javafx.scene.control.ChoiceBox; -import lombok.RequiredArgsConstructor; -import lombok.extern.log4j.Log4j2; -import one.util.streamex.StreamEx; - -@Log4j2 -@Component -@Prototype -@RequiredArgsConstructor -@Cmd(name = "Sound Device", fxml = "DefaultDevice", cmds = CommandVolumeDefaultDevice.class) -public class BtnDefaultDeviceController extends CommandController implements ButtonCommandController { - private final ISndCtrl sndCtrl; - private Collection allSoundDevices; - - @FXML private ChoiceBox sounddevices; - - @Override - public void postInit(CommandContext context) { - allSoundDevices = sndCtrl.getDevices(); - sounddevices.getItems().addAll(allSoundDevices); - } - - @Override - public void initFromCommand(CommandVolumeDefaultDevice cmd) { - sounddevices.setValue(getSoundDeviceById(cmd.getDeviceId())); - super.initFromCommand(cmd); - } - - @Override - public Command buildCommand() { - return sounddevices.getValue() == null ? NOOP : new CommandVolumeDefaultDevice(sounddevices.getValue().id()); - } - - @Override - protected Observable[] determineDependencies() { - return new Observable[] { sounddevices.valueProperty() }; - } - - @Nullable - private AudioDevice getSoundDeviceById(String id) { - return StreamEx.of(allSoundDevices).findFirst(sd -> sd.id().equals(id)).orElse(null); - } -} diff --git a/src/main/java/com/getpcpanel/ui/command/button/BtnDefaultDeviceToggleController.java b/src/main/java/com/getpcpanel/ui/command/button/BtnDefaultDeviceToggleController.java deleted file mode 100644 index 786e1e74..00000000 --- a/src/main/java/com/getpcpanel/ui/command/button/BtnDefaultDeviceToggleController.java +++ /dev/null @@ -1,87 +0,0 @@ -package com.getpcpanel.ui.command.button; - -import java.util.Collection; - -import javax.annotation.Nullable; - -import org.springframework.stereotype.Component; - -import com.getpcpanel.commands.command.Command; -import com.getpcpanel.commands.command.CommandVolumeDefaultDeviceToggle; -import com.getpcpanel.cpp.AudioDevice; -import com.getpcpanel.cpp.ISndCtrl; -import com.getpcpanel.spring.Prototype; -import com.getpcpanel.ui.SoundDeviceExportFactory; -import com.getpcpanel.ui.SoundDeviceImportFactory; -import com.getpcpanel.ui.command.ButtonCommandController; -import com.getpcpanel.ui.command.Cmd; -import com.getpcpanel.ui.command.CommandContext; -import com.getpcpanel.ui.command.CommandController; - -import javafx.beans.Observable; -import javafx.collections.ListChangeListener; -import javafx.fxml.FXML; -import javafx.scene.control.ListView; -import lombok.RequiredArgsConstructor; -import lombok.extern.log4j.Log4j2; -import one.util.streamex.StreamEx; - -@Log4j2 -@Component -@Prototype -@RequiredArgsConstructor -@Cmd(name = "Toggle Device", fxml = "DefaultDeviceToggle", cmds = CommandVolumeDefaultDeviceToggle.class) -public class BtnDefaultDeviceToggleController extends CommandController implements ButtonCommandController { - private final ISndCtrl sndCtrl; - private Collection allSoundDevices; - @FXML private ListView soundDevices2; - @FXML private ListView soundDeviceSource; - - @Override - public void postInit(CommandContext context) { - allSoundDevices = sndCtrl.getDevices(); - soundDeviceSource.getItems().addAll(allSoundDevices); - initDeviceToggleEvents(); - soundDevices2.setCellFactory(new SoundDeviceImportFactory(soundDevices2)); - } - - @Override - public void initFromCommand(CommandVolumeDefaultDeviceToggle cmd) { - var devices = StreamEx.of(cmd.getDevices()).map(this::getSoundDeviceById).toList(); - soundDevices2.getItems().addAll(devices); - soundDeviceSource.getItems().removeAll(devices); - super.initFromCommand(cmd); - } - - @Override - public Command buildCommand() { - return new CommandVolumeDefaultDeviceToggle(soundDevices2.getItems().stream().map(AudioDevice::id).toList()); - } - - @Override - protected Observable[] determineDependencies() { - return new Observable[] { soundDeviceSource.getChildrenUnmodifiable() }; - } - - private void initDeviceToggleEvents() { - var sourceRenderer = new SoundDeviceExportFactory(soundDeviceSource); - disableDeviceToggleOtherTypes(sourceRenderer); - soundDeviceSource.setCellFactory(sourceRenderer); - soundDeviceSource.getItems().addListener((ListChangeListener) change -> disableDeviceToggleOtherTypes(sourceRenderer)); - } - - private void disableDeviceToggleOtherTypes(SoundDeviceExportFactory sourceRenderer) { - if (!soundDevices2.getItems().isEmpty()) { - var df = soundDevices2.getItems().get(0).dataflow(); - sourceRenderer.setEnabledFlavor(df); - } else { - sourceRenderer.setEnabledFlavor(null); - } - soundDeviceSource.refresh(); - } - - @Nullable - private AudioDevice getSoundDeviceById(String id) { - return StreamEx.of(allSoundDevices).findFirst(sd -> sd.id().equals(id)).orElse(null); - } -} diff --git a/src/main/java/com/getpcpanel/ui/command/button/BtnDeviceMuteController.java b/src/main/java/com/getpcpanel/ui/command/button/BtnDeviceMuteController.java deleted file mode 100644 index 533bd2b5..00000000 --- a/src/main/java/com/getpcpanel/ui/command/button/BtnDeviceMuteController.java +++ /dev/null @@ -1,95 +0,0 @@ -package com.getpcpanel.ui.command.button; - -import java.util.Collection; - -import javax.annotation.Nullable; - -import org.apache.commons.lang3.StringUtils; -import org.springframework.stereotype.Component; - -import com.getpcpanel.commands.command.Command; -import com.getpcpanel.commands.command.CommandVolumeDeviceMute; -import com.getpcpanel.cpp.AudioDevice; -import com.getpcpanel.cpp.ISndCtrl; -import com.getpcpanel.cpp.MuteType; -import com.getpcpanel.spring.Prototype; -import com.getpcpanel.ui.command.ButtonCommandController; -import com.getpcpanel.ui.command.Cmd; -import com.getpcpanel.ui.command.CommandContext; -import com.getpcpanel.ui.command.CommandController; - -import javafx.beans.Observable; -import javafx.event.ActionEvent; -import javafx.fxml.FXML; -import javafx.scene.control.ChoiceBox; -import javafx.scene.control.RadioButton; -import lombok.RequiredArgsConstructor; -import lombok.extern.log4j.Log4j2; -import one.util.streamex.StreamEx; - -@Log4j2 -@Component -@Prototype -@RequiredArgsConstructor -@Cmd(name = "Mute Device", fxml = "DeviceMute", cmds = CommandVolumeDeviceMute.class) -public class BtnDeviceMuteController extends CommandController implements ButtonCommandController { - private final ISndCtrl sndCtrl; - private Collection allSoundDevices; - @FXML private ChoiceBox muteSoundDevice; - @FXML private RadioButton rdio_muteDevice_Default; - @FXML private RadioButton rdio_muteDevice_Specific; - @FXML private RadioButton rdio_muteDevice_mute; - @FXML private RadioButton rdio_muteDevice_toggle; - @FXML private RadioButton rdio_muteDevice_unmute; - - @Override - public void postInit(CommandContext context) { - allSoundDevices = sndCtrl.getDevices(); - muteSoundDevice.getItems().addAll(allSoundDevices); - } - - @Override - public void initFromCommand(CommandVolumeDeviceMute cmd) { - if (StringUtils.equalsAny(StringUtils.defaultString(cmd.getDeviceId(), ""), "", "default")) { - rdio_muteDevice_Default.setSelected(true); - } else { - rdio_muteDevice_Specific.setSelected(true); - muteSoundDevice.setValue(getSoundDeviceById(cmd.getDeviceId())); - } - switch (cmd.getMuteType()) { - case unmute -> rdio_muteDevice_unmute.setSelected(true); - case mute -> rdio_muteDevice_mute.setSelected(true); - case toggle -> rdio_muteDevice_toggle.setSelected(true); - } - onRadioButton(null); - super.initFromCommand(cmd); - } - - @Override - public Command buildCommand() { - var device = rdio_muteDevice_Default.isSelected() || muteSoundDevice.getValue() == null ? "" : muteSoundDevice.getValue().id(); - return new CommandVolumeDeviceMute(device, rdio_muteDevice_unmute.isSelected() ? MuteType.unmute : rdio_muteDevice_mute.isSelected() ? MuteType.mute : MuteType.toggle); - } - - @Override - protected Observable[] determineDependencies() { - return new Observable[] { - muteSoundDevice.valueProperty(), - rdio_muteDevice_Default.selectedProperty(), - rdio_muteDevice_Specific.selectedProperty(), - rdio_muteDevice_mute.selectedProperty(), - rdio_muteDevice_toggle.selectedProperty(), - rdio_muteDevice_unmute.selectedProperty() - }; - } - - @Nullable - private AudioDevice getSoundDeviceById(String id) { - return StreamEx.of(allSoundDevices).findFirst(sd -> sd.id().equals(id)).orElse(null); - } - - @FXML - private void onRadioButton(@Nullable ActionEvent event) { - muteSoundDevice.setDisable(!rdio_muteDevice_Specific.isSelected()); - } -} diff --git a/src/main/java/com/getpcpanel/ui/command/button/BtnDeviceToggleAdvancedController.java b/src/main/java/com/getpcpanel/ui/command/button/BtnDeviceToggleAdvancedController.java deleted file mode 100644 index 410cb75a..00000000 --- a/src/main/java/com/getpcpanel/ui/command/button/BtnDeviceToggleAdvancedController.java +++ /dev/null @@ -1,54 +0,0 @@ -package com.getpcpanel.ui.command.button; - -import static com.getpcpanel.spring.OsHelper.WINDOWS; - -import org.springframework.stereotype.Component; - -import com.getpcpanel.commands.command.Command; -import com.getpcpanel.commands.command.CommandVolumeDefaultDeviceToggleAdvanced; -import com.getpcpanel.spring.Prototype; -import com.getpcpanel.ui.AdvancedDevices; -import com.getpcpanel.ui.command.ButtonCommandController; -import com.getpcpanel.ui.command.Cmd; -import com.getpcpanel.ui.command.CommandContext; -import com.getpcpanel.ui.command.CommandController; - -import javafx.beans.Observable; -import javafx.event.ActionEvent; -import javafx.fxml.FXML; -import lombok.RequiredArgsConstructor; -import lombok.extern.log4j.Log4j2; - -@Log4j2 -@Component -@Prototype -@RequiredArgsConstructor -@Cmd(name = "Toggle Device Advanced", fxml = "DeviceToggleAdvanced", cmds = CommandVolumeDefaultDeviceToggleAdvanced.class, os = WINDOWS) -public class BtnDeviceToggleAdvancedController extends CommandController implements ButtonCommandController { - @FXML private AdvancedDevices defaultDeviceToggleAdvancedController; - - @Override - public void postInit(CommandContext context) { - defaultDeviceToggleAdvancedController.setAllowRemove(true); - } - - @Override - public void initFromCommand(CommandVolumeDefaultDeviceToggleAdvanced cmd) { - cmd.getDevices().forEach(defaultDeviceToggleAdvancedController::add); - super.initFromCommand(cmd); - } - - @Override - public Command buildCommand() { - return new CommandVolumeDefaultDeviceToggleAdvanced(defaultDeviceToggleAdvancedController.getEntries()); - } - - @Override - protected Observable[] determineDependencies() { - return new Observable[0]; - } - - public void addDefaultDeviceToggleAdvanced(ActionEvent ignored) { - defaultDeviceToggleAdvancedController.add(); - } -} diff --git a/src/main/java/com/getpcpanel/ui/command/button/BtnEndProgramController.java b/src/main/java/com/getpcpanel/ui/command/button/BtnEndProgramController.java deleted file mode 100644 index 493c3c1a..00000000 --- a/src/main/java/com/getpcpanel/ui/command/button/BtnEndProgramController.java +++ /dev/null @@ -1,70 +0,0 @@ -package com.getpcpanel.ui.command.button; - -import java.util.Set; - -import javax.annotation.Nullable; - -import org.springframework.stereotype.Component; - -import com.getpcpanel.commands.command.Command; -import com.getpcpanel.commands.command.CommandEndProgram; -import com.getpcpanel.spring.Prototype; -import com.getpcpanel.ui.PickProcessesController; -import com.getpcpanel.ui.PickProcessesController.PickType; -import com.getpcpanel.ui.command.ButtonCommandController; -import com.getpcpanel.ui.command.Cmd; -import com.getpcpanel.ui.command.CommandContext; -import com.getpcpanel.ui.command.CommandController; - -import javafx.beans.Observable; -import javafx.event.ActionEvent; -import javafx.fxml.FXML; -import javafx.scene.control.RadioButton; -import lombok.RequiredArgsConstructor; -import lombok.extern.log4j.Log4j2; - -@Log4j2 -@Component -@Prototype -@RequiredArgsConstructor -@Cmd(name = "End Program", fxml = "EndProgram", cmds = CommandEndProgram.class) -public class BtnEndProgramController extends CommandController implements ButtonCommandController { - @FXML private RadioButton rdioEndFocusedProgram; - @FXML private RadioButton rdioEndSpecificProgram; - @FXML private PickProcessesController applicationEndProcessController; - - @Override - public void postInit(CommandContext context) { - applicationEndProcessController.setPickType(PickType.process); - applicationEndProcessController.setSingle(true); - } - - @Override - public void initFromCommand(CommandEndProgram endProgram) { - if (endProgram.isSpecific()) { - rdioEndSpecificProgram.setSelected(true); - applicationEndProcessController.setSelection(Set.of(endProgram.getName())); - } else { - rdioEndFocusedProgram.setSelected(true); - } - onRadioButton(null); - super.initFromCommand(endProgram); - } - - @Override - public Command buildCommand() { - var selection = applicationEndProcessController.getSelection(); - var program = selection.isEmpty() ? "" : selection.get(0); - return new CommandEndProgram(rdioEndSpecificProgram.isSelected(), program); - } - - @Override - protected Observable[] determineDependencies() { - return new Observable[] { rdioEndFocusedProgram.selectedProperty(), rdioEndSpecificProgram.selectedProperty(), applicationEndProcessController.getObservable() }; - } - - @FXML - private void onRadioButton(@Nullable ActionEvent event) { - applicationEndProcessController.setDisable(!rdioEndSpecificProgram.isSelected()); - } -} diff --git a/src/main/java/com/getpcpanel/ui/command/button/BtnFocusMuteController.java b/src/main/java/com/getpcpanel/ui/command/button/BtnFocusMuteController.java deleted file mode 100644 index d35d1e0d..00000000 --- a/src/main/java/com/getpcpanel/ui/command/button/BtnFocusMuteController.java +++ /dev/null @@ -1,57 +0,0 @@ -package com.getpcpanel.ui.command.button; - -import org.springframework.stereotype.Component; - -import com.getpcpanel.commands.command.Command; -import com.getpcpanel.commands.command.CommandVolumeFocusMute; -import com.getpcpanel.cpp.MuteType; -import com.getpcpanel.spring.Prototype; -import com.getpcpanel.ui.command.ButtonCommandController; -import com.getpcpanel.ui.command.Cmd; -import com.getpcpanel.ui.command.CommandContext; -import com.getpcpanel.ui.command.CommandController; - -import javafx.beans.Observable; -import javafx.fxml.FXML; -import javafx.scene.control.RadioButton; -import lombok.RequiredArgsConstructor; -import lombok.extern.log4j.Log4j2; - -@Log4j2 -@Component -@Prototype -@RequiredArgsConstructor -@Cmd(name = "Focus Mute", fxml = "FocusMute", cmds = CommandVolumeFocusMute.class) -public class BtnFocusMuteController extends CommandController implements ButtonCommandController { - @FXML private RadioButton rdio_focus_mute_mute; - @FXML private RadioButton rdio_focus_mute_toggle; - @FXML private RadioButton rdio_focus_mute_unmute; - - @Override - public void postInit(CommandContext context) { - } - - @Override - public void initFromCommand(CommandVolumeFocusMute cmd) { - switch (cmd.getMuteType()) { - case unmute -> rdio_focus_mute_unmute.setSelected(true); - case mute -> rdio_focus_mute_mute.setSelected(true); - case toggle -> rdio_focus_mute_toggle.setSelected(true); - } - super.initFromCommand(cmd); - } - - @Override - public Command buildCommand() { - return new CommandVolumeFocusMute(rdio_focus_mute_unmute.isSelected() ? MuteType.unmute : rdio_focus_mute_mute.isSelected() ? MuteType.mute : MuteType.toggle); - } - - @Override - protected Observable[] determineDependencies() { - return new Observable[] { - rdio_focus_mute_mute.selectedProperty(), - rdio_focus_mute_toggle.selectedProperty(), - rdio_focus_mute_unmute.selectedProperty(), - }; - } -} diff --git a/src/main/java/com/getpcpanel/ui/command/button/BtnKeystrokeController.java b/src/main/java/com/getpcpanel/ui/command/button/BtnKeystrokeController.java deleted file mode 100644 index af96a7c8..00000000 --- a/src/main/java/com/getpcpanel/ui/command/button/BtnKeystrokeController.java +++ /dev/null @@ -1,105 +0,0 @@ -package com.getpcpanel.ui.command.button; - -import org.springframework.stereotype.Component; - -import com.getpcpanel.commands.command.Command; -import com.getpcpanel.commands.command.CommandKeystroke; -import com.getpcpanel.spring.Prototype; -import com.getpcpanel.ui.command.ButtonCommandController; -import com.getpcpanel.ui.command.Cmd; -import com.getpcpanel.ui.command.CommandContext; -import com.getpcpanel.ui.command.CommandController; - -import javafx.beans.Observable; -import javafx.event.ActionEvent; -import javafx.fxml.FXML; -import javafx.scene.control.TextField; -import javafx.scene.input.KeyCode; -import lombok.RequiredArgsConstructor; -import lombok.extern.log4j.Log4j2; - -@Log4j2 -@Component -@Prototype -@RequiredArgsConstructor -@Cmd(name = "Keystroke", fxml = "Keystroke", cmds = CommandKeystroke.class) -public class BtnKeystrokeController extends CommandController implements ButtonCommandController { - @FXML private TextField keystrokeField; - private boolean k_alt; - private boolean k_ctrl; - private boolean k_shift; - private boolean k_win; - - @Override - public void postInit(CommandContext context) { - keystrokeField.setOnKeyPressed(event -> { - var code = event.getCode(); - if (code == KeyCode.ALT) { - k_alt = true; - } else if (code == KeyCode.SHIFT) { - k_shift = true; - } else if (code == KeyCode.WINDOWS) { - k_win = true; - } else if (code == KeyCode.CONTROL) { - k_ctrl = true; - } else if (!k_alt && !k_shift && !k_win && !k_ctrl) { - if (code.name().startsWith("DIGIT")) { - keystrokeField.setText(code.name().substring(5)); - } else { - keystrokeField.setText(code.name()); - } - } else { - var str = new StringBuilder(); - var bools = new boolean[] { k_ctrl, k_shift, k_alt, k_win }; - var keys = new String[] { "ctrl", "shift", "alt", "windows" }; - for (var i = 0; i < 4; i++) { - if (bools[i]) - str.append(keys[i]).append(" + "); - } - if (code.name().startsWith("DIGIT")) { - str.append(code.name().substring(5)); - } else { - str.append(code.name()); - } - keystrokeField.setText(str.toString()); - } - }); - keystrokeField.setOnKeyReleased(event -> { - var code = event.getCode(); - if (code == KeyCode.ALT) { - k_alt = false; - } else if (code == KeyCode.SHIFT) { - k_shift = false; - } else if (code == KeyCode.WINDOWS) { - k_win = false; - } else if (code == KeyCode.CONTROL) { - k_ctrl = false; - } - }); - } - - @Override - public void initFromCommand(CommandKeystroke cmd) { - keystrokeField.setText(cmd.getKeystroke()); - super.initFromCommand(cmd); - } - - @Override - public Command buildCommand() { - return new CommandKeystroke(keystrokeField.getText()); - } - - @Override - protected Observable[] determineDependencies() { - return new Observable[] { keystrokeField.textProperty() }; - } - - @FXML - private void clearKeystroke(ActionEvent event) { - keystrokeField.setText(""); - k_alt = false; - k_shift = false; - k_win = false; - k_ctrl = false; - } -} diff --git a/src/main/java/com/getpcpanel/ui/command/button/BtnMediaController.java b/src/main/java/com/getpcpanel/ui/command/button/BtnMediaController.java deleted file mode 100644 index f98f10f3..00000000 --- a/src/main/java/com/getpcpanel/ui/command/button/BtnMediaController.java +++ /dev/null @@ -1,60 +0,0 @@ -package com.getpcpanel.ui.command.button; - -import static com.getpcpanel.spring.OsHelper.WINDOWS; - -import org.springframework.stereotype.Component; - -import com.getpcpanel.commands.command.Command; -import com.getpcpanel.commands.command.CommandMedia; -import com.getpcpanel.commands.command.CommandMedia.VolumeButton; -import com.getpcpanel.spring.Prototype; -import com.getpcpanel.ui.command.ButtonCommandController; -import com.getpcpanel.ui.command.Cmd; -import com.getpcpanel.ui.command.CommandContext; -import com.getpcpanel.ui.command.CommandController; - -import javafx.beans.Observable; -import javafx.fxml.FXML; -import javafx.scene.control.CheckBox; -import javafx.scene.control.RadioButton; -import javafx.scene.control.ToggleGroup; -import lombok.RequiredArgsConstructor; -import lombok.extern.log4j.Log4j2; - -@Log4j2 -@Component -@Prototype -@RequiredArgsConstructor -@Cmd(name = "Music Control", fxml = "Media", cmds = CommandMedia.class, os = WINDOWS) -public class BtnMediaController extends CommandController implements ButtonCommandController { - @FXML private ToggleGroup mediagroup; - @FXML private CheckBox cmdMediaSpotify; - - @Override - public void postInit(CommandContext context) { - } - - @Override - public void initFromCommand(CommandMedia cmd) { - mediagroup.getToggles().get(switch (cmd.getButton()) { - case playPause -> 0; - case stop -> 1; - case prev -> 2; - case next -> 3; - case mute -> 4; - } - ).setSelected(true); - cmdMediaSpotify.setSelected(cmd.isSpotify()); - super.initFromCommand(cmd); - } - - @Override - public Command buildCommand() { - return new CommandMedia(VolumeButton.valueOf(((RadioButton) mediagroup.getSelectedToggle()).getId()), cmdMediaSpotify.isSelected()); - } - - @Override - protected Observable[] determineDependencies() { - return new Observable[] { mediagroup.selectedToggleProperty(), cmdMediaSpotify.selectedProperty() }; - } -} diff --git a/src/main/java/com/getpcpanel/ui/command/button/BtnNoopController.java b/src/main/java/com/getpcpanel/ui/command/button/BtnNoopController.java deleted file mode 100644 index 8c02b205..00000000 --- a/src/main/java/com/getpcpanel/ui/command/button/BtnNoopController.java +++ /dev/null @@ -1,36 +0,0 @@ -package com.getpcpanel.ui.command.button; - -import org.springframework.stereotype.Component; - -import com.getpcpanel.commands.command.Command; -import com.getpcpanel.commands.command.CommandNoOp; -import com.getpcpanel.spring.Prototype; -import com.getpcpanel.ui.command.ButtonCommandController; -import com.getpcpanel.ui.command.Cmd; -import com.getpcpanel.ui.command.CommandContext; -import com.getpcpanel.ui.command.CommandController; - -import javafx.beans.Observable; -import lombok.RequiredArgsConstructor; -import lombok.extern.log4j.Log4j2; - -@Log4j2 -@Component -@Prototype -@RequiredArgsConstructor -@Cmd(name = "No action", fxml = "Noop", cmds = CommandNoOp.class) -public class BtnNoopController extends CommandController implements ButtonCommandController { - @Override - public void postInit(CommandContext context) { - } - - @Override - public Command buildCommand() { - return CommandNoOp.NOOP; - } - - @Override - protected Observable[] determineDependencies() { - return new Observable[0]; - } -} diff --git a/src/main/java/com/getpcpanel/ui/command/button/BtnObsController.java b/src/main/java/com/getpcpanel/ui/command/button/BtnObsController.java deleted file mode 100644 index b69eeace..00000000 --- a/src/main/java/com/getpcpanel/ui/command/button/BtnObsController.java +++ /dev/null @@ -1,108 +0,0 @@ -package com.getpcpanel.ui.command.button; - -import static com.getpcpanel.commands.command.CommandNoOp.NOOP; - -import javax.annotation.Nullable; - -import org.springframework.stereotype.Component; - -import com.getpcpanel.commands.command.Command; -import com.getpcpanel.commands.command.CommandObs; -import com.getpcpanel.commands.command.CommandObsMuteSource; -import com.getpcpanel.commands.command.CommandObsMuteSource.MuteType; -import com.getpcpanel.commands.command.CommandObsSetScene; -import com.getpcpanel.obs.OBS; -import com.getpcpanel.spring.Prototype; -import com.getpcpanel.ui.command.ButtonCommandController; -import com.getpcpanel.ui.command.Cmd; -import com.getpcpanel.ui.command.CommandContext; -import com.getpcpanel.ui.command.CommandController; -import com.getpcpanel.ui.command.ObsEnabled; - -import javafx.beans.Observable; -import javafx.event.ActionEvent; -import javafx.fxml.FXML; -import javafx.scene.control.ChoiceBox; -import javafx.scene.control.RadioButton; -import javafx.scene.layout.Pane; -import lombok.RequiredArgsConstructor; -import lombok.extern.log4j.Log4j2; - -@Log4j2 -@Component -@Prototype -@RequiredArgsConstructor -@Cmd(name = "OBS", fxml = "Obs", cmds = { CommandObsSetScene.class, CommandObsMuteSource.class }, enabled = ObsEnabled.class) -public class BtnObsController extends CommandController implements ButtonCommandController { - private final OBS obs; - - @FXML private ChoiceBox obsSetScene; - @FXML private ChoiceBox obsSourceToMute; - @FXML private Pane obsPaneMuteSource; - @FXML private Pane obsPaneSetScene; - @FXML private RadioButton obsMuteMute; - @FXML private RadioButton obsMuteToggle; - @FXML private RadioButton obsMuteUnmute; - @FXML private RadioButton obs_rdio_MuteSource; - @FXML private RadioButton obs_rdio_SetScene; - - @Override - public void postInit(CommandContext context) { - if (obs.isConnected()) { - var sourcesWithAudio = obs.getSourcesWithAudio(); - var scenes = obs.getScenes(); - obsSourceToMute.getItems().addAll(sourcesWithAudio); - obsSetScene.getItems().addAll(scenes); - } - } - - @Override - public void initFromCommand(CommandObs cmd) { - if (cmd instanceof CommandObsMuteSource ms) { - obsSourceToMute.getItems().add(ms.getSource()); - } else if (cmd instanceof CommandObsSetScene ss) { - obsSetScene.getItems().add(ss.getScene()); - } - - if (cmd instanceof CommandObsSetScene ss) { - obs_rdio_SetScene.setSelected(true); - obsSetScene.getSelectionModel().select(ss.getScene()); - } else if (cmd instanceof CommandObsMuteSource ms) { - obs_rdio_MuteSource.setSelected(true); - obsSourceToMute.getSelectionModel().select(ms.getSource()); - switch (ms.getType()) { - case unmute -> obsMuteUnmute.setSelected(true); - case mute -> obsMuteMute.setSelected(true); - case toggle -> obsMuteToggle.setSelected(true); - } - } - onRadioButton(null); - super.initFromCommand(cmd); - } - - @Override - public Command buildCommand() { - if (obs_rdio_SetScene.isSelected()) { - return new CommandObsSetScene(obsSetScene.getSelectionModel().getSelectedItem()); - } else if (obs_rdio_MuteSource.isSelected()) { - return new CommandObsMuteSource(obsSourceToMute.getSelectionModel().getSelectedItem(), - obsMuteUnmute.isSelected() ? MuteType.unmute : obsMuteMute.isSelected() ? MuteType.mute : MuteType.toggle); - } else { - log.error("ERROR INVALID RADIO BUTTON IN BUTTON OBS"); - return NOOP; - } - } - - @Override - protected Observable[] determineDependencies() { - return new Observable[] { obsSetScene.getSelectionModel().selectedItemProperty(), - obsSourceToMute.getSelectionModel().selectedItemProperty(), - obsMuteMute.selectedProperty(), obsMuteToggle.selectedProperty(), obsMuteUnmute.selectedProperty(), obs_rdio_MuteSource.selectedProperty(), obs_rdio_SetScene.selectedProperty() }; - } - - @FXML - private void onRadioButton(@Nullable ActionEvent event) { - obsPaneSetScene.setDisable(!obs_rdio_SetScene.isSelected()); - obsPaneMuteSource.setDisable(!obs_rdio_MuteSource.isSelected()); - } -} diff --git a/src/main/java/com/getpcpanel/ui/command/button/BtnProfileController.java b/src/main/java/com/getpcpanel/ui/command/button/BtnProfileController.java deleted file mode 100644 index 2ec2d006..00000000 --- a/src/main/java/com/getpcpanel/ui/command/button/BtnProfileController.java +++ /dev/null @@ -1,55 +0,0 @@ -package com.getpcpanel.ui.command.button; - -import org.springframework.stereotype.Component; - -import com.getpcpanel.commands.command.Command; -import com.getpcpanel.commands.command.CommandProfile; -import com.getpcpanel.profile.DeviceSave; -import com.getpcpanel.profile.Profile; -import com.getpcpanel.spring.Prototype; -import com.getpcpanel.ui.command.ButtonCommandController; -import com.getpcpanel.ui.command.Cmd; -import com.getpcpanel.ui.command.CommandContext; -import com.getpcpanel.ui.command.CommandController; - -import javafx.beans.Observable; -import javafx.fxml.FXML; -import javafx.scene.control.ChoiceBox; -import lombok.RequiredArgsConstructor; -import lombok.extern.log4j.Log4j2; -import one.util.streamex.StreamEx; - -@Log4j2 -@Component -@Prototype -@RequiredArgsConstructor -@Cmd(name = "Profile", fxml = "Profile", cmds = CommandProfile.class) -public class BtnProfileController extends CommandController implements ButtonCommandController { - private DeviceSave deviceSave; - - @FXML private ChoiceBox profileDropdown; - - @Override - public void postInit(CommandContext context) { - deviceSave = context.deviceSave(); - - var curProfile = context.profile().getName(); - StreamEx.of(deviceSave.getProfiles()).removeBy(Profile::getName, curProfile).toListAndThen(profileDropdown.getItems()::addAll); - } - - @Override - public void initFromCommand(CommandProfile cmd) { - deviceSave.getProfile(cmd.getProfile()).ifPresent(profile -> profileDropdown.setValue(profile)); - super.initFromCommand(cmd); - } - - @Override - public Command buildCommand() { - return new CommandProfile(profileDropdown.getValue() == null ? null : profileDropdown.getValue().getName()); - } - - @Override - protected Observable[] determineDependencies() { - return new Observable[] { profileDropdown.valueProperty() }; - } -} diff --git a/src/main/java/com/getpcpanel/ui/command/button/BtnShortcutController.java b/src/main/java/com/getpcpanel/ui/command/button/BtnShortcutController.java deleted file mode 100644 index 9ff550d9..00000000 --- a/src/main/java/com/getpcpanel/ui/command/button/BtnShortcutController.java +++ /dev/null @@ -1,53 +0,0 @@ -package com.getpcpanel.ui.command.button; - -import org.springframework.stereotype.Component; - -import com.getpcpanel.commands.command.Command; -import com.getpcpanel.commands.command.CommandShortcut; -import com.getpcpanel.spring.Prototype; -import com.getpcpanel.ui.UIHelper; -import com.getpcpanel.ui.command.ButtonCommandController; -import com.getpcpanel.ui.command.Cmd; -import com.getpcpanel.ui.command.CommandContext; -import com.getpcpanel.ui.command.CommandController; - -import javafx.beans.Observable; -import javafx.event.ActionEvent; -import javafx.fxml.FXML; -import javafx.scene.control.TextField; -import lombok.RequiredArgsConstructor; -import lombok.extern.log4j.Log4j2; - -@Log4j2 -@Component -@Prototype -@RequiredArgsConstructor -@Cmd(name = "Shortcut", fxml = "Shortcut", cmds = CommandShortcut.class) -public class BtnShortcutController extends CommandController implements ButtonCommandController { - @FXML private TextField shortcutField; - - @Override - public void postInit(CommandContext context) { - } - - @Override - public void initFromCommand(CommandShortcut cmd) { - shortcutField.setText(cmd.getShortcut()); - super.initFromCommand(cmd); - } - - @Override - public Command buildCommand() { - return new CommandShortcut(shortcutField.getText()); - } - - @Override - protected Observable[] determineDependencies() { - return new Observable[] { shortcutField.textProperty() }; - } - - @FXML - private void scFile(ActionEvent event) { - UIHelper.showFilePicker("Pick file", shortcutField); - } -} diff --git a/src/main/java/com/getpcpanel/ui/command/button/BtnVoiceMeeterController.java b/src/main/java/com/getpcpanel/ui/command/button/BtnVoiceMeeterController.java deleted file mode 100644 index f86bf1cb..00000000 --- a/src/main/java/com/getpcpanel/ui/command/button/BtnVoiceMeeterController.java +++ /dev/null @@ -1,126 +0,0 @@ -package com.getpcpanel.ui.command.button; - -import static com.getpcpanel.commands.command.CommandNoOp.NOOP; - -import org.springframework.stereotype.Component; - -import com.getpcpanel.commands.command.Command; -import com.getpcpanel.commands.command.CommandVoiceMeeter; -import com.getpcpanel.commands.command.CommandVoiceMeeterAdvancedButton; -import com.getpcpanel.commands.command.CommandVoiceMeeterBasicButton; -import com.getpcpanel.spring.Prototype; -import com.getpcpanel.ui.command.ButtonCommandController; -import com.getpcpanel.ui.command.Cmd; -import com.getpcpanel.ui.command.CommandContext; -import com.getpcpanel.ui.command.CommandController; -import com.getpcpanel.ui.command.VoiceMeeterEnabled; -import com.getpcpanel.util.Util; -import com.getpcpanel.voicemeeter.Voicemeeter; -import com.getpcpanel.voicemeeter.Voicemeeter.ButtonControlMode; -import com.getpcpanel.voicemeeter.Voicemeeter.ButtonType; -import com.getpcpanel.voicemeeter.Voicemeeter.ControlType; - -import javafx.beans.Observable; -import javafx.fxml.FXML; -import javafx.scene.control.ChoiceBox; -import javafx.scene.control.Label; -import javafx.scene.control.TabPane; -import javafx.scene.control.TextField; -import lombok.RequiredArgsConstructor; -import lombok.extern.log4j.Log4j2; - -@Log4j2 -@Component -@Prototype -@RequiredArgsConstructor -@Cmd(name = "Voicemeeter", fxml = "VoiceMeeter", cmds = { CommandVoiceMeeterBasicButton.class, CommandVoiceMeeterAdvancedButton.class }, enabled = VoiceMeeterEnabled.class) -public class BtnVoiceMeeterController extends CommandController implements ButtonCommandController { - private final Voicemeeter voiceMeeter; - - @FXML private ChoiceBox voicemeeterBasicButtonIndex; - @FXML private ChoiceBox voicemeeterButtonType; - @FXML private ChoiceBox voicemeeterBasicButton; - @FXML private ChoiceBox voicemeeterBasicButtonIO; - @FXML private TabPane voicemeeterTabPaneButton; - @FXML private TextField voicemeeterButtonParameter; - @FXML private Label voicemeeterStringValueLabel; - @FXML private TextField voicemeeterStringValue; - - @Override - public void postInit(CommandContext context) { - voicemeeterButtonType.getItems().addAll(ButtonControlMode.values()); - if (voiceMeeter.login()) { - voicemeeterBasicButtonIO.getItems().addAll(ControlType.values()); - voicemeeterBasicButtonIO.valueProperty().addListener((o, oldVal, newVal) -> { - if (newVal == null) { - Util.clearAndSetNull(voicemeeterBasicButtonIndex); - return; - } - Util.changeItemsTo(voicemeeterBasicButtonIndex, Util.numToList(voiceMeeter.getNum(newVal)), true); - }); - voicemeeterBasicButtonIndex.valueProperty().addListener((o, oldVal, newVal) -> { - if (newVal == null) { - Util.clearAndSetNull(voicemeeterBasicButton); - return; - } - Util.changeItemsTo(voicemeeterBasicButton, - voiceMeeter.getButtonTypes(voicemeeterBasicButtonIO.getValue(), voicemeeterBasicButtonIndex.getValue() - 1)); - }); - voicemeeterBasicButtonIO.getSelectionModel().selectFirst(); - voicemeeterBasicButtonIndex.getSelectionModel().selectFirst(); - } - - voicemeeterButtonType.valueProperty().addListener((o, oldVal, newVal) -> { - var isString = newVal == ButtonControlMode.STRING; - voicemeeterStringValueLabel.setVisible(isString); - voicemeeterStringValue.setVisible(isString); - if (!isString) { - voicemeeterStringValue.setText(""); - } - }); - } - - @Override - public void initFromCommand(CommandVoiceMeeter cmd) { - if (cmd instanceof CommandVoiceMeeterBasicButton basic) { - if (!voiceMeeter.login()) { - voicemeeterBasicButtonIO.getItems().add(basic.getCt()); - voicemeeterBasicButtonIndex.getItems().add(basic.getIndex() + 1); - voicemeeterBasicButton.getItems().add(basic.getBt()); - } - - voicemeeterTabPaneButton.getSelectionModel().select(0); - voicemeeterBasicButtonIO.setValue(basic.getCt()); - voicemeeterBasicButtonIndex.setValue(basic.getIndex() + 1); - voicemeeterBasicButton.setValue(basic.getBt()); - } else if (cmd instanceof CommandVoiceMeeterAdvancedButton advanced) { - voicemeeterTabPaneButton.getSelectionModel().select(1); - voicemeeterButtonParameter.setText(advanced.getFullParam()); - voicemeeterButtonType.setValue(advanced.getBt()); - if (advanced.getBt() == ButtonControlMode.STRING) { - voicemeeterStringValue.setText(advanced.getStringValue()); - } - } - super.initFromCommand(cmd); - } - - @Override - public Command buildCommand() { - if (voicemeeterTabPaneButton.getSelectionModel().getSelectedIndex() == 0) { - return new CommandVoiceMeeterBasicButton(voicemeeterBasicButtonIO.getValue(), voicemeeterBasicButtonIndex.getValue() - 1, voicemeeterBasicButton.getValue()); - } else if (voicemeeterTabPaneButton.getSelectionModel().getSelectedIndex() == 1) { - if (voicemeeterButtonType.getValue() == null) { - return NOOP; - } - return new CommandVoiceMeeterAdvancedButton(voicemeeterButtonParameter.getText(), voicemeeterButtonType.getValue(), voicemeeterStringValue.getText()); - } - return NOOP; - } - - @Override - protected Observable[] determineDependencies() { - return new Observable[] { voicemeeterTabPaneButton.getSelectionModel().selectedIndexProperty(), - voicemeeterBasicButtonIO.valueProperty(), voicemeeterBasicButtonIndex.valueProperty(), voicemeeterBasicButton.valueProperty(), - voicemeeterButtonParameter.textProperty(), voicemeeterButtonType.valueProperty(), voicemeeterStringValue.textProperty() }; - } -} diff --git a/src/main/java/com/getpcpanel/ui/command/button/BtnVolumeProcessMuteController.java b/src/main/java/com/getpcpanel/ui/command/button/BtnVolumeProcessMuteController.java deleted file mode 100644 index e373cb55..00000000 --- a/src/main/java/com/getpcpanel/ui/command/button/BtnVolumeProcessMuteController.java +++ /dev/null @@ -1,63 +0,0 @@ -package com.getpcpanel.ui.command.button; - -import static com.getpcpanel.spring.OsHelper.WINDOWS; - -import java.util.HashSet; - -import org.springframework.stereotype.Component; - -import com.getpcpanel.commands.command.Command; -import com.getpcpanel.commands.command.CommandVolumeProcessMute; -import com.getpcpanel.cpp.MuteType; -import com.getpcpanel.spring.Prototype; -import com.getpcpanel.ui.PickProcessesController; -import com.getpcpanel.ui.PickProcessesController.PickType; -import com.getpcpanel.ui.command.ButtonCommandController; -import com.getpcpanel.ui.command.Cmd; -import com.getpcpanel.ui.command.CommandContext; -import com.getpcpanel.ui.command.CommandController; - -import javafx.beans.Observable; -import javafx.fxml.FXML; -import javafx.scene.control.RadioButton; -import lombok.RequiredArgsConstructor; -import lombok.extern.log4j.Log4j2; - -@Log4j2 -@Component -@Prototype -@RequiredArgsConstructor -@Cmd(name = "Mute App", fxml = "VolumeProcessMute", cmds = CommandVolumeProcessMute.class, os = WINDOWS) -public class BtnVolumeProcessMuteController extends CommandController implements ButtonCommandController { - @FXML private PickProcessesController appMuteController; - @FXML private RadioButton rdio_mute_mute; - @FXML private RadioButton rdio_mute_toggle; - @FXML private RadioButton rdio_mute_unmute; - - @Override - public void postInit(CommandContext context) { - appMuteController.setPickType(PickType.soundSource); - } - - @Override - public void initFromCommand(CommandVolumeProcessMute cmd) { - appMuteController.setSelection(cmd.getProcessName()); - switch (cmd.getMuteType()) { - case unmute -> rdio_mute_unmute.setSelected(true); - case mute -> rdio_mute_mute.setSelected(true); - case toggle -> rdio_mute_toggle.setSelected(true); - } - super.initFromCommand(cmd); - } - - @Override - public Command buildCommand() { - return new CommandVolumeProcessMute(new HashSet<>(appMuteController.getSelection()), - rdio_mute_unmute.isSelected() ? MuteType.unmute : rdio_mute_mute.isSelected() ? MuteType.mute : MuteType.toggle); - } - - @Override - protected Observable[] determineDependencies() { - return new Observable[] { rdio_mute_mute.selectedProperty(), rdio_mute_unmute.selectedProperty(), rdio_mute_toggle.selectedProperty(), appMuteController.getObservable() }; - } -} diff --git a/src/main/java/com/getpcpanel/ui/command/dial/DialBrightnessController.java b/src/main/java/com/getpcpanel/ui/command/dial/DialBrightnessController.java deleted file mode 100644 index db008e02..00000000 --- a/src/main/java/com/getpcpanel/ui/command/dial/DialBrightnessController.java +++ /dev/null @@ -1,38 +0,0 @@ -package com.getpcpanel.ui.command.dial; - -import org.springframework.stereotype.Component; - -import com.getpcpanel.commands.command.Command; -import com.getpcpanel.commands.command.CommandBrightness; -import com.getpcpanel.commands.command.DialAction.DialCommandParams; -import com.getpcpanel.spring.Prototype; -import com.getpcpanel.ui.command.Cmd; -import com.getpcpanel.ui.command.CommandContext; -import com.getpcpanel.ui.command.CommandController; -import com.getpcpanel.ui.command.DialCommandController; - -import javafx.beans.Observable; -import lombok.RequiredArgsConstructor; -import lombok.extern.log4j.Log4j2; - -@Log4j2 -@Component -@Prototype -@RequiredArgsConstructor -@Cmd(name = "Device Brightness", fxml = "Brightness", cmds = CommandBrightness.class) -public class DialBrightnessController extends CommandController implements DialCommandController { - - @Override - public void postInit(CommandContext context) { - } - - @Override - public Command buildCommand(DialCommandParams params) { - return new CommandBrightness(params); - } - - @Override - protected Observable[] determineDependencies() { - return new Observable[0]; - } -} diff --git a/src/main/java/com/getpcpanel/ui/command/dial/DialObsController.java b/src/main/java/com/getpcpanel/ui/command/dial/DialObsController.java deleted file mode 100644 index 78da589a..00000000 --- a/src/main/java/com/getpcpanel/ui/command/dial/DialObsController.java +++ /dev/null @@ -1,60 +0,0 @@ -package com.getpcpanel.ui.command.dial; - -import org.springframework.stereotype.Component; - -import com.getpcpanel.commands.command.Command; -import com.getpcpanel.commands.command.CommandObsSetSourceVolume; -import com.getpcpanel.commands.command.DialAction.DialCommandParams; -import com.getpcpanel.obs.OBS; -import com.getpcpanel.spring.Prototype; -import com.getpcpanel.ui.command.Cmd; -import com.getpcpanel.ui.command.CommandContext; -import com.getpcpanel.ui.command.CommandController; -import com.getpcpanel.ui.command.DialCommandController; -import com.getpcpanel.ui.command.ObsEnabled; - -import javafx.beans.Observable; -import javafx.fxml.FXML; -import javafx.scene.control.ChoiceBox; -import lombok.RequiredArgsConstructor; -import lombok.extern.log4j.Log4j2; - -@Log4j2 -@Component -@Prototype -@RequiredArgsConstructor -@Cmd(name = "OBS", fxml = "Obs", cmds = CommandObsSetSourceVolume.class, enabled = ObsEnabled.class) -public class DialObsController extends CommandController implements DialCommandController { - private final OBS obs; - - @FXML private ChoiceBox obsAudioSources; - - @Override - public void postInit(CommandContext context) { - if (obs.isConnected()) { - var sourcesWithAudio = obs.getSourcesWithAudio(); - obsAudioSources.getItems().addAll(sourcesWithAudio); - } - } - - @Override - public void initFromCommand(CommandObsSetSourceVolume cmd) { - if (!obs.isConnected()) { - obsAudioSources.getItems().add(cmd.getSourceName()); - } - obsAudioSources.getSelectionModel().select(cmd.getSourceName()); - super.initFromCommand(cmd); - } - - @Override - public Command buildCommand(DialCommandParams params) { - return new CommandObsSetSourceVolume(obsAudioSources.getSelectionModel().getSelectedItem(), params); - } - - @Override - protected Observable[] determineDependencies() { - return new Observable[] { - obsAudioSources.valueProperty() - }; - } -} diff --git a/src/main/java/com/getpcpanel/ui/command/dial/DialVoiceMeeterController.java b/src/main/java/com/getpcpanel/ui/command/dial/DialVoiceMeeterController.java deleted file mode 100644 index e6b20c0c..00000000 --- a/src/main/java/com/getpcpanel/ui/command/dial/DialVoiceMeeterController.java +++ /dev/null @@ -1,113 +0,0 @@ -package com.getpcpanel.ui.command.dial; - -import static com.getpcpanel.commands.command.CommandNoOp.NOOP; - -import org.springframework.stereotype.Component; - -import com.getpcpanel.commands.command.Command; -import com.getpcpanel.commands.command.CommandVoiceMeeter; -import com.getpcpanel.commands.command.CommandVoiceMeeterAdvanced; -import com.getpcpanel.commands.command.CommandVoiceMeeterBasic; -import com.getpcpanel.commands.command.DialAction.DialCommandParams; -import com.getpcpanel.spring.Prototype; -import com.getpcpanel.ui.command.Cmd; -import com.getpcpanel.ui.command.CommandContext; -import com.getpcpanel.ui.command.CommandController; -import com.getpcpanel.ui.command.DialCommandController; -import com.getpcpanel.ui.command.VoiceMeeterEnabled; -import com.getpcpanel.util.Util; -import com.getpcpanel.voicemeeter.Voicemeeter; -import com.getpcpanel.voicemeeter.Voicemeeter.ControlType; -import com.getpcpanel.voicemeeter.Voicemeeter.DialControlMode; -import com.getpcpanel.voicemeeter.Voicemeeter.DialType; - -import javafx.beans.Observable; -import javafx.fxml.FXML; -import javafx.scene.control.ChoiceBox; -import javafx.scene.control.TabPane; -import javafx.scene.control.TextField; -import lombok.RequiredArgsConstructor; -import lombok.extern.log4j.Log4j2; - -@Log4j2 -@Component -@Prototype -@RequiredArgsConstructor -@Cmd(name = "Voicemeeter", fxml = "VoiceMeeter", cmds = { CommandVoiceMeeterBasic.class, CommandVoiceMeeterAdvanced.class }, enabled = VoiceMeeterEnabled.class) -public class DialVoiceMeeterController extends CommandController implements DialCommandController { - private final Voicemeeter voiceMeeter; - @FXML private ChoiceBox voicemeeterBasicDialIndex; - @FXML private ChoiceBox voicemeeterBasicDialIO; - @FXML private ChoiceBox voicemeeterDialType; - @FXML private ChoiceBox voicemeeterBasicDial; - @FXML private TabPane voicemeeterTabPaneDial; - @FXML private TextField voicemeeterDialParameter; - - @Override - public void postInit(CommandContext context) { - voicemeeterDialType.getItems().addAll(DialControlMode.values()); - if (voiceMeeter.login()) { - voicemeeterBasicDialIO.getItems().addAll(ControlType.values()); - voicemeeterBasicDialIO.valueProperty().addListener((o, oldVal, newVal) -> { - if (newVal == null) { - Util.clearAndSetNull(voicemeeterBasicDialIndex); - return; - } - Util.changeItemsTo(voicemeeterBasicDialIndex, Util.numToList(voiceMeeter.getNum(newVal)), true); - }); - voicemeeterBasicDialIndex.valueProperty().addListener((o, oldVal, newVal) -> { - if (newVal == null) { - Util.clearAndSetNull(voicemeeterBasicDial); - return; - } - Util.changeItemsTo(voicemeeterBasicDial, - voiceMeeter.getDialTypes(voicemeeterBasicDialIO.getValue(), voicemeeterBasicDialIndex.getValue() - 1)); - }); - voicemeeterBasicDialIO.getSelectionModel().selectFirst(); - voicemeeterBasicDialIndex.getSelectionModel().selectFirst(); - } - } - - @Override - public void initFromCommand(CommandVoiceMeeter cmd) { - if (cmd instanceof CommandVoiceMeeterBasic cmdBasic) { - if (!voiceMeeter.login()) { - voicemeeterBasicDialIO.getItems().add(cmdBasic.getCt()); - voicemeeterBasicDialIndex.getItems().add(cmdBasic.getIndex() + 1); - voicemeeterBasicDial.getItems().add(cmdBasic.getDt()); - } - voicemeeterTabPaneDial.getSelectionModel().select(0); - voicemeeterBasicDialIO.setValue(cmdBasic.getCt()); - voicemeeterBasicDialIndex.setValue(cmdBasic.getIndex() + 1); - voicemeeterBasicDial.setValue(cmdBasic.getDt()); - } else if (cmd instanceof CommandVoiceMeeterAdvanced cmdAdvanced) { - voicemeeterTabPaneDial.getSelectionModel().select(1); - voicemeeterDialParameter.setText(cmdAdvanced.getFullParam()); - voicemeeterDialType.setValue(cmdAdvanced.getCt()); - } - super.initFromCommand(cmd); - } - - @Override - public Command buildCommand(DialCommandParams params) { - if (voicemeeterTabPaneDial.getSelectionModel().getSelectedIndex() == 0) { - return new CommandVoiceMeeterBasic(voicemeeterBasicDialIO.getValue(), voicemeeterBasicDialIndex.getValue() - 1, voicemeeterBasicDial.getValue(), params); - } - if (voicemeeterTabPaneDial.getSelectionModel().getSelectedIndex() == 1) { - return new CommandVoiceMeeterAdvanced(voicemeeterDialParameter.getText(), voicemeeterDialType.getValue(), params); - } - return NOOP; - } - - @Override - protected Observable[] determineDependencies() { - return new Observable[] { - voicemeeterTabPaneDial.getSelectionModel().selectedIndexProperty(), - voicemeeterBasicDialIndex.valueProperty(), - voicemeeterBasicDialIO.valueProperty(), - voicemeeterDialType.valueProperty(), - voicemeeterBasicDial.valueProperty(), - voicemeeterDialParameter.textProperty() - }; - } -} diff --git a/src/main/java/com/getpcpanel/ui/command/dial/DialVolumeDeviceController.java b/src/main/java/com/getpcpanel/ui/command/dial/DialVolumeDeviceController.java deleted file mode 100644 index a840c12e..00000000 --- a/src/main/java/com/getpcpanel/ui/command/dial/DialVolumeDeviceController.java +++ /dev/null @@ -1,89 +0,0 @@ -package com.getpcpanel.ui.command.dial; - -import java.util.Collection; - -import javax.annotation.Nullable; - -import org.apache.commons.lang3.StringUtils; -import org.springframework.stereotype.Component; - -import com.getpcpanel.commands.command.Command; -import com.getpcpanel.commands.command.CommandVolumeDevice; -import com.getpcpanel.commands.command.DialAction.DialCommandParams; -import com.getpcpanel.cpp.AudioDevice; -import com.getpcpanel.cpp.ISndCtrl; -import com.getpcpanel.spring.Prototype; -import com.getpcpanel.ui.command.Cmd; -import com.getpcpanel.ui.command.CommandContext; -import com.getpcpanel.ui.command.CommandController; -import com.getpcpanel.ui.command.DialCommandController; - -import javafx.beans.Observable; -import javafx.event.ActionEvent; -import javafx.fxml.FXML; -import javafx.scene.control.CheckBox; -import javafx.scene.control.ChoiceBox; -import javafx.scene.control.RadioButton; -import lombok.RequiredArgsConstructor; -import lombok.extern.log4j.Log4j2; -import one.util.streamex.StreamEx; - -@Log4j2 -@Component -@Prototype -@RequiredArgsConstructor -@Cmd(name = "Device Volume", fxml = "VolumeDevice", cmds = CommandVolumeDevice.class) -public class DialVolumeDeviceController extends CommandController implements DialCommandController { - private final ISndCtrl sndCtrl; - private Collection allSoundDevices; - @FXML private CheckBox cb_device_unmute; - @FXML private ChoiceBox volumedevice; - @FXML private RadioButton rdio_device_default; - @FXML private RadioButton rdio_device_specific; - - @Override - public void postInit(CommandContext context) { - allSoundDevices = sndCtrl.getDevices(); - volumedevice.getItems().addAll(allSoundDevices); - } - - @Override - public void initFromCommand(CommandVolumeDevice cmd) { - if (StringUtils.isNotBlank(cmd.getDeviceId())) { - rdio_device_specific.setSelected(true); - volumedevice.setValue(getSoundDeviceById(cmd.getDeviceId())); - } else { - rdio_device_default.setSelected(true); - } - cb_device_unmute.setSelected(cmd.isUnMuteOnVolumeChange()); - onRadioButton(null); - super.initFromCommand(cmd); - } - - @Override - public Command buildCommand(DialCommandParams params) { - return new CommandVolumeDevice( - rdio_device_specific.isSelected() && volumedevice.getSelectionModel().getSelectedItem() != null ? volumedevice.getSelectionModel().getSelectedItem().id() : "", - cb_device_unmute.isSelected(), params); - } - - @Override - protected Observable[] determineDependencies() { - return new Observable[] { - cb_device_unmute.selectedProperty(), - volumedevice.valueProperty(), - rdio_device_default.selectedProperty(), - rdio_device_specific.selectedProperty() - }; - } - - @FXML - private void onRadioButton(@Nullable ActionEvent event) { - volumedevice.setDisable(!rdio_device_specific.isSelected()); - } - - @Nullable - private AudioDevice getSoundDeviceById(String id) { - return StreamEx.of(allSoundDevices).findFirst(sd -> sd.id().equals(id)).orElse(null); - } -} diff --git a/src/main/java/com/getpcpanel/ui/command/dial/DialVolumeFocusController.java b/src/main/java/com/getpcpanel/ui/command/dial/DialVolumeFocusController.java deleted file mode 100644 index b2f0f7b5..00000000 --- a/src/main/java/com/getpcpanel/ui/command/dial/DialVolumeFocusController.java +++ /dev/null @@ -1,38 +0,0 @@ -package com.getpcpanel.ui.command.dial; - -import org.springframework.stereotype.Component; - -import com.getpcpanel.commands.command.Command; -import com.getpcpanel.commands.command.CommandVolumeFocus; -import com.getpcpanel.commands.command.DialAction.DialCommandParams; -import com.getpcpanel.spring.Prototype; -import com.getpcpanel.ui.command.Cmd; -import com.getpcpanel.ui.command.CommandContext; -import com.getpcpanel.ui.command.CommandController; -import com.getpcpanel.ui.command.DialCommandController; - -import javafx.beans.Observable; -import lombok.RequiredArgsConstructor; -import lombok.extern.log4j.Log4j2; - -@Log4j2 -@Component -@Prototype -@RequiredArgsConstructor -@Cmd(name = "Focus Volume", fxml = "VolumeFocus", cmds = CommandVolumeFocus.class) -public class DialVolumeFocusController extends CommandController implements DialCommandController { - - @Override - public void postInit(CommandContext context) { - } - - @Override - public Command buildCommand(DialCommandParams params) { - return new CommandVolumeFocus(params); - } - - @Override - protected Observable[] determineDependencies() { - return new Observable[0]; - } -} diff --git a/src/main/java/com/getpcpanel/ui/command/dial/DialVolumeProcessController.java b/src/main/java/com/getpcpanel/ui/command/dial/DialVolumeProcessController.java deleted file mode 100644 index 8686fbea..00000000 --- a/src/main/java/com/getpcpanel/ui/command/dial/DialVolumeProcessController.java +++ /dev/null @@ -1,106 +0,0 @@ -package com.getpcpanel.ui.command.dial; - -import java.util.Collection; -import java.util.Optional; - -import javax.annotation.Nullable; - -import org.apache.commons.lang3.StringUtils; -import org.springframework.stereotype.Component; - -import com.getpcpanel.commands.command.Command; -import com.getpcpanel.commands.command.CommandVolumeProcess; -import com.getpcpanel.commands.command.DialAction.DialCommandParams; -import com.getpcpanel.cpp.AudioDevice; -import com.getpcpanel.cpp.ISndCtrl; -import com.getpcpanel.spring.Prototype; -import com.getpcpanel.ui.PickProcessesController; -import com.getpcpanel.ui.PickProcessesController.PickType; -import com.getpcpanel.ui.command.Cmd; -import com.getpcpanel.ui.command.CommandContext; -import com.getpcpanel.ui.command.CommandController; -import com.getpcpanel.ui.command.DialCommandController; - -import javafx.beans.Observable; -import javafx.event.ActionEvent; -import javafx.fxml.FXML; -import javafx.scene.control.CheckBox; -import javafx.scene.control.ChoiceBox; -import javafx.scene.control.RadioButton; -import lombok.RequiredArgsConstructor; -import lombok.extern.log4j.Log4j2; -import one.util.streamex.StreamEx; - -@Log4j2 -@Component -@Prototype -@RequiredArgsConstructor -@Cmd(name = "App Volume", fxml = "VolumeProcess", cmds = CommandVolumeProcess.class) -public class DialVolumeProcessController extends CommandController implements DialCommandController { - private final ISndCtrl sndCtrl; - private Collection allSoundDevices; - - @FXML private CheckBox cb_app_unmute; - @FXML private ChoiceBox app_vol_output_device; - @FXML private PickProcessesController appVolumeController; - @FXML private RadioButton rdio_app_output_all; - @FXML private RadioButton rdio_app_output_default; - @FXML private RadioButton rdio_app_output_specific; - - @Override - public void postInit(CommandContext context) { - appVolumeController.setPickType(PickType.soundSource); - - allSoundDevices = sndCtrl.getDevices(); - var outputDevices = allSoundDevices.stream().filter(AudioDevice::isOutput).toList(); - app_vol_output_device.getItems().addAll(outputDevices); - } - - @Override - public void initFromCommand(CommandVolumeProcess cmd) { - appVolumeController.setSelection(cmd.getProcessName()); - cb_app_unmute.setSelected(cmd.isUnMuteOnVolumeChange()); - - if (StringUtils.equals(cmd.getDevice(), "*")) { - rdio_app_output_all.setSelected(true); - } else if (StringUtils.isNotBlank(cmd.getDevice())) { - rdio_app_output_specific.setSelected(true); - app_vol_output_device.setValue(getSoundDeviceById(cmd.getDevice())); - } else { - rdio_app_output_default.setSelected(true); - } - onRadioButton(null); - super.initFromCommand(cmd); - } - - @Override - public Command buildCommand(DialCommandParams params) { - var device = - rdio_app_output_all.isSelected() ? "*" : - rdio_app_output_specific.isSelected() ? Optional.ofNullable(app_vol_output_device.getSelectionModel().getSelectedItem()).map(AudioDevice::id).orElse("") : - ""; - return new CommandVolumeProcess(appVolumeController.getSelection(), device, cb_app_unmute.isSelected(), params); - } - - @Override - protected Observable[] determineDependencies() { - return new Observable[] { - cb_app_unmute.selectedProperty(), - app_vol_output_device.valueProperty(), - appVolumeController.getObservable(), - rdio_app_output_all.selectedProperty(), - rdio_app_output_default.selectedProperty(), - rdio_app_output_specific.selectedProperty() - }; - } - - @Nullable - private AudioDevice getSoundDeviceById(String id) { - return StreamEx.of(allSoundDevices).findFirst(sd -> sd.id().equals(id)).orElse(null); - } - - @FXML - private void onRadioButton(@Nullable ActionEvent event) { - app_vol_output_device.setDisable(!rdio_app_output_specific.isSelected()); - } -} diff --git a/src/main/java/com/getpcpanel/ui/graphviewer/GraphViewer.java b/src/main/java/com/getpcpanel/ui/graphviewer/GraphViewer.java deleted file mode 100644 index 44706042..00000000 --- a/src/main/java/com/getpcpanel/ui/graphviewer/GraphViewer.java +++ /dev/null @@ -1,59 +0,0 @@ -package com.getpcpanel.ui.graphviewer; - -import com.getpcpanel.commands.command.Command; -import com.getpcpanel.hid.DialValueCalculator; -import com.getpcpanel.util.Util; - -import javafx.geometry.Insets; -import javafx.scene.control.Button; -import javafx.scene.paint.Color; -import javafx.scene.shape.LineTo; -import javafx.scene.shape.MoveTo; -import javafx.scene.shape.Path; -import lombok.Setter; - -@Setter -public class GraphViewer extends Button { - private static final int DATA_POINTS = 255; - - private final Path path = new Path(); - private DialValueCalculator calculator; - private Command cmd; - - public GraphViewer(DialValueCalculator calculator, Command cmd) { - this.calculator = calculator; - this.cmd = cmd; - - setStyle(""" - -fx-background-color: transparent; - -fx-border-color: #232428; - -fx-border-width: 1px; - """); - - path.setStroke(Color.SILVER); - setGraphic(path); - setPadding(Insets.EMPTY); - redraw(); - } - - @Override - public void setPrefSize(double prefWidth, double prefHeight) { - super.setPrefSize(prefWidth, prefHeight); - redraw(); - } - - public void redraw() { - path.getElements().clear(); - - for (var i = 0; i < DATA_POINTS; i++) { - var x = Util.map(i, 0, DATA_POINTS, 0, getPrefWidth()); - var y = 100 - Util.map(calculator.calcValue(cmd, i, 0F, 100F), 0, 100, 0, getPrefHeight()); - - if (i == 0) { - path.getElements().add(new MoveTo(x,y)); - } else { - path.getElements().add(new LineTo(x,y)); - } - } - } -} diff --git a/src/main/java/com/getpcpanel/util/Debouncer.java b/src/main/java/com/getpcpanel/util/Debouncer.java index 40380ed8..918508ba 100644 --- a/src/main/java/com/getpcpanel/util/Debouncer.java +++ b/src/main/java/com/getpcpanel/util/Debouncer.java @@ -9,7 +9,7 @@ import java.util.function.BiFunction; import java.util.function.Function; -import org.springframework.stereotype.Service; +import jakarta.enterprise.context.ApplicationScoped; import io.reactivex.Observable; import io.reactivex.disposables.Disposable; @@ -17,7 +17,7 @@ import io.reactivex.subjects.PublishSubject; import jakarta.annotation.PreDestroy; -@Service +@ApplicationScoped public class Debouncer { private final ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor(); private final ExecutorScheduler rxScheduler = new ExecutorScheduler(scheduler, false); diff --git a/src/main/java/com/getpcpanel/util/ExtractUtil.java b/src/main/java/com/getpcpanel/util/ExtractUtil.java index 38e1f738..3283ad40 100644 --- a/src/main/java/com/getpcpanel/util/ExtractUtil.java +++ b/src/main/java/com/getpcpanel/util/ExtractUtil.java @@ -6,16 +6,16 @@ import org.apache.commons.io.FileUtils; import org.apache.commons.io.FilenameUtils; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.stereotype.Service; +import org.eclipse.microprofile.config.inject.ConfigProperty; +import jakarta.enterprise.context.ApplicationScoped; -import lombok.extern.log4j.Log4j2; +import lombok.extern.jbosslog.JBossLog; -@Log4j2 -@Service +@JBossLog +@ApplicationScoped public class ExtractUtil { - @Value("${application.version}") private String version; - @Value("${application.build}") private String build; + @ConfigProperty(name="pcpanel.version") private String version; + @ConfigProperty(name="pcpanel.build") private String build; public File extractAndDeleteOnExit(String file) { var name = FilenameUtils.getBaseName(file); diff --git a/src/main/java/com/getpcpanel/util/FileChecker.java b/src/main/java/com/getpcpanel/util/FileChecker.java index 5cb3ff6c..1c341d69 100644 --- a/src/main/java/com/getpcpanel/util/FileChecker.java +++ b/src/main/java/com/getpcpanel/util/FileChecker.java @@ -10,9 +10,9 @@ import java.nio.file.WatchService; import java.util.concurrent.atomic.AtomicBoolean; -import lombok.extern.log4j.Log4j2; +import lombok.extern.jbosslog.JBossLog; -@Log4j2 +@JBossLog public class FileChecker extends Thread { @SuppressWarnings("AccessOfSystemProperties") public static final File FILES_ROOT = new File(System.getProperty("user.home"), ".pcpanel"); // This is not a bean so don't configure the root, just pick the default private static final File REOPEN_FILE = new File(FILES_ROOT, "reopen.txt"); diff --git a/src/main/java/com/getpcpanel/util/FileUtil.java b/src/main/java/com/getpcpanel/util/FileUtil.java index a9458d7f..de234c9f 100644 --- a/src/main/java/com/getpcpanel/util/FileUtil.java +++ b/src/main/java/com/getpcpanel/util/FileUtil.java @@ -2,18 +2,53 @@ import java.io.File; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.stereotype.Service; +import org.eclipse.microprofile.config.inject.ConfigProperty; + +import jakarta.annotation.PostConstruct; +import jakarta.enterprise.context.ApplicationScoped; +import lombok.extern.jbosslog.JBossLog; + +@JBossLog +@ApplicationScoped +public class FileUtil { + @ConfigProperty(name = "pcpanel.root") + String rootPath; + + private File root; + + @PostConstruct + void ensureRoot() { + root = new File(rootPath); + log.info("Using root: " + root); + if (!root.exists() && !root.mkdirs()) { + log.error("Unable to create file root: " + root); + } + } + + public File getFile(String file) { + return new File(root, file); + } + + public File getRoot() { + return root; + } +} + + +import java.io.File; + +import org.eclipse.microprofile.config.inject.ConfigProperty; +import jakarta.enterprise.context.ApplicationScoped; import jakarta.annotation.PostConstruct; import lombok.RequiredArgsConstructor; -import lombok.extern.log4j.Log4j2; +import lombok.extern.jbosslog.JBossLog; -@Log4j2 -@Service +@JBossLog +@ApplicationScoped @RequiredArgsConstructor public class FileUtil { - @Value("${application.root}") private final File root; + @ConfigProperty(name="pcpanel.root") private final File root; @PostConstruct void ensureRoot() { diff --git a/src/main/java/com/getpcpanel/util/IPlatformCommand.java b/src/main/java/com/getpcpanel/util/IPlatformCommand.java index 62fbf064..578fb2dc 100644 --- a/src/main/java/com/getpcpanel/util/IPlatformCommand.java +++ b/src/main/java/com/getpcpanel/util/IPlatformCommand.java @@ -3,7 +3,7 @@ import java.io.File; import java.io.IOException; -import org.springframework.stereotype.Service; +import jakarta.enterprise.context.ApplicationScoped; import com.getpcpanel.cpp.ISndCtrl; import com.getpcpanel.cpp.linux.LinuxProcessHelper; @@ -11,9 +11,9 @@ import com.getpcpanel.spring.ConditionalOnWindows; import lombok.RequiredArgsConstructor; -import lombok.extern.log4j.Log4j2; +import lombok.extern.jbosslog.JBossLog; -@Log4j2 +@JBossLog public abstract class IPlatformCommand { public static final String FOCUS = "FOCUS"; protected static final Runtime rt = Runtime.getRuntime(); @@ -22,8 +22,8 @@ public abstract class IPlatformCommand { public abstract void kill(String process); - @Service - @ConditionalOnLinux + @ApplicationScoped + @LinuxImpl @RequiredArgsConstructor public static class LinuxPlatformCommand extends IPlatformCommand { private final LinuxProcessHelper processHelper; @@ -56,8 +56,8 @@ public void kill(String process) { } } - @Service - @ConditionalOnWindows + @ApplicationScoped + @WindowsImpl @RequiredArgsConstructor public static class WindowsPlatformCommand extends IPlatformCommand { private final ISndCtrl sndCtrl; diff --git a/src/main/java/com/getpcpanel/util/Images.java b/src/main/java/com/getpcpanel/util/Images.java index 0af359a7..44d1a223 100644 --- a/src/main/java/com/getpcpanel/util/Images.java +++ b/src/main/java/com/getpcpanel/util/Images.java @@ -10,9 +10,9 @@ import javafx.scene.image.Image; import javafx.scene.paint.Color; import javafx.scene.shape.SVGPath; -import lombok.extern.log4j.Log4j2; +import lombok.extern.jbosslog.JBossLog; -@Log4j2 +@JBossLog public abstract class Images { private static final Pattern PATH_RE = Pattern.compile("path d=\"(.*?)\""); public static final Image lighting = new Image(Objects.requireNonNull(Images.class.getResource("/assets/lighting.png")).toExternalForm()); diff --git a/src/main/java/com/getpcpanel/util/ProcessHelper.java b/src/main/java/com/getpcpanel/util/ProcessHelper.java index 602cbf59..4e7d86f0 100644 --- a/src/main/java/com/getpcpanel/util/ProcessHelper.java +++ b/src/main/java/com/getpcpanel/util/ProcessHelper.java @@ -1,8 +1,8 @@ package com.getpcpanel.util; -import org.springframework.stereotype.Service; +import jakarta.enterprise.context.ApplicationScoped; -@Service +@ApplicationScoped public class ProcessHelper { public ProcessBuilder builder(String... command) { var result = new ProcessBuilder(command); diff --git a/src/main/java/com/getpcpanel/util/ShortcutHook.java b/src/main/java/com/getpcpanel/util/ShortcutHook.java index 9c122807..16c5dfc1 100644 --- a/src/main/java/com/getpcpanel/util/ShortcutHook.java +++ b/src/main/java/com/getpcpanel/util/ShortcutHook.java @@ -6,9 +6,7 @@ import java.util.function.Function; import org.apache.commons.lang3.StringUtils; -import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; -import org.springframework.context.event.EventListener; -import org.springframework.stereotype.Service; +import jakarta.enterprise.context.ApplicationScoped; import com.getpcpanel.hid.DeviceHolder; import com.getpcpanel.profile.Profile; @@ -25,14 +23,14 @@ import javafx.application.Platform; import lombok.RequiredArgsConstructor; import lombok.Setter; -import lombok.extern.log4j.Log4j2; +import lombok.extern.jbosslog.JBossLog; import one.util.streamex.EntryStream; -@Log4j2 -@Service -@ConditionalOnWindows +@JBossLog +@ApplicationScoped +@WindowsImpl @RequiredArgsConstructor -@ConditionalOnProperty(matchIfMissing = true, value = "pcpanel.shortcut-hook", havingValue = "true") + public class ShortcutHook implements NativeKeyListener { public static final Set modifiers = Set.of(NativeKeyEvent.VC_SHIFT, NativeKeyEvent.VC_CONTROL, NativeKeyEvent.VC_META, NativeKeyEvent.VC_ALT); private final SaveService saveService; @@ -74,8 +72,7 @@ public String toKeyString(NativeKeyEvent event) { return String.join("+", modifiers, key); } - @EventListener(SaveService.SaveEvent.class) - public void updateShortcuts() { + public void updateShortcuts() { shortcuts = EntryStream.of(saveService.get().getDevices()) .flatMapValues(ds -> ds.getProfiles().stream()) .filterValues(p -> StringUtils.isNotBlank(p.getActivationShortcut())) diff --git a/src/main/java/com/getpcpanel/util/Util.java b/src/main/java/com/getpcpanel/util/Util.java index ec326310..92321f07 100644 --- a/src/main/java/com/getpcpanel/util/Util.java +++ b/src/main/java/com/getpcpanel/util/Util.java @@ -20,10 +20,10 @@ import javafx.scene.control.TabPane; import javafx.scene.layout.StackPane; import javafx.scene.paint.Color; -import lombok.extern.log4j.Log4j2; +import lombok.extern.jbosslog.JBossLog; @SuppressWarnings("ALL") -@Log4j2 +@JBossLog public final class Util { private static final Set executables = Set.of("bat", "bin", "cmd", "com", "cpl", "exe", "gadget", "inf1", "ins", "inx", "isu", "job", "jse", "lnk", "msc", "msi", "msp", "mst", "paf", "pif", "ps1", "reg", "rgs", "scr", "sct", "shb", "shs", "u3p", "vb", "vbe", "vbs", "vbscript", "ws", "wsf", "wsh"); diff --git a/src/main/java/com/getpcpanel/util/coloroverride/OverrideColorService.java b/src/main/java/com/getpcpanel/util/coloroverride/OverrideColorService.java index 3ffefda3..5609b515 100644 --- a/src/main/java/com/getpcpanel/util/coloroverride/OverrideColorService.java +++ b/src/main/java/com/getpcpanel/util/coloroverride/OverrideColorService.java @@ -3,9 +3,8 @@ import java.util.List; import java.util.Optional; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.context.annotation.Lazy; -import org.springframework.stereotype.Service; +import jakarta.inject.Inject; +import jakarta.enterprise.context.ApplicationScoped; import com.getpcpanel.profile.SingleKnobLightingConfig; import com.getpcpanel.profile.SingleLogoLightingConfig; @@ -17,10 +16,9 @@ import one.util.streamex.StreamEx; @Setter -@Service -@RequiredArgsConstructor +@ApplicationScoped public class OverrideColorService { - @Autowired @Lazy private List overriders; + @Inject @Lazy private List overriders; public Optional getDialOverride(String deviceSerial, int dial) { return StreamEx.of(overriders).mapPartial(p -> p.getDialOverride(deviceSerial, dial)).findFirst(); diff --git a/src/main/java/com/getpcpanel/util/tray/ITrayService.java b/src/main/java/com/getpcpanel/util/tray/ITrayService.java new file mode 100644 index 00000000..454d65ec --- /dev/null +++ b/src/main/java/com/getpcpanel/util/tray/ITrayService.java @@ -0,0 +1,11 @@ +package com.getpcpanel.util.tray; + +public interface ITrayService { + void init(); + + class NoOp implements ITrayService { + @Override + public void init() { + } + } +} diff --git a/src/main/java/com/getpcpanel/util/tray/awt/AwtTrayImpl.java b/src/main/java/com/getpcpanel/util/tray/awt/AwtTrayImpl.java new file mode 100644 index 00000000..0af7898d --- /dev/null +++ b/src/main/java/com/getpcpanel/util/tray/awt/AwtTrayImpl.java @@ -0,0 +1,20 @@ +package com.getpcpanel.util.tray.awt; + +import static java.lang.annotation.ElementType.FIELD; +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.ElementType.PARAMETER; +import static java.lang.annotation.ElementType.TYPE; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +import jakarta.inject.Qualifier; + +@Qualifier +@Documented +@Retention(RUNTIME) +@Target({TYPE, METHOD, FIELD, PARAMETER}) +public @interface AwtTrayImpl { +} diff --git a/src/main/java/com/getpcpanel/util/tray/awt/ConditionalOnAwtTray.java b/src/main/java/com/getpcpanel/util/tray/awt/ConditionalOnAwtTray.java deleted file mode 100644 index a190ac6b..00000000 --- a/src/main/java/com/getpcpanel/util/tray/awt/ConditionalOnAwtTray.java +++ /dev/null @@ -1,45 +0,0 @@ -package com.getpcpanel.util.tray.awt; - -import java.lang.annotation.Documented; -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -import javax.annotation.Nonnull; - -import org.apache.commons.lang3.SystemUtils; -import org.springframework.context.annotation.Condition; -import org.springframework.context.annotation.ConditionContext; -import org.springframework.context.annotation.Conditional; -import org.springframework.core.type.AnnotatedTypeMetadata; - -/** - * Condition that matches when AWT SystemTray is expected to work: - * Windows (always) or Linux with X11 (not Wayland). - */ -@Documented -@Retention(RetentionPolicy.RUNTIME) -@Target(ElementType.TYPE) -@Conditional(ConditionalOnAwtTray.OnAwtTrayCondition.class) -@interface ConditionalOnAwtTray { - class OnAwtTrayCondition implements Condition { - private static final ConditionalOnX11.OnX11Condition X11_CONDITION = new ConditionalOnX11.OnX11Condition(); - - @Override - public boolean matches(@Nonnull ConditionContext context, @Nonnull AnnotatedTypeMetadata metadata) { - // Windows always supports AWT tray - if (SystemUtils.IS_OS_WINDOWS) { - return true; - } - - // Linux only supports AWT tray on X11, not Wayland - if (SystemUtils.IS_OS_LINUX) { - return X11_CONDITION.matches(context, metadata); - } - - // macOS and others - let AWT try (may or may not work) - return true; - } - } -} diff --git a/src/main/java/com/getpcpanel/util/tray/awt/ConditionalOnX11.java b/src/main/java/com/getpcpanel/util/tray/awt/ConditionalOnX11.java deleted file mode 100644 index ec9fc568..00000000 --- a/src/main/java/com/getpcpanel/util/tray/awt/ConditionalOnX11.java +++ /dev/null @@ -1,40 +0,0 @@ -package com.getpcpanel.util.tray.awt; - -import java.lang.annotation.Documented; -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -import javax.annotation.Nonnull; - -import org.apache.commons.lang3.StringUtils; -import org.springframework.context.annotation.Condition; -import org.springframework.context.annotation.ConditionContext; -import org.springframework.context.annotation.Conditional; -import org.springframework.core.type.AnnotatedTypeMetadata; - -@Documented -@Retention(RetentionPolicy.RUNTIME) -@Target({ ElementType.TYPE, ElementType.METHOD }) -@Conditional(ConditionalOnX11.OnX11Condition.class) -@interface ConditionalOnX11 { - class OnX11Condition implements Condition { - @Override - public boolean matches(@Nonnull ConditionContext context, @Nonnull AnnotatedTypeMetadata metadata) { - var env = context.getEnvironment(); - var sessionType = env.getProperty("XDG_SESSION_TYPE"); - var waylandDisplay = env.getProperty("WAYLAND_DISPLAY"); - var display = env.getProperty("DISPLAY"); - - // Explicit X11 session - if ("x11".equalsIgnoreCase(sessionType)) { - return true; - } - - // DISPLAY set but not Wayland (legacy X11 detection) - return StringUtils.isNotBlank(display) && StringUtils.isBlank(waylandDisplay) - && !"wayland".equalsIgnoreCase(sessionType); - } - } -} diff --git a/src/main/java/com/getpcpanel/util/tray/awt/TrayServiceAwt.java b/src/main/java/com/getpcpanel/util/tray/awt/TrayServiceAwt.java index 1c8c4431..614cce3a 100644 --- a/src/main/java/com/getpcpanel/util/tray/awt/TrayServiceAwt.java +++ b/src/main/java/com/getpcpanel/util/tray/awt/TrayServiceAwt.java @@ -10,27 +10,113 @@ import javax.imageio.ImageIO; -import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; -import org.springframework.context.ApplicationEventPublisher; -import org.springframework.stereotype.Service; +import com.getpcpanel.util.tray.ITrayService; + +import jakarta.annotation.PostConstruct; +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.enterprise.event.Event; +import jakarta.inject.Inject; +import lombok.extern.jbosslog.JBossLog; + +/** + * AWT-based system tray implementation for Windows and X11 Linux. + * Uses java.awt.SystemTray which relies on the XEmbed protocol on Linux. + */ +@JBossLog +@ApplicationScoped +@AwtTrayImpl +public class TrayServiceAwt implements ITrayService { + @Inject Event eventBus; + + @Override + @PostConstruct + public void init() { + var popup = new PopupMenu(); + TrayIcon trayIcon; + SystemTray tray; + try { + tray = SystemTray.getSystemTray(); + var trayIconImage = ImageIO.read(Objects.requireNonNull(TrayServiceAwt.class.getResource("/assets/32x32.png"))); + var trayIconWidth = new TrayIcon(trayIconImage).getSize().width; + trayIcon = new TrayIcon(trayIconImage.getScaledInstance(trayIconWidth, -1, 4)); + } catch (UnsupportedOperationException e) { + log.warn("Tray icon is not supported"); + return; + } catch (Exception e1) { + log.error("Unable to initialize tray icon", e1); + return; + } + var exitItem = new MenuItem("Exit"); + exitItem.addActionListener(e -> System.exit(0)); + trayIcon.addMouseListener(new MouseListener() { + @Override + public void mouseReleased(MouseEvent e) { + } + + @Override + public void mousePressed(MouseEvent e) { + } + + @Override + public void mouseExited(MouseEvent e) { + } + + @Override + public void mouseEntered(MouseEvent e) { + } + + @Override + public void mouseClicked(MouseEvent e) { + if (e.getButton() == 1) + eventBus.fire(new ShowMainEvent()); + } + }); + popup.add(exitItem); + trayIcon.setToolTip("PCPanel"); + trayIcon.setPopupMenu(popup); + try { + tray.add(trayIcon); + } catch (Exception e) { + log.error("TrayIcon could not be added.", e); + } + } + + public record ShowMainEvent() { + } +} + + +import java.awt.MenuItem; +import java.awt.PopupMenu; +import java.awt.SystemTray; +import java.awt.TrayIcon; +import java.awt.event.MouseEvent; +import java.awt.event.MouseListener; +import java.util.Objects; + +import javax.imageio.ImageIO; + +import jakarta.enterprise.event.Event; +import jakarta.inject.Inject; +import jakarta.enterprise.context.ApplicationScoped; import com.getpcpanel.ui.HomePage; import jakarta.annotation.PostConstruct; import lombok.RequiredArgsConstructor; -import lombok.extern.log4j.Log4j2; +import lombok.extern.jbosslog.JBossLog; /** * AWT-based system tray implementation for Windows and X11 Linux. * Uses java.awt.SystemTray which relies on the XEmbed protocol on Linux. */ -@Log4j2 -@Service -@RequiredArgsConstructor -@ConditionalOnProperty(value = "disable.tray", havingValue = "false", matchIfMissing = true) -@ConditionalOnAwtTray +@JBossLog +@ApplicationScoped + +@AwtTrayImpl class TrayServiceAwt { - private final ApplicationEventPublisher eventPublisher; + @Inject + Event eventBus; @PostConstruct public void init() { @@ -71,7 +157,7 @@ public void mouseEntered(MouseEvent e) { @Override public void mouseClicked(MouseEvent e) { if (e.getButton() == 1) - eventPublisher.publishEvent(new HomePage.ShowMainEvent()); + eventBus.fire(new HomePage.ShowMainEvent()); } }); popup.add(exitItem); diff --git a/src/main/java/com/getpcpanel/util/tray/wayland/StatusNotifierItemImpl.java b/src/main/java/com/getpcpanel/util/tray/wayland/StatusNotifierItemImpl.java index 541c47b8..7392433c 100644 --- a/src/main/java/com/getpcpanel/util/tray/wayland/StatusNotifierItemImpl.java +++ b/src/main/java/com/getpcpanel/util/tray/wayland/StatusNotifierItemImpl.java @@ -5,7 +5,8 @@ import java.util.Objects; import org.freedesktop.dbus.annotations.DBusInterfaceName; -import org.springframework.context.ApplicationEventPublisher; +import jakarta.enterprise.event.Event; +import jakarta.inject.Inject; import com.getpcpanel.ui.HomePage; @@ -13,20 +14,21 @@ import javafx.scene.control.Alert; import javafx.scene.control.ButtonType; import lombok.RequiredArgsConstructor; -import lombok.extern.log4j.Log4j2; +import lombok.extern.jbosslog.JBossLog; -@Log4j2 +@JBossLog @RequiredArgsConstructor @DBusInterfaceName(SNI_BUS_NAME) public class StatusNotifierItemImpl implements StatusNotifierItem { - private final ApplicationEventPublisher eventPublisher; + @Inject + Event eventBus; // @Override @Override public void Activate(int x, int y) { log.debug("Tray Activate (left-click) at {},{}", x, y); Platform.runLater(() -> - eventPublisher.publishEvent(new HomePage.ShowMainEvent()) + eventBus.fire(new HomePage.ShowMainEvent()) ); } diff --git a/src/main/java/com/getpcpanel/util/tray/wayland/TrayServiceWayland.java b/src/main/java/com/getpcpanel/util/tray/wayland/TrayServiceWayland.java index c8000be7..f1980c93 100644 --- a/src/main/java/com/getpcpanel/util/tray/wayland/TrayServiceWayland.java +++ b/src/main/java/com/getpcpanel/util/tray/wayland/TrayServiceWayland.java @@ -4,28 +4,108 @@ import org.freedesktop.dbus.connections.impl.DBusConnectionBuilder; import org.freedesktop.dbus.exceptions.DBusException; import org.freedesktop.dbus.interfaces.DBusInterface; -import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; -import org.springframework.context.ApplicationEventPublisher; -import org.springframework.stereotype.Service; + +import com.getpcpanel.spring.WaylandImpl; +import com.getpcpanel.util.tray.ITrayService; + +import jakarta.annotation.Nonnull; +import jakarta.annotation.PostConstruct; +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.enterprise.event.Event; +import jakarta.inject.Inject; +import lombok.extern.jbosslog.JBossLog; + +@JBossLog +@ApplicationScoped +@WaylandImpl +public class TrayServiceWayland implements ITrayService { + static final String SNI_BUS_NAME = "org.kde.StatusNotifierItem"; + static final String WATCHER_BUS_NAME = "org.kde.StatusNotifierWatcher"; + static final String WATCHER_OBJECT_PATH = "/StatusNotifierWatcher"; + + @Inject Event eventBus; + private DBusConnection connection; + + @Override + @PostConstruct + public void init() { + try { + connection = DBusConnectionBuilder.forSessionBus().build(); + registerIcon(); + log.debug("Wayland tray initialized"); + } catch (DBusException e) { + log.warn("D-Bus connection failed: {}", e.getMessage()); + } catch (Exception e) { + log.error("Failed to initialize Wayland tray", e); + } + } + + private void registerIcon() throws DBusException { + DBusInterface menuBarObject = () -> "/MenuBar"; + var statusNotifierItem = new StatusNotifierItemImpl(eventBus); + + var wellKnownName = requestSniBus(1); + connection.exportObject("/MenuBar", menuBarObject); + connection.exportObject(statusNotifierItem); + registerWithWatcher(wellKnownName); + } + + private @Nonnull String requestSniBus(int id) { + var wkn = SNI_BUS_NAME; + try { + wkn += "-" + ProcessHandle.current().pid() + "-" + id; + connection.requestBusName(wkn); + return wkn; + } catch (DBusException e) { + log.error("Could not request well-known bus name", e); + return "error"; + } + } + + private void registerWithWatcher(String wellKnownName) { + try { + getStatusNotifierWatcher().RegisterStatusNotifierItem(wellKnownName); + } catch (DBusException e) { + log.warn("StatusNotifierWatcher not available (Wayland tray may not work): {}", e.getMessage()); + } + } + + private StatusNotifierWatcher getStatusNotifierWatcher() throws DBusException { + return connection.getRemoteObject( + WATCHER_BUS_NAME, + WATCHER_OBJECT_PATH, + StatusNotifierWatcher.class + ); + } +} + + +import org.freedesktop.dbus.connections.impl.DBusConnection; +import org.freedesktop.dbus.connections.impl.DBusConnectionBuilder; +import org.freedesktop.dbus.exceptions.DBusException; +import org.freedesktop.dbus.interfaces.DBusInterface; +import jakarta.enterprise.event.Event; +import jakarta.inject.Inject; +import jakarta.enterprise.context.ApplicationScoped; import com.getpcpanel.spring.ConditionalOnWayland; import jakarta.annotation.Nonnull; import jakarta.annotation.PostConstruct; import lombok.RequiredArgsConstructor; -import lombok.extern.log4j.Log4j2; +import lombok.extern.jbosslog.JBossLog; + +@JBossLog +@ApplicationScoped -@Log4j2 -@Service -@RequiredArgsConstructor -@ConditionalOnProperty(value = "disable.tray", havingValue = "false", matchIfMissing = true) -@ConditionalOnWayland +@WaylandImpl class TrayServiceWayland { static final String SNI_BUS_NAME = "org.kde.StatusNotifierItem"; static final String WATCHER_BUS_NAME = "org.kde.StatusNotifierWatcher"; static final String WATCHER_OBJECT_PATH = "/StatusNotifierWatcher"; - private final ApplicationEventPublisher eventPublisher; + @Inject + Event eventBus; private DBusConnection connection; @PostConstruct diff --git a/src/main/java/com/getpcpanel/util/version/VersionChecker.java b/src/main/java/com/getpcpanel/util/version/VersionChecker.java index dfccaebc..6f056bc3 100644 --- a/src/main/java/com/getpcpanel/util/version/VersionChecker.java +++ b/src/main/java/com/getpcpanel/util/version/VersionChecker.java @@ -2,31 +2,124 @@ import static com.getpcpanel.util.version.Version.SNAPSHOT_POSTFIX; +import java.net.URI; +import java.net.http.HttpClient; +import java.net.http.HttpRequest; +import java.net.http.HttpResponse; import java.util.Comparator; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.context.ApplicationEventPublisher; -import org.springframework.stereotype.Service; -import org.springframework.web.client.RestTemplate; +import org.eclipse.microprofile.config.inject.ConfigProperty; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.getpcpanel.profile.SaveService; +import com.getpcpanel.util.version.Version.SemVer; + +import jakarta.annotation.PostConstruct; +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.enterprise.event.Event; +import jakarta.inject.Inject; +import lombok.extern.jbosslog.JBossLog; +import one.util.streamex.StreamEx; + +@JBossLog +@ApplicationScoped +public class VersionChecker extends Thread { + @Inject Event eventBus; + @Inject SaveService save; + @Inject ObjectMapper objectMapper; + + @ConfigProperty(name = "pcpanel.version") + String version; + + @ConfigProperty(name = "pcpanel.build") + int build; + + @ConfigProperty(name = "pcpanel.github.user-and-repo") + String githubUserAndRepo; + + private boolean currentIsSnapshot; + + @PostConstruct + public void init() { + currentIsSnapshot = version.endsWith(SNAPSHOT_POSTFIX); + setDaemon(true); + if (save.get().isStartupVersionCheck()) { + start(); + } + } + + @Override + public void run() { + try { + var url = "https://api.github.com/repos/" + githubUserAndRepo + "/releases?per_page=4"; + var client = HttpClient.newHttpClient(); + var request = HttpRequest.newBuilder(URI.create(url)).build(); + var response = client.send(request, HttpResponse.BodyHandlers.ofString()); + var versions = objectMapper.readValue(response.body(), Version[].class); + + var correctVersion = getVersionOfCorrectType(versions); + if (versionIsNewer(correctVersion)) { + eventBus.fire(new NewVersionAvailableEvent(correctVersion)); + } + } catch (Exception e) { + log.error("Unable to get latest version from GitHub", e); + } + } + + private Version getVersionOfCorrectType(Version[] versions) { + return StreamEx.of(versions) + .sorted(Comparator.comparing(Version::semVer).reversed()) + .filter(v -> currentIsSnapshot || !v.prerelease()) + .findFirst() + .orElseThrow(() -> new RuntimeException("Unable to find release")); + } + + private boolean versionIsNewer(Version remoteVersion) { + var currentSemVer = SemVer.fromName(version); + if (currentIsSnapshot) { + currentSemVer = currentSemVer.withBuild(build); + } + var compared = currentSemVer.compareTo(remoteVersion.semVer()); + if (compared == 0) { + return currentIsSnapshot && build < remoteVersion.getBuild(); + } + return compared < 0; + } + + public record NewVersionAvailableEvent(Version version) { + } +} + + +import static com.getpcpanel.util.version.Version.SNAPSHOT_POSTFIX; + +import java.util.Comparator; + +import org.eclipse.microprofile.config.inject.ConfigProperty; +import jakarta.enterprise.event.Event; +import jakarta.inject.Inject; +import jakarta.enterprise.context.ApplicationScoped; +import java.net.http.HttpClient; import com.getpcpanel.profile.SaveService; import com.getpcpanel.util.version.Version.SemVer; import jakarta.annotation.PostConstruct; import lombok.RequiredArgsConstructor; -import lombok.extern.log4j.Log4j2; +import lombok.extern.jbosslog.JBossLog; import one.util.streamex.StreamEx; -@Log4j2 -@Service +@JBossLog +@ApplicationScoped @RequiredArgsConstructor public class VersionChecker extends Thread { - private final ApplicationEventPublisher eventPublisher; + @Inject + Event eventBus; private final SaveService save; private final RestTemplate webClient; @Value("https://api.github.com/repos/${application.github.user-and-repo}/releases?per_page=4") private final String versionCheck; - @Value("${application.version}") private final String version; - @Value("${application.build}") private final int build; + @ConfigProperty(name="pcpanel.version") private final String version; + @ConfigProperty(name="pcpanel.build") private final int build; @Value("#{'${application.version}'.endsWith('" + SNAPSHOT_POSTFIX + "')}") private final boolean currentIsSnapshot; @PostConstruct @@ -73,7 +166,7 @@ private boolean versionIsNewer(Version remoteVersion) { } private void updateVersionLabel(Version remoteVersion) { - eventPublisher.publishEvent(new NewVersionAvailableEvent(remoteVersion)); + eventBus.fire(new NewVersionAvailableEvent(remoteVersion)); } public record NewVersionAvailableEvent(Version version) { diff --git a/src/main/java/com/getpcpanel/voicemeeter/VoiceMeeterConnectedVolumeService.java b/src/main/java/com/getpcpanel/voicemeeter/VoiceMeeterConnectedVolumeService.java index 7cb535a5..4b49e21a 100644 --- a/src/main/java/com/getpcpanel/voicemeeter/VoiceMeeterConnectedVolumeService.java +++ b/src/main/java/com/getpcpanel/voicemeeter/VoiceMeeterConnectedVolumeService.java @@ -1,25 +1,19 @@ package com.getpcpanel.voicemeeter; -import org.springframework.context.ApplicationEventPublisher; -import org.springframework.context.event.EventListener; -import org.springframework.stereotype.Service; +import jakarta.enterprise.event.Event; +import jakarta.enterprise.context.ApplicationScoped; import com.getpcpanel.commands.AbstractNewXVolumeService; import com.getpcpanel.commands.command.CommandVoiceMeeter; import com.getpcpanel.commands.command.CommandVoiceMeeterAdvanced; import com.getpcpanel.commands.command.CommandVoiceMeeterBasic; import com.getpcpanel.hid.DeviceHolder; -import com.getpcpanel.spring.ConditionalOnWindows; +import com.getpcpanel.spring.WindowsImpl; -@Service -@ConditionalOnWindows +@ApplicationScoped +@WindowsImpl public class VoiceMeeterConnectedVolumeService extends AbstractNewXVolumeService { - public VoiceMeeterConnectedVolumeService(DeviceHolder devices, ApplicationEventPublisher eventPublisher) { - super(devices, eventPublisher); - } - - @EventListener(VoiceMeeterConnectedEvent.class) - public void onVoiceMeeterConnected() { + public void onVoiceMeeterConnected() { triggerCommandsOf(CommandVoiceMeeter.class, s -> s.filterValues(cmd -> cmd instanceof CommandVoiceMeeterBasic || cmd instanceof CommandVoiceMeeterAdvanced) ); diff --git a/src/main/java/com/getpcpanel/voicemeeter/VoiceMeeterMuteService.java b/src/main/java/com/getpcpanel/voicemeeter/VoiceMeeterMuteService.java index af9e1666..c8ff12d8 100644 --- a/src/main/java/com/getpcpanel/voicemeeter/VoiceMeeterMuteService.java +++ b/src/main/java/com/getpcpanel/voicemeeter/VoiceMeeterMuteService.java @@ -5,9 +5,9 @@ import java.util.Map; import java.util.Objects; -import org.springframework.context.ApplicationEventPublisher; -import org.springframework.context.event.EventListener; -import org.springframework.stereotype.Service; +import jakarta.enterprise.event.Event; +import jakarta.inject.Inject; +import jakarta.enterprise.context.ApplicationScoped; import com.getpcpanel.ui.LightingChangedToDefaultEvent; import com.getpcpanel.voicemeeter.Voicemeeter.ButtonType; @@ -15,23 +15,22 @@ import lombok.RequiredArgsConstructor; -@Service -@RequiredArgsConstructor +@ApplicationScoped public class VoiceMeeterMuteService { - private final Voicemeeter voiceMeeter; - private final ApplicationEventPublisher eventPublisher; + @Inject + Voicemeeter voiceMeeter; + @Inject + Event eventBus; private final Map>> toggleMap = new EnumMap<>(ControlType.class); - @EventListener(LightingChangedToDefaultEvent.class) - public void resetMuteStates() { + public void resetMuteStates() { toggleMap.clear(); if (voiceMeeter.login()) { updateMuteState(); } } - @EventListener(VoiceMeeterDirtyEvent.class) - public void updateMuteState() { + public void updateMuteState() { updateMuteStateFor(ControlType.STRIP); updateMuteStateFor(ControlType.BUS); } @@ -51,7 +50,7 @@ private void updateMuteStateFor(ControlType type) { var newState = voiceMeeter.getButtonState(type, idx, button); if (!Objects.equals(current, newState)) { currentStates.put(button, newState); - eventPublisher.publishEvent(new VoiceMeeterMuteEvent(type, idx, button, newState)); + eventBus.fire(new VoiceMeeterMuteEvent(type, idx, button, newState)); } } } diff --git a/src/main/java/com/getpcpanel/voicemeeter/Voicemeeter.java b/src/main/java/com/getpcpanel/voicemeeter/Voicemeeter.java index 17767086..cd964f6c 100644 --- a/src/main/java/com/getpcpanel/voicemeeter/Voicemeeter.java +++ b/src/main/java/com/getpcpanel/voicemeeter/Voicemeeter.java @@ -13,24 +13,27 @@ import org.apache.commons.lang3.StringUtils; import org.jetbrains.annotations.Contract; -import org.springframework.context.ApplicationEventPublisher; -import org.springframework.scheduling.annotation.Scheduled; -import org.springframework.stereotype.Service; +import jakarta.enterprise.event.Event; +import jakarta.inject.Inject; +import io.quarkus.scheduler.Scheduled; +import jakarta.enterprise.context.ApplicationScoped; import com.getpcpanel.profile.SaveService; import lombok.Getter; import lombok.RequiredArgsConstructor; -import lombok.extern.log4j.Log4j2; +import lombok.extern.jbosslog.JBossLog; import one.util.streamex.StreamEx; -@Log4j2 -@Service -@RequiredArgsConstructor +@JBossLog +@ApplicationScoped public final class Voicemeeter { - private final SaveService save; - private final VoicemeeterAPI api; - private final ApplicationEventPublisher eventPublisher; + @Inject + SaveService save; + @Inject + VoicemeeterAPI api; + @Inject + Event eventBus; private int version = -1; private volatile boolean hasFinishedConnection; @@ -38,8 +41,7 @@ public final class Voicemeeter { private final VoicemeeterVersion[] versions = { VoicemeeterVersion.VOICEMEETER, VoicemeeterVersion.BANANA, VoicemeeterVersion.POTATO }; @Getter - @RequiredArgsConstructor - public enum ControlType { + public enum ControlType { STRIP("Input"), BUS("Output"); @@ -94,8 +96,7 @@ public String toString() { } } - @RequiredArgsConstructor - public enum ButtonType { + public enum ButtonType { MONO("Mono", "mono"), MUTE("Mute", "Mute"), SOLO("Solo", "solo"), @@ -168,7 +169,8 @@ public enum DialType { private final String dn; - private final DialControlMode dcm; + @Inject + DialControlMode dcm; DialType(String param, String dn, DialControlMode dcm) { this.param = param; @@ -206,7 +208,7 @@ public boolean login() { checkParamsDirty(); version = api.getVoicemeeterType(); hasFinishedConnection = true; - eventPublisher.publishEvent(new VoiceMeeterConnectedEvent()); + eventBus.fire(new VoiceMeeterConnectedEvent()); return true; } catch (Exception e) { return false; @@ -323,7 +325,7 @@ public void controlButton(String fullParam, ButtonControlMode bt, @Nullable Stri }); } - @Scheduled(fixedRate = 1_000) + @Scheduled(every="1_000s") void checkParamsDirtyScheduled() { disconnectIfDisconnectError(() -> { if (login()) { @@ -335,7 +337,7 @@ void checkParamsDirtyScheduled() { private void checkParamsDirty() { disconnectIfDisconnectError(() -> { if (api.areParametersDirty()) { - eventPublisher.publishEvent(new VoiceMeeterDirtyEvent()); + eventBus.fire(new VoiceMeeterDirtyEvent()); } }); } @@ -372,4 +374,3 @@ private interface VoiceMeeterExceptionThrowingRunnable { void run() throws VoicemeeterException; } } - diff --git a/src/main/java/com/getpcpanel/voicemeeter/VoicemeeterAPI.java b/src/main/java/com/getpcpanel/voicemeeter/VoicemeeterAPI.java index be81f5c1..fe33756a 100644 --- a/src/main/java/com/getpcpanel/voicemeeter/VoicemeeterAPI.java +++ b/src/main/java/com/getpcpanel/voicemeeter/VoicemeeterAPI.java @@ -3,7 +3,7 @@ import java.io.File; import java.util.Set; -import org.springframework.stereotype.Service; +import jakarta.enterprise.context.ApplicationScoped; import com.getpcpanel.profile.SaveService; import com.sun.jna.Memory; @@ -11,10 +11,10 @@ import com.sun.jna.Pointer; import lombok.RequiredArgsConstructor; -import lombok.extern.log4j.Log4j2; +import lombok.extern.jbosslog.JBossLog; -@Log4j2 -@Service +@JBossLog +@ApplicationScoped @RequiredArgsConstructor public final class VoicemeeterAPI { private final SaveService saveService; @@ -245,4 +245,3 @@ private Pointer getPointer(int size) { return new Memory(size); } } - diff --git a/src/main/java/com/getpcpanel/voicemeeter/VoicemeeterException.java b/src/main/java/com/getpcpanel/voicemeeter/VoicemeeterException.java index 95723dac..9dde135b 100644 --- a/src/main/java/com/getpcpanel/voicemeeter/VoicemeeterException.java +++ b/src/main/java/com/getpcpanel/voicemeeter/VoicemeeterException.java @@ -15,4 +15,3 @@ public VoicemeeterException(String message, boolean disconnected) { this.disconnected = disconnected; } } - diff --git a/src/main/java/com/getpcpanel/voicemeeter/VoicemeeterVersion.java b/src/main/java/com/getpcpanel/voicemeeter/VoicemeeterVersion.java index 6899c3bf..c010ab07 100644 --- a/src/main/java/com/getpcpanel/voicemeeter/VoicemeeterVersion.java +++ b/src/main/java/com/getpcpanel/voicemeeter/VoicemeeterVersion.java @@ -181,4 +181,3 @@ public Voicemeeter.ButtonType[][] getBusButtons() { return null; } } - diff --git a/src/main/java/com/getpcpanel/wavelink/WaveLinkIconHandler.java b/src/main/java/com/getpcpanel/wavelink/WaveLinkIconHandler.java index f8a67065..0552d740 100644 --- a/src/main/java/com/getpcpanel/wavelink/WaveLinkIconHandler.java +++ b/src/main/java/com/getpcpanel/wavelink/WaveLinkIconHandler.java @@ -4,7 +4,8 @@ import javax.annotation.Nullable; -import org.springframework.stereotype.Service; +import jakarta.inject.Inject; +import jakarta.enterprise.context.ApplicationScoped; import com.getpcpanel.commands.IIconHandler; import com.getpcpanel.commands.IconService; @@ -18,13 +19,13 @@ import dev.niels.wavelink.impl.model.WaveLinkImage; import javafx.scene.image.Image; import lombok.RequiredArgsConstructor; -import lombok.extern.log4j.Log4j2; +import lombok.extern.jbosslog.JBossLog; -@Log4j2 -@Service -@RequiredArgsConstructor +@JBossLog +@ApplicationScoped public class WaveLinkIconHandler implements IIconHandler { - private final WaveLinkService waveLinkService; + @Inject + WaveLinkService waveLinkService; @Override public Class getCommandClass() { diff --git a/src/main/java/com/getpcpanel/wavelink/WaveLinkService.java b/src/main/java/com/getpcpanel/wavelink/WaveLinkService.java index bd2f6ae0..6b7bd617 100644 --- a/src/main/java/com/getpcpanel/wavelink/WaveLinkService.java +++ b/src/main/java/com/getpcpanel/wavelink/WaveLinkService.java @@ -3,19 +3,18 @@ import java.net.ConnectException; import java.util.concurrent.CompletionException; -import org.springframework.context.event.EventListener; -import org.springframework.scheduling.annotation.Scheduled; -import org.springframework.stereotype.Service; +import io.quarkus.scheduler.Scheduled; +import jakarta.enterprise.context.ApplicationScoped; import com.getpcpanel.profile.SaveService; import com.getpcpanel.profile.SaveService.SaveEvent; import dev.niels.wavelink.IWaveLinkClientEventListener; import dev.niels.wavelink.WaveLinkClient; -import lombok.extern.log4j.Log4j2; +import lombok.extern.jbosslog.JBossLog; -@Log4j2 -@Service +@JBossLog +@ApplicationScoped public class WaveLinkService extends WaveLinkClient implements IWaveLinkClientEventListener { private final SaveService saveService; private boolean wasEnabled; @@ -31,8 +30,7 @@ public boolean isEnabled() { return saveService.get().getWaveLink().enabled(); } - @EventListener(SaveEvent.class) - public void settingsChanged() { + public void settingsChanged() { var is = isEnabled(); if (wasEnabled && !is) { disconnect(); @@ -43,7 +41,7 @@ public void settingsChanged() { wasEnabled = isEnabled(); } - @Scheduled(fixedDelay = 10_000) + @Scheduled(every="10s") public void checkConnection() { if (!isEnabled()) { return; diff --git a/src/main/java/com/getpcpanel/wavelink/command/CommandWaveLinkAddFocusToChannel.java b/src/main/java/com/getpcpanel/wavelink/command/CommandWaveLinkAddFocusToChannel.java index 754a7717..663bad06 100644 --- a/src/main/java/com/getpcpanel/wavelink/command/CommandWaveLinkAddFocusToChannel.java +++ b/src/main/java/com/getpcpanel/wavelink/command/CommandWaveLinkAddFocusToChannel.java @@ -10,10 +10,10 @@ import lombok.Getter; import lombok.ToString; -import lombok.extern.log4j.Log4j2; +import lombok.extern.jbosslog.JBossLog; @Getter -@Log4j2 +@JBossLog @ToString(callSuper = true) public final class CommandWaveLinkAddFocusToChannel extends CommandWaveLink implements ButtonAction { @Nullable private final String channelId; diff --git a/src/main/java/com/getpcpanel/wavelink/command/CommandWaveLinkChangeLevel.java b/src/main/java/com/getpcpanel/wavelink/command/CommandWaveLinkChangeLevel.java index 495af2df..8e509970 100644 --- a/src/main/java/com/getpcpanel/wavelink/command/CommandWaveLinkChangeLevel.java +++ b/src/main/java/com/getpcpanel/wavelink/command/CommandWaveLinkChangeLevel.java @@ -11,10 +11,10 @@ import dev.niels.wavelink.impl.model.WaveLinkControlAction; import lombok.Getter; import lombok.ToString; -import lombok.extern.log4j.Log4j2; +import lombok.extern.jbosslog.JBossLog; @Getter -@Log4j2 +@JBossLog @ToString(callSuper = true) public final class CommandWaveLinkChangeLevel extends CommandWaveLinkChange implements DialAction { @Nullable private final DialCommandParams dialParams; diff --git a/src/main/java/com/getpcpanel/wavelink/command/CommandWaveLinkChangeMute.java b/src/main/java/com/getpcpanel/wavelink/command/CommandWaveLinkChangeMute.java index 2d66cc42..f52c9833 100644 --- a/src/main/java/com/getpcpanel/wavelink/command/CommandWaveLinkChangeMute.java +++ b/src/main/java/com/getpcpanel/wavelink/command/CommandWaveLinkChangeMute.java @@ -12,10 +12,10 @@ import lombok.Getter; import lombok.ToString; -import lombok.extern.log4j.Log4j2; +import lombok.extern.jbosslog.JBossLog; @Getter -@Log4j2 +@JBossLog @ToString(callSuper = true) public final class CommandWaveLinkChangeMute extends CommandWaveLinkChange implements ButtonAction { private final MuteType muteType; diff --git a/src/main/java/com/getpcpanel/wavelink/command/CommandWaveLinkChannelEffect.java b/src/main/java/com/getpcpanel/wavelink/command/CommandWaveLinkChannelEffect.java index 394c151f..3ef44430 100644 --- a/src/main/java/com/getpcpanel/wavelink/command/CommandWaveLinkChannelEffect.java +++ b/src/main/java/com/getpcpanel/wavelink/command/CommandWaveLinkChannelEffect.java @@ -13,11 +13,11 @@ import dev.niels.wavelink.impl.model.WaveLinkEffect; import lombok.Getter; import lombok.ToString; -import lombok.extern.log4j.Log4j2; +import lombok.extern.jbosslog.JBossLog; import one.util.streamex.StreamEx; @Getter -@Log4j2 +@JBossLog @ToString(callSuper = true) public final class CommandWaveLinkChannelEffect extends CommandWaveLink implements ButtonAction { @Nullable private final String channelId; diff --git a/src/main/java/com/getpcpanel/wavelink/command/CommandWaveLinkMainOutput.java b/src/main/java/com/getpcpanel/wavelink/command/CommandWaveLinkMainOutput.java index 42c6b8d8..7b082778 100644 --- a/src/main/java/com/getpcpanel/wavelink/command/CommandWaveLinkMainOutput.java +++ b/src/main/java/com/getpcpanel/wavelink/command/CommandWaveLinkMainOutput.java @@ -10,10 +10,10 @@ import lombok.Getter; import lombok.ToString; -import lombok.extern.log4j.Log4j2; +import lombok.extern.jbosslog.JBossLog; @Getter -@Log4j2 +@JBossLog @ToString(callSuper = true) public final class CommandWaveLinkMainOutput extends CommandWaveLink implements ButtonAction { @Nullable private final String id; diff --git a/src/main/java/com/getpcpanel/wavelink/ui/BaseWaveLinkController.java b/src/main/java/com/getpcpanel/wavelink/ui/BaseWaveLinkController.java deleted file mode 100644 index d3b6831b..00000000 --- a/src/main/java/com/getpcpanel/wavelink/ui/BaseWaveLinkController.java +++ /dev/null @@ -1,150 +0,0 @@ -package com.getpcpanel.wavelink.ui; - -import java.util.Objects; -import java.util.Optional; -import java.util.Set; - -import javax.annotation.Nonnull; -import javax.annotation.Nullable; - -import com.getpcpanel.ui.command.CommandContext; -import com.getpcpanel.ui.command.CommandController; -import com.getpcpanel.wavelink.WaveLinkService; -import com.getpcpanel.wavelink.command.CommandWaveLink; -import com.getpcpanel.wavelink.command.CommandWaveLinkChange; -import com.getpcpanel.wavelink.command.WaveLinkCommandTarget; - -import dev.niels.wavelink.impl.model.WaveLinkChannel; -import dev.niels.wavelink.impl.model.WaveLinkInputDevice; -import dev.niels.wavelink.impl.model.WaveLinkMix; -import dev.niels.wavelink.impl.model.WaveLinkOutputDevice; -import io.reactivex.annotations.NonNull; -import javafx.beans.Observable; -import javafx.fxml.FXML; -import javafx.scene.control.ChoiceBox; -import javafx.scene.control.Label; -import lombok.RequiredArgsConstructor; -import lombok.extern.log4j.Log4j2; -import one.util.streamex.EntryStream; - -@Log4j2 -@RequiredArgsConstructor -class BaseWaveLinkController extends CommandController { - static final Set withChoice2 = Set.of(WaveLinkCommandTarget.Mix); - protected final WaveLinkService waveLinkService; - @FXML protected Label choice1Label; - @FXML protected Label choice2Label; - @FXML protected Label typeLabel; - @FXML protected ChoiceBox typeChoice; - @FXML protected ChoiceBox choice1; - @FXML protected ChoiceBox choice2; - - @Override - public void postInit(CommandContext context) { - if (!waveLinkService.isConnected()) { - return; - } - - typeChoice.getItems().setAll(WaveLinkCommandTarget.values()); - typeChoice.valueProperty().addListener((obs, oldV, newV) -> { - triggerVisibility(); - - choice1.getItems().clear(); - choice2.getItems().clear(); - switch (newV) { - case Input -> { - choice1Label.setText("Input"); - EntryStream.of(waveLinkService.getInputDevices()) - .mapValues(WaveLinkInputDevice::name) - .mapKeyValue(Entry::new) - .into(choice1.getItems()); - } - case Channel -> { - choice1Label.setText("Channel"); - EntryStream.of(waveLinkService.getChannels()) - .mapValues(WaveLinkChannel::name) - .mapKeyValue(Entry::new) - .into(choice1.getItems()); - } - case Mix -> { - choice1Label.setText("Channel"); - - EntryStream.of(waveLinkService.getChannels()) - .mapValues(WaveLinkChannel::name) - .mapKeyValue(Entry::new) - .into(choice1.getItems()); - EntryStream.of(waveLinkService.getMixes()) - .mapValues(WaveLinkMix::name) - .mapKeyValue(Entry::new) - .into(choice2.getItems()); - } - case Output -> { - choice1Label.setText("Output device"); - EntryStream.of(waveLinkService.getOutputDevices()) - .mapValues(WaveLinkOutputDevice::name) - .mapKeyValue(Entry::new) - .into(choice1.getItems()); - } - } - }); - } - - protected void triggerVisibility() { - setVisible(choice1Label, true); - setVisible(choice1, true); - setVisible(choice2Label, withChoice2.contains(typeChoice.getValue())); - setVisible(choice2, withChoice2.contains(typeChoice.getValue())); - } - - @Override - public void initFromCommand(T cmd) { - if (cmd instanceof CommandWaveLinkChange changeCmd) { - typeChoice.setValue(changeCmd.getCommandType()); - selectId(choice1, changeCmd.getId1()); - selectId(choice2, changeCmd.getId2()); - } - - super.initFromCommand(cmd); - } - - private void selectId(ChoiceBox choice, @Nullable String cmd) { - EntryStream.of(choice.getItems()).filterValues(v -> Objects.equals(cmd, v.id())) - .keys().findFirst() - .ifPresent(integer -> choice.getSelectionModel().select(integer)); - } - - @Override - protected Observable[] determineDependencies() { - return new Observable[] { - typeChoice.valueProperty(), - choice1.valueProperty(), - choice2.valueProperty() - }; - } - - record Entry(String id, String name) { - @NonNull - @Override - public String toString() { - return name; - } - } - - protected ArgBase buildArgBase() { - var command = Optional.ofNullable(typeChoice.getValue()).orElse(WaveLinkCommandTarget.Channel); - var choice1Id = Optional.ofNullable(choice1.getValue()).map(Entry::id).orElse(null); - var choice2Id = Optional.ofNullable(choice2.getValue()).map(Entry::id).orElse(null); - return new ArgBase(command, choice1Id, choice2Id); - } - - public Optional getChoice1() { - return Optional.ofNullable(choice1.getValue()); - } - - public Optional getChoice2() { - return Optional.ofNullable(choice2.getValue()); - } - - protected record ArgBase(@Nonnull WaveLinkCommandTarget target, @Nullable String id1, @Nullable String id2) { - } -} diff --git a/src/main/java/com/getpcpanel/wavelink/ui/BtnWaveLinkController.java b/src/main/java/com/getpcpanel/wavelink/ui/BtnWaveLinkController.java deleted file mode 100644 index 43366051..00000000 --- a/src/main/java/com/getpcpanel/wavelink/ui/BtnWaveLinkController.java +++ /dev/null @@ -1,205 +0,0 @@ -package com.getpcpanel.wavelink.ui; - -import java.util.List; -import java.util.Objects; - -import javax.annotation.Nullable; - -import org.springframework.stereotype.Component; - -import com.getpcpanel.commands.command.Command; -import com.getpcpanel.commands.command.CommandNoOp; -import com.getpcpanel.cpp.MuteType; -import com.getpcpanel.spring.Prototype; -import com.getpcpanel.ui.command.ButtonCommandController; -import com.getpcpanel.ui.command.Cmd; -import com.getpcpanel.ui.command.CommandContext; -import com.getpcpanel.wavelink.WaveLinkService; -import com.getpcpanel.wavelink.command.CommandWaveLink; -import com.getpcpanel.wavelink.command.CommandWaveLinkAddFocusToChannel; -import com.getpcpanel.wavelink.command.CommandWaveLinkChangeMute; -import com.getpcpanel.wavelink.command.CommandWaveLinkChannelEffect; -import com.getpcpanel.wavelink.command.CommandWaveLinkMainOutput; -import com.getpcpanel.wavelink.command.WaveLinkCommandTarget; - -import dev.niels.wavelink.impl.model.WaveLinkEffect; -import javafx.beans.Observable; -import javafx.fxml.FXML; -import javafx.scene.control.RadioButton; -import javafx.scene.control.ToggleGroup; -import javafx.scene.layout.VBox; -import lombok.extern.log4j.Log4j2; -import one.util.streamex.StreamEx; - -@Log4j2 -@Component -@Prototype -@Cmd(name = "WaveLink", fxml = "WaveLink", cmds = { CommandWaveLinkChangeMute.class, CommandWaveLinkMainOutput.class, CommandWaveLinkAddFocusToChannel.class, CommandWaveLinkChannelEffect.class }, enabled = WaveLinkEnabled.class) -public class BtnWaveLinkController extends BaseWaveLinkController implements ButtonCommandController { - @FXML private RadioButton type_mute; - @FXML private RadioButton type_mainoutput; - @FXML private RadioButton type_add_focus_to_channel; - @FXML private RadioButton type_channel_effect; - @FXML private ToggleGroup type_grp; - - @FXML private VBox mute_box; - @FXML private RadioButton mute_toggle; - @FXML private RadioButton mute_mute; - @FXML private RadioButton mute_unmute; - @FXML private ToggleGroup mute_toggle_grp; - - public BtnWaveLinkController(WaveLinkService waveLinkService) { - super(waveLinkService); - } - - @Override - public void postInit(CommandContext context) { - super.postInit(context); - - type_grp.selectedToggleProperty().addListener((obs, oldV, newV) -> { - if (type_mainoutput.isSelected()) { - typeChoice.setValue(WaveLinkCommandTarget.Output); - } else if (type_add_focus_to_channel.isSelected()) { - typeChoice.setValue(WaveLinkCommandTarget.Channel); - } else if (type_channel_effect.isSelected()) { - choice2Label.setText("Effect:"); - typeChoice.setValue(WaveLinkCommandTarget.Channel); - } - triggerVisibility(); - }); - - keepChoice2EffectsUpToDate(); - } - - private void keepChoice2EffectsUpToDate() { - choice1.valueProperty().addListener((obs, oldV, newV) -> { - if (!type_channel_effect.isSelected()) { - return; - } - - var channel = waveLinkService.getChannelFromId(newV.id()); - var effects = Objects.requireNonNullElseGet(channel.effects(), List::of); - choice2.getItems().setAll(StreamEx.of(effects) - .map(effect -> new Entry(effect.id(), effect.name())) - .toList()); - }); - } - - @Override - protected void triggerVisibility() { - var selectedToggle = type_grp.getSelectedToggle(); - if (selectedToggle == type_mute) { - setVisible(typeLabel, true); - setVisible(typeChoice, true); - setVisible(mute_box, true); - setMuteLabels(true); - super.triggerVisibility(); - } else if (selectedToggle == type_mainoutput || selectedToggle == type_add_focus_to_channel || selectedToggle == type_channel_effect) { - setVisible(typeLabel, false); - setVisible(typeChoice, false); - setVisible(choice1Label, true); - setVisible(choice1, true); - - setMuteLabels(false); - setVisible(choice2Label, selectedToggle == type_channel_effect); - setVisible(choice2, selectedToggle == type_channel_effect); - setVisible(mute_box, selectedToggle == type_channel_effect); - } - } - - private void setMuteLabels(boolean mute) { - if (mute) { - mute_mute.setText("Mute"); - mute_unmute.setText("Unmute"); - mute_toggle.setText("Toggle Mute/Unmute"); - } else { - mute_mute.setText("Effect on"); - mute_unmute.setText("Effect off"); - mute_toggle.setText("Toggle Effect"); - } - } - - @Override - public void initFromCommand(CommandWaveLink cmd) { - switch (cmd) { - case CommandWaveLinkMainOutput mainOutputCmd -> { - type_grp.selectToggle(type_mainoutput); - typeChoice.setValue(WaveLinkCommandTarget.Output); - choice1.setValue(new Entry(mainOutputCmd.getId(), mainOutputCmd.getName())); - } - case CommandWaveLinkChangeMute muteCmd -> { - type_grp.selectToggle(type_mute); - setMuteToggle(muteCmd.getMuteType()); - } - case CommandWaveLinkAddFocusToChannel addFocusToChannel -> { - type_grp.selectToggle(type_add_focus_to_channel); - typeChoice.setValue(WaveLinkCommandTarget.Channel); - choice1.setValue(new Entry(addFocusToChannel.getChannelId(), addFocusToChannel.getChannelName())); - } - case CommandWaveLinkChannelEffect channelEffect -> { - type_grp.selectToggle(type_channel_effect); - typeChoice.setValue(WaveLinkCommandTarget.Channel); - choice1.setValue(new Entry(channelEffect.getChannelId(), channelEffect.getChannelName())); - choice2.setValue(new Entry(channelEffect.getEffectId(), channelEffect.getEffectName())); - setMuteToggle(channelEffect.getToggleType()); - } - default -> { - log.debug("Unknown command {}", cmd); - } - } - super.initFromCommand(cmd); - } - - private void setMuteToggle(@Nullable MuteType toggleType) { - mute_toggle_grp.selectToggle( - switch (toggleType) { - case mute -> mute_mute; - case unmute -> mute_unmute; - case toggle -> mute_toggle; - case null -> mute_toggle; - } - ); - } - - @Override - public Command buildCommand() { - var base = buildArgBase(); - var choice1 = getChoice1(); - if (choice1.isEmpty()) { - return CommandNoOp.NOOP; - } - - if (type_mute.isSelected()) { - return new CommandWaveLinkChangeMute(base.target(), base.id1(), base.id2(), getMuteType()); - } - if (type_mainoutput.isSelected()) { - return new CommandWaveLinkMainOutput(choice1.get().id(), choice1.get().name()); - } - if (type_add_focus_to_channel.isSelected()) { - return new CommandWaveLinkAddFocusToChannel(choice1.get().id(), choice1.get().name()); - } - if (type_channel_effect.isSelected()) { - var choice2 = getChoice2(); - if (choice2.isEmpty()) { - return CommandNoOp.NOOP; - } - return new CommandWaveLinkChannelEffect(choice1.get().id(), choice1.get().name(), choice2.get().id(), choice2.get().name(), getMuteType()); - } - return CommandNoOp.NOOP; - } - - private MuteType getMuteType() { - if (mute_mute.isSelected()) - return MuteType.mute; - if (mute_unmute.isSelected()) - return MuteType.unmute; - return MuteType.toggle; - } - - @Override - protected Observable[] determineDependencies() { - return StreamEx.of(super.determineDependencies()) - .append(mute_toggle_grp.selectedToggleProperty()) - .toArray(Observable[]::new); - } -} diff --git a/src/main/java/com/getpcpanel/wavelink/ui/DialWaveLinkController.java b/src/main/java/com/getpcpanel/wavelink/ui/DialWaveLinkController.java deleted file mode 100644 index 0fd7a527..00000000 --- a/src/main/java/com/getpcpanel/wavelink/ui/DialWaveLinkController.java +++ /dev/null @@ -1,29 +0,0 @@ -package com.getpcpanel.wavelink.ui; - -import org.springframework.stereotype.Component; - -import com.getpcpanel.commands.command.Command; -import com.getpcpanel.commands.command.DialAction.DialCommandParams; -import com.getpcpanel.spring.Prototype; -import com.getpcpanel.ui.command.Cmd; -import com.getpcpanel.ui.command.DialCommandController; -import com.getpcpanel.wavelink.WaveLinkService; -import com.getpcpanel.wavelink.command.CommandWaveLinkChangeLevel; - -import lombok.extern.log4j.Log4j2; - -@Log4j2 -@Component -@Prototype -@Cmd(name = "WaveLink", fxml = "WaveLink", cmds = CommandWaveLinkChangeLevel.class, enabled = WaveLinkEnabled.class) -public class DialWaveLinkController extends BaseWaveLinkController implements DialCommandController { - public DialWaveLinkController(WaveLinkService waveLinkService) { - super(waveLinkService); - } - - @Override - public Command buildCommand(DialCommandParams params) { - var base = buildArgBase(); - return new CommandWaveLinkChangeLevel(base.target(), base.id1(), base.id2(), params); - } -} diff --git a/src/main/java/com/getpcpanel/wavelink/ui/WaveLinkEnabled.java b/src/main/java/com/getpcpanel/wavelink/ui/WaveLinkEnabled.java deleted file mode 100644 index 2acb2b37..00000000 --- a/src/main/java/com/getpcpanel/wavelink/ui/WaveLinkEnabled.java +++ /dev/null @@ -1,12 +0,0 @@ -package com.getpcpanel.wavelink.ui; - -import com.getpcpanel.MainFX; -import com.getpcpanel.ui.command.Cmd.CmdEnabled; -import com.getpcpanel.wavelink.WaveLinkService; - -class WaveLinkEnabled extends CmdEnabled { - @Override - public boolean isEnabled() { - return MainFX.getBean(WaveLinkService.class).isEnabled(); - } -} diff --git a/src/main/java/dev/niels/wavelink/WaveLinkClient.java b/src/main/java/dev/niels/wavelink/WaveLinkClient.java index 4e1adaa5..1dfa0f8b 100644 --- a/src/main/java/dev/niels/wavelink/WaveLinkClient.java +++ b/src/main/java/dev/niels/wavelink/WaveLinkClient.java @@ -1,9 +1,9 @@ package dev.niels.wavelink; import dev.niels.wavelink.impl.WaveLinkClientImpl; -import lombok.extern.log4j.Log4j2; +import lombok.extern.jbosslog.JBossLog; -@Log4j2 +@JBossLog public class WaveLinkClient extends WaveLinkClientImpl { public WaveLinkClient(boolean autoConnect) { super(autoConnect); diff --git a/src/main/java/dev/niels/wavelink/impl/WaveLinkClientImpl.java b/src/main/java/dev/niels/wavelink/impl/WaveLinkClientImpl.java index 5252212e..f8f6123e 100644 --- a/src/main/java/dev/niels/wavelink/impl/WaveLinkClientImpl.java +++ b/src/main/java/dev/niels/wavelink/impl/WaveLinkClientImpl.java @@ -50,9 +50,9 @@ import dev.niels.wavelink.impl.rpc.WaveLinkSetOutputDeviceCommand; import dev.niels.wavelink.impl.rpc.WaveLinkSetOutputDeviceCommand.WaveLinkSetOutputDeviceParams; import lombok.Getter; -import lombok.extern.log4j.Log4j2; +import lombok.extern.jbosslog.JBossLog; -@Log4j2 +@JBossLog public abstract class WaveLinkClientImpl implements IWaveLinkClient, AutoCloseable { private static final int DEFAULT_WAVELINK_PORT = 1884; private CompletableFuture websocket = CompletableFuture.completedFuture(null); diff --git a/src/main/java/dev/niels/wavelink/impl/WaveLinkListener.java b/src/main/java/dev/niels/wavelink/impl/WaveLinkListener.java index db6df324..a82a20e3 100644 --- a/src/main/java/dev/niels/wavelink/impl/WaveLinkListener.java +++ b/src/main/java/dev/niels/wavelink/impl/WaveLinkListener.java @@ -27,9 +27,9 @@ import dev.niels.wavelink.impl.rpc.WaveLinkSetSubscription; import lombok.RequiredArgsConstructor; import lombok.SneakyThrows; -import lombok.extern.log4j.Log4j2; +import lombok.extern.jbosslog.JBossLog; -@Log4j2 +@JBossLog @RequiredArgsConstructor public class WaveLinkListener implements Listener { private final ObjectMapper mapper = new ObjectMapper().configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); diff --git a/src/main/java/dev/niels/wavelink/impl/model/WaveLinkImage.java b/src/main/java/dev/niels/wavelink/impl/model/WaveLinkImage.java index dfae354c..ecf4ac31 100644 --- a/src/main/java/dev/niels/wavelink/impl/model/WaveLinkImage.java +++ b/src/main/java/dev/niels/wavelink/impl/model/WaveLinkImage.java @@ -9,9 +9,9 @@ import com.fasterxml.jackson.annotation.JsonInclude.Include; import javafx.scene.image.Image; -import lombok.extern.log4j.Log4j2; +import lombok.extern.jbosslog.JBossLog; -@Log4j2 +@JBossLog @JsonInclude(Include.NON_NULL) public record WaveLinkImage( @Nullable String name, diff --git a/src/main/resources/assets/1.css b/src/main/resources/assets/1.css deleted file mode 100644 index ac48269d..00000000 --- a/src/main/resources/assets/1.css +++ /dev/null @@ -1,141 +0,0 @@ -.root { - -fx-base: #25262A; - -fx-accent: #e7eff7; - -fx-default-button: #7f878f; - - -fx-focus-color: transparent; - -fx-faint-focus-color: transparent; - - -fx-focused-text-base-color: ladder( - -fx-selection-bar, - -fx-light-text-color 45%, - -fx-dark-text-color 46%, - -fx-dark-text-color 59%, - -fx-mid-text-color 60% - ); - -fx-focused-mark-color: -fx-focused-text-base-color; -} - -.text-input:focused { - -fx-highlight-text-fill: ladder( - -fx-highlight-fill, - -fx-light-text-color 45%, - -fx-dark-text-color 46%, - -fx-dark-text-color 59%, - -fx-mid-text-color 60% - ); -} - -#pane { - -fx-background-image: url("background_image.png"), url("dots.png"); - -fx-background-repeat: stretch, round; - -fx-background-size: cover, contain; - -fx-background-position: center center, center center; - -fx-effect: dropshadow(three-pass-box, black, 30, 0.5, 0, 0); -} - -.list-cell { - -fx-font-size: 20.0; - -fx-graphic-text-gap: 5.0; - -fx-text-fill: white; -} - - -.list-cell:odd { - -fx-background-color: #25262A; -} - -.list-cell:even { - -fx-background-color: #1C1D20; -} - - -.list-cell:focused, -.toggle-button:selected { - -fx-text-fill: black; - -fx-background-color: #FFC940; -} - -.list-view { - -fx-focus-color: transparent; -} - -#close { - -icon-paint: white; - -fx-background-color: -icon-paint; - -size: 20; - -fx-min-height: -size; - -fx-min-width: -size; - -fx-max-height: -size; - -fx-max-width: -size; - - -fx-shape: "M194.6,164.8L322.7,36.7c8.3-8.3,8.3-21.8,0-30.1s-21.8-8.3-30.1,0l-128.1,128L36.4,6.5c-8.3-8.3-21.8-8.3-30.1,0 s-8.3,21.8,0,30.1l128.1,128.1L6.3,292.9c-8.3,8.3-8.3,21.8,0,30.1c4.2,4.2,9.6,6.2,15.1,6.2s10.9-2.1,15.1-6.2l128.1-128.1 l128,128.1c4.2,4.2,9.6,6.2,15.1,6.2s10.9-2.1,15.1-6.2c8.3-8.3,8.3-21.8,0-30.1L194.6,164.8z"; -} - -#addProfile2 { - -icon-paint: white; - -fx-background-color: -icon-paint; - -size: 20; - -fx-min-height: -size; - -fx-min-width: -size; - -fx-max-height: -size; - -fx-max-width: -size; - - -fx-shape: "M28,14H18V4c0-1.104-0.896-2-2-2s-2,0.896-2,2v10H4c-1.104,0-2,0.896-2,2s0.896,2,2,2h10v10c0,1.104,0.896,2,2,2 s2-0.896,2-2V18h10c1.104,0,2-0.896,2-2S29.104,14,28,14z"; -} - -#versionLabel { - -fx-font-size: 16; -} - -#deviceListToggle { - -icon-paint: white; - -fx-background-color: -icon-paint; - -height: 20; - -width: 25; - -fx-min-height: -height; - -fx-min-width: -width; - -fx-max-height: -height; - -fx-max-width: -width; - -fx-shape: "M10.368,0h102.144c5.703,0,10.367,4.665,10.367,10.367v0 c0,5.702-4.664,10.368-10.367,10.368H10.368C4.666,20.735,0,16.07,0,10.368v0C0,4.665,4.666,0,10.368,0L10.368,0z M10.368,82.875 h102.144c5.703,0,10.367,4.665,10.367,10.367l0,0c0,5.702-4.664,10.367-10.367,10.367H10.368C4.666,103.609,0,98.944,0,93.242l0,0 C0,87.54,4.666,82.875,10.368,82.875L10.368,82.875z M10.368,41.438h102.144c5.703,0,10.367,4.665,10.367,10.367l0,0 c0,5.702-4.664,10.368-10.367,10.368H10.368C4.666,62.173,0,57.507,0,51.805l0,0C0,46.103,4.666,41.438,10.368,41.438 L10.368,41.438z"; -} - -#settings { - -icon-paint: white; - -fx-background-color: -icon-paint; - -height: 28; - -width: 28; - -fx-min-height: -height; - -fx-min-width: -width; - -fx-max-height: -height; - -fx-max-width: -width; - -fx-shape: "M24.38,10.175l-2.231-0.268c-0.228-0.851-0.562-1.655-0.992-2.401l1.387-1.763c0.212-0.271,0.188-0.69-0.057-0.934 l-2.299-2.3c-0.242-0.243-0.662-0.269-0.934-0.057l-1.766,1.389c-0.743-0.43-1.547-0.764-2.396-0.99L14.825,0.62 C14.784,0.279,14.469,0,14.125,0h-3.252c-0.344,0-0.659,0.279-0.699,0.62L9.906,2.851c-0.85,0.227-1.655,0.562-2.398,0.991 L5.743,2.455c-0.27-0.212-0.69-0.187-0.933,0.056L2.51,4.812C2.268,5.054,2.243,5.474,2.456,5.746L3.842,7.51 c-0.43,0.744-0.764,1.549-0.991,2.4l-2.23,0.267C0.28,10.217,0,10.532,0,10.877v3.252c0,0.344,0.279,0.657,0.621,0.699l2.231,0.268 c0.228,0.848,0.561,1.652,0.991,2.396l-1.386,1.766c-0.211,0.271-0.187,0.69,0.057,0.934l2.296,2.301 c0.243,0.242,0.663,0.269,0.933,0.057l1.766-1.39c0.744,0.43,1.548,0.765,2.398,0.991l0.268,2.23 c0.041,0.342,0.355,0.62,0.699,0.62h3.252c0.345,0,0.659-0.278,0.699-0.62l0.268-2.23c0.851-0.228,1.655-0.562,2.398-0.991 l1.766,1.387c0.271,0.212,0.69,0.187,0.933-0.056l2.299-2.301c0.244-0.242,0.269-0.662,0.056-0.935l-1.388-1.764 c0.431-0.744,0.764-1.548,0.992-2.397l2.23-0.268C24.721,14.785,25,14.473,25,14.127v-3.252 C25.001,10.529,24.723,10.216,24.38,10.175z M12.501,18.75c-3.452,0-6.25-2.798-6.25-6.25s2.798-6.25,6.25-6.25 s6.25,2.798,6.25,6.25S15.954,18.75,12.501,18.75z" -} - - -#min { - -fx-border-color: transparent; - -fx-background-radius: 0; - -fx-background-color: transparent; - - -size: 20; - -fx-min-height: -size; - -fx-min-width: -size; - -fx-max-height: -size; - -fx-max-width: -size; - - -icon-paint: red; - -fx-content-display: graphic-only; -} - -#icon { - -icon-paint: white; - -fx-background-color: -icon-paint; - -size: 10; - -fx-min-height: 3; - -fx-min-width: 22; - -fx-max-height: 3; - -fx-max-width: 22; - - -fx-shape: "M306.6,139.3l-284-0.5c-12.4,0-22.5,7.1-22.5,15.9l0,10.6c0,8.8,10,15.9,22.4,16l284,0.5c12.4,0,22.5-7.1,22.5-15.9l0-10.6 C329,146.4,319,139.3,306.6,139.3z"; -} diff --git a/src/main/resources/assets/256x256.png b/src/main/resources/assets/256x256.png deleted file mode 100644 index 99be2cece71f5ddec11625256fb6a8046985d3ec..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 15533 zcmYj&cQ{ zR&R@+_vic1?~i-tJ~QWa&OCGH-h1Yh1lt|W>*-OSvPkKtgZ*)mW zc^>sJkdihhbW8nu{AlIAH%|^VN+ClmN9?uNV=-7XoLB5MJ9S--Hv{ukF9`$Q6G*?H zG8G3163x|kTy7NT@Bg_j>uf1+zbjD#}q>aD#H!?Oxs9r;c(3(vN*tMCO2DMQ0z z+TXqxnH>O(5dbwR1n_d!ig=Ge0KFLiFF+_)0Jb!W&@$vRt!LRo(0`148-dyQFfmsi zf>?>~54k{q^TTdDk$kt~5;96u!BE{I3E&KflK9lg$S}h?S}eyVhn-NUko+4Dc*b-) z1i*UFtdWws$FY~H?e#t=;(-s!@83SxJP$SL5EE?_%1Xz-$GUjyp3D64!GA;j>{lxP z0cyJg|LbS$_|U}lU;jQB9xhliio!gzUbEgWj=EZLkR1JBD7x%u?Io`paF%H|M^TQC ziY`PHb9y;zW|7%w0JsYai4^fv95EAbpU)Nx6Nt906O*!pB}vHPsq`=ZQX#aeeGthH z`Yj*amo=fQlC|~PdPB}S(Q8&9&Qds)y3!wLQgA2FcU=GN!Q-EFsJ%e}ub};}pAHxq zWY}iAd^YT<7(O)u_FnJ)LXFY#4as}~JnOm*{j|%-&`x0Xh>pWWr)FtnJ|9tB1Ah#Y zybmz^Fgl7tjmblEkRKNO=h)1<*9~W|q@)&cLqXQ_cdE?11*}ZJry>x=SM^$gLYToj zZmy8ftL~Kpqw9e-U*BDvX-mA@1Ua6HE4*j_=;F`u6A@a?dvFB8CUb1*ko)n0qqT{A zV@FuRFkmCB$oc1ykt@=mRCge%gzwMIylK>vY>_Wydx&DCBHdE2q!LKU22hD&@JmD& zE%X6Y8M^92b@A?_+jKC&StKP~3l7>n3At0AsgUn{@)R*UhG%x^DBj-C%kmcb#e}O+ zgLF8=i=F}4_)zyZ?gnsSE@0z{ALMBQLRjZ8q~RZ4$gTRkvX$Ua5^dz2ktZ2jkOf%Q z)!8GnN7p&ntlZ}IKhuu+k>e|?Vh$!p#x(EK5NA|CK?4qx(WKbP&-L3iPd#R_*!P=f z3&nM{9bmY~lzrP|mJ;<_$oTQ+!?^`Y4UcpU@`oF2MfPbU4iLuHK?|pkb0*)Lohv~|0FYmt(Nut?KLctfP?P~wP?yQ)0NZD6W43@#+hqVoAT0Ba%BAbSLPFLZ&dQi_#QfSj|cO$>?ISU z=qKUUGmG{QWMG#^x>`>kjF(3Rhj3ZE170i>48m1_S$+<1fHIp zRR09ZhnPJ3(qx^1Fa^-y3JPvwBSZL(8303e6ReDojE|%bbRExOkEz~A(fiPgVwwXEbG5D2DZwIA_y!@}c=4^{02|bUwsD@VMxAv%o z{AK}vB7@|bmr_f+V@Of9fP3(AzS_%3)={FdZ~ZCrZl<;Rh<)(T!~}@(#Tz^5Yg+42 z4ob`!J^lLJl{?@KfDbWWRp&XTXTU5Gesx*1OW2sG`{8x>nKrX=_r5hgTLPVCbf;&C zD5xG7Pp=Pqn=Z*{!a z_`K$t7(Yct%8QCBvx@dAz1*O~@J2~902u&rNSX0VSMGDZofvI;Xv6~7YV&UF-*OVA zlRBp}Q>8lE2w5e`2at{sg%1-0nBMzX78p2sZkjvBOKOYnU!t(m14^r=D{tA>Bu1bC zP}W4TtS(il;@rp0FiNLww?k>Zlb(1XfO@%QV&d`=#g;zJ}c;8 zWPQM~2H_sn-Q)BzyFGtxbR-~rtq-!;mn{l{zpP+lVYwgik+q()&z7Wj)KfxtNGr4! z=zPdD;JHBdO!Rq0nLuNgf9HX;{$uBc0u_?wP8Ce>wlqbm9AVy+%r-!%>E=dh>xr3( zlolw4RKOZv_vbeGy?gpX&m&nOvM+0zG}oe9pjnU`LRKQr&sf>RGqPXfy?CVC4~59M zfCszlsH3gdBj0NIIe^D!UgDj-56;v_j^ZSpcB5A z^DGs>g)H}8sq=MPmh<(nL`zyELN2nrQPff2>fr1iF1+K?$dsvP&=?waUBSWTvBZx; z?P9S~w8f*NqkiF8MFK?7qce5)@P1n4qv>ObOxHrn{GB?FWtUw&TwWbrrWTH7IQ5Q2i zj&7;(oS5L)>qe+U<**R1wjT+A&$5k&J8-UgCZL5Rd|^%vAFizUSk8l4um?JByJJ2% zs}*)Atbsn>%+!dW=4s5t%1-nIoRGbGkSvE6!+pO@N$Nb6L*cf5+O-+q{R1a3&rE6o z>%U0svEKJb3Gkcp#}6LCa20VPdiqg`&t#87J@5CVmPZKayZmomg{CIR%$S--E3Kl; zs;mmY%NKD38tuSprBJgn*OBYgj;fh?2&jXjF8veO2-d@a6IZwtSX_{av- zMbde=dFl9OJ*B>hP#LW(Tp=ZjmpvkmA5{P>hP{3jRa{K@_T-ClOFFcl8jy@F9V^93 zGW}CmR1EI7=$*A<6>?+WF=G{aZiSZ7iu&5Aev_#V4LY!a6a2-~0BGPFL8+ix^~@8| zi@%c4vtRIwh;E;1E%!sPZNAAB=N|=5dnO7a(pL}6?3E>HcU*JmEM2vt#JXbV#8zK& zD4eKaQugsGAOzn<#l>CbM7%zsc6J#1k-Ja)1c74^q{H5b#PL<-i5}LQf?SE@`Nrkt zbm8!%n_Q7ZR%`-RUk)$OmK~ylUjNNxvU{^k*SMaNp>5m5Odo!IfxPg@#QM6ZRTw^$ z>9VNTp^u2#pCL-f_o^W{b9yi^b)v0U8bO{{>R8!P_c>JQCSM);M|882OrmA~P1(PS zHj>hmh*@_`fZuyCyhqW46E~E={dYwoa+MXP3JR~Oh%uAQ>6Z;94SERvJHZl^Wi*gd zPSh`FyArf>oCn5;Ey5Ffq{pLhIJ4ZEui+#?MUvV+EntI)bmnNci&MZ-E{2hJPyM~esB>g7V&w*WX<@m7< zebe&f?dNl4T?PgA)eXMYxL|;I$=xcTJUT8u&~m9 znEJ9ZwU;tvtSSKU9i-F4jS*+j_HOyE&DRvt=f04xlTi-gCdUHPlNbom zYQ#zlN6U$%(n-wrb-33SkNAsL>`pj99i|+9%S7bXz=f!)%3zg1RB}Q9+50^)oFikE zSP}F^u*PkT&O$CG*;UjPhSrL{A%b+xbt!v-xe$xZ%=ZJAJSG0^FHD)U~_-^HMbb%MULqFgtV3{Yd89uU7+UOAfg3?GRs>Vi<;K<6>Ps zK?hBw+j&2&`48ztSsNtN8npKyf z<5S;^L3p^mY-PnM;;=>pw7MA?RcR6{L_=bldBmE==p_U!8uMg8+c6!h6f62)w!U6Y z{c|hP@RD3<`1Xn-xb`a;N!NE^!J!>Nm$;Js`I#K5l6C60URq&mQZBxi zx5*;t7b!{75m)_=gV#b}YX%@TPF=fYL%CR;S}41ee1>BA_H7JS_L1U4TzJXBnSUW> zwnVC#+++?5c2^Q zbYTHOE>`gq5t9075`nY|z6VCGCwRV9k|AXxreDIp7*Zg4X7_112=Ms>AW6OWd=**t z09Zh8{hh#9E2A}Ma>L$YR&f*FZoHsuN9Ry{_(;E){KuN+V0{0KB7pyA(fIQKit@BKhs#*XTC{RlJ&U( zVkFJUeh%^i?Y0AS4QYL(6G%y(Z23lVPz6qX9!l;=0W|*m_AS2LiaaK=ek<*WW4Md>Z`C@KwTr>v7H;!_A-^W<%wTtw9U$CJ`%YhHULjMEzF2qQ zLoGL)B-tAMeZQ5g_5om#s=A^-h}P=(N<#mQ!Up}_9#v|;jJE=x2e%y`bepB!A5oQ0Hqw88{^=5kf&)SSh#%`}#3xIwJ@N?>G^I3FHGM7tp zHN}fZKgw2TqWR1qHe=lQ7)p6jnfC6f<3d?kyq75vBR9SIR`8vZmh_dZkENuz>x~sO zyzBPLYH&~LGr{;0-+Q)nme!`BM2hAMAJ7sWH?6$s(9Uw zG6lW#b&BML&z4@~^L@Msqx@)I0I3!(ssYpZG}b3_zvTNlf4Z)rYFVewiFfE@DhZ7p z5{~rR@rG|W=$4jsOKTI_*DR#7FX6d_&`q_TECS`(#$GpMM~|+@eR}6T&8KAJRew#y z!L)n7ql-aF^4eLuu4g96dQ1CwDNV{L+Q^8|zOa^#lq7+bW`73utn@u$q>K&M&;%B@yv#YrB@xB0=}Xr@y^~ z_$IHQBc_CD|4N>QAfRLQ3JefsxxIofz{TcW*@b!MZt>wPq4f_br{9}!6VB~rHMuV{jWBV3?YNkN{ z6+)%9OQcB${+Na=s4|9TH6nz%5`1;j=)Gc8a*zxUCI4Ys{|RSWv}=stFyBHBE;Kr) zJU|B3o1RZ6rWDQlF@3+JT~3udJ;{P75-Ye|Swn|p9UG(vG^9&*n^=sl=o_Dn328d| z_9~;+53V}7jK+_zq>ltJg}97NGL_2t!H<@oYuEcA=e0MFr^7KO)M7Q2 zZ8HG6R7r7+-Bp`~?JKED?Q5keIMwaTOkBi%U>AO|CU}v?3GIQqGjE89GrikBO|)`2E<($HbTc=1%4$#06jP5&z`6>1J#P z<({m0_j#zey8|&isSZ%;q<~|?h;=p%*kS7SwQ2`Px(n{l&p|nP^bOUyRaoA6domrO z|M;yAv+jd`38&^d=&osw?yq4=Umr1J2Bj;3|UJ)p$ z1$HmGSiafwK3i%I0ZzF%-Z`%*A2n;Ly!#S|DB2|H)!2C{_td{`|Guv{02ENx*NE`}dzeRt- zzV25CqBs~`p8j14BpPd1YEYR_IXt@hR=y(ZhTy^uDH52-Y>Bezb~1T?et^MyjX}G! zPimHB+<6yBw?TBzK3Sk7?K^5;5}l7&v2^VSrgW6I2J9VnrY(G({ia{pl;6{eH?6&P z@#`7`JNPnzp?3spbp2L&_9UB^Y23ZE-xSOx&!y;xOeampX9})xQ2_rGxrK0)f!Br? zzO3#q>?nM^O_mA}iZ7j=KNYL-=ke}a4tV#dv#|^;Dr#MO?QDFem#}%u-G37iE^lmz zKH9y~Y_Fp%noAlFFIXA6@oK>XOCx$P_Oa8{EkyJOP>NCDM)XuF#Z2*($wE zYP`=I+Z{NL{MyM}^9?!2(fpqhc;;yZt2sjNc32y!wZ6|hGb=cgpi~@qPwAL_&y#QJ z?*XKDw9lr)bsqyB;$1v#rGviB$%>38E{;9 z5x8y$10x;~iNEDHaEKr~TqdlEK}K$To{ao~KH0~SH(z^Zko`ir%J2&mY^pArXIj zUw7$RT~`NJU%N}hVipasy=B9`2wObNH8KM5KlxhxeLJD~K7d8qo=&j%%6fO@lNW!LU{-px4roc;k72%Cx^g%`<&j9n%8$6vx+%h6 zx6})+rVp#Tp1c7!YBPy4JxYB?WmI;ayrYdI`H6Y&3q#xSTJF1Ep>BscH(tqHsz(>n zYvk6M>4$xed;#h%Dol$k&C)&84ce^(+Iqe>jW%3&6MU*5TAL16V;5XKN_)mQs-pys zW8}yp5Qkb7K9aRfjB)6cn7Wz!07KvJn7z8PzZK1o9%$M3-7&b3V{5RE?u|y;L#cEV zxGDHj=PHC;T!<4ruSImrE(l_YW?{3)3v#!~NF&#mxu2byES*R|9K!?5*+~kLC^iYKpPJr~RYOF>Lx!WZkM%7io+S_{x zEBB*4vinq%s5X>I_c(}yZXgam)NTv z72gm9nL7Va)O$~YmK;U(`}k^R_NARN=xO|WzHcW0YH9Qg$F^V9Zjrs_jm6Efg)sFsXYPOuO)h}2N!9f6K{YW@%%e4A05LysQ zFtsBW)92)K&v@b>ISRJ3k`*qNE^5h4v0`D0w1P3it@cCT7s!zL`cA511imyjYMh+4 zkw*-VW7q@&$mLEY)SY=OG2QD&Sq{h2)?~5eh-eZ!{d;w#PXu^U($tHk^*HeL5C zhSb=jhdFBWD`_aHr<=odi9`h<$%gTwS^<6(dvcfQW)jH z!s$Eak*QV5PjS^D2l-K$@Pmq!0+Pv>Qq6_n>lwl}b4pMIxpS1QbCadn0GLmh4dlJS zpEfE_{udiJU}chvBUs1&Ug#j~#XjLHUwYEHb?1gW4+A#HnaB!L&KRj6##hxxQb{$x zE*j6BRh*o4L(jFqO-O2dkf}^Hrle0s_H)tG{#FvIFVq87)J1d?93reBZr>j>Xi2Jw zYbhz4e5&4?Hw)8E+m~>6B!4|EafZZ>I~R^-+;VwXmZK{)ZOQYmw0nS+lEcf^UuC)m zQ1qS=Cataer5+*>!wW?@jyXne_sQ{l)$ zefC~Vr0S9ABvKx(`bJDxxVGsXXTk#gz#K@oYQCL` z$QuNCn*nZtgJeuY508@Xxuw~q+0md}W~9fDy(P!4?MhC<$O!k&h|Ir3q^<;wATO`9 zIiAN)`FPMXml=XeHV47qZ&awsp&(35ynr++^AWtKfc!HAsRCnnVfb@3ky?eO^O#34 z+x(@o7{2nTi+uAY>5WTLyzXfP%Y!_H7x(`ClY|Zycz@L3-wwyydjoEL3rJ+Z`EKp2 zgqH?e;fSD5)U@9#Ajyfr4wo$gY4690y_iVG^vrP*O%JL*lOKzLOvGqfENrB6`X~Hq zr0B>MRXz)P^_!Jk&zamdgWK&}cTM;xiX=fFzZTIoy(;usd)`C~IJqABR2Lj79QaSx zmla6UVrALXU6Eh(;fo|v6z}i$=*tgDKx82ik_7Iow3x%7-_+Zwu-h;%rt zCI~R9j-vNxCyM9q%bq9d`@{ZN;v)IibjrrWcFb%caNbdUq^@q)_)v;*p2i8xp?7s1 zq2c%@aHmS`kv~iCSbz`RAXG>YkN0%y15Wooj^J4z8pf)TfLFW4RI7zY*WxU=J%z5* zCJ7n4t@!o{UEvI)y9gv)Em8gf*6jdZaO);%?kZLjU;M6P1!tYSwmK#@9;Bj6HzjKZ zPFdqmCDYGcU0J)gd(UxUC--#~DPEl44~CWFEP$^JyN7^Z=83`W(jcFJ#5ddhJXsl@ z@cCOCOiT(-l?%@A{Vc5@A|aDrB^kYQ9_@*t9o^$cJ`q5UWfM{SvQ{ z=f(80Kg&4{5kD$?b-Nd7`oO)AaMpD||4+wTS&~RtX@I!~{Ot$AC0VypgVw`GZ3ROR zlwk&94D9vTaB_g`&Xj{EY3t|Sf?j#YYF~Quj`@6l$Kg(%arC8nU69MFg8t^^0YP)y z(*grYd!W}E`}y~2k-VScH40CU3hA7b8-zL+Nz5xK)d?n)2S-1LQLg}U0*9`vm;QXB zjOc_NP`X>fwN`&h2m^=!M#QaiW6wo7q7p+QE_V-ScQ6MbO3YW^SpbrtHZ$xYT+cjL z+cdH5tM0E8+mQPD=Zk=D*}J$^p1svEK?~wt*c`3ru*6s3bizx&>1BhFb`)l6VQTg` z9wc^aanDV$an)(T{9&)rC2>2jYE~!)T`3ZBz3|5t(8^0vM4G}(+!FDLz9Z!3H?UeV z>P8Wezh>=ZMFB!7DZQgArZH|V=G=sC`c;W|$M$&o35H0vmaDhM0iJlc2340^j*7g{ z&nV?fe10wqXiWvYSS@i6lt!9-SxI^}5!D(Zs^otAc++(W9{K~+8VhKJPU((q|9bU? z97E?=sm;j+aJcDU=eox+*596~>?{Q9+y@vpgPbmz(%Lw8zaGS~s?siw4`wDL4OC_A*I>$geeX8b7m?hi6;|*O(cP?$C2|UsT zX6=A^DQA+^{nCI&)Sn2i=h7LlGdv+_uH&$!p#G(20~cQfZUrcx>dE|Tt!o3NysXYwlIw|c z@anx=Ga^adsqIGqX6(Jw53ikI)_m$T_@49 z50zI;_aHt|Zf%Z_)1n3yo}#A}HXkUJsM{@s1u{rAn2K6l36yd76#f15GXBGTci_A?T@7+J>S!P3^gi<0mzWE zgkKpKAM;i}kWQ3eZd&9p+k1v*3Td(L{2qXfXS03{mm$e3bxSeTrfY?2?7hQNTPZMJ zCkA2o7rxub6&rs^-u!J}n~OHI>utlL(J}91u2!$C>N-Ty zW8mFvWUyz+i}>?T9?}pClKr;PHM~sAYo2rJBVvZ3KYxdJ@8Tic!t3WRyADoTeF<8+ zQSIV8t~3Xg0fcqj0`phw-#XB^e`c*f|C3H;qqPt-+AwA}Ayf*m-2W&BWU){q@deab zH;rR=J1x|3JnMiqm^biP8xm*#AQL?r=SyI0$yxK>;A6hG_~=e`GmTuhztZ=g6jX-9 zxqSfB_@`l_Q9(fZP)hDESG1MaT!n}HN6c@Smf?f?8r)q=*j&V=>9@rm zm?A6$E}wZ0nc4~&DE^ZRXj?R^4R+tV5AS<;s3))(`?|j7cbkKH_BiX*nsu=*7Y|od zD8eJ}-OzXkfSUxsABX>;7chKj+agPnQWO@$@r83xoMk$Sf&P%V zivVKm{G2ccZ*uvm>RQ`eY;z2)-W~3J0W78y^q!e6g8?pILbsEvX3XFlNxp6 z0g&+UEHB=ytRq4Fz-uMn=(2BkwRLqG^uuVi`3D&`etCt6zE%F0c z&{o_7O+8i~a?>W~C$DXeuBz&CpH;;3EW>FfX&1?9Mqp~`Rr7Nzj8$8bP1i%5Z;Vd3 zzXm~{&F?=r2%JlV{yfOm{$V`}L5>(`+bobxdGcjG%fO=}@uN>`)UTrPmn}`uDKg5d zeb4$v2`W%llHGXxwqr@3c@a-y49rnU=bJR04xD1a5Z&S6MO2xk26&! z-fBt>PbZA{==~}CZVmw)oiKwQ&Xkv=;&=mVsjd6~NHkz^wH_lrM1942apiFGS4~pa zibnnI;A3W*RXUDW{n^=73$y#OXg`0B=OmZCwA(1J;+%#HB-*ecy3g*z#FL0bEeUjQh22RmpA;j;N-RSc9KUjEy&E*o7>BB%xTAuz7G~B`u&&z<3grHe6=tWG7$(wcywNbwK z_6sXc+&L_LADq5zQ6;(Q`u3L`E zE@I*%%B;=uABH7|U!{y>ui7CO^OfWsK2`S>H49i=Gkbrr>%e#7mP2twqN|2i>O>CVd_gC05tJfqnpfZirC}L9Lfk|YMyVSI^aaGXRHgjE^)`uWAVYXa2hW&Qi`sr-rkAL7>b z-0j)mJ`UQFHiGWi=-(PQa5|}MU2h)>zXvvL><_PPk zz8Nq{_o5h8_-A^d_lF5@EFFC-r?J!> zJls79%4`2Kwv&oqH+2dA)TlVc5_fHE%12de09X@4o18>+<$6f~`FuSQK0e4QuZ2E5 zmpS>M>CTkeR95nE9Yi1A3=2zK4g=U>^p|wI-&hN^RQ>7oqqEc~*c-fl?s-&BN!xYW z!&b)&5zbbHEDI@|3bWYA?>Az$fsbT>#RB`@b}A#kYzPfB?&aF-!uLaO7C|(FDT;pX zF%B>Y=WS+%YsD=H+8dauiFscm)50gebx9ZKP6rjy0Q@{`EWH3cok2vf3FLowt4lJ^`ByaW@361h-E8=7#9Op~?ER z3q*}Q;bI=RQZObPY0tl5xW2_S4TpTwi{I%S9Y68CV97Hx;b6^ZTdOe}fU~!mG<@4w z7+9afj~I1Ws5`wp%SzpjMwW==Ys0>u_(YX)#(t%-yaiUaee|!dtJNF%gMaH8h98FX zjw%f`dBMezKL{9|+*cXJkZzpSRFgWEm$TgD)0dQ?tQcLczr^jwq@6w9^)#p?J~ce$ zex==`JafIK*fZ@6?c{q2IuyAJT93KTeJJlbyNrV$Jstk58f)(mP}>+n!HF=6!oQXR zmpeE>vuM?Ksff&p|JFe6{QIyRHtnDc!|?0|lHsm@Mnv|A)a2vQ6p2#|V=PxA_)U%^NuPGWIB`fJp+MohNRGxYl`o>k(daV=IW zw_W*)P=29X#7%SsVLrgwEShIPx)wQFXiu$DA>O?)zUW}!DeG!W?@riArR+=d4GF@8u_>X z>eZXcO+wjcM)iJ+A7^mPihzsZ2o)s{aW;;pPkJyTXg?Nml=j0H`3hunZUjLo9#PK- zSXQGp4H#eOXe$ZxN=NJi(cphT#k(J7tOxgb-Eup&*MQ$CuLOy~ZHHLN z=$?OUF2!6Ws|T5TQ^4Xwq{D^=)G1MzJgGa zkH%eKghil{Ye@a03!!akh{f^40g-P+gR{uJ^DajRhvJqB!q~VtsNwk0-x*wIdB4> zM-^@`Q>BwtYu&pJ*^S|C^yQf;z6pMEq^&`ZcH29cr&;_RoA6>4fCiK4zm)3nXPJxFS&SNr9G+2iVU572Ip-| z%l%(&GJ8BFY>$Humr`gWHtayq)l@}r+sgce&7B9*{Nne3B+JFXE%0_D^Mt+(klP}a z=O3^qC?m)<-#NdryNeL0ZS6VaXMW-a7u>5bZh7XgUF;mrNHvImzxOFFoXyCiU3uyiBR-+n`*12ua4y712tfG4NpotX>!N% ziUF*1g-Ym2dtMGe)(`rmyx$m{bt6=Jj;5v%Bp2yN6N*-zrM&@njy6nUIXo zo_}lOh)O_u+OkfW5;J>TPzGJKNWU=LR5P-BpPR$eqszBylnimo9#ROxa(FGe^zw-N zvI`Z&^ns-U{^Hv`&WS~TP~V6vF3{11AlZNhHd4n#*uzo*w8R%kG0AfnE9)c1f!ft_ ztH@)7h<-0#-u{zeq6h}$G9~1V$%okhs0~y5c#lO2 zfPezdvucBD$u^X1%@RerzpV7zJeql5SBDo9!EvcaaSoAwE%C!jk&QSU6!|xx`gq@9 z?Y@cdx8Dao2sMa$$(h77ldw|6B{w$33ao6`GlpI-Qa+M>&P;5}0OB-OvbnI}<9Z=+ zqC7^Q(r2st&K^P#W_OFoj8SJMthJR>R3uSRQK@%dXl_S$3!><|uwd*3C~f4)TB@-! z`wtS1jIzn{^71CIoI&P|Iw{4m1tff#ybd4d#{iLkH~;W&2k9klR?i+z6_brI`8hY? z-8MH36#55~x)yL|SXeCW2V)j!c@Y<)2>d;eldN6GL|DELvj7eg#T z9BTatG?xnLrx4d5IA_i|eryiAJ~5mFu#mu408*B4)d!Qkf2vK5R17tWs3pSXx>RG; zT0rS2E>*mYrtD7_w?8}X_w09)#wxckuAR9qP)eT5(I6WH?w*Z>8UT{_TP$c{bin)b zAo-IhWvQh7t&@o!fi$^Od5wZ#s>GbRnLjQ4c-P_AW7Nr1)cGqxkBbJX?BU-tB;6Pg zelpsis0P7|0oa#HD9>d4+N@RNbA`zqLBSt%S~uaY@cAyqFuZfQm6L2?c4H)=z_O%k z|MwwDWz)T3prQjsPr8P2D?sMmj&xeaB*kxBdI)R@)7r4K(b05bYfY+(2E6)0H_lH5 zO-@u!GNr`}kun|KrhKM%^3&6l8bqbepNTN9g$&veOl%5D3)491r@LrQ3UC)^51Plt z{C+SzGNKd~oFh2>hyT3y5yRGnI~LM;GH;1wHTm-QI8TvS@hQ%E;lSk5GM>J`eJ;EM zT(YdS>t%-yrs>7vs|KK7|9<$BwFVIcV7np=%y@kqD8D?CmKQ?&4s>0f88Glndf=B! zZ)BFzU5Lj?oEQOT$G05XDJjY@dwBvc&~LH%SKRtG(LihbIILJ>0J1``N#}BDN|B*h znS*O8j@pX9{Rx{QUKS~=ckUO%QWzrY$9!ox&K~UaXE=nc! zk(B-#vO-7FkVxYwbr#X7%e74fA&ex|xA3xG<@sx6>dPjV1%|Nl0I=jlA3mJn96Niz zev_b@BYiRm@Xj1wR*D7~W)<-XDTf5G)A%O29{cTqE-%wDY>+FFs*skgEVlRZtTw?55(^(%_Ei2$=V%w3yTn9YUa{CXcErpo>Y>nrtPxQ>58P+@PHi6fCl zLH#>V+Oeg$p9Hr4?UGm?k6y-KJicTs$)9^RqXAn1&*;&Sl2k2$hP`Nrg$|%71C`7~ z)X2qVUF1Jif}c>;uawwX-ADLN&MwTy@W3c=9-^{xA7hd|W8}(Q{N^7ZR8^PV6{KwWi+AsI{ zet73)u*H;ac37O<%zzd}IRW~|7Cs`!mSH;IZ@Mx<8VO#Kh@|=1FwzQfuG8KB5Ju>e zbIOhPe5JVl>e~wD%>8rXMpPyy3V}7AQV;L25e*`Nlm+8n{CzAnux@bbX?7CNO86A; z1S(*?kx1Aiq6Y~Sb5@aV!5_S^Qg+y!-r80=L303C%NY4I#L}`IW-}?mmL6e4erSr4 zgV6(dgi(%_HOAXrL_lwWV?qT|;HXjmia5o7`5xkH+aAlZI>}Q24fb>|`oNVPtTcr) z?srz|xXj%z*=H{dXF1%Y_MfL91=;)DCyK#=XpWLr^|LZoTQi}%dk3U%F|3ydd^>F3 z^_Rre2}R5}^iL)%HoSK+^^#|Mi88q<=If=U&f<(ebtn^1QcmV41P=PfOI&IUqXtD8 zKY2&zrW*BclJ|ZS)^&g<)d`K*3aNQrYxmGv?;Q(`3*>&`hpI~?pXyJb3?I?A4Mr}c zz9h-$YI^`>SS2kkx4i*##mn4WP8CDH{CPEB@V7eAQ^37(qeq{26CF`&l zFUgxNbzH{SHj+tB*~&Mg_RfsR{l=U{BEs+upFqG%4srrb>tzVcAKnf5Pg^3i^HG{-yOi|1T|dr>a6HLN7~vXsNSU0&~R)0dPo3Ns|ivrn}*|sW2s~E z)~_+t{Rv~UOU9X4$%{8b7xc?Pmx_TqPxH5dPXRUXf&^LX4curHujn`x3`uE~lLBv4 zX{4(*2W9Nr%GApprOZ!PHG44y|HzXAS3D+1z1j?QT}ZP4+<_rn2*gMru&$rb6tllD zqyK(v>g1kK@@1&oFSNQMjOXgii~kyRDQ-;P9oPu71$UFAe48*SY;i-Bz5UAQQsls zhn(if;SrqaZhR5_B*Xyx#EDzZ7ZvxWS0(h|qKh=cg?~qSZ|s-GS`FXiIpO>eYA3TK zix{jZC;SN%Jy22PhN#+2#3qpr^j1=>zf*YjPoB>wbqlz2aMx5-8jTw@C`&S*|{IMl1+ z^|JdDwy}6*;UH2oI^xk4{)L^Bl~>f=rd|X+=Oj>}r+yr-ssQLly%1_@eRcp~f>{|+ zrTyw8BH9dou(OH#@O-EPvIyV=7)PZkV{MfQ6z&e+mm)mt$~XD*ubT$Y>fKrP+P{5c z#L|8tyQ4etP>`bUl*jw{dK(WF?Zl4}wD;rQ78$dMI9l-)sw_W6IBuAG;i=r|XJBZj z`E~ln!PUc($VgI;V6EC5Wx*LGr1-A)9FxwpvR|Y`*?6ylPfV%Ke>XWe%sJu2R>~0r$2X$LLE-L%u5I0Mb#)pBe{lRMg4*NcOv_3L8PR6J%L%-SzBQ@ zrn;YrOfHLMUnSnH-7eyB2yZerDnm`_PV5-*?>+*OM|Xv;x=#W#?J|iCgrPzuot>dt z--+fHfdsK9{l0vu2|;0nld{h706*2x(3W=u_x}8xp|-X*(a+=8-n{mCz~&kB3Rw;* zq8eXXvMBom%o1`V1>&h_At=LX{H6}Cs1AaTv@U?CTS7n#kcGJ=jq|Sl7@(>4LbX=e HI`aPkOEBf4 diff --git a/src/main/resources/assets/32x32.png b/src/main/resources/assets/32x32.png deleted file mode 100644 index 962d9ea8f6a7190e0eb346f1f31ad7595aa64c08..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1452 zcmV;d1ylNoP)~0H`#1LPgq&sO=w9t-%UDN^NavY0SoMp8LAHd+*-8 zcSdHClj+y;MAJNCalhY> zwrB@RB?I|<7LMZxf4fovb*Zqg$0NPl`f6wDq^&K6Kp+Ie$Rp=EDE04#)xQT!3&FXs z4^oxDbLAJv#UivzL-1TZkEmA@KKXnGg+g8|$Vu9ErD1)twRd|{0z9%rVx{;$Ihn@v zvoE2v?;xNCA=wbXQWXu#iKsuGK;YuXh{spJvdWmAp15m(WXslVO$j8E-Ef>LCdwsD zzV#8@uJr=IGck_PZx;}}^aBX@WDsMe>j9V>2f=&^K~7Fzhx)}4B-R=_w0rB&>J8m2&q^E zJNXn=C*qi#N;f36@Y%F^YYhQaQPJJK7OB)Nq~HAnZr26@-ucce(Eh%`0|s>&+`3Gc zB?*#5KnTFJFq}t^f`qq#OkIcc-C-mXD>3rdO_AI+ubNScg&K{vBbS>&=9$BAS8RZi zGZ1_G2;|Z0a4Fz86ufkKvk&*>Y?y;Dh-3?XeoTN9NfyuRf$LS+aN9}&&iU6M+kkh~A!PEVsEm5N1_IZvU52&e z5lC_nO122^*-u3SY6W~=xh?l`GM7O|&Omm9aC`$88M*QA1h_hVdm-Bd-l5ato$-<+ zOZ;&kULuv$mtq$@!JQ2_f+%IR$uygZq1jp=`4gI|fhRqWY$?Kh6j?#FlmaV^LY2GF z3`=3Gu??`Eoq_yk zaT%CPAypaV3L4GInbPdQJCF@?g1Ev z0iI8-R=~V+AMUHjai}yb%CadIPmFqHzn_I<6R7JR2bDcAO>@ZvxDW4^nz9aFtU|Lcwqxy@)irb6hTBpagiqOws5*%0+;KQ=zI6gz&F|j_y?s@b zW9QRf!BwIdyPZO@#4i|BM4C({(9^SKu1ctQ#%(AKA?kkuao=-bl`O`LqfIeHcTFT$ z-1SBzd33~|ABI9gAeM#O=Uzm4Vi=6t&_g;DRk>rE$z(u?hM=z(y00H`{{aNadi**$ z0EZRsTB@WokzDjjCTThnkDY`!xLNFmF>(gkkrSwz<8Ug|!bVkM(3Ey~)wKwyo6zBX z611Adm6-#Jf(rs2ory&W%uD;G*U`S}Whg8pZame(EmVw~kQ{(GfQBdnQV(QDK{|g7 zqs8}HSr#M~?`(+x7g^Dv$Mzu<+69l&4c)s-bj)IY7*#8ULU|Y&^J_SC@u+JEw0Cs0 zOyK{Jrian;5@>QjEGq%Z82?JW7aylwhKL@C+`G?nVE;d77KdPHN?}6)0000 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/main/resources/assets/DefaultExeIcon.ico b/src/main/resources/assets/DefaultExeIcon.ico deleted file mode 100644 index 23e5fb8412efdc4349d1ba96e1fdf1582569e046..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2267 zcmcgudr(tX8b1jFF(8D8LR=+ORLG77F?CrOAu-5;MIHiHiC|C=cAy27f>a@i0?mlH zfu$k=+k^(BK6r^1T$D@02DW1>uv7$#gg{GZ0w&Z*NXTQ)jnmoLnVtUGzs`5>{l4$~ z9^XCpUXF{2Scl(;2LP-)5V=1d01ON<0A~l67*#fUcB zg3DwOc@RTCi3hF;pt2Ur{ii$FdJ}jD{LYVxRpU0spNBXT2hVKjle2X{$Zl+UT|~8l z^9?m2+LW>Ue;@1pe{0v4uz7~ zraLSri+SeCH>?CqUxgR#e4$wuuI_!ktXUc9m%ltW)vLXMm>1Rk&R%V%Fc-S>6i>Gf3L3%jM}+~>6Sp6pQy*6)a4Gur|HqueNo%{`m6=T( zrjWt3m}Ygygp4e_s8I7VkOwg@dflr3#q!m{5YGrm9A_`-2IUznqOQ^Jt= z8;JtU8VQZHGI&JPq*P-*;LNaeA1l&m4k6OJjFnYK%f!UQ6hB4W94mZ3q?gziBo|VR z>~-^DZ-{-Nw3inOS#+4iSrl?71ehCcW-&`qIarVRsNsd9h$mF?2xdV)$JHy6ngK3q zhCJ9*Zm1uZu~J6!u_fRTP3CMTDAVYOY!$d`Q+rjTThCTfZTw1+A$W0VN1u((pzN@oY@?G*}!rd*OBk;}hUq;Br}pcHA-BnV9aFIQ8IW0>zx*%5FY zmb^K1732OZVSnSq|DqV>{ss!TE$rEuoghDHtwJL9Fi2!BeWeWXcu<0zqM?M42M-w} zR7`(kUbc(jUu-Kc^MVS*#Kzw7k_E#gd)Ip;$jFpr2PH`TuXh1kB$m~#8EYmQThrA`bxXTBH03yp`r9*viT!aw_-QL$fs!y zR)MGaSruhiID;ex?RLlpJZxQ1%6?aI6gB?`g7FCRY6&_Lv>_oaw-~R;GS(NVu!(RZ z>h-Y(zEz1^kT4~r%u0{?;CPsITm%x=OVC#6rf1Y!Hx+~^)GsIsDH~@sPLV&h!MGF3 zO7m#hqdoKj+pV0z(})aG(;vAiN~=b3ZKw87Id)u!b>L70CCIfe!16oBAe@N;-zQ7( zT9b+lQ9ci!*NMSbrJVI^EW7Zc)3P1chh?8*Q>#kZ8}VEz!SFK~{KI!ed~1+_8Qx~K z*Ond#Nj^X72kKl6DS3PzmOH?#5n;J@=LLE{PK;79L?(|G+cze$VyvQa;Th-zDT>uPS0u7YefV#XL?tR}{6S81Js+~3A z%`TYmcP~nge}(kRu8uh1rd*u)Dr4_o9{;TjTT5^A=(0@-hix diff --git a/src/main/resources/assets/DialCutoffOptions.fxml b/src/main/resources/assets/DialCutoffOptions.fxml deleted file mode 100644 index 02eda430..00000000 --- a/src/main/resources/assets/DialCutoffOptions.fxml +++ /dev/null @@ -1,49 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/main/resources/assets/MiniLightingDialog.fxml b/src/main/resources/assets/MiniLightingDialog.fxml deleted file mode 100644 index d2da023c..00000000 --- a/src/main/resources/assets/MiniLightingDialog.fxml +++ /dev/null @@ -1,90 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/main/resources/assets/OSCSettingsDialog.fxml b/src/main/resources/assets/OSCSettingsDialog.fxml deleted file mode 100644 index ae267dd4..00000000 --- a/src/main/resources/assets/OSCSettingsDialog.fxml +++ /dev/null @@ -1,40 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/main/resources/assets/command/button/ApplicationDeviceToggle.fxml b/src/main/resources/assets/command/button/ApplicationDeviceToggle.fxml deleted file mode 100644 index 9fe4e8c7..00000000 --- a/src/main/resources/assets/command/button/ApplicationDeviceToggle.fxml +++ /dev/null @@ -1,30 +0,0 @@ - - - - - - - - - - - - -