mirror of
https://github.com/DSheirer/sdrtrunk.git
synced 2026-11-13 09:07:27 -05:00
Compare commits
22 Commits
v0.4.0
...
v0.5.0-alp
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
89cb54676d | ||
|
|
73c251ff4d | ||
|
|
0d51b39604 | ||
|
|
af0dbf1566 | ||
|
|
01146a5624 | ||
|
|
316cdb4068 | ||
|
|
91cae70fa1 | ||
|
|
2723926f83 | ||
|
|
2aff9ee817 | ||
|
|
c87e6553dc | ||
|
|
5d580544e7 | ||
|
|
08ea0e0f18 | ||
|
|
c8b441dbd4 | ||
|
|
1aa22789e5 | ||
|
|
cff5494db4 | ||
|
|
9bd6507342 | ||
|
|
379461e649 | ||
|
|
cc52ae5bd7 | ||
|
|
39f6d6c831 | ||
|
|
649258bd2b | ||
|
|
de9431f649 | ||
|
|
18f9e38090 |
1
.idea/dictionaries/denny.xml
generated
1
.idea/dictionaries/denny.xml
generated
@@ -23,6 +23,7 @@
|
|||||||
<w>shoutcast</w>
|
<w>shoutcast</w>
|
||||||
<w>streamable</w>
|
<w>streamable</w>
|
||||||
<w>talkgroup</w>
|
<w>talkgroup</w>
|
||||||
|
<w>talkgroups</w>
|
||||||
<w>tgid</w>
|
<w>tgid</w>
|
||||||
<w>ultravox</w>
|
<w>ultravox</w>
|
||||||
<w>wacn</w>
|
<w>wacn</w>
|
||||||
|
|||||||
2
.idea/misc.xml
generated
2
.idea/misc.xml
generated
@@ -1,7 +1,7 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<project version="4">
|
<project version="4">
|
||||||
<component name="ExternalStorageConfigurationManager" enabled="true" />
|
<component name="ExternalStorageConfigurationManager" enabled="true" />
|
||||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_X" default="true" project-jdk-name="14" project-jdk-type="JavaSDK">
|
<component name="ProjectRootManager" version="2" languageLevel="JDK_13" default="false" project-jdk-name="13.0.2" project-jdk-type="JavaSDK">
|
||||||
<output url="file://$PROJECT_DIR$/out" />
|
<output url="file://$PROJECT_DIR$/out" />
|
||||||
</component>
|
</component>
|
||||||
</project>
|
</project>
|
||||||
10
.idea/modules.xml
generated
Normal file
10
.idea/modules.xml
generated
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="ProjectModuleManager">
|
||||||
|
<modules>
|
||||||
|
<module fileurl="file://$PROJECT_DIR$/.idea/modules/sdr-trunk.iml" filepath="$PROJECT_DIR$/.idea/modules/sdr-trunk.iml" group="sdr-trunk" />
|
||||||
|
<module fileurl="file://$PROJECT_DIR$/.idea/modules/sdr-trunk_main.iml" filepath="$PROJECT_DIR$/.idea/modules/sdr-trunk_main.iml" group="sdr-trunk" />
|
||||||
|
<module fileurl="file://$PROJECT_DIR$/.idea/modules/sdr-trunk_test.iml" filepath="$PROJECT_DIR$/.idea/modules/sdr-trunk_test.iml" group="sdr-trunk" />
|
||||||
|
</modules>
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
@@ -3,6 +3,7 @@ A cross-platform java application for decoding, monitoring, recording and stream
|
|||||||
|
|
||||||
* [Getting Started](https://github.com/DSheirer/sdrtrunk/wiki/GettingStarted_V0.3.0)
|
* [Getting Started](https://github.com/DSheirer/sdrtrunk/wiki/GettingStarted_V0.3.0)
|
||||||
* [User's Manual Version 0.3.0 and 0.4.0](https://github.com/DSheirer/sdrtrunk/wiki/UserManual_V0.3.0)
|
* [User's Manual Version 0.3.0 and 0.4.0](https://github.com/DSheirer/sdrtrunk/wiki/UserManual_V0.3.0)
|
||||||
|
* [Playlist Editor User Manual Version 0.5.0](https://github.com/DSheirer/sdrtrunk/wiki/Playlist-Editor)
|
||||||
* [Download](https://github.com/DSheirer/sdrtrunk/releases)
|
* [Download](https://github.com/DSheirer/sdrtrunk/releases)
|
||||||
* [Support Group](https://groups.google.com/forum/#!forum/sdrtrunk)
|
* [Support Group](https://groups.google.com/forum/#!forum/sdrtrunk)
|
||||||
* [Discord Support Channel](https://discord.gg/HJQaKYE)
|
* [Discord Support Channel](https://discord.gg/HJQaKYE)
|
||||||
@@ -62,5 +63,5 @@ The **/build/distributions** folder will contain the zip file of the compiled pr
|
|||||||
All dependencies/versions are controlled from build.gradle.
|
All dependencies/versions are controlled from build.gradle.
|
||||||
To change the new release version tag of artifact - change property:
|
To change the new release version tag of artifact - change property:
|
||||||
```
|
```
|
||||||
version = '0.4.0'
|
version = '0.5.0'
|
||||||
```
|
```
|
||||||
|
|||||||
14
build.gradle
14
build.gradle
@@ -24,7 +24,7 @@ plugins {
|
|||||||
id 'java'
|
id 'java'
|
||||||
id 'idea'
|
id 'idea'
|
||||||
id 'org.openjfx.javafxplugin' version '0.0.8'
|
id 'org.openjfx.javafxplugin' version '0.0.8'
|
||||||
id 'org.beryx.runtime' version '1.8.0'
|
id 'org.beryx.runtime' version '1.8.1'
|
||||||
}
|
}
|
||||||
|
|
||||||
repositories {
|
repositories {
|
||||||
@@ -33,7 +33,7 @@ repositories {
|
|||||||
maven { url "https://jitpack.io" }
|
maven { url "https://jitpack.io" }
|
||||||
}
|
}
|
||||||
|
|
||||||
version = '0.4.0'
|
version = '0.5.0-alpha1'
|
||||||
sourceCompatibility = '13'
|
sourceCompatibility = '13'
|
||||||
|
|
||||||
sourceSets {
|
sourceSets {
|
||||||
@@ -65,13 +65,13 @@ dependencies {
|
|||||||
implementation 'com.miglayout:miglayout-swing:5.2'
|
implementation 'com.miglayout:miglayout-swing:5.2'
|
||||||
implementation 'com.mpatric:mp3agic:0.9.1'
|
implementation 'com.mpatric:mp3agic:0.9.1'
|
||||||
implementation 'eu.hansolo:charts:1.0.5'
|
implementation 'eu.hansolo:charts:1.0.5'
|
||||||
implementation 'io.github.dsheirer:radio-reference-api:15.1.2'
|
implementation 'io.github.dsheirer:radio-reference-api:15.1.4'
|
||||||
implementation 'javax.usb:usb-api:1.0.2'
|
implementation 'javax.usb:usb-api:1.0.2'
|
||||||
implementation 'net.coderazzi:tablefilter-swing:5.4.0'
|
implementation 'net.coderazzi:tablefilter-swing:5.4.0'
|
||||||
implementation 'org.apache.commons:commons-lang3:3.8.1'
|
implementation 'org.apache.commons:commons-lang3:3.8.1'
|
||||||
implementation 'org.apache.commons:commons-io:1.3.2'
|
implementation 'org.apache.commons:commons-io:1.3.2'
|
||||||
implementation 'org.apache.mina:mina-core:2.0.19'
|
implementation 'org.apache.mina:mina-core:2.1.3'
|
||||||
implementation 'org.apache.mina:mina-http:2.0.19'
|
implementation 'org.apache.mina:mina-http:2.1.3'
|
||||||
implementation 'org.controlsfx:controlsfx:11.0.1'
|
implementation 'org.controlsfx:controlsfx:11.0.1'
|
||||||
implementation 'org.slf4j:slf4j-api:1.7.25'
|
implementation 'org.slf4j:slf4j-api:1.7.25'
|
||||||
implementation 'org.usb4java:libusb4java:1.3.0'
|
implementation 'org.usb4java:libusb4java:1.3.0'
|
||||||
@@ -124,7 +124,7 @@ jar {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Java Development Kit (JDK) locations. In order to build OS-specific images, these paths must point to the 'bin'
|
* Java Development Kit (JDK) locations. In order to build OS-specific images, these paths must point to the 'bin'
|
||||||
* directory within a JDK for each of the specified architectures. These JDKs are used by the runtime and runtimeZip
|
* directory within a JDK for each of the specified architectures. These JDKs are used by the runtime and runtimeZip
|
||||||
* tasks to produce platform-specific builds. If none of these paths exists, then an image will be created for the
|
* tasks to produce platform-specific builds. If none of these paths exists, then an image will be created for the
|
||||||
* host OS using the installed JDK.
|
* host OS using the installed JDK.
|
||||||
@@ -174,7 +174,7 @@ runtime {
|
|||||||
}
|
}
|
||||||
|
|
||||||
options = ['--strip-debug', '--compress', '2', '--no-header-files', '--no-man-pages']
|
options = ['--strip-debug', '--compress', '2', '--no-header-files', '--no-man-pages']
|
||||||
modules = ['java.desktop', 'java.naming', 'jdk.unsupported', 'jdk.unsupported.desktop', 'java.net.http']
|
modules = ['java.desktop', 'java.naming', 'jdk.unsupported', 'jdk.unsupported.desktop', 'java.net.http', 'java.sql']
|
||||||
imageZip = hasTargetJdk ? file("$buildDir/image/sdr-trunk.zip") : file("$buildDir/image/sdr-trunk-" + version + ".zip")
|
imageZip = hasTargetJdk ? file("$buildDir/image/sdr-trunk.zip") : file("$buildDir/image/sdr-trunk-" + version + ".zip")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
131
sdr-trunk.ipr
131
sdr-trunk.ipr
@@ -34,6 +34,8 @@
|
|||||||
<component name="GradleSettings">
|
<component name="GradleSettings">
|
||||||
<option name="linkedExternalProjectsSettings">
|
<option name="linkedExternalProjectsSettings">
|
||||||
<GradleProjectSettings>
|
<GradleProjectSettings>
|
||||||
|
<option name="delegatedBuild" value="false" />
|
||||||
|
<option name="testRunner" value="PLATFORM" />
|
||||||
<option name="distributionType" value="WRAPPED" />
|
<option name="distributionType" value="WRAPPED" />
|
||||||
<option name="externalProjectPath" value="$PROJECT_DIR$" />
|
<option name="externalProjectPath" value="$PROJECT_DIR$" />
|
||||||
<option name="gradleHome" value="$USER_HOME$/gradle/gradle-2.12" />
|
<option name="gradleHome" value="$USER_HOME$/gradle/gradle-2.12" />
|
||||||
@@ -199,9 +201,36 @@
|
|||||||
<module fileurl="file://$PROJECT_DIR$/sdr-trunk_test.iml" filepath="$PROJECT_DIR$/sdr-trunk_test.iml" group="sdr-trunk" />
|
<module fileurl="file://$PROJECT_DIR$/sdr-trunk_test.iml" filepath="$PROJECT_DIR$/sdr-trunk_test.iml" group="sdr-trunk" />
|
||||||
</modules>
|
</modules>
|
||||||
</component>
|
</component>
|
||||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_11" project-jdk-name="11" project-jdk-type="JavaSDK">
|
<component name="ProjectRootManager" version="2" languageLevel="JDK_11" default="false" project-jdk-name="13" project-jdk-type="JavaSDK">
|
||||||
<output url="file://$PROJECT_DIR$/out" />
|
<output url="file://$PROJECT_DIR$/out" />
|
||||||
</component>
|
</component>
|
||||||
|
<component name="RemoteRepositoriesConfiguration">
|
||||||
|
<remote-repository>
|
||||||
|
<option name="id" value="central" />
|
||||||
|
<option name="name" value="Maven Central repository" />
|
||||||
|
<option name="url" value="https://repo1.maven.org/maven2" />
|
||||||
|
</remote-repository>
|
||||||
|
<remote-repository>
|
||||||
|
<option name="id" value="jboss.community" />
|
||||||
|
<option name="name" value="JBoss Community repository" />
|
||||||
|
<option name="url" value="https://repository.jboss.org/nexus/content/repositories/public/" />
|
||||||
|
</remote-repository>
|
||||||
|
<remote-repository>
|
||||||
|
<option name="id" value="MavenRepo" />
|
||||||
|
<option name="name" value="MavenRepo" />
|
||||||
|
<option name="url" value="https://repo.maven.apache.org/maven2/" />
|
||||||
|
</remote-repository>
|
||||||
|
<remote-repository>
|
||||||
|
<option name="id" value="BintrayJCenter" />
|
||||||
|
<option name="name" value="BintrayJCenter" />
|
||||||
|
<option name="url" value="https://jcenter.bintray.com/" />
|
||||||
|
</remote-repository>
|
||||||
|
<remote-repository>
|
||||||
|
<option name="id" value="maven" />
|
||||||
|
<option name="name" value="maven" />
|
||||||
|
<option name="url" value="https://jitpack.io" />
|
||||||
|
</remote-repository>
|
||||||
|
</component>
|
||||||
<component name="SvnBranchConfigurationManager">
|
<component name="SvnBranchConfigurationManager">
|
||||||
<option name="mySupportsUserInfoFilter" value="true" />
|
<option name="mySupportsUserInfoFilter" value="true" />
|
||||||
</component>
|
</component>
|
||||||
@@ -309,6 +338,22 @@
|
|||||||
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.fazecast/jSerialComm/2.5.0/b9e6222384a17cb10474b7f25768edf4b742ec16/jSerialComm-2.5.0-sources.jar!/" />
|
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.fazecast/jSerialComm/2.5.0/b9e6222384a17cb10474b7f25768edf4b742ec16/jSerialComm-2.5.0-sources.jar!/" />
|
||||||
</SOURCES>
|
</SOURCES>
|
||||||
</library>
|
</library>
|
||||||
|
<library name="Gradle: com.github.dnault:libresample4j:master">
|
||||||
|
<CLASSES>
|
||||||
|
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.github.dnault/libresample4j/master/188fc23592420c3848916d34e1df327d45e5ccd3/libresample4j-master.jar!/" />
|
||||||
|
</CLASSES>
|
||||||
|
<JAVADOC />
|
||||||
|
<SOURCES>
|
||||||
|
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.github.dnault/libresample4j/master/169939a76de61aee15dc6a1b230569e82d115bb7/libresample4j-master-sources.jar!/" />
|
||||||
|
</SOURCES>
|
||||||
|
</library>
|
||||||
|
<library name="Gradle: com.github.DSheirer.jmbe:jmbe-api:1.0.0">
|
||||||
|
<CLASSES>
|
||||||
|
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.github.DSheirer.jmbe/jmbe-api/1.0.0/b914e8f914b7b3d535d1e7bbbbb09ddd37bef726/jmbe-api-1.0.0.jar!/" />
|
||||||
|
</CLASSES>
|
||||||
|
<JAVADOC />
|
||||||
|
<SOURCES />
|
||||||
|
</library>
|
||||||
<library name="Gradle: com.github.jiconfont:jiconfont-font_awesome:4.7.0.1">
|
<library name="Gradle: com.github.jiconfont:jiconfont-font_awesome:4.7.0.1">
|
||||||
<CLASSES>
|
<CLASSES>
|
||||||
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.github.jiconfont/jiconfont-font_awesome/4.7.0.1/121d019023f631af04a1357e637dd05a2cc91f7b/jiconfont-font_awesome-4.7.0.1.jar!/" />
|
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.github.jiconfont/jiconfont-font_awesome/4.7.0.1/121d019023f631af04a1357e637dd05a2cc91f7b/jiconfont-font_awesome-4.7.0.1.jar!/" />
|
||||||
@@ -353,6 +398,13 @@
|
|||||||
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.github.jiconfont/jiconfont/1.0.0/e79316fba9458423ce944fba6da101262549464/jiconfont-1.0.0-sources.jar!/" />
|
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.github.jiconfont/jiconfont/1.0.0/e79316fba9458423ce944fba6da101262549464/jiconfont-1.0.0-sources.jar!/" />
|
||||||
</SOURCES>
|
</SOURCES>
|
||||||
</library>
|
</library>
|
||||||
|
<library name="Gradle: com.github.nwaldispuehl:java-lame:v3.98.4">
|
||||||
|
<CLASSES>
|
||||||
|
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.github.nwaldispuehl/java-lame/v3.98.4/b4917736b83335704dc5af44671ea27a339cc496/java-lame-v3.98.4.jar!/" />
|
||||||
|
</CLASSES>
|
||||||
|
<JAVADOC />
|
||||||
|
<SOURCES />
|
||||||
|
</library>
|
||||||
<library name="Gradle: com.github.wendykierp:JTransforms:3.1">
|
<library name="Gradle: com.github.wendykierp:JTransforms:3.1">
|
||||||
<CLASSES>
|
<CLASSES>
|
||||||
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.github.wendykierp/JTransforms/3.1/f1f6d3c9c9bc04f848e38bdb77dea01e8764a01f/JTransforms-3.1.jar!/" />
|
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.github.wendykierp/JTransforms/3.1/f1f6d3c9c9bc04f848e38bdb77dea01e8764a01f/JTransforms-3.1.jar!/" />
|
||||||
@@ -417,13 +469,13 @@
|
|||||||
</library>
|
</library>
|
||||||
<library name="Gradle: com.google.j2objc:j2objc-annotations:1.1">
|
<library name="Gradle: com.google.j2objc:j2objc-annotations:1.1">
|
||||||
<CLASSES>
|
<CLASSES>
|
||||||
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.google.j2objc/j2objc-annotations/1.1/976d8d30bebc251db406f2bdb3eb01962b5685b3/j2objc-annotations-1.1.jar!/" />
|
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.google.j2objc/j2objc-annotations/1.1/ed28ded51a8b1c6b112568def5f4b455e6809019/j2objc-annotations-1.1.jar!/" />
|
||||||
</CLASSES>
|
</CLASSES>
|
||||||
<JAVADOC>
|
<JAVADOC>
|
||||||
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.google.j2objc/j2objc-annotations/1.1/2e1e0c8db5be95ec85d19d456186f8989ea263fa/j2objc-annotations-1.1-javadoc.jar!/" />
|
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.google.j2objc/j2objc-annotations/1.1/5d415cbe46a293283658196abdb9e6d06dc68ccf/j2objc-annotations-1.1-javadoc.jar!/" />
|
||||||
</JAVADOC>
|
</JAVADOC>
|
||||||
<SOURCES>
|
<SOURCES>
|
||||||
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.google.j2objc/j2objc-annotations/1.1/dcd31de68a90e41e336f6b7afce0d39285c433d7/j2objc-annotations-1.1-sources.jar!/" />
|
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/com.google.j2objc/j2objc-annotations/1.1/1efdf5b737b02f9b72ebdec4f72c37ec411302ff/j2objc-annotations-1.1-sources.jar!/" />
|
||||||
</SOURCES>
|
</SOURCES>
|
||||||
</library>
|
</library>
|
||||||
<library name="Gradle: com.jidesoft:jide-oss:3.6.18">
|
<library name="Gradle: com.jidesoft:jide-oss:3.6.18">
|
||||||
@@ -481,15 +533,15 @@
|
|||||||
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/eu.hansolo/charts/1.0.5/aa26011a2c525ddf86cb92ed23558b18c0174fa4/charts-1.0.5-sources.jar!/" />
|
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/eu.hansolo/charts/1.0.5/aa26011a2c525ddf86cb92ed23558b18c0174fa4/charts-1.0.5-sources.jar!/" />
|
||||||
</SOURCES>
|
</SOURCES>
|
||||||
</library>
|
</library>
|
||||||
<library name="Gradle: io.github.dsheirer:radio-reference-api:15.0.1">
|
<library name="Gradle: io.github.dsheirer:radio-reference-api:15.1.2">
|
||||||
<CLASSES>
|
<CLASSES>
|
||||||
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/io.github.dsheirer/radio-reference-api/15.0.1/8af16506c26025517f0bc971ba35485e96130c9a/radio-reference-api-15.0.1.jar!/" />
|
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/io.github.dsheirer/radio-reference-api/15.1.2/5af53224d83af52ba68bd690c8101e5fe2a492ff/radio-reference-api-15.1.2.jar!/" />
|
||||||
</CLASSES>
|
</CLASSES>
|
||||||
<JAVADOC>
|
<JAVADOC>
|
||||||
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/io.github.dsheirer/radio-reference-api/15.0.1/219528429b3ed73f285a8c2c4ded40d8eae61fc0/radio-reference-api-15.0.1-javadoc.jar!/" />
|
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/io.github.dsheirer/radio-reference-api/15.1.2/75cfe3736f421290f6796d943c1a4b05b32d8526/radio-reference-api-15.1.2-javadoc.jar!/" />
|
||||||
</JAVADOC>
|
</JAVADOC>
|
||||||
<SOURCES>
|
<SOURCES>
|
||||||
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/io.github.dsheirer/radio-reference-api/15.0.1/b699a9821030b54fbb7f472f97fcc634afb114fb/radio-reference-api-15.0.1-sources.jar!/" />
|
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/io.github.dsheirer/radio-reference-api/15.1.2/250df667ec2cea0219a9951d3b2144d87d0cf755/radio-reference-api-15.1.2-sources.jar!/" />
|
||||||
</SOURCES>
|
</SOURCES>
|
||||||
</library>
|
</library>
|
||||||
<library name="Gradle: javax.usb:usb-api:1.0.2">
|
<library name="Gradle: javax.usb:usb-api:1.0.2">
|
||||||
@@ -536,15 +588,15 @@
|
|||||||
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.apache.commons/commons-lang3/3.8.1/e9748d7783594da3735ad9724390a10e7cebc01d/commons-lang3-3.8.1-sources.jar!/" />
|
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.apache.commons/commons-lang3/3.8.1/e9748d7783594da3735ad9724390a10e7cebc01d/commons-lang3-3.8.1-sources.jar!/" />
|
||||||
</SOURCES>
|
</SOURCES>
|
||||||
</library>
|
</library>
|
||||||
<library name="Gradle: org.apache.commons:commons-math3:3.5">
|
<library name="Gradle: org.apache.commons:commons-math3:3.6.1">
|
||||||
<CLASSES>
|
<CLASSES>
|
||||||
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.apache.commons/commons-math3/3.5/13af68e9a11576953f5c4b09436f8207be9d3a27/commons-math3-3.5.jar!/" />
|
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.apache.commons/commons-math3/3.6.1/e4ba98f1d4b3c80ec46392f25e094a6a2e58fcbf/commons-math3-3.6.1.jar!/" />
|
||||||
</CLASSES>
|
</CLASSES>
|
||||||
<JAVADOC>
|
<JAVADOC>
|
||||||
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.apache.commons/commons-math3/3.5/e6106a9927c5ee66d0e647ad116af26c9b6b5f06/commons-math3-3.5-javadoc.jar!/" />
|
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.apache.commons/commons-math3/3.6.1/2e894b7acae199b5dff08bb596334594b5f23c28/commons-math3-3.6.1-javadoc.jar!/" />
|
||||||
</JAVADOC>
|
</JAVADOC>
|
||||||
<SOURCES>
|
<SOURCES>
|
||||||
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.apache.commons/commons-math3/3.5/1d3f412d28c8bff0e8f048240b08bcd21cfa432e/commons-math3-3.5-sources.jar!/" />
|
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.apache.commons/commons-math3/3.6.1/8fab23986ea8886af34818daf32a718e81dc98ba/commons-math3-3.6.1-sources.jar!/" />
|
||||||
</SOURCES>
|
</SOURCES>
|
||||||
</library>
|
</library>
|
||||||
<library name="Gradle: org.apache.mina:mina-core:2.0.19">
|
<library name="Gradle: org.apache.mina:mina-core:2.0.19">
|
||||||
@@ -613,6 +665,17 @@
|
|||||||
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.codehaus.woodstox/stax2-api/3.1.4/b40604d61c01cc081a4a7bdfc562aa9847ee8857/stax2-api-3.1.4-sources.jar!/" />
|
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.codehaus.woodstox/stax2-api/3.1.4/b40604d61c01cc081a4a7bdfc562aa9847ee8857/stax2-api-3.1.4-sources.jar!/" />
|
||||||
</SOURCES>
|
</SOURCES>
|
||||||
</library>
|
</library>
|
||||||
|
<library name="Gradle: org.controlsfx:controlsfx:11.0.1">
|
||||||
|
<CLASSES>
|
||||||
|
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.controlsfx/controlsfx/11.0.1/66df6f3526734d37226ea36cc32940083e6fb697/controlsfx-11.0.1.jar!/" />
|
||||||
|
</CLASSES>
|
||||||
|
<JAVADOC>
|
||||||
|
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.controlsfx/controlsfx/11.0.1/79c5c32f926c8315a344a04eafa8dd250b545927/controlsfx-11.0.1-javadoc.jar!/" />
|
||||||
|
</JAVADOC>
|
||||||
|
<SOURCES>
|
||||||
|
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.controlsfx/controlsfx/11.0.1/5d8b9a51a5b463f583e114e95fdecc0071564a0b/controlsfx-11.0.1-sources.jar!/" />
|
||||||
|
</SOURCES>
|
||||||
|
</library>
|
||||||
<library name="Gradle: org.hamcrest:hamcrest-core:1.3">
|
<library name="Gradle: org.hamcrest:hamcrest-core:1.3">
|
||||||
<CLASSES>
|
<CLASSES>
|
||||||
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.hamcrest/hamcrest-core/1.3/42a25dc3219429f0e5d060061f71acb49bf010a0/hamcrest-core-1.3.jar!/" />
|
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.hamcrest/hamcrest-core/1.3/42a25dc3219429f0e5d060061f71acb49bf010a0/hamcrest-core-1.3.jar!/" />
|
||||||
@@ -668,6 +731,17 @@
|
|||||||
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.openjfx/javafx-base/11.0.2/c082cdf645f9833408dcd960bf68344c9384f569/javafx-base-11.0.2-sources.jar!/" />
|
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.openjfx/javafx-base/11.0.2/c082cdf645f9833408dcd960bf68344c9384f569/javafx-base-11.0.2-sources.jar!/" />
|
||||||
</SOURCES>
|
</SOURCES>
|
||||||
</library>
|
</library>
|
||||||
|
<library name="Gradle: org.openjfx:javafx-controls:11.0.2">
|
||||||
|
<CLASSES>
|
||||||
|
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.openjfx/javafx-controls/11.0.2/4ab633cf1eea60f76e2ae9905aedac862da88b08/javafx-controls-11.0.2.jar!/" />
|
||||||
|
</CLASSES>
|
||||||
|
<JAVADOC>
|
||||||
|
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.openjfx/javafx-controls/11.0.2/baa088249ee757119688e78d9c3a256928189902/javafx-controls-11.0.2-javadoc.jar!/" />
|
||||||
|
</JAVADOC>
|
||||||
|
<SOURCES>
|
||||||
|
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.openjfx/javafx-controls/11.0.2/1535a8b57f01a712405a739a0d69e09ab99dbb6b/javafx-controls-11.0.2-sources.jar!/" />
|
||||||
|
</SOURCES>
|
||||||
|
</library>
|
||||||
<library name="Gradle: org.openjfx:javafx-controls:linux:11.0.2">
|
<library name="Gradle: org.openjfx:javafx-controls:linux:11.0.2">
|
||||||
<CLASSES>
|
<CLASSES>
|
||||||
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.openjfx/javafx-controls/11.0.2/5f6929050a744aab39ebfc1e8e5dd03bcd2ad47b/javafx-controls-11.0.2-linux.jar!/" />
|
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.openjfx/javafx-controls/11.0.2/5f6929050a744aab39ebfc1e8e5dd03bcd2ad47b/javafx-controls-11.0.2-linux.jar!/" />
|
||||||
@@ -745,6 +819,28 @@
|
|||||||
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.openjfx/javafx-graphics/11.0.2/15eadef01f327a851a6d3af0ac89d56292076b13/javafx-graphics-11.0.2-sources.jar!/" />
|
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.openjfx/javafx-graphics/11.0.2/15eadef01f327a851a6d3af0ac89d56292076b13/javafx-graphics-11.0.2-sources.jar!/" />
|
||||||
</SOURCES>
|
</SOURCES>
|
||||||
</library>
|
</library>
|
||||||
|
<library name="Gradle: org.openjfx:javafx-media:11.0.2">
|
||||||
|
<CLASSES>
|
||||||
|
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.openjfx/javafx-media/11.0.2/30b3246c67f308de4d7aefbf99acef8d1dce32ad/javafx-media-11.0.2.jar!/" />
|
||||||
|
</CLASSES>
|
||||||
|
<JAVADOC>
|
||||||
|
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.openjfx/javafx-media/11.0.2/20c892c5aa52ce3d87e3c37c9f60be46e328e342/javafx-media-11.0.2-javadoc.jar!/" />
|
||||||
|
</JAVADOC>
|
||||||
|
<SOURCES>
|
||||||
|
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.openjfx/javafx-media/11.0.2/b189a6ab9f7abdd2ede840e5a90b7002c521f370/javafx-media-11.0.2-sources.jar!/" />
|
||||||
|
</SOURCES>
|
||||||
|
</library>
|
||||||
|
<library name="Gradle: org.openjfx:javafx-media:linux:11.0.2">
|
||||||
|
<CLASSES>
|
||||||
|
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.openjfx/javafx-media/11.0.2/8bc5daaa19f2ff103cd85f751ad021eb71ba7db7/javafx-media-11.0.2-linux.jar!/" />
|
||||||
|
</CLASSES>
|
||||||
|
<JAVADOC>
|
||||||
|
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.openjfx/javafx-media/11.0.2/20c892c5aa52ce3d87e3c37c9f60be46e328e342/javafx-media-11.0.2-javadoc.jar!/" />
|
||||||
|
</JAVADOC>
|
||||||
|
<SOURCES>
|
||||||
|
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.openjfx/javafx-media/11.0.2/b189a6ab9f7abdd2ede840e5a90b7002c521f370/javafx-media-11.0.2-sources.jar!/" />
|
||||||
|
</SOURCES>
|
||||||
|
</library>
|
||||||
<library name="Gradle: org.openjfx:javafx-swing:linux:11.0.2">
|
<library name="Gradle: org.openjfx:javafx-swing:linux:11.0.2">
|
||||||
<CLASSES>
|
<CLASSES>
|
||||||
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.openjfx/javafx-swing/11.0.2/cb57db3e5bd1e2b62b17c8a504858bfc26fa54b1/javafx-swing-11.0.2-linux.jar!/" />
|
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.openjfx/javafx-swing/11.0.2/cb57db3e5bd1e2b62b17c8a504858bfc26fa54b1/javafx-swing-11.0.2-linux.jar!/" />
|
||||||
@@ -778,6 +874,17 @@
|
|||||||
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.openjfx/javafx-swing/11.0.2/bcc8c0a402ff01a89a50888c9dc26824a9f3f2cc/javafx-swing-11.0.2-sources.jar!/" />
|
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.openjfx/javafx-swing/11.0.2/bcc8c0a402ff01a89a50888c9dc26824a9f3f2cc/javafx-swing-11.0.2-sources.jar!/" />
|
||||||
</SOURCES>
|
</SOURCES>
|
||||||
</library>
|
</library>
|
||||||
|
<library name="Gradle: org.openjfx:javafx-web:linux:11.0.2">
|
||||||
|
<CLASSES>
|
||||||
|
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.openjfx/javafx-web/11.0.2/49ca03e2fd58cbb0ad94f6a6bd11e0c043e541a7/javafx-web-11.0.2-linux.jar!/" />
|
||||||
|
</CLASSES>
|
||||||
|
<JAVADOC>
|
||||||
|
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.openjfx/javafx-web/11.0.2/851a1c3ecb65f69d69e25cb63958b9a607f665f2/javafx-web-11.0.2-javadoc.jar!/" />
|
||||||
|
</JAVADOC>
|
||||||
|
<SOURCES>
|
||||||
|
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.openjfx/javafx-web/11.0.2/b33e1bfa75ebf5a6816525a4c77d2a8aea0915c4/javafx-web-11.0.2-sources.jar!/" />
|
||||||
|
</SOURCES>
|
||||||
|
</library>
|
||||||
<library name="Gradle: org.slf4j:slf4j-api:1.7.25">
|
<library name="Gradle: org.slf4j:slf4j-api:1.7.25">
|
||||||
<CLASSES>
|
<CLASSES>
|
||||||
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.slf4j/slf4j-api/1.7.25/da76ca59f6a57ee3102f8f9bd9cee742973efa8a/slf4j-api-1.7.25.jar!/" />
|
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.slf4j/slf4j-api/1.7.25/da76ca59f6a57ee3102f8f9bd9cee742973efa8a/slf4j-api-1.7.25.jar!/" />
|
||||||
|
|||||||
@@ -1,21 +1,23 @@
|
|||||||
/*
|
/*
|
||||||
* ******************************************************************************
|
|
||||||
* sdrtrunk
|
|
||||||
* Copyright (C) 2014-2018 Dennis Sheirer
|
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* * ******************************************************************************
|
||||||
* it under the terms of the GNU General Public License as published by
|
* * Copyright (C) 2014-2020 Dennis Sheirer
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
* *
|
||||||
* (at your option) any later version.
|
* * This program is free software: you can redistribute it and/or modify
|
||||||
|
* * it under the terms of the GNU General Public License as published by
|
||||||
|
* * the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* * (at your option) any later version.
|
||||||
|
* *
|
||||||
|
* * This program is distributed in the hope that it will be useful,
|
||||||
|
* * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* * GNU General Public License for more details.
|
||||||
|
* *
|
||||||
|
* * You should have received a copy of the GNU General Public License
|
||||||
|
* * along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||||
|
* * *****************************************************************************
|
||||||
*
|
*
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
*
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
|
||||||
* *****************************************************************************
|
|
||||||
*/
|
*/
|
||||||
package io.github.dsheirer.alias;
|
package io.github.dsheirer.alias;
|
||||||
|
|
||||||
@@ -28,161 +30,465 @@ import io.github.dsheirer.alias.id.AliasIDType;
|
|||||||
import io.github.dsheirer.alias.id.broadcast.BroadcastChannel;
|
import io.github.dsheirer.alias.id.broadcast.BroadcastChannel;
|
||||||
import io.github.dsheirer.alias.id.priority.Priority;
|
import io.github.dsheirer.alias.id.priority.Priority;
|
||||||
import io.github.dsheirer.alias.id.record.Record;
|
import io.github.dsheirer.alias.id.record.Record;
|
||||||
|
import javafx.beans.Observable;
|
||||||
|
import javafx.beans.binding.Bindings;
|
||||||
|
import javafx.beans.binding.BooleanBinding;
|
||||||
|
import javafx.beans.property.BooleanProperty;
|
||||||
|
import javafx.beans.property.IntegerProperty;
|
||||||
|
import javafx.beans.property.SimpleBooleanProperty;
|
||||||
|
import javafx.beans.property.SimpleIntegerProperty;
|
||||||
|
import javafx.beans.property.SimpleStringProperty;
|
||||||
|
import javafx.beans.property.StringProperty;
|
||||||
|
import javafx.collections.FXCollections;
|
||||||
|
import javafx.collections.ObservableList;
|
||||||
|
import javafx.collections.transformation.FilteredList;
|
||||||
|
import javafx.util.Callback;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import java.awt.Color;
|
import java.awt.Color;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.TreeSet;
|
import java.util.TreeSet;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Alias provides an aliasing (e.g. name, color, etc) container that is linked to multiple alias identifiers and
|
||||||
|
* provides a corresponding set of actions related to the alias.
|
||||||
|
*/
|
||||||
@JacksonXmlRootElement(localName = "alias")
|
@JacksonXmlRootElement(localName = "alias")
|
||||||
public class Alias
|
public class Alias
|
||||||
{
|
{
|
||||||
private String mList;
|
private final static Logger mLog = LoggerFactory.getLogger(Alias.class);
|
||||||
private String mGroup;
|
|
||||||
private String mName;
|
|
||||||
private int mColor;
|
|
||||||
private String mIconName;
|
|
||||||
private List<AliasID> mAliasIDs = new ArrayList<AliasID>();
|
|
||||||
private List<AliasAction> mAliasActions = new ArrayList<AliasAction>();
|
|
||||||
|
|
||||||
public Alias()
|
private BooleanProperty mRecordable = new SimpleBooleanProperty();
|
||||||
{
|
private BooleanProperty mStreamable = new SimpleBooleanProperty();
|
||||||
}
|
private BooleanProperty mOverlap = new SimpleBooleanProperty();
|
||||||
|
private IntegerProperty mColor = new SimpleIntegerProperty();
|
||||||
|
private IntegerProperty mPriority = new SimpleIntegerProperty(Priority.DEFAULT_PRIORITY);
|
||||||
|
private IntegerProperty mNonAudioIdentifierCount = new SimpleIntegerProperty();
|
||||||
|
private StringProperty mAliasListName = new SimpleStringProperty();
|
||||||
|
private StringProperty mGroup = new SimpleStringProperty();
|
||||||
|
private StringProperty mIconName = new SimpleStringProperty();
|
||||||
|
private StringProperty mName = new SimpleStringProperty();
|
||||||
|
private ObservableList<AliasID> mAliasIDs = FXCollections.observableArrayList();
|
||||||
|
private ObservableList<AliasAction> mAliasActions = FXCollections.observableArrayList();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs an instance and sets the specified name.
|
||||||
|
* @param name for the alias
|
||||||
|
*/
|
||||||
public Alias(String name)
|
public Alias(String name)
|
||||||
{
|
{
|
||||||
mName = name;
|
mName.set(name);
|
||||||
|
|
||||||
|
//Bind the non-audio identifier count property to the size of a filtered list of alias identifiers
|
||||||
|
mNonAudioIdentifierCount.bind(Bindings.size(new FilteredList<>(mAliasIDs, aliasID -> {
|
||||||
|
return aliasID != null && !aliasID.isAudioIdentifier();
|
||||||
|
})));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs an instance
|
||||||
|
*/
|
||||||
|
public Alias()
|
||||||
|
{
|
||||||
|
this(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Count of non-audio identifiers for this alias
|
||||||
|
*/
|
||||||
|
@JsonIgnore
|
||||||
|
public IntegerProperty nonAudioIdentifierCountProperty()
|
||||||
|
{
|
||||||
|
return mNonAudioIdentifierCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Audio playback priority
|
||||||
|
*/
|
||||||
|
@JsonIgnore
|
||||||
|
public IntegerProperty priorityProperty()
|
||||||
|
{
|
||||||
|
return mPriority;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicates if one or more of the identifiers for this alias overlap with identifiers from another alias
|
||||||
|
* within the same alias list.
|
||||||
|
*/
|
||||||
|
@JsonIgnore
|
||||||
|
public BooleanProperty overlapProperty()
|
||||||
|
{
|
||||||
|
return mOverlap;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Recordable property
|
||||||
|
*/
|
||||||
|
@JsonIgnore
|
||||||
|
public BooleanProperty recordableProperty()
|
||||||
|
{
|
||||||
|
return mRecordable;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Streamable property
|
||||||
|
*/
|
||||||
|
@JsonIgnore
|
||||||
|
public BooleanProperty streamableProperty()
|
||||||
|
{
|
||||||
|
return mStreamable;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Alias list name property
|
||||||
|
*/
|
||||||
|
@JsonIgnore
|
||||||
|
public StringProperty aliasListNameProperty()
|
||||||
|
{
|
||||||
|
return mAliasListName;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Group property
|
||||||
|
*/
|
||||||
|
@JsonIgnore
|
||||||
|
public StringProperty groupProperty()
|
||||||
|
{
|
||||||
|
return mGroup;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Alias name property
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
@JsonIgnore
|
||||||
|
public StringProperty nameProperty()
|
||||||
|
{
|
||||||
|
return mName;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Alias color value property
|
||||||
|
*/
|
||||||
|
@JsonIgnore
|
||||||
|
public IntegerProperty colorProperty()
|
||||||
|
{
|
||||||
|
return mColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Icon name property
|
||||||
|
*/
|
||||||
|
@JsonIgnore
|
||||||
|
public StringProperty iconNameProperty()
|
||||||
|
{
|
||||||
|
return mIconName;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Alias identifiers list property
|
||||||
|
*/
|
||||||
|
@JsonIgnore
|
||||||
|
public ObservableList<AliasID> aliasIds()
|
||||||
|
{
|
||||||
|
return mAliasIDs;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Alias actions list property
|
||||||
|
*/
|
||||||
|
@JsonIgnore
|
||||||
|
public ObservableList<AliasAction> aliasActions()
|
||||||
|
{
|
||||||
|
return mAliasActions;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Uses name value for string representation
|
||||||
|
*/
|
||||||
public String toString()
|
public String toString()
|
||||||
{
|
{
|
||||||
return getName();
|
return getName();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Alias name
|
||||||
|
*/
|
||||||
@JacksonXmlProperty(isAttribute = true, localName = "name")
|
@JacksonXmlProperty(isAttribute = true, localName = "name")
|
||||||
public String getName()
|
public String getName()
|
||||||
{
|
{
|
||||||
return mName;
|
return mName.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setName(String name)
|
public void setName(String name)
|
||||||
{
|
{
|
||||||
mName = name;
|
mName.set(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Alias list name for the alias list that this alias belongs to
|
||||||
|
*/
|
||||||
@JacksonXmlProperty(isAttribute = true, localName = "list")
|
@JacksonXmlProperty(isAttribute = true, localName = "list")
|
||||||
public String getList()
|
public String getAliasListName()
|
||||||
{
|
{
|
||||||
return mList;
|
return mAliasListName.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setList(String list)
|
public void setAliasListName(String aliasListName)
|
||||||
{
|
{
|
||||||
mList = list;
|
mAliasListName.set(aliasListName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@JsonIgnore
|
||||||
|
public boolean matchesAliasList(String aliasList)
|
||||||
|
{
|
||||||
|
boolean isEmptyArgument = aliasList == null || aliasList.isEmpty() || aliasList.contentEquals(AliasModel.NO_ALIAS_LIST);
|
||||||
|
boolean isEmptyThis = getAliasListName() == null || getAliasListName().isEmpty();
|
||||||
|
|
||||||
|
if(isEmptyArgument && isEmptyThis)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else if(!isEmptyArgument && !isEmptyThis && aliasList.contentEquals(getAliasListName()))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicates if this alias has a specified alias list name
|
||||||
|
*/
|
||||||
public boolean hasList()
|
public boolean hasList()
|
||||||
{
|
{
|
||||||
return mList != null;
|
return mAliasListName.get() != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Grouping tag for this alias.
|
||||||
|
*/
|
||||||
@JacksonXmlProperty(isAttribute = true, localName = "group")
|
@JacksonXmlProperty(isAttribute = true, localName = "group")
|
||||||
public String getGroup()
|
public String getGroup()
|
||||||
{
|
{
|
||||||
return mGroup;
|
return mGroup.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setGroup(String group)
|
public void setGroup(String group)
|
||||||
{
|
{
|
||||||
mGroup = group;
|
mGroup.set(group);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicates if this alias has a grouping tag
|
||||||
|
*/
|
||||||
public boolean hasGroup()
|
public boolean hasGroup()
|
||||||
{
|
{
|
||||||
return mGroup != null;
|
return mGroup.get() != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Color (RGBA) value to use for this alias
|
||||||
|
*/
|
||||||
@JacksonXmlProperty(isAttribute = true, localName = "color")
|
@JacksonXmlProperty(isAttribute = true, localName = "color")
|
||||||
public int getColor()
|
public int getColor()
|
||||||
{
|
{
|
||||||
return mColor;
|
return mColor.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
@JsonIgnore
|
||||||
|
public String getColorHex()
|
||||||
|
{
|
||||||
|
return String.format("#%06X", (0xFFFFFF & mColor.get()));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setColor(int color)
|
public void setColor(int color)
|
||||||
{
|
{
|
||||||
mColor = color;
|
mColor.set(color);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Display color for this alias.
|
||||||
|
*/
|
||||||
@JsonIgnore
|
@JsonIgnore
|
||||||
public Color getDisplayColor()
|
public Color getDisplayColor()
|
||||||
{
|
{
|
||||||
return new Color(getColor());
|
return new Color(getColor());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Icon name of the icon to use for this alias.
|
||||||
|
*/
|
||||||
@JacksonXmlProperty(isAttribute = true, localName = "iconName")
|
@JacksonXmlProperty(isAttribute = true, localName = "iconName")
|
||||||
public String getIconName()
|
public String getIconName()
|
||||||
{
|
{
|
||||||
return mIconName;
|
return mIconName.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setIconName(String iconName)
|
public void setIconName(String iconName)
|
||||||
{
|
{
|
||||||
mIconName = iconName;
|
mIconName.set(iconName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* List of alias identifiers for this alias.
|
||||||
|
*/
|
||||||
@JacksonXmlProperty(isAttribute = false, localName = "id")
|
@JacksonXmlProperty(isAttribute = false, localName = "id")
|
||||||
public List<AliasID> getId()
|
public List<AliasID> getAliasIdentifiers()
|
||||||
{
|
{
|
||||||
return mAliasIDs;
|
return mAliasIDs;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setId(ArrayList<AliasID> id)
|
public void setAliasIdentifiers(List<AliasID> id)
|
||||||
{
|
{
|
||||||
mAliasIDs = id;
|
mAliasIDs.clear();
|
||||||
|
|
||||||
|
for(AliasID aliasID : id)
|
||||||
|
{
|
||||||
|
addAliasID(aliasID);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds an alias identifier to this alias and updates the recordable and streamable attributes.
|
||||||
|
*/
|
||||||
public void addAliasID(AliasID id)
|
public void addAliasID(AliasID id)
|
||||||
{
|
{
|
||||||
mAliasIDs.add(id);
|
mAliasIDs.add(id);
|
||||||
|
validate();
|
||||||
|
updateOverlapBinding();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes an alias identifier from this alias and updates the recordable and streamable attributes.
|
||||||
|
*/
|
||||||
public void removeAliasID(AliasID id)
|
public void removeAliasID(AliasID id)
|
||||||
{
|
{
|
||||||
mAliasIDs.remove(id);
|
if(id != null)
|
||||||
|
{
|
||||||
|
mAliasIDs.remove(id);
|
||||||
|
validate();
|
||||||
|
updateOverlapBinding();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* List of alias actions associated with this alias.
|
||||||
|
*/
|
||||||
@JacksonXmlProperty(isAttribute = false, localName = "action")
|
@JacksonXmlProperty(isAttribute = false, localName = "action")
|
||||||
public List<AliasAction> getAction()
|
public List<AliasAction> getAliasActions()
|
||||||
{
|
{
|
||||||
return mAliasActions;
|
return mAliasActions;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setAction(List<AliasAction> actions)
|
public void setAliasActions(List<AliasAction> actions)
|
||||||
{
|
{
|
||||||
mAliasActions = actions;
|
mAliasActions.setAll(actions);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds the specified alias action to this alias
|
||||||
|
*/
|
||||||
public void addAliasAction(AliasAction action)
|
public void addAliasAction(AliasAction action)
|
||||||
{
|
{
|
||||||
mAliasActions.add(action);
|
mAliasActions.add(action);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes the specified alias action from this alias
|
||||||
|
*/
|
||||||
public void removeAliasAction(AliasAction action)
|
public void removeAliasAction(AliasAction action)
|
||||||
{
|
{
|
||||||
mAliasActions.remove(action);
|
mAliasActions.remove(action);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicates if this alias has any associated
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
public boolean hasActions()
|
public boolean hasActions()
|
||||||
{
|
{
|
||||||
return !mAliasActions.isEmpty();
|
return !mAliasActions.isEmpty();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates the playback priority this alias
|
||||||
|
*/
|
||||||
|
private void updatePriority()
|
||||||
|
{
|
||||||
|
for(AliasID aliasID: mAliasIDs)
|
||||||
|
{
|
||||||
|
if(aliasID instanceof Priority)
|
||||||
|
{
|
||||||
|
mPriority.set(((Priority)aliasID).getPriority());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mPriority.set(Priority.DEFAULT_PRIORITY);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateOverlapBinding()
|
||||||
|
{
|
||||||
|
mOverlap.unbind();
|
||||||
|
|
||||||
|
BooleanBinding binding = new SimpleBooleanProperty(true).not();
|
||||||
|
|
||||||
|
for(AliasID aliasID: mAliasIDs)
|
||||||
|
{
|
||||||
|
binding = binding.or(aliasID.overlapProperty());
|
||||||
|
}
|
||||||
|
|
||||||
|
mOverlap.bind(binding);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates the recordable status for this alias
|
||||||
|
*/
|
||||||
|
private void updateRecordable()
|
||||||
|
{
|
||||||
|
for(AliasID aliasID: mAliasIDs)
|
||||||
|
{
|
||||||
|
if(aliasID instanceof Record)
|
||||||
|
{
|
||||||
|
mRecordable.set(true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mRecordable.set(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates the streamable status for this alias
|
||||||
|
*/
|
||||||
|
private void updateStreamable()
|
||||||
|
{
|
||||||
|
for(AliasID aliasID: mAliasIDs)
|
||||||
|
{
|
||||||
|
if(aliasID instanceof BroadcastChannel)
|
||||||
|
{
|
||||||
|
mStreamable.set(true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mStreamable.set(false);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Perform any validation/cleanup actions on this alias.
|
* Perform any validation/cleanup actions on this alias.
|
||||||
*/
|
*/
|
||||||
public void validate()
|
public void validate()
|
||||||
{
|
{
|
||||||
|
updatePriority();
|
||||||
|
updateRecordable();
|
||||||
|
updateStreamable();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -220,22 +526,23 @@ public class Alias
|
|||||||
*/
|
*/
|
||||||
public void setCallPriority(int priority)
|
public void setCallPriority(int priority)
|
||||||
{
|
{
|
||||||
if(priority == Priority.DO_NOT_MONITOR ||
|
AliasID priorityID = null;
|
||||||
(Priority.MIN_PRIORITY <= priority && priority <= Priority.MAX_PRIORITY))
|
|
||||||
{
|
|
||||||
for(AliasID id : mAliasIDs)
|
|
||||||
{
|
|
||||||
if(id.getType() == AliasIDType.PRIORITY)
|
|
||||||
{
|
|
||||||
((Priority)id).setPriority(priority);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//If we don't find a priority id, create one
|
for(AliasID aliasID: mAliasIDs)
|
||||||
Priority p = new Priority();
|
{
|
||||||
p.setPriority(priority);
|
if(aliasID instanceof Priority)
|
||||||
addAliasID(p);
|
{
|
||||||
|
priorityID = aliasID;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
removeAliasID(priorityID);
|
||||||
|
|
||||||
|
//Don't add a priority alias id if the priority is MAX_PRIORITY or DEFAULT_PRIORITY since the getCallPriority()
|
||||||
|
//will return that as the default value
|
||||||
|
if(priority == Priority.DO_NOT_MONITOR || (Priority.MIN_PRIORITY <= priority && priority < Priority.MAX_PRIORITY))
|
||||||
|
{
|
||||||
|
addAliasID(new Priority(priority));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -245,15 +552,7 @@ public class Alias
|
|||||||
@JsonIgnore
|
@JsonIgnore
|
||||||
public boolean isRecordable()
|
public boolean isRecordable()
|
||||||
{
|
{
|
||||||
for(AliasID id : getId())
|
return mRecordable.get();
|
||||||
{
|
|
||||||
if(id.getType() == AliasIDType.RECORD)
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -261,36 +560,22 @@ public class Alias
|
|||||||
*/
|
*/
|
||||||
public void setRecordable(boolean recordable)
|
public void setRecordable(boolean recordable)
|
||||||
{
|
{
|
||||||
|
AliasID recordID = null;
|
||||||
|
|
||||||
|
for(AliasID aliasID: mAliasIDs)
|
||||||
|
{
|
||||||
|
if(aliasID instanceof Record)
|
||||||
|
{
|
||||||
|
recordID = aliasID;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
removeAliasID(recordID);
|
||||||
|
|
||||||
if(recordable)
|
if(recordable)
|
||||||
{
|
{
|
||||||
for(AliasID id : getId())
|
|
||||||
{
|
|
||||||
if(id.getType() == AliasIDType.RECORD)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
addAliasID(new Record());
|
addAliasID(new Record());
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
AliasID toRemove = null;
|
|
||||||
|
|
||||||
for(AliasID id : getId())
|
|
||||||
{
|
|
||||||
if(id.getType() == AliasIDType.RECORD)
|
|
||||||
{
|
|
||||||
toRemove = id;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(toRemove != null)
|
|
||||||
{
|
|
||||||
removeAliasID(toRemove);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -299,15 +584,7 @@ public class Alias
|
|||||||
@JsonIgnore
|
@JsonIgnore
|
||||||
public boolean isStreamable()
|
public boolean isStreamable()
|
||||||
{
|
{
|
||||||
for(AliasID id : getId())
|
return mStreamable.get();
|
||||||
{
|
|
||||||
if(id.getType() == AliasIDType.BROADCAST_CHANNEL)
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -318,7 +595,7 @@ public class Alias
|
|||||||
{
|
{
|
||||||
Set<BroadcastChannel> broadcastChannels = new TreeSet<>();
|
Set<BroadcastChannel> broadcastChannels = new TreeSet<>();
|
||||||
|
|
||||||
for(AliasID id : getId())
|
for(AliasID id : getAliasIdentifiers())
|
||||||
{
|
{
|
||||||
if(id.getType() == AliasIDType.BROADCAST_CHANNEL)
|
if(id.getType() == AliasIDType.BROADCAST_CHANNEL)
|
||||||
{
|
{
|
||||||
@@ -339,15 +616,100 @@ public class Alias
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
for(AliasID id : getId())
|
for(AliasID id : getAliasIdentifiers())
|
||||||
{
|
{
|
||||||
if(id.getType() == AliasIDType.BROADCAST_CHANNEL &&
|
if(id instanceof BroadcastChannel)
|
||||||
((BroadcastChannel)id).getChannelName().contentEquals(channel))
|
|
||||||
{
|
{
|
||||||
return true;
|
BroadcastChannel broadcastChannel = (BroadcastChannel)id;
|
||||||
|
|
||||||
|
if(broadcastChannel.getChannelName() != null && broadcastChannel.getChannelName().contentEquals(channel))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes any broadcast channel(s) that match the argument
|
||||||
|
*/
|
||||||
|
public void removeBroadcastChannel(String channel)
|
||||||
|
{
|
||||||
|
if(channel == null || channel.isEmpty())
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
List<AliasID> toRemove = new ArrayList<>();
|
||||||
|
|
||||||
|
for(AliasID aliasID: getAliasIdentifiers())
|
||||||
|
{
|
||||||
|
if(aliasID instanceof BroadcastChannel &&
|
||||||
|
((BroadcastChannel)aliasID).getChannelName().contentEquals(channel))
|
||||||
|
{
|
||||||
|
toRemove.add(aliasID);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for(AliasID aliasID: toRemove)
|
||||||
|
{
|
||||||
|
removeAliasID(aliasID);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes (clears) all broadcast channels from this alias
|
||||||
|
*/
|
||||||
|
public void removeAllBroadcastChannels()
|
||||||
|
{
|
||||||
|
List<AliasID> toRemove = new ArrayList<>();
|
||||||
|
for(AliasID aliasID: mAliasIDs)
|
||||||
|
{
|
||||||
|
if(aliasID instanceof BroadcastChannel)
|
||||||
|
{
|
||||||
|
toRemove.add(aliasID);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for(AliasID aliasID: toRemove)
|
||||||
|
{
|
||||||
|
removeAliasID(aliasID);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes all non audio identifiers to prepare for replacing them from an editor.
|
||||||
|
*/
|
||||||
|
public void removeNonAudioIdentifiers()
|
||||||
|
{
|
||||||
|
Iterator<AliasID> it = mAliasIDs.iterator();
|
||||||
|
|
||||||
|
while(it.hasNext())
|
||||||
|
{
|
||||||
|
if(!it.next().isAudioIdentifier())
|
||||||
|
{
|
||||||
|
it.remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes all alias actions from this alias
|
||||||
|
*/
|
||||||
|
public void removeAllActions()
|
||||||
|
{
|
||||||
|
mAliasActions.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates an observable property extractor for use with observable lists to detect changes internal to this object.
|
||||||
|
*/
|
||||||
|
public static Callback<Alias,Observable[]> extractor()
|
||||||
|
{
|
||||||
|
return (Alias a) -> new Observable[] {a.recordableProperty(), a.streamableProperty(), a.colorProperty(),
|
||||||
|
a.aliasListNameProperty(), a.groupProperty(), a.iconNameProperty(), a.nameProperty(), a.aliasIds(),
|
||||||
|
a.aliasActions(), a.nonAudioIdentifierCountProperty(), a.overlapProperty()};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,381 +0,0 @@
|
|||||||
/*
|
|
||||||
* ******************************************************************************
|
|
||||||
* sdrtrunk
|
|
||||||
* Copyright (C) 2014-2019 Dennis Sheirer
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
|
||||||
* *****************************************************************************
|
|
||||||
*/
|
|
||||||
|
|
||||||
package io.github.dsheirer.alias;
|
|
||||||
|
|
||||||
import com.jidesoft.swing.JideSplitPane;
|
|
||||||
import io.github.dsheirer.audio.broadcast.BroadcastModel;
|
|
||||||
import io.github.dsheirer.gui.editor.Editor;
|
|
||||||
import io.github.dsheirer.icon.IconManager;
|
|
||||||
import io.github.dsheirer.preference.UserPreferences;
|
|
||||||
import io.github.dsheirer.preference.swing.JTableColumnWidthMonitor;
|
|
||||||
import net.coderazzi.filters.gui.AutoChoices;
|
|
||||||
import net.coderazzi.filters.gui.TableFilterHeader;
|
|
||||||
import net.miginfocom.swing.MigLayout;
|
|
||||||
|
|
||||||
import javax.swing.ImageIcon;
|
|
||||||
import javax.swing.JButton;
|
|
||||||
import javax.swing.JLabel;
|
|
||||||
import javax.swing.JOptionPane;
|
|
||||||
import javax.swing.JPanel;
|
|
||||||
import javax.swing.JScrollPane;
|
|
||||||
import javax.swing.JTable;
|
|
||||||
import javax.swing.ListSelectionModel;
|
|
||||||
import javax.swing.event.ListSelectionEvent;
|
|
||||||
import javax.swing.event.ListSelectionListener;
|
|
||||||
import javax.swing.table.DefaultTableCellRenderer;
|
|
||||||
import java.awt.Color;
|
|
||||||
import java.awt.Component;
|
|
||||||
import java.awt.Dimension;
|
|
||||||
import java.awt.Rectangle;
|
|
||||||
import java.awt.event.ActionEvent;
|
|
||||||
import java.awt.event.ActionListener;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
public class AliasController extends JPanel implements ActionListener, ListSelectionListener
|
|
||||||
{
|
|
||||||
private static final long serialVersionUID = 1L;
|
|
||||||
|
|
||||||
private static final String TABLE_PREFERENCE_KEY = "alias.controller";
|
|
||||||
private AliasModel mAliasModel;
|
|
||||||
private JTable mAliasTable;
|
|
||||||
private TableFilterHeader mTableFilterHeader;
|
|
||||||
private AliasEditor mAliasEditor;
|
|
||||||
private MultipleAliasEditor mMultipleAliasEditor;
|
|
||||||
private JideSplitPane mSplitPane;
|
|
||||||
private UserPreferences mUserPreferences;
|
|
||||||
|
|
||||||
private static final String NEW_ALIAS = "New";
|
|
||||||
private static final String COPY_ALIAS = "Copy";
|
|
||||||
private static final String DELETE_ALIAS = "Delete";
|
|
||||||
|
|
||||||
private JButton mNewButton = new JButton(NEW_ALIAS);
|
|
||||||
private JButton mCopyButton = new JButton(COPY_ALIAS);
|
|
||||||
private JButton mDeleteButton = new JButton(DELETE_ALIAS);
|
|
||||||
|
|
||||||
private IconCellRenderer mIconCellRenderer;
|
|
||||||
private JTableColumnWidthMonitor mColumnWidthMonitor;
|
|
||||||
|
|
||||||
public AliasController(AliasModel aliasModel, BroadcastModel broadcastModel, IconManager iconManager,
|
|
||||||
UserPreferences userPreferences)
|
|
||||||
{
|
|
||||||
mAliasModel = aliasModel;
|
|
||||||
mAliasEditor = new AliasEditor(mAliasModel, broadcastModel, iconManager);
|
|
||||||
mMultipleAliasEditor = new MultipleAliasEditor(mAliasModel, broadcastModel, iconManager);
|
|
||||||
mIconCellRenderer = new IconCellRenderer(iconManager);
|
|
||||||
mUserPreferences = userPreferences;
|
|
||||||
|
|
||||||
init();
|
|
||||||
|
|
||||||
mAliasModel.addListener(mAliasEditor);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void init()
|
|
||||||
{
|
|
||||||
setLayout(new MigLayout("insets 0 0 0 0", "[grow,fill]", "[grow,fill]"));
|
|
||||||
|
|
||||||
//System Configuration View and Editor
|
|
||||||
mAliasTable = new JTable(mAliasModel);
|
|
||||||
mAliasTable.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
|
|
||||||
mAliasTable.getSelectionModel().addListSelectionListener(this);
|
|
||||||
mAliasTable.setAutoCreateRowSorter(true);
|
|
||||||
|
|
||||||
mAliasTable.getColumnModel().getColumn(AliasModel.COLUMN_COLOR).setCellRenderer(new ColorCellRenderer());
|
|
||||||
|
|
||||||
mAliasTable.getColumnModel().getColumn(AliasModel.COLUMN_ICON).setCellRenderer(mIconCellRenderer);
|
|
||||||
|
|
||||||
mColumnWidthMonitor = new JTableColumnWidthMonitor(mUserPreferences, mAliasTable, TABLE_PREFERENCE_KEY);
|
|
||||||
|
|
||||||
mTableFilterHeader = new TableFilterHeader(mAliasTable, AutoChoices.ENABLED);
|
|
||||||
mTableFilterHeader.setFilterOnUpdates(true);
|
|
||||||
|
|
||||||
JScrollPane tableScroller = new JScrollPane(mAliasTable);
|
|
||||||
|
|
||||||
JPanel buttonsPanel = new JPanel();
|
|
||||||
|
|
||||||
buttonsPanel.setLayout(new MigLayout("insets 0 0 0 0",
|
|
||||||
"[grow,fill][grow,fill][grow,fill]", "[]"));
|
|
||||||
|
|
||||||
mNewButton.addActionListener(this);
|
|
||||||
mNewButton.setToolTipText("Adds a new alias");
|
|
||||||
buttonsPanel.add(mNewButton);
|
|
||||||
|
|
||||||
mCopyButton.addActionListener(this);
|
|
||||||
mCopyButton.setEnabled(false);
|
|
||||||
mCopyButton.setToolTipText("Creates a copy of the currently selected alias and adds it");
|
|
||||||
buttonsPanel.add(mCopyButton);
|
|
||||||
|
|
||||||
mDeleteButton.addActionListener(this);
|
|
||||||
mDeleteButton.setEnabled(false);
|
|
||||||
mDeleteButton.setToolTipText("Deletes the currently selected alias");
|
|
||||||
buttonsPanel.add(mDeleteButton);
|
|
||||||
|
|
||||||
JPanel listAndButtonsPanel = new JPanel();
|
|
||||||
|
|
||||||
listAndButtonsPanel.setLayout(new MigLayout("insets 0 0 0 0", "[grow,fill]",
|
|
||||||
"[grow,fill][]"));
|
|
||||||
|
|
||||||
listAndButtonsPanel.add(tableScroller, "wrap");
|
|
||||||
listAndButtonsPanel.add(buttonsPanel);
|
|
||||||
|
|
||||||
mSplitPane = new JideSplitPane(JideSplitPane.HORIZONTAL_SPLIT);
|
|
||||||
mSplitPane.setDividerSize(5);
|
|
||||||
mSplitPane.setShowGripper(true);
|
|
||||||
mSplitPane.add(listAndButtonsPanel);
|
|
||||||
mSplitPane.add(mAliasEditor);
|
|
||||||
|
|
||||||
add(mSplitPane);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the editor argument as the currently visible alias editor
|
|
||||||
*/
|
|
||||||
private void setEditor(Editor<?> editor)
|
|
||||||
{
|
|
||||||
if(mSplitPane.getComponentCount() == 3)
|
|
||||||
{
|
|
||||||
Component component = mSplitPane.getComponent(2);
|
|
||||||
|
|
||||||
if(component instanceof Editor<?> && component != editor)
|
|
||||||
{
|
|
||||||
//Get current divider location so that we can reapply it after
|
|
||||||
//changing out the editors, so that the interface doesn't move
|
|
||||||
int location = mSplitPane.getDividerLocation(0);
|
|
||||||
|
|
||||||
((Editor<?>) component).setItem(null);
|
|
||||||
|
|
||||||
mSplitPane.remove(component);
|
|
||||||
editor.setPreferredSize(new Dimension(100, 100));
|
|
||||||
mSplitPane.add(editor);
|
|
||||||
|
|
||||||
mSplitPane.validate();
|
|
||||||
mSplitPane.setDividerLocation(0, location);
|
|
||||||
mSplitPane.repaint();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private Alias getAlias(int selectedRow)
|
|
||||||
{
|
|
||||||
if(selectedRow >= 0)
|
|
||||||
{
|
|
||||||
int index = mAliasTable.convertRowIndexToModel(selectedRow);
|
|
||||||
|
|
||||||
return mAliasModel.getAliasAtIndex(index);
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void valueChanged(ListSelectionEvent event)
|
|
||||||
{
|
|
||||||
//Limits event firing to only when selection is complete
|
|
||||||
if(!event.getValueIsAdjusting())
|
|
||||||
{
|
|
||||||
int[] selectedRows = mAliasTable.getSelectedRows();
|
|
||||||
|
|
||||||
if(selectedRows.length == 0)
|
|
||||||
{
|
|
||||||
mAliasEditor.setItem(null);
|
|
||||||
mCopyButton.setEnabled(false);
|
|
||||||
mDeleteButton.setEnabled(false);
|
|
||||||
}
|
|
||||||
else if(selectedRows.length == 1)
|
|
||||||
{
|
|
||||||
setEditor(mAliasEditor);
|
|
||||||
|
|
||||||
Alias selectedAlias = getAlias(selectedRows[0]);
|
|
||||||
|
|
||||||
mAliasEditor.setItem(selectedAlias);
|
|
||||||
|
|
||||||
if(selectedAlias != null)
|
|
||||||
{
|
|
||||||
mCopyButton.setEnabled(true);
|
|
||||||
mDeleteButton.setEnabled(true);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
mCopyButton.setEnabled(false);
|
|
||||||
mDeleteButton.setEnabled(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
setEditor(mMultipleAliasEditor);
|
|
||||||
|
|
||||||
mCopyButton.setEnabled(true);
|
|
||||||
mDeleteButton.setEnabled(true);
|
|
||||||
|
|
||||||
List<Alias> selectedAliases = new ArrayList<Alias>();
|
|
||||||
|
|
||||||
for(int selectedRow : selectedRows)
|
|
||||||
{
|
|
||||||
Alias alias = getAlias(selectedRow);
|
|
||||||
|
|
||||||
selectedAliases.add(alias);
|
|
||||||
}
|
|
||||||
|
|
||||||
mMultipleAliasEditor.setItem(selectedAliases);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Adds the alias to the alias table/model and scrolls the view it
|
|
||||||
*/
|
|
||||||
private void addAlias(Alias alias)
|
|
||||||
{
|
|
||||||
//Remove any column headers so that the newly added alias shows correctly.
|
|
||||||
mTableFilterHeader.resetFilter();
|
|
||||||
|
|
||||||
int index = mAliasModel.addAlias(alias);
|
|
||||||
|
|
||||||
if(index >= 0)
|
|
||||||
{
|
|
||||||
int translatedIndex = mAliasTable.convertRowIndexToView(index);
|
|
||||||
mAliasTable.setRowSelectionInterval(translatedIndex, translatedIndex);
|
|
||||||
mAliasTable.scrollRectToVisible(
|
|
||||||
new Rectangle(mAliasTable.getCellRect(translatedIndex, 0, true)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private List<Alias> getSelectedAliases()
|
|
||||||
{
|
|
||||||
List<Alias> selected = new ArrayList<>();
|
|
||||||
|
|
||||||
int[] rows = mAliasTable.getSelectedRows();
|
|
||||||
|
|
||||||
for(int row : rows)
|
|
||||||
{
|
|
||||||
if(row >= 0)
|
|
||||||
{
|
|
||||||
Alias alias = mAliasModel.getAliasAtIndex(mAliasTable.convertRowIndexToModel(row));
|
|
||||||
|
|
||||||
if(alias != null)
|
|
||||||
{
|
|
||||||
selected.add(alias);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return selected;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Responds to New, Copy and Delete Channel button invocations
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public void actionPerformed(ActionEvent event)
|
|
||||||
{
|
|
||||||
switch(event.getActionCommand())
|
|
||||||
{
|
|
||||||
case NEW_ALIAS:
|
|
||||||
addAlias(new Alias("New Alias"));
|
|
||||||
break;
|
|
||||||
case COPY_ALIAS:
|
|
||||||
for(Alias alias : getSelectedAliases())
|
|
||||||
{
|
|
||||||
addAlias(AliasFactory.copyOf(alias));
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case DELETE_ALIAS:
|
|
||||||
List<Alias> toDelete = getSelectedAliases();
|
|
||||||
|
|
||||||
if(toDelete != null && !toDelete.isEmpty())
|
|
||||||
{
|
|
||||||
String title = toDelete.size() == 1 ? "Delete Alias?" : "Delete Aliases?";
|
|
||||||
String prompt = toDelete.size() == 1 ? "Do you want to delete this alias?" :
|
|
||||||
"Do you want to delete these " + toDelete.size() + " aliases?";
|
|
||||||
|
|
||||||
int choice = JOptionPane.showConfirmDialog(AliasController.this,
|
|
||||||
prompt, title, JOptionPane.YES_NO_OPTION, JOptionPane.WARNING_MESSAGE);
|
|
||||||
|
|
||||||
if(choice == JOptionPane.YES_OPTION)
|
|
||||||
{
|
|
||||||
for(Alias alias : toDelete)
|
|
||||||
{
|
|
||||||
mAliasModel.removeAlias(alias);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Colorizes the cell based on the cell's integer value
|
|
||||||
*/
|
|
||||||
public class ColorCellRenderer extends DefaultTableCellRenderer
|
|
||||||
{
|
|
||||||
private static final long serialVersionUID = 1L;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Component getTableCellRendererComponent(JTable table,
|
|
||||||
Object value, boolean isSelected, boolean hasFocus, int row,
|
|
||||||
int column)
|
|
||||||
{
|
|
||||||
JLabel label = (JLabel) super.getTableCellRendererComponent(table,
|
|
||||||
value, isSelected, hasFocus, row, column);
|
|
||||||
|
|
||||||
label.setBackground(new Color((int) value));
|
|
||||||
|
|
||||||
label.setText("");
|
|
||||||
|
|
||||||
return label;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Displays cell's icon and name
|
|
||||||
*/
|
|
||||||
public class IconCellRenderer extends DefaultTableCellRenderer
|
|
||||||
{
|
|
||||||
private static final long serialVersionUID = 1L;
|
|
||||||
|
|
||||||
private IconManager mIconManager;
|
|
||||||
|
|
||||||
public IconCellRenderer(IconManager iconManager)
|
|
||||||
{
|
|
||||||
mIconManager = iconManager;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Component getTableCellRendererComponent(JTable table,
|
|
||||||
Object value, boolean isSelected, boolean hasFocus, int row,
|
|
||||||
int column)
|
|
||||||
{
|
|
||||||
JLabel label = (JLabel) super.getTableCellRendererComponent(table,
|
|
||||||
value, isSelected, hasFocus, row, column);
|
|
||||||
|
|
||||||
ImageIcon icon = mIconManager.getIcon(label.getText(), 12);
|
|
||||||
|
|
||||||
if(icon != null)
|
|
||||||
{
|
|
||||||
label.setIcon(icon);
|
|
||||||
}
|
|
||||||
|
|
||||||
return label;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,157 +0,0 @@
|
|||||||
/*******************************************************************************
|
|
||||||
* SDR Trunk
|
|
||||||
* Copyright (C) 2014-2016 Dennis Sheirer
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
|
||||||
******************************************************************************/
|
|
||||||
package io.github.dsheirer.alias;
|
|
||||||
|
|
||||||
import com.jidesoft.swing.JideTabbedPane;
|
|
||||||
import io.github.dsheirer.alias.action.AliasActionEditor;
|
|
||||||
import io.github.dsheirer.alias.id.AliasIdentifierEditor;
|
|
||||||
import io.github.dsheirer.audio.broadcast.BroadcastModel;
|
|
||||||
import io.github.dsheirer.gui.editor.Editor;
|
|
||||||
import io.github.dsheirer.icon.IconManager;
|
|
||||||
import io.github.dsheirer.sample.Listener;
|
|
||||||
import net.miginfocom.swing.MigLayout;
|
|
||||||
|
|
||||||
import javax.swing.*;
|
|
||||||
import java.awt.*;
|
|
||||||
import java.awt.event.ActionEvent;
|
|
||||||
import java.awt.event.ActionListener;
|
|
||||||
|
|
||||||
public class AliasEditor extends Editor<Alias> implements ActionListener, Listener<AliasEvent>
|
|
||||||
{
|
|
||||||
private static final long serialVersionUID = 1L;
|
|
||||||
|
|
||||||
private static final String DEFAULT_NAME = "select an alias";
|
|
||||||
|
|
||||||
private AliasModel mAliasModel;
|
|
||||||
|
|
||||||
private JLabel mAliasLabel;
|
|
||||||
private AliasNameEditor mAliasNameEditor;
|
|
||||||
private AliasIdentifierEditor mAliasIdentifierEditor;
|
|
||||||
private AliasActionEditor mAliasActionEditor;
|
|
||||||
|
|
||||||
public AliasEditor(AliasModel aliasModel, BroadcastModel broadcastModel, IconManager iconManager )
|
|
||||||
{
|
|
||||||
mAliasModel = aliasModel;
|
|
||||||
mAliasNameEditor = new AliasNameEditor( mAliasModel, iconManager );
|
|
||||||
mAliasIdentifierEditor = new AliasIdentifierEditor( aliasModel, broadcastModel );
|
|
||||||
mAliasActionEditor = new AliasActionEditor( aliasModel );
|
|
||||||
|
|
||||||
mAliasModel.addListener( this );
|
|
||||||
|
|
||||||
init();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setItem( Alias alias )
|
|
||||||
{
|
|
||||||
super.setItem( alias );
|
|
||||||
|
|
||||||
if( hasItem() )
|
|
||||||
{
|
|
||||||
mAliasLabel.setText( getItem().getName() );
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
mAliasLabel.setText( DEFAULT_NAME );
|
|
||||||
}
|
|
||||||
|
|
||||||
mAliasNameEditor.setItem( alias );
|
|
||||||
mAliasIdentifierEditor.setItem( alias );
|
|
||||||
mAliasActionEditor.setItem( alias );
|
|
||||||
}
|
|
||||||
|
|
||||||
private void init()
|
|
||||||
{
|
|
||||||
setLayout( new MigLayout( "fill,wrap 2", "[grow,fill][grow,fill]",
|
|
||||||
"[][][][][][grow,fill]" ) );
|
|
||||||
|
|
||||||
add( new JLabel( "Alias:" ), "align right" );
|
|
||||||
mAliasLabel = new JLabel( DEFAULT_NAME );
|
|
||||||
add( mAliasLabel, "align left" );
|
|
||||||
|
|
||||||
add( new JSeparator( JSeparator.HORIZONTAL ), "span,growx,push" );
|
|
||||||
|
|
||||||
JideTabbedPane tabs = new JideTabbedPane();
|
|
||||||
tabs.setFont( this.getFont() );
|
|
||||||
tabs.setForeground( Color.BLACK );
|
|
||||||
|
|
||||||
tabs.addTab( "Alias", mAliasNameEditor );
|
|
||||||
tabs.addTab( "Audio / Identifier", mAliasIdentifierEditor );
|
|
||||||
tabs.addTab( "Action", mAliasActionEditor );
|
|
||||||
add( tabs, "span,grow" );
|
|
||||||
|
|
||||||
//Playlist management buttons
|
|
||||||
JButton btnSave = new JButton( "Save" );
|
|
||||||
btnSave.setToolTipText( "Save alias changes to the playlist" );
|
|
||||||
btnSave.addActionListener( AliasEditor.this );
|
|
||||||
add( btnSave );
|
|
||||||
|
|
||||||
JButton btnReset = new JButton( "Reset" );
|
|
||||||
btnReset.setToolTipText( "Reset alias changes since last save" );
|
|
||||||
btnReset.addActionListener( AliasEditor.this );
|
|
||||||
add( btnReset, "wrap" );
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void receive( AliasEvent event )
|
|
||||||
{
|
|
||||||
//If this is the currently displayed alias, reload it
|
|
||||||
if( hasItem() && getItem() == event.getAlias() )
|
|
||||||
{
|
|
||||||
switch( event.getEvent() )
|
|
||||||
{
|
|
||||||
case CHANGE:
|
|
||||||
//Alias changed - reset the editor with changed alias
|
|
||||||
setItem( event.getAlias() );
|
|
||||||
break;
|
|
||||||
case DELETE:
|
|
||||||
setItem( null );
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void actionPerformed( ActionEvent e )
|
|
||||||
{
|
|
||||||
if( hasItem() )
|
|
||||||
{
|
|
||||||
String command = e.getActionCommand();
|
|
||||||
|
|
||||||
if( command.contentEquals( "Save" ) )
|
|
||||||
{
|
|
||||||
save();
|
|
||||||
}
|
|
||||||
else if( command.contentEquals( "Reset" ) )
|
|
||||||
{
|
|
||||||
reset();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void save()
|
|
||||||
{
|
|
||||||
mAliasNameEditor.save();
|
|
||||||
mAliasIdentifierEditor.save();
|
|
||||||
mAliasActionEditor.save();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,55 +0,0 @@
|
|||||||
/*******************************************************************************
|
|
||||||
* SDR Trunk
|
|
||||||
* Copyright (C) 2014-2016 Dennis Sheirer
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
|
||||||
******************************************************************************/
|
|
||||||
package io.github.dsheirer.alias;
|
|
||||||
|
|
||||||
public class AliasEvent
|
|
||||||
{
|
|
||||||
private Alias mAlias;
|
|
||||||
private Event mEvent;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* AliasEvent - event describing any changes to an alias
|
|
||||||
* @param alias - alias that changed
|
|
||||||
* @param event - change event
|
|
||||||
*/
|
|
||||||
public AliasEvent( Alias alias, Event event )
|
|
||||||
{
|
|
||||||
mAlias = alias;
|
|
||||||
mEvent = event;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Alias getAlias()
|
|
||||||
{
|
|
||||||
return mAlias;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Event getEvent()
|
|
||||||
{
|
|
||||||
return mEvent;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Channel events to describe the specific event
|
|
||||||
*/
|
|
||||||
public enum Event
|
|
||||||
{
|
|
||||||
ADD,
|
|
||||||
CHANGE,
|
|
||||||
DELETE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
*
|
*
|
||||||
* * ******************************************************************************
|
* * ******************************************************************************
|
||||||
* * Copyright (C) 2014-2019 Dennis Sheirer
|
* * Copyright (C) 2014-2020 Dennis Sheirer
|
||||||
* *
|
* *
|
||||||
* * This program is free software: you can redistribute it and/or modify
|
* * This program is free software: you can redistribute it and/or modify
|
||||||
* * it under the terms of the GNU General Public License as published by
|
* * it under the terms of the GNU General Public License as published by
|
||||||
@@ -24,55 +24,40 @@ package io.github.dsheirer.alias;
|
|||||||
import io.github.dsheirer.alias.action.AliasAction;
|
import io.github.dsheirer.alias.action.AliasAction;
|
||||||
import io.github.dsheirer.alias.action.AliasActionType;
|
import io.github.dsheirer.alias.action.AliasActionType;
|
||||||
import io.github.dsheirer.alias.action.beep.BeepAction;
|
import io.github.dsheirer.alias.action.beep.BeepAction;
|
||||||
import io.github.dsheirer.alias.action.beep.BeepActionEditor;
|
|
||||||
import io.github.dsheirer.alias.action.clip.ClipAction;
|
import io.github.dsheirer.alias.action.clip.ClipAction;
|
||||||
import io.github.dsheirer.alias.action.clip.ClipActionEditor;
|
|
||||||
import io.github.dsheirer.alias.action.script.ScriptAction;
|
import io.github.dsheirer.alias.action.script.ScriptAction;
|
||||||
import io.github.dsheirer.alias.action.script.ScriptActionEditor;
|
|
||||||
import io.github.dsheirer.alias.id.AliasID;
|
import io.github.dsheirer.alias.id.AliasID;
|
||||||
import io.github.dsheirer.alias.id.AliasIDType;
|
import io.github.dsheirer.alias.id.AliasIDType;
|
||||||
import io.github.dsheirer.alias.id.broadcast.BroadcastChannel;
|
import io.github.dsheirer.alias.id.broadcast.BroadcastChannel;
|
||||||
import io.github.dsheirer.alias.id.broadcast.BroadcastChannelEditor;
|
|
||||||
import io.github.dsheirer.alias.id.esn.ESNEditor;
|
|
||||||
import io.github.dsheirer.alias.id.esn.Esn;
|
import io.github.dsheirer.alias.id.esn.Esn;
|
||||||
import io.github.dsheirer.alias.id.legacy.fleetsync.FleetsyncID;
|
import io.github.dsheirer.alias.id.legacy.fleetsync.FleetsyncID;
|
||||||
import io.github.dsheirer.alias.id.legacy.fleetsync.FleetsyncIDEditor;
|
|
||||||
import io.github.dsheirer.alias.id.legacy.mdc.MDC1200ID;
|
import io.github.dsheirer.alias.id.legacy.mdc.MDC1200ID;
|
||||||
import io.github.dsheirer.alias.id.legacy.mdc.MDC1200IDEditor;
|
|
||||||
import io.github.dsheirer.alias.id.legacy.mobileID.MINEditor;
|
|
||||||
import io.github.dsheirer.alias.id.legacy.mobileID.Min;
|
import io.github.dsheirer.alias.id.legacy.mobileID.Min;
|
||||||
import io.github.dsheirer.alias.id.legacy.mpt1327.MPT1327ID;
|
import io.github.dsheirer.alias.id.legacy.mpt1327.MPT1327ID;
|
||||||
import io.github.dsheirer.alias.id.legacy.mpt1327.MPT1327IDEditor;
|
|
||||||
import io.github.dsheirer.alias.id.legacy.nonrecordable.NonRecordable;
|
import io.github.dsheirer.alias.id.legacy.nonrecordable.NonRecordable;
|
||||||
import io.github.dsheirer.alias.id.legacy.nonrecordable.NonRecordableEditor;
|
|
||||||
import io.github.dsheirer.alias.id.legacy.siteID.SiteID;
|
import io.github.dsheirer.alias.id.legacy.siteID.SiteID;
|
||||||
import io.github.dsheirer.alias.id.legacy.siteID.SiteIDEditor;
|
|
||||||
import io.github.dsheirer.alias.id.legacy.talkgroup.LegacyTalkgroupID;
|
import io.github.dsheirer.alias.id.legacy.talkgroup.LegacyTalkgroupID;
|
||||||
import io.github.dsheirer.alias.id.legacy.talkgroup.LegacyTalkgroupIDEditor;
|
|
||||||
import io.github.dsheirer.alias.id.legacy.uniqueID.UniqueID;
|
import io.github.dsheirer.alias.id.legacy.uniqueID.UniqueID;
|
||||||
import io.github.dsheirer.alias.id.legacy.uniqueID.UniqueIDEditor;
|
|
||||||
import io.github.dsheirer.alias.id.lojack.LoJackFunctionAndID;
|
import io.github.dsheirer.alias.id.lojack.LoJackFunctionAndID;
|
||||||
import io.github.dsheirer.alias.id.lojack.LoJackIDEditor;
|
|
||||||
import io.github.dsheirer.alias.id.priority.Priority;
|
import io.github.dsheirer.alias.id.priority.Priority;
|
||||||
import io.github.dsheirer.alias.id.priority.PriorityEditor;
|
|
||||||
import io.github.dsheirer.alias.id.radio.Radio;
|
import io.github.dsheirer.alias.id.radio.Radio;
|
||||||
import io.github.dsheirer.alias.id.radio.RadioEditor;
|
|
||||||
import io.github.dsheirer.alias.id.radio.RadioRange;
|
import io.github.dsheirer.alias.id.radio.RadioRange;
|
||||||
import io.github.dsheirer.alias.id.radio.RadioRangeEditor;
|
|
||||||
import io.github.dsheirer.alias.id.record.Record;
|
import io.github.dsheirer.alias.id.record.Record;
|
||||||
import io.github.dsheirer.alias.id.record.RecordEditor;
|
import io.github.dsheirer.alias.id.status.UnitStatusID;
|
||||||
import io.github.dsheirer.alias.id.status.StatusID;
|
import io.github.dsheirer.alias.id.status.UserStatusID;
|
||||||
import io.github.dsheirer.alias.id.status.StatusIDEditor;
|
|
||||||
import io.github.dsheirer.alias.id.talkgroup.Talkgroup;
|
import io.github.dsheirer.alias.id.talkgroup.Talkgroup;
|
||||||
import io.github.dsheirer.alias.id.talkgroup.TalkgroupEditor;
|
|
||||||
import io.github.dsheirer.alias.id.talkgroup.TalkgroupRange;
|
import io.github.dsheirer.alias.id.talkgroup.TalkgroupRange;
|
||||||
import io.github.dsheirer.alias.id.talkgroup.TalkgroupRangeEditor;
|
import io.github.dsheirer.alias.id.tone.TonesID;
|
||||||
import io.github.dsheirer.audio.broadcast.BroadcastModel;
|
import org.slf4j.Logger;
|
||||||
import io.github.dsheirer.gui.editor.Editor;
|
import org.slf4j.LoggerFactory;
|
||||||
import io.github.dsheirer.gui.editor.EmptyEditor;
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
public class AliasFactory
|
public class AliasFactory
|
||||||
{
|
{
|
||||||
|
private static final Logger mLog = LoggerFactory.getLogger(AliasFactory.class);
|
||||||
|
|
||||||
public static AliasID copyOf(AliasID id)
|
public static AliasID copyOf(AliasID id)
|
||||||
{
|
{
|
||||||
switch(id.getType())
|
switch(id.getType())
|
||||||
@@ -87,11 +72,6 @@ public class AliasFactory
|
|||||||
Esn copyESN = new Esn();
|
Esn copyESN = new Esn();
|
||||||
copyESN.setEsn(originalESN.getEsn());
|
copyESN.setEsn(originalESN.getEsn());
|
||||||
return copyESN;
|
return copyESN;
|
||||||
case LTR_NET_UID:
|
|
||||||
UniqueID originalUniqueID = (UniqueID)id;
|
|
||||||
UniqueID copyUniqueID = new UniqueID();
|
|
||||||
copyUniqueID.setUid(originalUniqueID.getUid());
|
|
||||||
return copyUniqueID;
|
|
||||||
case LOJACK:
|
case LOJACK:
|
||||||
LoJackFunctionAndID originalLoJackFunctionAndID = (LoJackFunctionAndID)id;
|
LoJackFunctionAndID originalLoJackFunctionAndID = (LoJackFunctionAndID)id;
|
||||||
LoJackFunctionAndID copyLoJackFunctionAndID = new LoJackFunctionAndID();
|
LoJackFunctionAndID copyLoJackFunctionAndID = new LoJackFunctionAndID();
|
||||||
@@ -111,11 +91,13 @@ public class AliasFactory
|
|||||||
case RADIO_ID:
|
case RADIO_ID:
|
||||||
Radio originalRadio = (Radio)id;
|
Radio originalRadio = (Radio)id;
|
||||||
Radio copyRadio = new Radio(originalRadio.getProtocol(), originalRadio.getValue());
|
Radio copyRadio = new Radio(originalRadio.getProtocol(), originalRadio.getValue());
|
||||||
|
copyRadio.setOverlap(originalRadio.overlapProperty().get());
|
||||||
return copyRadio;
|
return copyRadio;
|
||||||
case RADIO_ID_RANGE:
|
case RADIO_ID_RANGE:
|
||||||
RadioRange originalRadioRange = (RadioRange)id;
|
RadioRange originalRadioRange = (RadioRange)id;
|
||||||
RadioRange copyRadioRange = new RadioRange(originalRadioRange.getProtocol(), originalRadioRange.getMinRadio(),
|
RadioRange copyRadioRange = new RadioRange(originalRadioRange.getProtocol(), originalRadioRange.getMinRadio(),
|
||||||
originalRadioRange.getMaxRadio());
|
originalRadioRange.getMaxRadio());
|
||||||
|
copyRadioRange.setOverlap(originalRadioRange.overlapProperty().get());
|
||||||
return copyRadioRange;
|
return copyRadioRange;
|
||||||
case RECORD:
|
case RECORD:
|
||||||
return new Record();
|
return new Record();
|
||||||
@@ -125,27 +107,45 @@ public class AliasFactory
|
|||||||
copySiteID.setSite(originalSiteID.getSite());
|
copySiteID.setSite(originalSiteID.getSite());
|
||||||
return copySiteID;
|
return copySiteID;
|
||||||
case STATUS:
|
case STATUS:
|
||||||
StatusID originalStatusID = (StatusID)id;
|
UserStatusID originalUserStatusID = (UserStatusID)id;
|
||||||
StatusID copyStatusID = new StatusID();
|
UserStatusID copyUserStatusID = new UserStatusID();
|
||||||
copyStatusID.setStatus(originalStatusID.getStatus());
|
copyUserStatusID.setStatus(originalUserStatusID.getStatus());
|
||||||
return copyStatusID;
|
copyUserStatusID.setOverlap(originalUserStatusID.overlapProperty().get());
|
||||||
|
return copyUserStatusID;
|
||||||
case TALKGROUP:
|
case TALKGROUP:
|
||||||
Talkgroup originalTalkgroup = (Talkgroup)id;
|
Talkgroup originalTalkgroup = (Talkgroup)id;
|
||||||
Talkgroup copyTalkgroup = new Talkgroup(originalTalkgroup.getProtocol(), originalTalkgroup.getValue());
|
Talkgroup copyTalkgroup = new Talkgroup(originalTalkgroup.getProtocol(), originalTalkgroup.getValue());
|
||||||
|
copyTalkgroup.setOverlap(originalTalkgroup.overlapProperty().get());
|
||||||
return copyTalkgroup;
|
return copyTalkgroup;
|
||||||
case TALKGROUP_RANGE:
|
case TALKGROUP_RANGE:
|
||||||
TalkgroupRange originalRange = (TalkgroupRange)id;
|
TalkgroupRange originalRange = (TalkgroupRange)id;
|
||||||
TalkgroupRange copyRange = new TalkgroupRange(originalRange.getProtocol(), originalRange.getMinTalkgroup(),
|
TalkgroupRange copyRange = new TalkgroupRange(originalRange.getProtocol(), originalRange.getMinTalkgroup(),
|
||||||
originalRange.getMaxTalkgroup());
|
originalRange.getMaxTalkgroup());
|
||||||
|
copyRange.setOverlap(originalRange.overlapProperty().get());
|
||||||
return copyRange;
|
return copyRange;
|
||||||
|
case TONES:
|
||||||
|
TonesID originalTones = (TonesID)id;
|
||||||
|
TonesID copyTones = new TonesID();
|
||||||
|
copyTones.setToneSequence(originalTones.getToneSequence().copyOf());
|
||||||
|
return copyTones;
|
||||||
|
case UNIT_STATUS:
|
||||||
|
UnitStatusID originalUnitStatus = (UnitStatusID)id;
|
||||||
|
UnitStatusID copyUnitStatusID = new UnitStatusID();
|
||||||
|
copyUnitStatusID.setStatus(originalUnitStatus.getStatus());
|
||||||
|
copyUnitStatusID.setOverlap(originalUnitStatus.overlapProperty().get());
|
||||||
|
return copyUnitStatusID;
|
||||||
|
|
||||||
//Legacy identifiers ... not supported
|
//Legacy identifiers ... not supported
|
||||||
case FLEETSYNC:
|
case FLEETSYNC:
|
||||||
case LEGACY_TALKGROUP:
|
case LEGACY_TALKGROUP:
|
||||||
|
case LTR_NET_UID:
|
||||||
case MDC1200:
|
case MDC1200:
|
||||||
case MPT1327:
|
case MPT1327:
|
||||||
case NON_RECORDABLE:
|
case NON_RECORDABLE:
|
||||||
|
return null;
|
||||||
default:
|
default:
|
||||||
|
mLog.warn("Unrecognized Alias ID Type [" + id.getType() + "] cannot make copy of instance");
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
@@ -155,7 +155,11 @@ public class AliasFactory
|
|||||||
{
|
{
|
||||||
if(action instanceof BeepAction)
|
if(action instanceof BeepAction)
|
||||||
{
|
{
|
||||||
return new BeepAction();
|
BeepAction original = (BeepAction)action;
|
||||||
|
BeepAction copyBeep = new BeepAction();
|
||||||
|
copyBeep.setInterval(original.getInterval());
|
||||||
|
copyBeep.setPeriod(original.getPeriod());
|
||||||
|
return copyBeep;
|
||||||
}
|
}
|
||||||
else if(action instanceof ClipAction)
|
else if(action instanceof ClipAction)
|
||||||
{
|
{
|
||||||
@@ -179,106 +183,59 @@ public class AliasFactory
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Alias copyOf(Alias original)
|
public static Alias shallowCopyOf(Alias original)
|
||||||
{
|
{
|
||||||
Alias copy = new Alias(original.getName());
|
Alias copy = new Alias(original.getName());
|
||||||
copy.setList(original.getList());
|
copy.setAliasListName(original.getAliasListName());
|
||||||
copy.setGroup(original.getGroup());
|
copy.setGroup(original.getGroup());
|
||||||
copy.setColor(original.getColor());
|
copy.setColor(original.getColor());
|
||||||
copy.setIconName(original.getIconName());
|
copy.setIconName(original.getIconName());
|
||||||
|
|
||||||
for(AliasID id : original.getId())
|
|
||||||
{
|
|
||||||
AliasID copyID = copyOf(id);
|
|
||||||
|
|
||||||
if(copyID != null)
|
|
||||||
{
|
|
||||||
copy.addAliasID(copyID);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for(AliasAction action : original.getAction())
|
|
||||||
{
|
|
||||||
AliasAction copyAction = copyOf(action);
|
|
||||||
|
|
||||||
if(copyAction != null)
|
|
||||||
{
|
|
||||||
copy.addAliasAction(copyAction);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return copy;
|
return copy;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Editor<AliasID> getEditor(AliasID aliasID, BroadcastModel broadcastModel)
|
public static List<AliasID> copyAliasIDs(Alias original)
|
||||||
{
|
{
|
||||||
if(aliasID != null)
|
List<AliasID> aliasIDS = new ArrayList<>();
|
||||||
{
|
|
||||||
switch(aliasID.getType())
|
|
||||||
{
|
|
||||||
case BROADCAST_CHANNEL:
|
|
||||||
return new BroadcastChannelEditor(aliasID, broadcastModel);
|
|
||||||
case ESN:
|
|
||||||
return new ESNEditor(aliasID);
|
|
||||||
case LTR_NET_UID:
|
|
||||||
return new UniqueIDEditor(aliasID);
|
|
||||||
case LOJACK:
|
|
||||||
return new LoJackIDEditor(aliasID);
|
|
||||||
case MIN:
|
|
||||||
return new MINEditor(aliasID);
|
|
||||||
case PRIORITY:
|
|
||||||
return new PriorityEditor(aliasID);
|
|
||||||
case RADIO_ID:
|
|
||||||
return new RadioEditor(aliasID);
|
|
||||||
case RADIO_ID_RANGE:
|
|
||||||
return new RadioRangeEditor(aliasID);
|
|
||||||
case RECORD:
|
|
||||||
return new RecordEditor(aliasID);
|
|
||||||
case SITE:
|
|
||||||
return new SiteIDEditor(aliasID);
|
|
||||||
case STATUS:
|
|
||||||
return new StatusIDEditor(aliasID);
|
|
||||||
case TALKGROUP:
|
|
||||||
return new TalkgroupEditor(aliasID);
|
|
||||||
case TALKGROUP_RANGE:
|
|
||||||
return new TalkgroupRangeEditor(aliasID);
|
|
||||||
|
|
||||||
case FLEETSYNC:
|
for(AliasID id : original.getAliasIdentifiers())
|
||||||
return new FleetsyncIDEditor(aliasID);
|
{
|
||||||
case LEGACY_TALKGROUP:
|
aliasIDS.add(copyOf(id));
|
||||||
return new LegacyTalkgroupIDEditor(aliasID);
|
|
||||||
case MDC1200:
|
|
||||||
return new MDC1200IDEditor(aliasID);
|
|
||||||
case MPT1327:
|
|
||||||
return new MPT1327IDEditor(aliasID);
|
|
||||||
case NON_RECORDABLE:
|
|
||||||
return new NonRecordableEditor(aliasID);
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return new EmptyEditor<AliasID>();
|
return aliasIDS;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Editor<AliasAction> getEditor(AliasAction aliasAction)
|
public static List<AliasAction> copyAliasActions(Alias original)
|
||||||
{
|
{
|
||||||
if(aliasAction != null)
|
List<AliasAction> actions = new ArrayList<>();
|
||||||
|
|
||||||
|
for(AliasAction aliasAction: original.getAliasActions())
|
||||||
{
|
{
|
||||||
switch(aliasAction.getType())
|
actions.add(copyOf(aliasAction));
|
||||||
{
|
|
||||||
case BEEP:
|
|
||||||
return new BeepActionEditor(aliasAction);
|
|
||||||
case CLIP:
|
|
||||||
return new ClipActionEditor(aliasAction);
|
|
||||||
case SCRIPT:
|
|
||||||
return new ScriptActionEditor(aliasAction);
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return new EmptyEditor<AliasAction>();
|
return actions;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Alias copyOf(Alias original)
|
||||||
|
{
|
||||||
|
Alias copy = shallowCopyOf(original);
|
||||||
|
|
||||||
|
List<AliasID> aliasIDS = copyAliasIDs(original);
|
||||||
|
|
||||||
|
for(AliasID aliasID: aliasIDS)
|
||||||
|
{
|
||||||
|
copy.addAliasID(aliasID);
|
||||||
|
}
|
||||||
|
|
||||||
|
List<AliasAction> actions = copyAliasActions(original);
|
||||||
|
|
||||||
|
for(AliasAction action: actions)
|
||||||
|
{
|
||||||
|
copy.addAliasAction(action);
|
||||||
|
}
|
||||||
|
|
||||||
|
return copy;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static AliasID getAliasID(AliasIDType type)
|
public static AliasID getAliasID(AliasIDType type)
|
||||||
@@ -318,7 +275,7 @@ public class AliasFactory
|
|||||||
case SITE:
|
case SITE:
|
||||||
return new SiteID();
|
return new SiteID();
|
||||||
case STATUS:
|
case STATUS:
|
||||||
return new StatusID();
|
return new UserStatusID();
|
||||||
case LEGACY_TALKGROUP:
|
case LEGACY_TALKGROUP:
|
||||||
return new LegacyTalkgroupID();
|
return new LegacyTalkgroupID();
|
||||||
default:
|
default:
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
*
|
*
|
||||||
* * ******************************************************************************
|
* * ******************************************************************************
|
||||||
* * Copyright (C) 2014-2019 Dennis Sheirer
|
* * Copyright (C) 2014-2020 Dennis Sheirer
|
||||||
* *
|
* *
|
||||||
* * This program is free software: you can redistribute it and/or modify
|
* * This program is free software: you can redistribute it and/or modify
|
||||||
* * it under the terms of the GNU General Public License as published by
|
* * it under the terms of the GNU General Public License as published by
|
||||||
@@ -28,9 +28,11 @@ import io.github.dsheirer.alias.id.esn.Esn;
|
|||||||
import io.github.dsheirer.alias.id.priority.Priority;
|
import io.github.dsheirer.alias.id.priority.Priority;
|
||||||
import io.github.dsheirer.alias.id.radio.Radio;
|
import io.github.dsheirer.alias.id.radio.Radio;
|
||||||
import io.github.dsheirer.alias.id.radio.RadioRange;
|
import io.github.dsheirer.alias.id.radio.RadioRange;
|
||||||
import io.github.dsheirer.alias.id.status.StatusID;
|
import io.github.dsheirer.alias.id.status.UnitStatusID;
|
||||||
|
import io.github.dsheirer.alias.id.status.UserStatusID;
|
||||||
import io.github.dsheirer.alias.id.talkgroup.Talkgroup;
|
import io.github.dsheirer.alias.id.talkgroup.Talkgroup;
|
||||||
import io.github.dsheirer.alias.id.talkgroup.TalkgroupRange;
|
import io.github.dsheirer.alias.id.talkgroup.TalkgroupRange;
|
||||||
|
import io.github.dsheirer.alias.id.tone.TonesID;
|
||||||
import io.github.dsheirer.identifier.Identifier;
|
import io.github.dsheirer.identifier.Identifier;
|
||||||
import io.github.dsheirer.identifier.IdentifierCollection;
|
import io.github.dsheirer.identifier.IdentifierCollection;
|
||||||
import io.github.dsheirer.identifier.esn.ESNIdentifier;
|
import io.github.dsheirer.identifier.esn.ESNIdentifier;
|
||||||
@@ -40,32 +42,41 @@ import io.github.dsheirer.identifier.radio.RadioIdentifier;
|
|||||||
import io.github.dsheirer.identifier.status.UnitStatusIdentifier;
|
import io.github.dsheirer.identifier.status.UnitStatusIdentifier;
|
||||||
import io.github.dsheirer.identifier.status.UserStatusIdentifier;
|
import io.github.dsheirer.identifier.status.UserStatusIdentifier;
|
||||||
import io.github.dsheirer.identifier.talkgroup.TalkgroupIdentifier;
|
import io.github.dsheirer.identifier.talkgroup.TalkgroupIdentifier;
|
||||||
|
import io.github.dsheirer.identifier.tone.ToneIdentifier;
|
||||||
|
import io.github.dsheirer.identifier.tone.ToneSequence;
|
||||||
import io.github.dsheirer.protocol.Protocol;
|
import io.github.dsheirer.protocol.Protocol;
|
||||||
import io.github.dsheirer.sample.Listener;
|
import javafx.collections.FXCollections;
|
||||||
|
import javafx.collections.ObservableList;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
import java.util.EnumMap;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Iterator;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
import java.util.TreeMap;
|
import java.util.TreeMap;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* List of aliases that share the same alias list name and provides convenient methods for looking up alias
|
* List of aliases that share the same alias list name and provides convenient methods for looking up alias
|
||||||
* objects that match an identifier.
|
* objects that match an identifier.
|
||||||
*/
|
*/
|
||||||
public class AliasList implements Listener<AliasEvent>
|
public class AliasList
|
||||||
{
|
{
|
||||||
private final static Logger mLog = LoggerFactory.getLogger(AliasList.class);
|
private final static Logger mLog = LoggerFactory.getLogger(AliasList.class);
|
||||||
private Map<Protocol,TalkgroupAliasList> mTalkgroupProtocolMap = new HashMap<>();
|
private Map<Protocol,TalkgroupAliasList> mTalkgroupProtocolMap = new EnumMap<>(Protocol.class);
|
||||||
private Map<Protocol,RadioAliasList> mRadioProtocolMap = new HashMap<>();
|
private Map<Protocol,RadioAliasList> mRadioProtocolMap = new EnumMap<>(Protocol.class);
|
||||||
private Map<String,Alias> mESNMap = new HashMap<>();
|
private Map<String,Alias> mESNMap = new HashMap<>();
|
||||||
private Map<Integer,Alias> mStatusMap = new HashMap<>();
|
private Map<Integer,Alias> mUnitStatusMap = new HashMap<>();
|
||||||
|
private Map<Integer,Alias> mUserStatusMap = new HashMap<>();
|
||||||
|
private Map<ToneSequence,Alias> mToneSequenceMap = new HashMap<>();
|
||||||
private boolean mHasAliasActions = false;
|
private boolean mHasAliasActions = false;
|
||||||
private String mName;
|
private String mName;
|
||||||
|
private ObservableList<Alias> mAliases = FXCollections.observableArrayList(Alias.extractor());
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* List of aliases where all aliases share the same list name. Contains
|
* List of aliases where all aliases share the same list name. Contains
|
||||||
@@ -78,6 +89,14 @@ public class AliasList implements Listener<AliasEvent>
|
|||||||
mName = name;
|
mName = name;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Observable list of aliases contained in this alias list
|
||||||
|
*/
|
||||||
|
public ObservableList<Alias> aliases()
|
||||||
|
{
|
||||||
|
return mAliases;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds the alias to this list
|
* Adds the alias to this list
|
||||||
*/
|
*/
|
||||||
@@ -85,16 +104,18 @@ public class AliasList implements Listener<AliasEvent>
|
|||||||
{
|
{
|
||||||
if(alias != null)
|
if(alias != null)
|
||||||
{
|
{
|
||||||
for(AliasID aliasID : alias.getId())
|
alias.getAliasIdentifiers().stream().forEach(aliasID -> addAliasID(aliasID, alias));
|
||||||
{
|
|
||||||
addAliasID(aliasID, alias);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if(alias.hasActions())
|
if(alias.hasActions())
|
||||||
{
|
{
|
||||||
mHasAliasActions = true;
|
mHasAliasActions = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(!mAliases.contains(alias))
|
||||||
|
{
|
||||||
|
mAliases.add(alias);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -161,7 +182,7 @@ public class AliasList implements Listener<AliasEvent>
|
|||||||
radioRangeAliasList.add(radioRange, alias);
|
radioRangeAliasList.add(radioRange, alias);
|
||||||
break;
|
break;
|
||||||
case ESN:
|
case ESN:
|
||||||
String esn = ((Esn) id).getEsn();
|
String esn = ((Esn)id).getEsn();
|
||||||
|
|
||||||
if(esn != null && !esn.isEmpty())
|
if(esn != null && !esn.isEmpty())
|
||||||
{
|
{
|
||||||
@@ -169,7 +190,67 @@ public class AliasList implements Listener<AliasEvent>
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case STATUS:
|
case STATUS:
|
||||||
mStatusMap.put(((StatusID) id).getStatus(), alias);
|
int userStatus = ((UserStatusID)id).getStatus();
|
||||||
|
|
||||||
|
if(mUserStatusMap.containsKey(userStatus) && !mUserStatusMap.get(userStatus).equals(alias))
|
||||||
|
{
|
||||||
|
id.setOverlap(true);
|
||||||
|
|
||||||
|
Alias existing = mUserStatusMap.get(userStatus);
|
||||||
|
|
||||||
|
for(AliasID aliasID: existing.getAliasIdentifiers())
|
||||||
|
{
|
||||||
|
if(aliasID instanceof UserStatusID && ((UserStatusID)aliasID).getStatus() == userStatus)
|
||||||
|
{
|
||||||
|
aliasID.setOverlap(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mUserStatusMap.put(userStatus, alias);
|
||||||
|
break;
|
||||||
|
case UNIT_STATUS:
|
||||||
|
int unitStatus = ((UnitStatusID)id).getStatus();
|
||||||
|
|
||||||
|
if(mUnitStatusMap.containsKey(unitStatus) && !mUnitStatusMap.get(unitStatus).equals(alias))
|
||||||
|
{
|
||||||
|
id.setOverlap(true);
|
||||||
|
|
||||||
|
Alias existing = mUnitStatusMap.get(unitStatus);
|
||||||
|
|
||||||
|
for(AliasID aliasID: existing.getAliasIdentifiers())
|
||||||
|
{
|
||||||
|
if(aliasID instanceof UnitStatusID && ((UnitStatusID)aliasID).getStatus() == unitStatus)
|
||||||
|
{
|
||||||
|
aliasID.setOverlap(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mUnitStatusMap.put(unitStatus, alias);
|
||||||
|
break;
|
||||||
|
case TONES:
|
||||||
|
ToneSequence toneSequence = ((TonesID)id).getToneSequence();
|
||||||
|
|
||||||
|
if(toneSequence != null)
|
||||||
|
{
|
||||||
|
if(mToneSequenceMap.containsKey(toneSequence) && !mToneSequenceMap.get(toneSequence).equals(alias))
|
||||||
|
{
|
||||||
|
id.setOverlap(true);
|
||||||
|
|
||||||
|
Alias existing = mToneSequenceMap.get(toneSequence);
|
||||||
|
|
||||||
|
for(AliasID aliasID: existing.getAliasIdentifiers())
|
||||||
|
{
|
||||||
|
if(aliasID instanceof TonesID && aliasID.equals(id))
|
||||||
|
{
|
||||||
|
aliasID.setOverlap(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
mToneSequenceMap.put(toneSequence, alias);
|
||||||
|
}
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -185,13 +266,45 @@ public class AliasList implements Listener<AliasEvent>
|
|||||||
*/
|
*/
|
||||||
public void removeAlias(Alias alias)
|
public void removeAlias(Alias alias)
|
||||||
{
|
{
|
||||||
for(TalkgroupAliasList talkgroupAliasList: mTalkgroupProtocolMap.values())
|
//Note: because the alias' identifiers could have changed from when we initially added the alias, we have to
|
||||||
|
//inspect every collection and map to remove the alias completely.
|
||||||
|
mAliases.remove(alias);
|
||||||
|
|
||||||
|
mTalkgroupProtocolMap.values().stream().forEach(talkgroupAliasList -> talkgroupAliasList.remove(alias));
|
||||||
|
mRadioProtocolMap.values().stream().forEach(radioAliasList -> radioAliasList.remove(alias));
|
||||||
|
|
||||||
|
Collection<Alias> collection = Collections.singleton(alias);
|
||||||
|
mESNMap.values().removeAll(collection);
|
||||||
|
mUnitStatusMap.values().removeAll(collection);
|
||||||
|
mUserStatusMap.values().removeAll(collection);
|
||||||
|
mToneSequenceMap.values().removeAll(collection);
|
||||||
|
|
||||||
|
validate();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Identifies all aliases with an alias identifier that has the overlap flag set, resets the flag, and then readds
|
||||||
|
* each alias back to this alias list so that overlap can be detected again.
|
||||||
|
*/
|
||||||
|
public void validate()
|
||||||
|
{
|
||||||
|
Set<Alias> overlapAliases = new HashSet<>();
|
||||||
|
|
||||||
|
List<Alias> aliases = new ArrayList<>(mAliases);
|
||||||
|
|
||||||
|
for(Alias alias: aliases)
|
||||||
{
|
{
|
||||||
talkgroupAliasList.remove(alias);
|
for(AliasID aliasID: alias.getAliasIdentifiers())
|
||||||
|
{
|
||||||
|
if(aliasID.overlapProperty().get())
|
||||||
|
{
|
||||||
|
aliasID.setOverlap(false);
|
||||||
|
overlapAliases.add(alias);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
remove(alias, mStatusMap);
|
overlapAliases.stream().forEach(alias -> addAlias(alias));
|
||||||
remove(alias, mESNMap);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -235,39 +348,15 @@ public class AliasList implements Listener<AliasEvent>
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Receive alias change event notifications and modify this list accordingly
|
* Updates the alias by removing it from this list and then adding it back to this list when the list name matches.
|
||||||
*/
|
*/
|
||||||
@Override
|
public void updateAlias(Alias alias)
|
||||||
public void receive(AliasEvent event)
|
|
||||||
{
|
{
|
||||||
if(hasName())
|
removeAlias(alias);
|
||||||
{
|
|
||||||
Alias alias = event.getAlias();
|
|
||||||
|
|
||||||
switch(event.getEvent())
|
if(hasName() && getName().equals(alias.getAliasListName()))
|
||||||
{
|
{
|
||||||
case ADD:
|
addAlias(alias);
|
||||||
if(alias.getList() != null && getName().equalsIgnoreCase(alias.getList()))
|
|
||||||
{
|
|
||||||
addAlias(alias);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case CHANGE:
|
|
||||||
if(alias.getList() != null && getName().equalsIgnoreCase(alias.getList()))
|
|
||||||
{
|
|
||||||
removeAlias(alias);
|
|
||||||
addAlias(alias);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case DELETE:
|
|
||||||
if(alias.getList() != null && getName().equalsIgnoreCase(alias.getList()))
|
|
||||||
{
|
|
||||||
removeAlias(alias);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -342,14 +431,31 @@ public class AliasList implements Listener<AliasEvent>
|
|||||||
if(identifier instanceof UnitStatusIdentifier)
|
if(identifier instanceof UnitStatusIdentifier)
|
||||||
{
|
{
|
||||||
int status = ((UnitStatusIdentifier)identifier).getValue();
|
int status = ((UnitStatusIdentifier)identifier).getValue();
|
||||||
return toList(mStatusMap.get(status));
|
return toList(mUserStatusMap.get(status));
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case USER_STATUS:
|
case USER_STATUS:
|
||||||
if(identifier instanceof UserStatusIdentifier)
|
if(identifier instanceof UserStatusIdentifier)
|
||||||
{
|
{
|
||||||
int status = ((UserStatusIdentifier)identifier).getValue();
|
int status = ((UserStatusIdentifier)identifier).getValue();
|
||||||
return toList(mStatusMap.get(status));
|
return toList(mUserStatusMap.get(status));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case TONE:
|
||||||
|
if(identifier instanceof ToneIdentifier)
|
||||||
|
{
|
||||||
|
ToneSequence toneSequence = ((ToneIdentifier)identifier).getValue();
|
||||||
|
|
||||||
|
if(toneSequence != null && toneSequence.hasTones())
|
||||||
|
{
|
||||||
|
for(Map.Entry<ToneSequence,Alias> entry: mToneSequenceMap.entrySet())
|
||||||
|
{
|
||||||
|
if(entry.getKey().isContainedIn(toneSequence))
|
||||||
|
{
|
||||||
|
return toList(entry.getValue());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -482,23 +588,6 @@ public class AliasList implements Listener<AliasEvent>
|
|||||||
return channels;
|
return channels;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Removes the alias (as a value) from the specified map
|
|
||||||
*/
|
|
||||||
public static void remove(Alias alias, Map map)
|
|
||||||
{
|
|
||||||
Iterator<Map.Entry> it = map.entrySet().iterator();
|
|
||||||
|
|
||||||
while(it.hasNext())
|
|
||||||
{
|
|
||||||
if(it.next().getValue().equals(alias))
|
|
||||||
{
|
|
||||||
it.remove();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Listing of talkgroups and ranges for a specific protocol
|
* Listing of talkgroups and ranges for a specific protocol
|
||||||
*/
|
*/
|
||||||
@@ -515,16 +604,17 @@ public class AliasList implements Listener<AliasEvent>
|
|||||||
{
|
{
|
||||||
int value = identifier.getValue();
|
int value = identifier.getValue();
|
||||||
|
|
||||||
if(mTalkgroupAliasMap.containsKey(value))
|
Alias mapValue = mTalkgroupAliasMap.get(value);
|
||||||
|
if (mapValue != null)
|
||||||
{
|
{
|
||||||
return mTalkgroupAliasMap.get(value);
|
return mapValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
for(TalkgroupRange talkgroupRange: mTalkgroupRangeAliasMap.keySet())
|
for(Map.Entry<TalkgroupRange, Alias> entry : mTalkgroupRangeAliasMap.entrySet())
|
||||||
{
|
{
|
||||||
if(talkgroupRange.contains(value))
|
if(entry.getKey().contains(value))
|
||||||
{
|
{
|
||||||
return mTalkgroupRangeAliasMap.get(talkgroupRange);
|
return entry.getValue();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -533,16 +623,22 @@ public class AliasList implements Listener<AliasEvent>
|
|||||||
|
|
||||||
public void add(Talkgroup talkgroup, Alias alias)
|
public void add(Talkgroup talkgroup, Alias alias)
|
||||||
{
|
{
|
||||||
//Detect talkgroup collisions
|
//Detect talkgroup collisions and set overlap flag for both
|
||||||
if(mTalkgroupAliasMap.containsKey(talkgroup.getValue()))
|
if(mTalkgroupAliasMap.containsKey(talkgroup.getValue()))
|
||||||
{
|
{
|
||||||
Alias existing = mTalkgroupAliasMap.get(talkgroup.getValue());
|
Alias existing = mTalkgroupAliasMap.get(talkgroup.getValue());
|
||||||
|
|
||||||
if(!existing.equals(alias))
|
if(!existing.equals(alias))
|
||||||
{
|
{
|
||||||
mLog.warn("Alias [" + alias.getName() + "] talkgroup [" + talkgroup.getValue() +
|
talkgroup.setOverlap(true);
|
||||||
"] has the same talkgroup value as alias [" + existing.getName() +
|
|
||||||
"] - alias [" + alias.getName() + "] will be used for alias list [" + getName() + "]");
|
for(AliasID aliasID: existing.getAliasIdentifiers())
|
||||||
|
{
|
||||||
|
if(aliasID instanceof Talkgroup && ((Talkgroup)aliasID).getValue() == talkgroup.getValue())
|
||||||
|
{
|
||||||
|
aliasID.setOverlap(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -556,32 +652,21 @@ public class AliasList implements Listener<AliasEvent>
|
|||||||
{
|
{
|
||||||
if(talkgroupRange.overlaps(entry.getKey()) && !entry.getValue().equals(alias))
|
if(talkgroupRange.overlaps(entry.getKey()) && !entry.getValue().equals(alias))
|
||||||
{
|
{
|
||||||
mLog.warn("Alias [" + alias.getName() + "] with talkgroup range [" + talkgroupRange.toString() +
|
talkgroupRange.setOverlap(true);
|
||||||
"] overlaps with alias [" + entry.getValue().getName() +
|
entry.getKey().setOverlap(true);
|
||||||
"] with talkgroup range [" + entry.getKey().toString() + "] for alias list [" + getName() + "]");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mTalkgroupRangeAliasMap.put(talkgroupRange, alias);
|
mTalkgroupRangeAliasMap.put(talkgroupRange, alias);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void remove(Talkgroup talkgroup)
|
|
||||||
{
|
|
||||||
mTalkgroupAliasMap.remove(talkgroup.getValue());
|
|
||||||
}
|
|
||||||
|
|
||||||
public void remove(TalkgroupRange talkgroupRange)
|
|
||||||
{
|
|
||||||
mTalkgroupRangeAliasMap.remove(talkgroupRange);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Removes the alias from all internal maps
|
* Removes the alias from both the talkgroup and the talkgroup range maps.
|
||||||
*/
|
*/
|
||||||
public void remove(Alias alias)
|
public void remove(Alias alias)
|
||||||
{
|
{
|
||||||
AliasList.remove(alias, mTalkgroupAliasMap);
|
mTalkgroupAliasMap.values().removeAll(Collections.singleton(alias));
|
||||||
AliasList.remove(alias, mTalkgroupRangeAliasMap);
|
mTalkgroupRangeAliasMap.values().removeAll(Collections.singleton(alias));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -601,16 +686,17 @@ public class AliasList implements Listener<AliasEvent>
|
|||||||
{
|
{
|
||||||
int value = identifier.getValue();
|
int value = identifier.getValue();
|
||||||
|
|
||||||
if(mRadioAliasMap.containsKey(value))
|
Alias mapValue = mRadioAliasMap.get(value);
|
||||||
|
if(mapValue != null)
|
||||||
{
|
{
|
||||||
return mRadioAliasMap.get(value);
|
return mapValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
for(RadioRange radioRange: mRadioRangeAliasMap.keySet())
|
for(Map.Entry<RadioRange, Alias> entry : mRadioRangeAliasMap.entrySet())
|
||||||
{
|
{
|
||||||
if(radioRange.contains(value))
|
if(entry.getKey().contains(value))
|
||||||
{
|
{
|
||||||
return mRadioRangeAliasMap.get(radioRange);
|
return entry.getValue();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -626,9 +712,15 @@ public class AliasList implements Listener<AliasEvent>
|
|||||||
|
|
||||||
if(!existing.equals(alias))
|
if(!existing.equals(alias))
|
||||||
{
|
{
|
||||||
mLog.warn("Alias [" + alias.getName() + "] radio ID [" + radio.getValue() +
|
radio.setOverlap(true);
|
||||||
"] has the same value as alias [" + existing.getName() +
|
|
||||||
"] - alias [" + alias.getName() + "] will be used for alias list [" + getName() + "]");
|
for(AliasID aliasID: existing.getAliasIdentifiers())
|
||||||
|
{
|
||||||
|
if(aliasID instanceof Radio && ((Radio)aliasID).getValue() == radio.getValue())
|
||||||
|
{
|
||||||
|
aliasID.setOverlap(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -642,32 +734,21 @@ public class AliasList implements Listener<AliasEvent>
|
|||||||
{
|
{
|
||||||
if(radioRange.overlaps(entry.getKey()) && !entry.getValue().equals(alias))
|
if(radioRange.overlaps(entry.getKey()) && !entry.getValue().equals(alias))
|
||||||
{
|
{
|
||||||
mLog.warn("Alias [" + alias.getName() + "] with radio ID range [" + radioRange.toString() +
|
radioRange.setOverlap(true);
|
||||||
"] overlaps with alias [" + entry.getValue().getName() +
|
entry.getKey().setOverlap(true);
|
||||||
"] with range [" + entry.getKey().toString() + "] for alias list [" + getName() + "]");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mRadioRangeAliasMap.put(radioRange, alias);
|
mRadioRangeAliasMap.put(radioRange, alias);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void remove(Radio radio)
|
|
||||||
{
|
|
||||||
mRadioAliasMap.remove(radio.getValue());
|
|
||||||
}
|
|
||||||
|
|
||||||
public void remove(RadioRange radioRange)
|
|
||||||
{
|
|
||||||
mRadioRangeAliasMap.remove(radioRange);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Removes the alias from all internal maps
|
* Removes the alias from both the radio and the radio range maps.
|
||||||
*/
|
*/
|
||||||
public void remove(Alias alias)
|
public void remove(Alias alias)
|
||||||
{
|
{
|
||||||
AliasList.remove(alias, mRadioAliasMap);
|
mRadioAliasMap.values().removeAll(Collections.singleton(alias));
|
||||||
AliasList.remove(alias, mRadioRangeAliasMap);
|
mRadioRangeAliasMap.values().removeAll(Collections.singleton(alias));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,59 +1,70 @@
|
|||||||
/*
|
/*
|
||||||
* ******************************************************************************
|
|
||||||
* sdrtrunk
|
|
||||||
* Copyright (C) 2014-2018 Dennis Sheirer
|
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* * ******************************************************************************
|
||||||
* it under the terms of the GNU General Public License as published by
|
* * Copyright (C) 2014-2020 Dennis Sheirer
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
* *
|
||||||
* (at your option) any later version.
|
* * This program is free software: you can redistribute it and/or modify
|
||||||
|
* * it under the terms of the GNU General Public License as published by
|
||||||
|
* * the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* * (at your option) any later version.
|
||||||
|
* *
|
||||||
|
* * This program is distributed in the hope that it will be useful,
|
||||||
|
* * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* * GNU General Public License for more details.
|
||||||
|
* *
|
||||||
|
* * You should have received a copy of the GNU General Public License
|
||||||
|
* * along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||||
|
* * *****************************************************************************
|
||||||
*
|
*
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
*
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
|
||||||
* *****************************************************************************
|
|
||||||
*/
|
*/
|
||||||
package io.github.dsheirer.alias;
|
package io.github.dsheirer.alias;
|
||||||
|
|
||||||
|
import io.github.dsheirer.alias.id.AliasID;
|
||||||
|
import io.github.dsheirer.alias.id.AliasIDType;
|
||||||
import io.github.dsheirer.alias.id.broadcast.BroadcastChannel;
|
import io.github.dsheirer.alias.id.broadcast.BroadcastChannel;
|
||||||
import io.github.dsheirer.identifier.IdentifierCollection;
|
import io.github.dsheirer.identifier.IdentifierCollection;
|
||||||
import io.github.dsheirer.identifier.configuration.AliasListConfigurationIdentifier;
|
import io.github.dsheirer.identifier.configuration.AliasListConfigurationIdentifier;
|
||||||
import io.github.dsheirer.sample.Broadcaster;
|
import javafx.collections.FXCollections;
|
||||||
import io.github.dsheirer.sample.Listener;
|
import javafx.collections.ListChangeListener;
|
||||||
|
import javafx.collections.ObservableList;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import javax.swing.table.AbstractTableModel;
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.concurrent.CopyOnWriteArrayList;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Alias Model contains all aliases and is responsible for creation and management of alias lists. Alias lists are a
|
* Alias Model contains all aliases and is responsible for creation and management of alias lists. Alias lists are a
|
||||||
* set of aliases that all share a common alias list name and can be attached to a decoding channel for aliasing
|
* set of aliases that all share a common alias list name and can be attached to a decoding channel for aliasing
|
||||||
* identifiers produced by channel decoder(s).
|
* identifiers produced by channel decoder(s).
|
||||||
*/
|
*/
|
||||||
public class AliasModel extends AbstractTableModel
|
public class AliasModel
|
||||||
{
|
{
|
||||||
private static final long serialVersionUID = 1L;
|
private final static Logger mLog = LoggerFactory.getLogger(AliasModel.class);
|
||||||
|
public static final String NO_ALIAS_LIST = "(No Alias List)";
|
||||||
public static final int COLUMN_LIST = 0;
|
private ObservableList<Alias> mAliases = FXCollections.observableArrayList(Alias.extractor());
|
||||||
public static final int COLUMN_GROUP = 1;
|
private ObservableList<String> mAliasListNames = FXCollections.observableArrayList();
|
||||||
public static final int COLUMN_NAME = 2;
|
|
||||||
public static final int COLUMN_ICON = 3;
|
|
||||||
public static final int COLUMN_COLOR = 4;
|
|
||||||
|
|
||||||
private List<Alias> mAliases = new CopyOnWriteArrayList<>();
|
|
||||||
private Broadcaster<AliasEvent> mAliasEventBroadcaster = new Broadcaster<>();
|
|
||||||
private Map<String,AliasList> mAliasListMap = new HashMap<>();
|
private Map<String,AliasList> mAliasListMap = new HashMap<>();
|
||||||
|
|
||||||
public AliasModel()
|
public AliasModel()
|
||||||
{
|
{
|
||||||
|
//Register a listener to detect alias changes and broadcast change events to cause playlist save requests
|
||||||
|
mAliases.addListener(new AliasListChangeListener());
|
||||||
|
}
|
||||||
|
|
||||||
|
public ObservableList<Alias> aliasList()
|
||||||
|
{
|
||||||
|
return mAliases;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ObservableList<String> aliasListNames()
|
||||||
|
{
|
||||||
|
return mAliasListNames;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -64,14 +75,17 @@ public class AliasModel extends AbstractTableModel
|
|||||||
return Collections.unmodifiableList(mAliases);
|
return Collections.unmodifiableList(mAliases);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Alias getAliasAtIndex(int row)
|
/**
|
||||||
|
* Removes all aliases from the list and broadcasts the alias delete event for each
|
||||||
|
*/
|
||||||
|
public void clear()
|
||||||
{
|
{
|
||||||
if(mAliases.size() >= row)
|
List<Alias> aliasToRemove = new ArrayList<>(mAliases);
|
||||||
{
|
|
||||||
return mAliases.get(row);
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
for(Alias alias: aliasToRemove)
|
||||||
|
{
|
||||||
|
removeAlias(alias);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -117,16 +131,17 @@ public class AliasModel extends AbstractTableModel
|
|||||||
return new AliasList(name);
|
return new AliasList(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(mAliasListMap.containsKey(name))
|
AliasList mapValue = mAliasListMap.get(name);
|
||||||
|
if (mapValue != null)
|
||||||
{
|
{
|
||||||
return mAliasListMap.get(name);
|
return mapValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
AliasList aliasList = new AliasList(name);
|
AliasList aliasList = new AliasList(name);
|
||||||
|
|
||||||
for(Alias alias : mAliases)
|
for(Alias alias : mAliases)
|
||||||
{
|
{
|
||||||
if(alias.hasList() && alias.getList().equalsIgnoreCase(name))
|
if(alias.hasList() && alias.getAliasListName().equalsIgnoreCase(name))
|
||||||
{
|
{
|
||||||
aliasList.addAlias(alias);
|
aliasList.addAlias(alias);
|
||||||
}
|
}
|
||||||
@@ -134,9 +149,6 @@ public class AliasModel extends AbstractTableModel
|
|||||||
|
|
||||||
mAliasListMap.put(name, aliasList);
|
mAliasListMap.put(name, aliasList);
|
||||||
|
|
||||||
//Register the new alias list to receive updates from this model
|
|
||||||
addListener(aliasList);
|
|
||||||
|
|
||||||
return aliasList;
|
return aliasList;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -145,19 +157,7 @@ public class AliasModel extends AbstractTableModel
|
|||||||
*/
|
*/
|
||||||
public List<String> getListNames()
|
public List<String> getListNames()
|
||||||
{
|
{
|
||||||
List<String> listNames = new ArrayList<>();
|
return mAliasListNames;
|
||||||
|
|
||||||
for(Alias alias : mAliases)
|
|
||||||
{
|
|
||||||
if(alias.hasList() && !listNames.contains(alias.getList()))
|
|
||||||
{
|
|
||||||
listNames.add(alias.getList());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Collections.sort(listNames);
|
|
||||||
|
|
||||||
return listNames;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -180,33 +180,6 @@ public class AliasModel extends AbstractTableModel
|
|||||||
return groupNames;
|
return groupNames;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns a list of alias group names for all aliases that have a matching
|
|
||||||
* list name value
|
|
||||||
*/
|
|
||||||
public List<String> getGroupNames(String listName)
|
|
||||||
{
|
|
||||||
List<String> groupNames = new ArrayList<>();
|
|
||||||
|
|
||||||
if(listName != null)
|
|
||||||
{
|
|
||||||
for(Alias alias : mAliases)
|
|
||||||
{
|
|
||||||
if(alias.hasList() &&
|
|
||||||
alias.hasGroup() &&
|
|
||||||
listName.equals(alias.getList()) &&
|
|
||||||
!groupNames.contains(alias.getGroup()))
|
|
||||||
{
|
|
||||||
groupNames.add(alias.getGroup());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Collections.sort(groupNames);
|
|
||||||
|
|
||||||
return groupNames;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Bulk loading of aliases
|
* Bulk loading of aliases
|
||||||
*/
|
*/
|
||||||
@@ -221,173 +194,177 @@ public class AliasModel extends AbstractTableModel
|
|||||||
/**
|
/**
|
||||||
* Adds the alias to the model
|
* Adds the alias to the model
|
||||||
*/
|
*/
|
||||||
public int addAlias(Alias alias)
|
public void addAlias(Alias alias)
|
||||||
{
|
{
|
||||||
if(alias != null)
|
if(mAliases.contains(alias))
|
||||||
{
|
{
|
||||||
mAliases.add(alias);
|
removeAlias(alias);
|
||||||
|
|
||||||
int index = mAliases.size() - 1;
|
|
||||||
|
|
||||||
fireTableRowsInserted(index, index);
|
|
||||||
|
|
||||||
broadcast(new AliasEvent(alias, AliasEvent.Event.ADD));
|
|
||||||
|
|
||||||
return index;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return -1;
|
mAliases.add(alias);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean hasAliasList(String aliasListName)
|
||||||
|
{
|
||||||
|
return aliasListName != null && mAliasListMap.containsKey(aliasListName);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addAliasList(String aliasListName)
|
||||||
|
{
|
||||||
|
if(aliasListName != null && !aliasListName.isEmpty())
|
||||||
|
{
|
||||||
|
if(!mAliasListNames.contains(aliasListName))
|
||||||
|
{
|
||||||
|
mAliasListNames.add(aliasListName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if(!mAliasListNames.contains(NO_ALIAS_LIST))
|
||||||
|
{
|
||||||
|
//This list allows users to view unassigned aliases so that they can move them to a valid alias list, but
|
||||||
|
// it is not assignable to a channel
|
||||||
|
mAliasListNames.add(NO_ALIAS_LIST);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Removes the channel from the model and broadcasts a channel remove event
|
* Removes the alias from this model and an alias list (if one exists)
|
||||||
*/
|
*/
|
||||||
public void removeAlias(Alias alias)
|
public void removeAlias(Alias alias)
|
||||||
{
|
{
|
||||||
if(alias != null)
|
if(alias != null)
|
||||||
{
|
{
|
||||||
int index = mAliases.indexOf(alias);
|
|
||||||
|
|
||||||
mAliases.remove(alias);
|
mAliases.remove(alias);
|
||||||
|
|
||||||
fireTableRowsDeleted(index, index);
|
if(hasAliasList(alias.getAliasListName()))
|
||||||
|
{
|
||||||
broadcast(new AliasEvent(alias, AliasEvent.Event.DELETE));
|
getAliasList(alias.getAliasListName()).removeAlias(alias);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Renames any broadcast channels that have the previous name.
|
* Retrieves all aliases that match the alias list and have at least one alias ID of the specified type
|
||||||
*
|
* @param aliasListName to search
|
||||||
* @param previousName to rename
|
* @param type to find
|
||||||
* @param newName to assign to the broadcast channel
|
* @return list of aliases
|
||||||
*/
|
*/
|
||||||
public void renameBroadcastChannel(String previousName, String newName)
|
public List<Alias> getAliases(String aliasListName, AliasIDType type)
|
||||||
{
|
{
|
||||||
if(previousName == null || previousName.isEmpty() || newName == null || newName.isEmpty())
|
List<Alias> aliases = new ArrayList<>();
|
||||||
|
|
||||||
|
for(Alias alias : mAliases)
|
||||||
|
{
|
||||||
|
if(alias.hasList() && alias.getAliasListName().equalsIgnoreCase(aliasListName))
|
||||||
|
{
|
||||||
|
for(AliasID aliasID: alias.getAliasIdentifiers())
|
||||||
|
{
|
||||||
|
if(aliasID.getType() == type)
|
||||||
|
{
|
||||||
|
aliases.add(alias);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return aliases;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicates that one or more of the aliases managed by this model are configured to stream to the specified
|
||||||
|
* broadcast channel argument.
|
||||||
|
* @param broadcastChannel to check
|
||||||
|
* @return true if the broadcast channel is non-null, non-empty and at least one alias is configured to stream to
|
||||||
|
* the specified stream name.
|
||||||
|
*/
|
||||||
|
public boolean hasAliasesWithBroadcastChannel(String broadcastChannel)
|
||||||
|
{
|
||||||
|
if(broadcastChannel == null || broadcastChannel.isEmpty())
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for(Alias alias: mAliases)
|
||||||
|
{
|
||||||
|
if(alias.hasBroadcastChannel(broadcastChannel))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates all aliases configured to stream to the previousStreamName with the updatedStreamName
|
||||||
|
* @param previousStreamName to be removed
|
||||||
|
* @param updatedStreamName to be added
|
||||||
|
*/
|
||||||
|
public void updateBroadcastChannel(String previousStreamName, String updatedStreamName)
|
||||||
|
{
|
||||||
|
if(previousStreamName == null || previousStreamName.isEmpty() || updatedStreamName == null || updatedStreamName.isEmpty())
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
for(Alias alias : mAliases)
|
for(Alias alias: mAliases)
|
||||||
{
|
{
|
||||||
if(alias.hasBroadcastChannel(previousName))
|
if(alias.hasBroadcastChannel(previousStreamName))
|
||||||
{
|
{
|
||||||
for(BroadcastChannel broadcastChannel : alias.getBroadcastChannels())
|
for(BroadcastChannel broadcastChannel: alias.getBroadcastChannels())
|
||||||
{
|
{
|
||||||
if(broadcastChannel.getChannelName().contentEquals(previousName))
|
if(broadcastChannel.getChannelName().contentEquals(previousStreamName))
|
||||||
{
|
{
|
||||||
broadcastChannel.setChannelName(newName);
|
alias.removeAliasID(broadcastChannel);
|
||||||
|
|
||||||
|
if(!alias.hasBroadcastChannel(updatedStreamName))
|
||||||
|
{
|
||||||
|
alias.addAliasID(new BroadcastChannel(updatedStreamName));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
/**
|
||||||
public int getRowCount()
|
* Monitors alias additions and removals and updates alias lists
|
||||||
|
*/
|
||||||
|
public class AliasListChangeListener implements ListChangeListener<Alias>
|
||||||
{
|
{
|
||||||
return mAliases.size();
|
@Override
|
||||||
}
|
public void onChanged(ListChangeListener.Change<? extends Alias> change)
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getColumnCount()
|
|
||||||
{
|
|
||||||
return 5;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getColumnName(int columnIndex)
|
|
||||||
{
|
|
||||||
switch(columnIndex)
|
|
||||||
{
|
{
|
||||||
case COLUMN_LIST:
|
while(change.next())
|
||||||
return "List";
|
{
|
||||||
case COLUMN_GROUP:
|
if(change.wasAdded())
|
||||||
return "Group";
|
{
|
||||||
case COLUMN_NAME:
|
for(Alias alias: change.getAddedSubList())
|
||||||
return "Name";
|
{
|
||||||
case COLUMN_ICON:
|
addAliasList(alias.getAliasListName());
|
||||||
return "Icon";
|
|
||||||
case COLUMN_COLOR:
|
if(hasAliasList(alias.getAliasListName()))
|
||||||
return "Color";
|
{
|
||||||
|
getAliasList(alias.getAliasListName()).addAlias(alias);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if(change.wasRemoved())
|
||||||
|
{
|
||||||
|
for(Alias alias: change.getRemoved())
|
||||||
|
{
|
||||||
|
if(hasAliasList(alias.getAliasListName()))
|
||||||
|
{
|
||||||
|
AliasList aliasList = getAliasList(alias.getName());
|
||||||
|
|
||||||
|
if(alias != null)
|
||||||
|
{
|
||||||
|
aliasList.removeAlias(alias);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Class<?> getColumnClass(int columnIndex)
|
|
||||||
{
|
|
||||||
if(columnIndex == COLUMN_COLOR)
|
|
||||||
{
|
|
||||||
return Integer.class;
|
|
||||||
}
|
|
||||||
|
|
||||||
return String.class;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isCellEditable(int rowIndex, int columnIndex)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Object getValueAt(int rowIndex, int columnIndex)
|
|
||||||
{
|
|
||||||
Alias alias = mAliases.get(rowIndex);
|
|
||||||
|
|
||||||
switch(columnIndex)
|
|
||||||
{
|
|
||||||
case COLUMN_LIST:
|
|
||||||
return alias.getList();
|
|
||||||
case COLUMN_GROUP:
|
|
||||||
return alias.getGroup();
|
|
||||||
case COLUMN_NAME:
|
|
||||||
return alias.getName();
|
|
||||||
case COLUMN_ICON:
|
|
||||||
return alias.getIconName();
|
|
||||||
case COLUMN_COLOR:
|
|
||||||
return alias.getColor();
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setValueAt(Object aValue, int rowIndex, int columnIndex)
|
|
||||||
{
|
|
||||||
throw new IllegalArgumentException("Not yet implemented");
|
|
||||||
}
|
|
||||||
|
|
||||||
public void broadcast(AliasEvent event)
|
|
||||||
{
|
|
||||||
Alias alias = event.getAlias();
|
|
||||||
|
|
||||||
//Validate the alias following a user action that changed the alias or any alias IDs
|
|
||||||
if(alias != null)
|
|
||||||
{
|
|
||||||
alias.validate();
|
|
||||||
}
|
|
||||||
|
|
||||||
if(event.getEvent() == AliasEvent.Event.CHANGE)
|
|
||||||
{
|
|
||||||
int index = mAliases.indexOf(event.getAlias());
|
|
||||||
|
|
||||||
fireTableRowsUpdated(index, index);
|
|
||||||
}
|
|
||||||
|
|
||||||
mAliasEventBroadcaster.broadcast(event);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void addListener(Listener<AliasEvent> listener)
|
|
||||||
{
|
|
||||||
mAliasEventBroadcaster.addListener(listener);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void removeListener(Listener<AliasEvent> listener)
|
|
||||||
{
|
|
||||||
mAliasEventBroadcaster.removeListener(listener);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,333 +0,0 @@
|
|||||||
/*******************************************************************************
|
|
||||||
* sdrtrunk
|
|
||||||
* Copyright (C) 2014-2017 Dennis Sheirer
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
|
||||||
*
|
|
||||||
******************************************************************************/
|
|
||||||
package io.github.dsheirer.alias;
|
|
||||||
|
|
||||||
import io.github.dsheirer.gui.editor.Editor;
|
|
||||||
import io.github.dsheirer.icon.Icon;
|
|
||||||
import io.github.dsheirer.icon.IconListCellRenderer;
|
|
||||||
import io.github.dsheirer.icon.IconManager;
|
|
||||||
import io.github.dsheirer.icon.IconTableModel;
|
|
||||||
import net.miginfocom.swing.MigLayout;
|
|
||||||
|
|
||||||
import javax.swing.*;
|
|
||||||
import javax.swing.event.DocumentEvent;
|
|
||||||
import javax.swing.event.DocumentListener;
|
|
||||||
import javax.swing.event.TableModelEvent;
|
|
||||||
import javax.swing.event.TableModelListener;
|
|
||||||
import java.awt.*;
|
|
||||||
import java.awt.event.ActionEvent;
|
|
||||||
import java.awt.event.ActionListener;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
public class AliasNameEditor extends Editor<Alias>
|
|
||||||
{
|
|
||||||
private static final long serialVersionUID = 1L;
|
|
||||||
|
|
||||||
private static ComboBoxModel<String> EMPTY_LIST_MODEL = new DefaultComboBoxModel<>();
|
|
||||||
private static ComboBoxModel<String> EMPTY_GROUP_MODEL = new DefaultComboBoxModel<>();
|
|
||||||
|
|
||||||
private JComboBox<String> mListCombo = new JComboBox<>(EMPTY_LIST_MODEL);
|
|
||||||
private JComboBox<String> mGroupCombo = new JComboBox<>(EMPTY_GROUP_MODEL);
|
|
||||||
private JComboBox<Icon> mIconCombo;
|
|
||||||
private JTextField mName;
|
|
||||||
private JButton mButtonColor;
|
|
||||||
private JButton mBtnIconManager;
|
|
||||||
|
|
||||||
private AliasModel mAliasModel;
|
|
||||||
private IconManager mIconManager;
|
|
||||||
|
|
||||||
public AliasNameEditor(AliasModel aliasModel, IconManager iconManager)
|
|
||||||
{
|
|
||||||
mAliasModel = aliasModel;
|
|
||||||
mIconManager = iconManager;
|
|
||||||
|
|
||||||
mIconManager.getModel().addTableModelListener(new TableModelListener()
|
|
||||||
{
|
|
||||||
@Override
|
|
||||||
public void tableChanged(TableModelEvent e)
|
|
||||||
{
|
|
||||||
refreshIcons();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
init();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void init()
|
|
||||||
{
|
|
||||||
setLayout(new MigLayout("fill,wrap 2", "[right][grow,fill]", "[][][][][][][grow]"));
|
|
||||||
|
|
||||||
add(new JLabel("Name:"));
|
|
||||||
mName = new JTextField();
|
|
||||||
mName.getDocument().addDocumentListener(new DocumentListener()
|
|
||||||
{
|
|
||||||
@Override
|
|
||||||
public void removeUpdate(DocumentEvent e)
|
|
||||||
{
|
|
||||||
setModified(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void insertUpdate(DocumentEvent e)
|
|
||||||
{
|
|
||||||
setModified(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void changedUpdate(DocumentEvent e)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
});
|
|
||||||
add(mName, "wrap");
|
|
||||||
|
|
||||||
add(new JLabel("List:"));
|
|
||||||
mListCombo.setEditable(true);
|
|
||||||
mListCombo.addActionListener(new ActionListener()
|
|
||||||
{
|
|
||||||
@Override
|
|
||||||
public void actionPerformed(ActionEvent e)
|
|
||||||
{
|
|
||||||
if(mListCombo.getSelectedItem() != null)
|
|
||||||
{
|
|
||||||
List<String> groups = mAliasModel
|
|
||||||
.getGroupNames((String) mListCombo.getSelectedItem());
|
|
||||||
|
|
||||||
if(groups.isEmpty())
|
|
||||||
{
|
|
||||||
mGroupCombo.setModel(EMPTY_GROUP_MODEL);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
mGroupCombo.setModel(new DefaultComboBoxModel<String>(
|
|
||||||
groups.toArray(new String[groups.size()])));
|
|
||||||
;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
setModified(true);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
add(mListCombo, "wrap");
|
|
||||||
|
|
||||||
add(new JLabel("Group:"));
|
|
||||||
mGroupCombo.setEditable(true);
|
|
||||||
mGroupCombo.addActionListener(new ActionListener()
|
|
||||||
{
|
|
||||||
@Override
|
|
||||||
public void actionPerformed(ActionEvent e)
|
|
||||||
{
|
|
||||||
setModified(true);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
add(mGroupCombo, "wrap");
|
|
||||||
|
|
||||||
add(new JLabel("Color:"));
|
|
||||||
|
|
||||||
mButtonColor = new JButton("Select ...");
|
|
||||||
mButtonColor.addActionListener(new ActionListener()
|
|
||||||
{
|
|
||||||
@Override
|
|
||||||
public void actionPerformed(ActionEvent e)
|
|
||||||
{
|
|
||||||
Color newColor = JColorChooser.showDialog(
|
|
||||||
AliasNameEditor.this,
|
|
||||||
"Choose color for this alias",
|
|
||||||
(hasItem() ? getItem().getDisplayColor() : null));
|
|
||||||
|
|
||||||
if(newColor != null)
|
|
||||||
{
|
|
||||||
mButtonColor.setForeground(newColor);
|
|
||||||
mButtonColor.setBackground(newColor);
|
|
||||||
|
|
||||||
setModified(true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
add(mButtonColor, "wrap");
|
|
||||||
|
|
||||||
add(new JLabel("Icon:"));
|
|
||||||
|
|
||||||
mIconCombo = new JComboBox<Icon>(mIconManager.getIcons());
|
|
||||||
|
|
||||||
IconListCellRenderer renderer = new IconListCellRenderer(mIconManager);
|
|
||||||
renderer.setPreferredSize(new Dimension(200, 30));
|
|
||||||
mIconCombo.setRenderer(renderer);
|
|
||||||
mIconCombo.addActionListener(new ActionListener()
|
|
||||||
{
|
|
||||||
@Override
|
|
||||||
public void actionPerformed(ActionEvent e)
|
|
||||||
{
|
|
||||||
setModified(true);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
add(mIconCombo, "wrap");
|
|
||||||
|
|
||||||
//Dummy place holder
|
|
||||||
add(new JLabel());
|
|
||||||
|
|
||||||
mBtnIconManager = new JButton("Icon Manager");
|
|
||||||
mBtnIconManager.addActionListener(new ActionListener()
|
|
||||||
{
|
|
||||||
@Override
|
|
||||||
public void actionPerformed(ActionEvent arg0)
|
|
||||||
{
|
|
||||||
mIconManager.showEditor(AliasNameEditor.this);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
add(mBtnIconManager, "span 2,wrap");
|
|
||||||
|
|
||||||
setModified(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void refreshIcons()
|
|
||||||
{
|
|
||||||
if(mIconCombo != null)
|
|
||||||
{
|
|
||||||
EventQueue.invokeLater(new Runnable()
|
|
||||||
{
|
|
||||||
@Override
|
|
||||||
public void run()
|
|
||||||
{
|
|
||||||
mIconCombo.setModel(new DefaultComboBoxModel<>(mIconManager.getIcons()));
|
|
||||||
|
|
||||||
if(hasItem())
|
|
||||||
{
|
|
||||||
String iconName = getItem().getIconName();
|
|
||||||
Icon aliasIcon = mIconManager.getModel().getIcon(iconName);
|
|
||||||
|
|
||||||
if(aliasIcon != null)
|
|
||||||
{
|
|
||||||
mIconCombo.setSelectedItem(aliasIcon);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setItem(Alias alias)
|
|
||||||
{
|
|
||||||
super.setItem(alias);
|
|
||||||
|
|
||||||
if(hasItem())
|
|
||||||
{
|
|
||||||
mName.setText(alias.getName());
|
|
||||||
|
|
||||||
mIconCombo.setModel(new DefaultComboBoxModel<>(mIconManager.getIcons()));
|
|
||||||
|
|
||||||
List<String> listNames = mAliasModel.getListNames();
|
|
||||||
|
|
||||||
if(listNames.isEmpty())
|
|
||||||
{
|
|
||||||
mListCombo.setModel(EMPTY_LIST_MODEL);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
mListCombo.setModel(new DefaultComboBoxModel<String>(
|
|
||||||
listNames.toArray(new String[listNames.size()])));
|
|
||||||
;
|
|
||||||
}
|
|
||||||
|
|
||||||
mListCombo.setSelectedItem(alias.getList());
|
|
||||||
|
|
||||||
List<String> groupNames = mAliasModel.getGroupNames(alias.getList());
|
|
||||||
|
|
||||||
if(groupNames.isEmpty())
|
|
||||||
{
|
|
||||||
mGroupCombo.setModel(EMPTY_GROUP_MODEL);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
mGroupCombo.setModel(new DefaultComboBoxModel<String>(
|
|
||||||
groupNames.toArray(new String[groupNames.size()])));
|
|
||||||
;
|
|
||||||
}
|
|
||||||
|
|
||||||
mGroupCombo.setSelectedItem(alias.getGroup());
|
|
||||||
|
|
||||||
Color color = alias.getDisplayColor();
|
|
||||||
|
|
||||||
mButtonColor.setBackground(color);
|
|
||||||
mButtonColor.setForeground(color);
|
|
||||||
|
|
||||||
String iconName = alias.getIconName();
|
|
||||||
|
|
||||||
if(iconName == null)
|
|
||||||
{
|
|
||||||
iconName = IconTableModel.DEFAULT_ICON;
|
|
||||||
}
|
|
||||||
|
|
||||||
Icon savedIcon = mIconManager.getModel().getIcon(iconName);
|
|
||||||
|
|
||||||
if(savedIcon != null)
|
|
||||||
{
|
|
||||||
mIconCombo.setSelectedItem(savedIcon);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
mListCombo.setModel(EMPTY_LIST_MODEL);
|
|
||||||
mGroupCombo.setModel(EMPTY_GROUP_MODEL);
|
|
||||||
mName.setText(null);
|
|
||||||
|
|
||||||
mButtonColor.setBackground(getBackground());
|
|
||||||
mButtonColor.setForeground(getForeground());
|
|
||||||
}
|
|
||||||
|
|
||||||
repaint();
|
|
||||||
|
|
||||||
setModified(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void save()
|
|
||||||
{
|
|
||||||
if(hasItem() && isModified())
|
|
||||||
{
|
|
||||||
Alias alias = getItem();
|
|
||||||
|
|
||||||
if(mListCombo.getSelectedItem() != null)
|
|
||||||
{
|
|
||||||
alias.setList((String) mListCombo.getSelectedItem());
|
|
||||||
}
|
|
||||||
|
|
||||||
if(mGroupCombo.getSelectedItem() != null)
|
|
||||||
{
|
|
||||||
alias.setGroup((String) mGroupCombo.getSelectedItem());
|
|
||||||
}
|
|
||||||
|
|
||||||
alias.setName(mName.getText());
|
|
||||||
|
|
||||||
alias.setColor(mButtonColor.getBackground().getRGB());
|
|
||||||
|
|
||||||
if(mIconCombo.getSelectedItem() != null)
|
|
||||||
{
|
|
||||||
alias.setIconName(((Icon) mIconCombo.getSelectedItem()).getName());
|
|
||||||
}
|
|
||||||
|
|
||||||
setModified(false);
|
|
||||||
|
|
||||||
//Broadcast an alias change event to save the updates
|
|
||||||
mAliasModel.broadcast(new AliasEvent(getItem(), AliasEvent.Event.CHANGE));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,121 +0,0 @@
|
|||||||
/*******************************************************************************
|
|
||||||
* SDR Trunk
|
|
||||||
* Copyright (C) 2014-2016 Dennis Sheirer
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
|
||||||
******************************************************************************/
|
|
||||||
package io.github.dsheirer.alias;
|
|
||||||
|
|
||||||
import javax.swing.*;
|
|
||||||
import javax.swing.event.DocumentListener;
|
|
||||||
|
|
||||||
public abstract class ComponentEditor<T> extends JPanel implements DocumentListener
|
|
||||||
{
|
|
||||||
private static final long serialVersionUID = 1L;
|
|
||||||
private boolean mModified;
|
|
||||||
private ComponentModificationListener mListener;
|
|
||||||
protected T mComponent;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Abstract class for editor with notifications when contents are modified
|
|
||||||
* and methods for saving or resetting the component values with the
|
|
||||||
* contents of the editor.
|
|
||||||
*/
|
|
||||||
public ComponentEditor( T component )
|
|
||||||
{
|
|
||||||
mComponent = component;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ComponentEditor()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public void dispose()
|
|
||||||
{
|
|
||||||
mComponent = null;
|
|
||||||
mListener = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public T getComponent()
|
|
||||||
{
|
|
||||||
return mComponent;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void setModified( boolean modified )
|
|
||||||
{
|
|
||||||
mModified = modified;
|
|
||||||
|
|
||||||
if( modified && mListener != null )
|
|
||||||
{
|
|
||||||
mListener.componentModified();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Indicates if the contents of the editor have been modified
|
|
||||||
*/
|
|
||||||
public boolean isModified()
|
|
||||||
{
|
|
||||||
return mModified;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setComponentModificationListener( ComponentModificationListener listener )
|
|
||||||
{
|
|
||||||
mListener = listener;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets or resets the editable component
|
|
||||||
*/
|
|
||||||
public abstract void setComponent( T t );
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Saves changed editor values to the component
|
|
||||||
*/
|
|
||||||
public abstract void save();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reloads the editor contents from the source component and resets the
|
|
||||||
* modified flag
|
|
||||||
*/
|
|
||||||
public void reset()
|
|
||||||
{
|
|
||||||
setComponent( getComponent() );
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Validate contents of other editors against this component editor before
|
|
||||||
* saving. Override this method to allow controller to pass other editors
|
|
||||||
* to this editor for content inspection/validation before indicating that
|
|
||||||
* this editor passes validation. This allows for validating editor contents
|
|
||||||
* that are contingent on settings in other editors.
|
|
||||||
*
|
|
||||||
* @throws ComponentValidationException if the contents of the editor
|
|
||||||
* argument cannot be validated, with the exception containing a detailed
|
|
||||||
* description of the issue.
|
|
||||||
*/
|
|
||||||
public void validate( ComponentEditor<?> editor ) throws ComponentValidationException
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Listener interface to receive notification that the contents of the
|
|
||||||
* editor have been modified from the original component value and either
|
|
||||||
* need to be saved or reset
|
|
||||||
*/
|
|
||||||
public interface ComponentModificationListener
|
|
||||||
{
|
|
||||||
public void componentModified();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
package io.github.dsheirer.alias;
|
|
||||||
|
|
||||||
public class ComponentValidationException extends Exception
|
|
||||||
{
|
|
||||||
private static final long serialVersionUID = 1L;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Component editor contents validation error
|
|
||||||
*/
|
|
||||||
public ComponentValidationException( String message )
|
|
||||||
{
|
|
||||||
super( message );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,466 +0,0 @@
|
|||||||
package io.github.dsheirer.alias;
|
|
||||||
|
|
||||||
import io.github.dsheirer.alias.id.AliasIDType;
|
|
||||||
import io.github.dsheirer.alias.id.broadcast.BroadcastChannel;
|
|
||||||
import io.github.dsheirer.alias.id.priority.Priority;
|
|
||||||
import io.github.dsheirer.audio.broadcast.BroadcastModel;
|
|
||||||
import io.github.dsheirer.gui.editor.Editor;
|
|
||||||
import io.github.dsheirer.icon.Icon;
|
|
||||||
import io.github.dsheirer.icon.IconListCellRenderer;
|
|
||||||
import io.github.dsheirer.icon.IconManager;
|
|
||||||
import io.github.dsheirer.sample.Listener;
|
|
||||||
import net.miginfocom.swing.MigLayout;
|
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
|
|
||||||
import javax.swing.*;
|
|
||||||
import javax.swing.event.ChangeEvent;
|
|
||||||
import javax.swing.event.ChangeListener;
|
|
||||||
import javax.swing.event.TableModelEvent;
|
|
||||||
import javax.swing.event.TableModelListener;
|
|
||||||
import java.awt.*;
|
|
||||||
import java.awt.event.ActionEvent;
|
|
||||||
import java.awt.event.ActionListener;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
public class MultipleAliasEditor extends Editor<List<Alias>>
|
|
||||||
implements Listener<AliasEvent>
|
|
||||||
{
|
|
||||||
private static final long serialVersionUID = 1L;
|
|
||||||
private final static Logger mLog = LoggerFactory.getLogger(MultipleAliasEditor.class);
|
|
||||||
|
|
||||||
private static String HELP_TEXT =
|
|
||||||
"<html>Select attributes below to change for all selected aliases</html>";
|
|
||||||
|
|
||||||
private JLabel mAliasCountLabel;
|
|
||||||
private JLabel mAliasCount;
|
|
||||||
|
|
||||||
private JCheckBox mListCheckBox = new JCheckBox("List");
|
|
||||||
private JComboBox<String> mListCombo = new JComboBox<>();
|
|
||||||
private JCheckBox mGroupCheckBox = new JCheckBox("Group");
|
|
||||||
private JComboBox<String> mGroupCombo = new JComboBox<>();
|
|
||||||
private JCheckBox mIconCheckBox = new JCheckBox("Icon");
|
|
||||||
private JComboBox<Icon> mIconCombo;
|
|
||||||
private JCheckBox mColorCheckBox = new JCheckBox("Color");
|
|
||||||
private JButton mButtonColor;
|
|
||||||
private JButton mBtnIconManager;
|
|
||||||
private JCheckBox mRecordCheckBox = new JCheckBox("Record");
|
|
||||||
private JComboBox<Record> mRecordActionCombo = new JComboBox<>(Record.values());
|
|
||||||
private JCheckBox mPriorityCheckBox = new JCheckBox("Priority");
|
|
||||||
private JSlider mPrioritySlider;
|
|
||||||
private JLabel mPrioritySliderLabel;
|
|
||||||
private JCheckBox mStreamCheckBox = new JCheckBox("Stream");
|
|
||||||
private JComboBox<String> mStreamCombo;
|
|
||||||
|
|
||||||
private AliasModel mAliasModel;
|
|
||||||
private BroadcastModel mBroadcastModel;
|
|
||||||
private IconManager mIconManager;
|
|
||||||
|
|
||||||
public MultipleAliasEditor(AliasModel aliasModel, BroadcastModel broadcastModel, IconManager iconManager)
|
|
||||||
{
|
|
||||||
mAliasModel = aliasModel;
|
|
||||||
mBroadcastModel = broadcastModel;
|
|
||||||
mIconManager = iconManager;
|
|
||||||
|
|
||||||
mAliasModel.addListener(this);
|
|
||||||
mIconManager.getModel().addTableModelListener(new TableModelListener()
|
|
||||||
{
|
|
||||||
@Override
|
|
||||||
public void tableChanged(TableModelEvent e)
|
|
||||||
{
|
|
||||||
refreshIcons();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
init();
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<Alias> getAliases()
|
|
||||||
{
|
|
||||||
if (hasItem())
|
|
||||||
{
|
|
||||||
return (List<Alias>) getItem();
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void init()
|
|
||||||
{
|
|
||||||
setLayout(new MigLayout("fill,wrap 2", "[grow,fill][grow,fill]",
|
|
||||||
"[][][][][][][][][][][][][][grow,fill]"));
|
|
||||||
|
|
||||||
mAliasCountLabel = new JLabel("Alias:");
|
|
||||||
add(mAliasCountLabel);
|
|
||||||
|
|
||||||
mAliasCount = new JLabel("Multiple");
|
|
||||||
add(mAliasCount, "span");
|
|
||||||
|
|
||||||
add(new JSeparator(), "span");
|
|
||||||
|
|
||||||
add(new JLabel(HELP_TEXT), "span");
|
|
||||||
|
|
||||||
add(mListCheckBox);
|
|
||||||
|
|
||||||
mListCombo.setEditable(true);
|
|
||||||
add(mListCombo, "wrap");
|
|
||||||
|
|
||||||
add(mGroupCheckBox);
|
|
||||||
|
|
||||||
mGroupCombo.setEditable(true);
|
|
||||||
add(mGroupCombo, "wrap");
|
|
||||||
|
|
||||||
add(mColorCheckBox);
|
|
||||||
|
|
||||||
mButtonColor = new JButton("Select ...");
|
|
||||||
mButtonColor.addActionListener(new ActionListener()
|
|
||||||
{
|
|
||||||
@Override
|
|
||||||
public void actionPerformed(ActionEvent e)
|
|
||||||
{
|
|
||||||
Color newColor = JColorChooser.showDialog(
|
|
||||||
MultipleAliasEditor.this,
|
|
||||||
"Choose color for this alias", null);
|
|
||||||
|
|
||||||
if (newColor != null)
|
|
||||||
{
|
|
||||||
mButtonColor.setForeground(newColor);
|
|
||||||
mButtonColor.setBackground(newColor);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
add(mButtonColor, "wrap");
|
|
||||||
|
|
||||||
add(mIconCheckBox);
|
|
||||||
|
|
||||||
mIconCombo = new JComboBox<Icon>(mIconManager.getIcons());
|
|
||||||
|
|
||||||
IconListCellRenderer renderer = new IconListCellRenderer(mIconManager);
|
|
||||||
renderer.setPreferredSize(new Dimension(200, 30));
|
|
||||||
mIconCombo.setRenderer(renderer);
|
|
||||||
add(mIconCombo, "wrap");
|
|
||||||
|
|
||||||
//Dummy place holder
|
|
||||||
add(new JLabel());
|
|
||||||
|
|
||||||
mBtnIconManager = new JButton("Icon Manager");
|
|
||||||
mBtnIconManager.addActionListener(new ActionListener()
|
|
||||||
{
|
|
||||||
@Override
|
|
||||||
public void actionPerformed(ActionEvent arg0)
|
|
||||||
{
|
|
||||||
mIconManager.showEditor(MultipleAliasEditor.this);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
add(mBtnIconManager, "span,wrap");
|
|
||||||
|
|
||||||
add(mRecordCheckBox);
|
|
||||||
add(mRecordActionCombo);
|
|
||||||
|
|
||||||
//Placeholder
|
|
||||||
add(new JLabel(" "));
|
|
||||||
mPrioritySliderLabel = new JLabel("Audio Priority: " + Priority.MIN_PRIORITY);
|
|
||||||
add(mPrioritySliderLabel);
|
|
||||||
|
|
||||||
add(mPriorityCheckBox);
|
|
||||||
|
|
||||||
mPrioritySlider = new JSlider(JSlider.HORIZONTAL,
|
|
||||||
Priority.MIN_PRIORITY,
|
|
||||||
Priority.MAX_PRIORITY + 1,
|
|
||||||
Priority.MIN_PRIORITY);
|
|
||||||
|
|
||||||
mPrioritySlider.setMajorTickSpacing(20);
|
|
||||||
mPrioritySlider.setMinorTickSpacing(5);
|
|
||||||
mPrioritySlider.setPaintTicks(true);
|
|
||||||
mPrioritySlider.setLabelTable(mPrioritySlider.createStandardLabels(25, 25));
|
|
||||||
mPrioritySlider.setPaintLabels(true);
|
|
||||||
mPrioritySlider.addChangeListener(new ChangeListener()
|
|
||||||
{
|
|
||||||
@Override
|
|
||||||
public void stateChanged(ChangeEvent e)
|
|
||||||
{
|
|
||||||
int priority = mPrioritySlider.getValue();
|
|
||||||
|
|
||||||
if (priority > Priority.MAX_PRIORITY)
|
|
||||||
{
|
|
||||||
mPrioritySliderLabel.setText("Audio Priority: Do Not Monitor");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
mPrioritySliderLabel.setText("Audio Priority: " + mPrioritySlider.getValue());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
mPrioritySlider.setToolTipText(HELP_TEXT);
|
|
||||||
|
|
||||||
add(mPrioritySlider);
|
|
||||||
|
|
||||||
add(mStreamCheckBox);
|
|
||||||
|
|
||||||
mStreamCombo = new JComboBox<String>();
|
|
||||||
mStreamCombo.setEditable(true);
|
|
||||||
add(mStreamCombo, "wrap");
|
|
||||||
|
|
||||||
JButton saveButton = new JButton("Save");
|
|
||||||
saveButton.addActionListener(new ActionListener()
|
|
||||||
{
|
|
||||||
@Override
|
|
||||||
public void actionPerformed(ActionEvent e)
|
|
||||||
{
|
|
||||||
save();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
add(saveButton);
|
|
||||||
|
|
||||||
JButton resetButton = new JButton("Reset");
|
|
||||||
resetButton.addActionListener(new ActionListener()
|
|
||||||
{
|
|
||||||
@Override
|
|
||||||
public void actionPerformed(ActionEvent e)
|
|
||||||
{
|
|
||||||
reset();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
add(resetButton);
|
|
||||||
|
|
||||||
setModified(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void refreshIcons()
|
|
||||||
{
|
|
||||||
if(mIconCombo != null)
|
|
||||||
{
|
|
||||||
EventQueue.invokeLater(new Runnable()
|
|
||||||
{
|
|
||||||
@Override
|
|
||||||
public void run()
|
|
||||||
{
|
|
||||||
mIconCombo.setModel(new DefaultComboBoxModel<>(mIconManager.getIcons()));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setItem(List<Alias> item)
|
|
||||||
{
|
|
||||||
super.setItem(item);
|
|
||||||
|
|
||||||
StringBuilder sb = new StringBuilder();
|
|
||||||
sb.append("Multiple Selected [ ");
|
|
||||||
|
|
||||||
if (hasItem())
|
|
||||||
{
|
|
||||||
sb.append(String.valueOf(getAliases().size()));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
sb.append("0");
|
|
||||||
}
|
|
||||||
|
|
||||||
sb.append(" ]");
|
|
||||||
|
|
||||||
mAliasCount.setText(sb.toString());
|
|
||||||
|
|
||||||
List<String> listNames = mAliasModel.getListNames();
|
|
||||||
listNames.add(0, "");
|
|
||||||
mListCombo.setModel(new DefaultComboBoxModel<String>(
|
|
||||||
listNames.toArray(new String[listNames.size()])));
|
|
||||||
|
|
||||||
List<String> groupNames = mAliasModel.getGroupNames();
|
|
||||||
groupNames.add(0, "");
|
|
||||||
mGroupCombo.setModel(new DefaultComboBoxModel<String>(
|
|
||||||
groupNames.toArray(new String[groupNames.size()])));
|
|
||||||
|
|
||||||
List<String> streamNames = mBroadcastModel.getBroadcastConfigurationNames();
|
|
||||||
mStreamCombo.setModel(new DefaultComboBoxModel<String>(streamNames.toArray(new String[streamNames.size()])));
|
|
||||||
|
|
||||||
mIconCombo.setModel(new DefaultComboBoxModel<>(mIconManager.getIcons()));
|
|
||||||
|
|
||||||
setCheckBoxesUnselected();
|
|
||||||
|
|
||||||
setModified(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void setCheckBoxesUnselected()
|
|
||||||
{
|
|
||||||
mListCheckBox.setSelected(false);
|
|
||||||
mGroupCheckBox.setSelected(false);
|
|
||||||
mColorCheckBox.setSelected(false);
|
|
||||||
mIconCheckBox.setSelected(false);
|
|
||||||
mRecordCheckBox.setSelected(false);
|
|
||||||
mPriorityCheckBox.setSelected(false);
|
|
||||||
mStreamCheckBox.setSelected(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void reset()
|
|
||||||
{
|
|
||||||
List<String> listNames = mAliasModel.getListNames();
|
|
||||||
listNames.add(0, "");
|
|
||||||
mListCombo.setModel(new DefaultComboBoxModel<String>(
|
|
||||||
listNames.toArray(new String[listNames.size()])));
|
|
||||||
|
|
||||||
List<String> groupNames = mAliasModel.getGroupNames();
|
|
||||||
groupNames.add(0, "");
|
|
||||||
mGroupCombo.setModel(new DefaultComboBoxModel<String>(
|
|
||||||
groupNames.toArray(new String[groupNames.size()])));
|
|
||||||
|
|
||||||
mButtonColor.setForeground(getForeground());
|
|
||||||
mButtonColor.setBackground(getBackground());
|
|
||||||
|
|
||||||
super.reset();
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean hasRequestedChanges()
|
|
||||||
{
|
|
||||||
return mListCheckBox.isSelected() ||
|
|
||||||
mGroupCheckBox.isSelected() ||
|
|
||||||
mColorCheckBox.isSelected() ||
|
|
||||||
mIconCheckBox.isSelected() ||
|
|
||||||
mRecordCheckBox.isSelected() ||
|
|
||||||
mPriorityCheckBox.isSelected() ||
|
|
||||||
mStreamCheckBox.isSelected();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void save()
|
|
||||||
{
|
|
||||||
if (hasRequestedChanges() && hasItem())
|
|
||||||
{
|
|
||||||
List<Alias> aliases = getAliases();
|
|
||||||
|
|
||||||
for (Alias alias : aliases)
|
|
||||||
{
|
|
||||||
if (mListCheckBox.isSelected())
|
|
||||||
{
|
|
||||||
String list = null;
|
|
||||||
|
|
||||||
if (mListCombo.getSelectedItem() != null)
|
|
||||||
{
|
|
||||||
list = (String) mListCombo.getSelectedItem();
|
|
||||||
}
|
|
||||||
|
|
||||||
alias.setList(list);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mGroupCheckBox.isSelected())
|
|
||||||
{
|
|
||||||
String group = null;
|
|
||||||
|
|
||||||
if (mGroupCombo.getSelectedItem() != null)
|
|
||||||
{
|
|
||||||
group = (String) mGroupCombo.getSelectedItem();
|
|
||||||
}
|
|
||||||
|
|
||||||
alias.setGroup(group);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mColorCheckBox.isSelected())
|
|
||||||
{
|
|
||||||
alias.setColor(mButtonColor.getForeground().getRGB());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mIconCheckBox.isSelected())
|
|
||||||
{
|
|
||||||
if (mIconCombo.getSelectedItem() != null)
|
|
||||||
{
|
|
||||||
alias.setIconName(((Icon)mIconCombo.getSelectedItem()).getName());
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
alias.setIconName(null);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mRecordCheckBox.isSelected())
|
|
||||||
{
|
|
||||||
Record action =
|
|
||||||
(Record) mRecordActionCombo.getSelectedItem();
|
|
||||||
|
|
||||||
alias.setRecordable(action == Record.RECORDABLE);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mPriorityCheckBox.isSelected())
|
|
||||||
{
|
|
||||||
int priority = mPrioritySlider.getValue();
|
|
||||||
|
|
||||||
//This is a work-around -- we use max priority + 1 in the
|
|
||||||
//gui to indicate do not monitor, and change the value here
|
|
||||||
if (priority == Priority.MAX_PRIORITY + 1)
|
|
||||||
{
|
|
||||||
priority = Priority.DO_NOT_MONITOR;
|
|
||||||
}
|
|
||||||
|
|
||||||
alias.setCallPriority(priority);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(mStreamCheckBox.isSelected())
|
|
||||||
{
|
|
||||||
if(mStreamCombo.getSelectedItem() != null)
|
|
||||||
{
|
|
||||||
String streamName = (String)mStreamCombo.getSelectedItem();
|
|
||||||
|
|
||||||
boolean hasChannel = false;
|
|
||||||
|
|
||||||
//Ensure the alias doesn't already have the channel
|
|
||||||
for(BroadcastChannel channel: alias.getBroadcastChannels())
|
|
||||||
{
|
|
||||||
if(channel.getChannelName().equals(streamName))
|
|
||||||
{
|
|
||||||
hasChannel = true;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(!hasChannel)
|
|
||||||
{
|
|
||||||
BroadcastChannel channel = (BroadcastChannel)AliasFactory
|
|
||||||
.getAliasID(AliasIDType.BROADCAST_CHANNEL);
|
|
||||||
|
|
||||||
channel.setChannelName(streamName);
|
|
||||||
alias.addAliasID(channel);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (Alias alias : aliases)
|
|
||||||
{
|
|
||||||
//Broadcast an alias change event to save the updates
|
|
||||||
mAliasModel.broadcast(new AliasEvent(alias, AliasEvent.Event.CHANGE));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void receive(AliasEvent event)
|
|
||||||
{
|
|
||||||
if (event.getEvent() == AliasEvent.Event.DELETE &&
|
|
||||||
hasItem() &&
|
|
||||||
getAliases().contains(event.getAlias()))
|
|
||||||
{
|
|
||||||
getAliases().remove(event.getAlias());
|
|
||||||
setItem(getAliases());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public enum Record
|
|
||||||
{
|
|
||||||
NON_RECORDABLE("Non-Recordable"),
|
|
||||||
RECORDABLE("Recordable");
|
|
||||||
|
|
||||||
private String mLabel;
|
|
||||||
|
|
||||||
private Record(String label)
|
|
||||||
{
|
|
||||||
mLabel = label;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String toString()
|
|
||||||
{
|
|
||||||
return mLabel;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -27,6 +27,9 @@ import io.github.dsheirer.alias.action.beep.BeepAction;
|
|||||||
import io.github.dsheirer.alias.action.clip.ClipAction;
|
import io.github.dsheirer.alias.action.clip.ClipAction;
|
||||||
import io.github.dsheirer.alias.action.script.ScriptAction;
|
import io.github.dsheirer.alias.action.script.ScriptAction;
|
||||||
import io.github.dsheirer.message.IMessage;
|
import io.github.dsheirer.message.IMessage;
|
||||||
|
import javafx.beans.Observable;
|
||||||
|
import javafx.beans.property.SimpleStringProperty;
|
||||||
|
import javafx.util.Callback;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Alias action defines an action to execute when an alias is detected active.
|
* Alias action defines an action to execute when an alias is detected active.
|
||||||
@@ -41,10 +44,33 @@ import io.github.dsheirer.message.IMessage;
|
|||||||
@JacksonXmlRootElement(localName = "action")
|
@JacksonXmlRootElement(localName = "action")
|
||||||
public abstract class AliasAction
|
public abstract class AliasAction
|
||||||
{
|
{
|
||||||
|
private SimpleStringProperty mValueProperty = new SimpleStringProperty();
|
||||||
|
|
||||||
public AliasAction()
|
public AliasAction()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* String property representation of this alias ID
|
||||||
|
*/
|
||||||
|
@JsonIgnore
|
||||||
|
public SimpleStringProperty valueProperty()
|
||||||
|
{
|
||||||
|
return mValueProperty;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates the value property for this alias ID. Note: this method is intended to be invoked by all subclasses
|
||||||
|
* each time that any of the subclass member variable values change.
|
||||||
|
*/
|
||||||
|
public void updateValueProperty()
|
||||||
|
{
|
||||||
|
valueProperty().set(toString());
|
||||||
|
|
||||||
|
//Hack: harmless, but the list extractor does not consistently update unless we do this.
|
||||||
|
valueProperty().get();
|
||||||
|
}
|
||||||
|
|
||||||
@JsonIgnore
|
@JsonIgnore
|
||||||
public abstract AliasActionType getType();
|
public abstract AliasActionType getType();
|
||||||
|
|
||||||
@@ -60,4 +86,12 @@ public abstract class AliasAction
|
|||||||
* Dismiss a persistent alias action
|
* Dismiss a persistent alias action
|
||||||
*/
|
*/
|
||||||
public abstract void dismiss(boolean reset);
|
public abstract void dismiss(boolean reset);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates an observable property extractor for use with observable lists to detect changes internal to this object.
|
||||||
|
*/
|
||||||
|
public static Callback<AliasAction,Observable[]> extractor()
|
||||||
|
{
|
||||||
|
return (AliasAction aliasAction) -> new Observable[] {aliasAction.valueProperty()};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,339 +0,0 @@
|
|||||||
/*******************************************************************************
|
|
||||||
* SDR Trunk
|
|
||||||
* Copyright (C) 2014-2016 Dennis Sheirer
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
|
||||||
******************************************************************************/
|
|
||||||
package io.github.dsheirer.alias.action;
|
|
||||||
|
|
||||||
import io.github.dsheirer.alias.Alias;
|
|
||||||
import io.github.dsheirer.alias.AliasEvent;
|
|
||||||
import io.github.dsheirer.alias.AliasEvent.Event;
|
|
||||||
import io.github.dsheirer.alias.AliasFactory;
|
|
||||||
import io.github.dsheirer.alias.AliasModel;
|
|
||||||
import io.github.dsheirer.gui.editor.Editor;
|
|
||||||
import io.github.dsheirer.gui.editor.EmptyEditor;
|
|
||||||
import net.miginfocom.swing.MigLayout;
|
|
||||||
|
|
||||||
import javax.swing.*;
|
|
||||||
import javax.swing.event.ListSelectionEvent;
|
|
||||||
import javax.swing.event.ListSelectionListener;
|
|
||||||
import java.awt.*;
|
|
||||||
import java.awt.event.ActionEvent;
|
|
||||||
import java.awt.event.ActionListener;
|
|
||||||
import java.awt.event.MouseAdapter;
|
|
||||||
import java.awt.event.MouseEvent;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.Comparator;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
public class AliasActionEditor extends Editor<Alias>
|
|
||||||
{
|
|
||||||
private static final long serialVersionUID = 1L;
|
|
||||||
|
|
||||||
private static ListModel<AliasAction> EMPTY_MODEL = new DefaultListModel<>();
|
|
||||||
private JList<AliasAction> mAliasActionList = new JList<>( EMPTY_MODEL );
|
|
||||||
private JButton mNewActionButton;
|
|
||||||
private JButton mCloneActionButton;
|
|
||||||
private JButton mDeleteActionButton;
|
|
||||||
private EditorContainer mEditorContainer = new EditorContainer();
|
|
||||||
|
|
||||||
private AliasModel mAliasModel;
|
|
||||||
|
|
||||||
public AliasActionEditor( AliasModel model )
|
|
||||||
{
|
|
||||||
mAliasModel = model;
|
|
||||||
init();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void init()
|
|
||||||
{
|
|
||||||
setLayout( new MigLayout( "fill,wrap 3",
|
|
||||||
"[grow,fill][grow,fill][grow,fill]", "[][grow,fill][]" ) );
|
|
||||||
|
|
||||||
mAliasActionList.setVisibleRowCount( 6 );
|
|
||||||
mAliasActionList.setSelectionMode( ListSelectionModel.SINGLE_SELECTION );
|
|
||||||
mAliasActionList.setLayoutOrientation( JList.VERTICAL );
|
|
||||||
mAliasActionList.addListSelectionListener( new ListSelectionListener()
|
|
||||||
{
|
|
||||||
@Override
|
|
||||||
public void valueChanged( ListSelectionEvent event )
|
|
||||||
{
|
|
||||||
if( !event.getValueIsAdjusting() )
|
|
||||||
{
|
|
||||||
JList<?> list = (JList<?>)event.getSource();
|
|
||||||
|
|
||||||
Object selectedItem = list.getSelectedValue();
|
|
||||||
|
|
||||||
if( selectedItem != null && selectedItem instanceof AliasAction )
|
|
||||||
{
|
|
||||||
AliasAction selected = (AliasAction)selectedItem;
|
|
||||||
|
|
||||||
mEditorContainer.setAliasAction( selected );
|
|
||||||
|
|
||||||
mCloneActionButton.setEnabled( true );
|
|
||||||
mDeleteActionButton.setEnabled( true );
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
mCloneActionButton.setEnabled( false );
|
|
||||||
mDeleteActionButton.setEnabled( false );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} );
|
|
||||||
|
|
||||||
JScrollPane scroller = new JScrollPane( mAliasActionList );
|
|
||||||
add( scroller, "span,grow" );
|
|
||||||
|
|
||||||
add( mEditorContainer, "span,grow" );
|
|
||||||
|
|
||||||
mNewActionButton = new JButton( "New ..." );
|
|
||||||
mNewActionButton.setToolTipText( "Create a new Alias Action" );
|
|
||||||
mNewActionButton.addMouseListener( new MouseAdapter()
|
|
||||||
{
|
|
||||||
@Override
|
|
||||||
public void mouseClicked( MouseEvent e )
|
|
||||||
{
|
|
||||||
JPopupMenu menu = new JPopupMenu();
|
|
||||||
|
|
||||||
menu.add( new AddAliasActionItem( AliasActionType.BEEP ) );
|
|
||||||
menu.add( new AddAliasActionItem( AliasActionType.CLIP ) );
|
|
||||||
menu.add( new AddAliasActionItem( AliasActionType.SCRIPT ) );
|
|
||||||
|
|
||||||
menu.show( e.getComponent(), e.getX(), e.getY() );
|
|
||||||
}
|
|
||||||
} );
|
|
||||||
|
|
||||||
add( mNewActionButton );
|
|
||||||
|
|
||||||
mCloneActionButton = new JButton( "Clone" );
|
|
||||||
mCloneActionButton.setToolTipText( "Create a copy of the currently selected action" );
|
|
||||||
mCloneActionButton.setEnabled( false );
|
|
||||||
mCloneActionButton.addActionListener( new ActionListener()
|
|
||||||
{
|
|
||||||
@Override
|
|
||||||
public void actionPerformed( ActionEvent e )
|
|
||||||
{
|
|
||||||
AliasAction selected = mAliasActionList.getSelectedValue();
|
|
||||||
|
|
||||||
if( selected != null )
|
|
||||||
{
|
|
||||||
AliasAction clone = AliasFactory.copyOf( selected );
|
|
||||||
|
|
||||||
if( clone != null )
|
|
||||||
{
|
|
||||||
addAliasActionToList( clone );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} );
|
|
||||||
add( mCloneActionButton );
|
|
||||||
|
|
||||||
mDeleteActionButton = new JButton( "Delete" );
|
|
||||||
mDeleteActionButton.setToolTipText( "Delete the currently selected action" );
|
|
||||||
mDeleteActionButton.setEnabled( false );
|
|
||||||
mDeleteActionButton.addActionListener( new ActionListener()
|
|
||||||
{
|
|
||||||
@Override
|
|
||||||
public void actionPerformed( ActionEvent e )
|
|
||||||
{
|
|
||||||
final AliasAction selected = mAliasActionList.getSelectedValue();
|
|
||||||
|
|
||||||
if( selected != null )
|
|
||||||
{
|
|
||||||
int choice = JOptionPane.showConfirmDialog( mDeleteActionButton,
|
|
||||||
"Do you want to delete [" + selected.toString() + "]",
|
|
||||||
"Are you sure?", JOptionPane.YES_NO_OPTION );
|
|
||||||
|
|
||||||
if( choice == JOptionPane.YES_OPTION && hasItem() )
|
|
||||||
{
|
|
||||||
Alias alias = getItem();
|
|
||||||
|
|
||||||
alias.removeAliasAction( selected );
|
|
||||||
|
|
||||||
mAliasModel.broadcast( new AliasEvent( alias, Event.CHANGE ) );
|
|
||||||
|
|
||||||
setItem( alias );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} );
|
|
||||||
add( mDeleteActionButton, "wrap" );
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setItem( Alias alias )
|
|
||||||
{
|
|
||||||
super.setItem( alias );
|
|
||||||
|
|
||||||
if( alias == null || alias.getAction().isEmpty() )
|
|
||||||
{
|
|
||||||
mAliasActionList.setModel( EMPTY_MODEL );
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
DefaultListModel<AliasAction> model = new DefaultListModel<>();
|
|
||||||
|
|
||||||
List<AliasAction> actions = alias.getAction();
|
|
||||||
|
|
||||||
Collections.sort( actions, new Comparator<AliasAction>()
|
|
||||||
{
|
|
||||||
@Override
|
|
||||||
public int compare( AliasAction o1, AliasAction o2 )
|
|
||||||
{
|
|
||||||
return o1.toString().compareTo( o2.toString() );
|
|
||||||
}
|
|
||||||
} );
|
|
||||||
|
|
||||||
for( AliasAction action: actions )
|
|
||||||
{
|
|
||||||
model.addElement( action );
|
|
||||||
}
|
|
||||||
|
|
||||||
mAliasActionList.setModel( model );
|
|
||||||
}
|
|
||||||
|
|
||||||
mEditorContainer.setAliasAction( null );
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void save()
|
|
||||||
{
|
|
||||||
if( isModified() || mEditorContainer.isModified() )
|
|
||||||
{
|
|
||||||
if( mEditorContainer.isModified() )
|
|
||||||
{
|
|
||||||
mEditorContainer.save();
|
|
||||||
}
|
|
||||||
|
|
||||||
setModified( false );
|
|
||||||
|
|
||||||
if( hasItem() )
|
|
||||||
{
|
|
||||||
mAliasModel.broadcast( new AliasEvent( getItem(), Event.CHANGE ) );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class EditorContainer extends JPanel
|
|
||||||
{
|
|
||||||
private static final long serialVersionUID = 1L;
|
|
||||||
|
|
||||||
private Editor<AliasAction> mEditor = new EmptyEditor<>();
|
|
||||||
|
|
||||||
public EditorContainer()
|
|
||||||
{
|
|
||||||
setLayout( new MigLayout( "","[grow,fill]", "[grow,fill]" ) );
|
|
||||||
|
|
||||||
add( mEditor );
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isModified()
|
|
||||||
{
|
|
||||||
if( mEditor != null )
|
|
||||||
{
|
|
||||||
return mEditor.isModified();
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void save()
|
|
||||||
{
|
|
||||||
if( mEditor != null )
|
|
||||||
{
|
|
||||||
mEditor.save();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setAliasAction( AliasAction aliasAction )
|
|
||||||
{
|
|
||||||
if( mEditor != null )
|
|
||||||
{
|
|
||||||
if( mEditor.isModified() )
|
|
||||||
{
|
|
||||||
int option = JOptionPane.showConfirmDialog(
|
|
||||||
EditorContainer.this,
|
|
||||||
"Action settings have changed. Do you want to save these changes?",
|
|
||||||
"Save Changes?",
|
|
||||||
JOptionPane.YES_NO_OPTION,
|
|
||||||
JOptionPane.QUESTION_MESSAGE );
|
|
||||||
|
|
||||||
if( option == JOptionPane.YES_OPTION )
|
|
||||||
{
|
|
||||||
mEditor.save();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
removeAll();
|
|
||||||
|
|
||||||
//This will always give us an editor
|
|
||||||
mEditor = AliasFactory.getEditor( aliasAction );
|
|
||||||
|
|
||||||
add( mEditor );
|
|
||||||
|
|
||||||
revalidate();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void addAliasActionToList( final AliasAction action )
|
|
||||||
{
|
|
||||||
if( action != null && hasItem() )
|
|
||||||
{
|
|
||||||
final Alias alias = getItem();
|
|
||||||
|
|
||||||
alias.addAliasAction( action );
|
|
||||||
|
|
||||||
EventQueue.invokeLater( new Runnable()
|
|
||||||
{
|
|
||||||
@Override
|
|
||||||
public void run()
|
|
||||||
{
|
|
||||||
setItem( alias );
|
|
||||||
|
|
||||||
mAliasActionList.setSelectedValue( action, true );
|
|
||||||
|
|
||||||
AliasActionEditor.this.setModified( true );
|
|
||||||
}
|
|
||||||
} );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class AddAliasActionItem extends JMenuItem
|
|
||||||
{
|
|
||||||
private static final long serialVersionUID = 1L;
|
|
||||||
|
|
||||||
private AliasActionType mAliasActionType;
|
|
||||||
|
|
||||||
public AddAliasActionItem( AliasActionType type )
|
|
||||||
{
|
|
||||||
super( type.toString() );
|
|
||||||
|
|
||||||
mAliasActionType = type;
|
|
||||||
|
|
||||||
addActionListener( new ActionListener()
|
|
||||||
{
|
|
||||||
@Override
|
|
||||||
public void actionPerformed( ActionEvent e )
|
|
||||||
{
|
|
||||||
final AliasAction action =
|
|
||||||
AliasFactory.getAliasAction( mAliasActionType );
|
|
||||||
|
|
||||||
addAliasActionToList( action );
|
|
||||||
}
|
|
||||||
} );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,21 +1,23 @@
|
|||||||
/*
|
/*
|
||||||
* ******************************************************************************
|
|
||||||
* sdrtrunk
|
|
||||||
* Copyright (C) 2014-2018 Dennis Sheirer
|
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* * ******************************************************************************
|
||||||
* it under the terms of the GNU General Public License as published by
|
* * Copyright (C) 2014-2020 Dennis Sheirer
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
* *
|
||||||
* (at your option) any later version.
|
* * This program is free software: you can redistribute it and/or modify
|
||||||
|
* * it under the terms of the GNU General Public License as published by
|
||||||
|
* * the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* * (at your option) any later version.
|
||||||
|
* *
|
||||||
|
* * This program is distributed in the hope that it will be useful,
|
||||||
|
* * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* * GNU General Public License for more details.
|
||||||
|
* *
|
||||||
|
* * You should have received a copy of the GNU General Public License
|
||||||
|
* * along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||||
|
* * *****************************************************************************
|
||||||
*
|
*
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
*
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
|
||||||
* *****************************************************************************
|
|
||||||
*/
|
*/
|
||||||
package io.github.dsheirer.alias.action;
|
package io.github.dsheirer.alias.action;
|
||||||
|
|
||||||
@@ -63,7 +65,7 @@ public class AliasActionManager extends Module implements IMessageListener, List
|
|||||||
{
|
{
|
||||||
if(alias != null && alias.hasActions())
|
if(alias != null && alias.hasActions())
|
||||||
{
|
{
|
||||||
for(AliasAction action: alias.getAction())
|
for(AliasAction action: alias.getAliasActions())
|
||||||
{
|
{
|
||||||
action.execute(alias, message);
|
action.execute(alias, message);
|
||||||
}
|
}
|
||||||
@@ -76,13 +78,13 @@ public class AliasActionManager extends Module implements IMessageListener, List
|
|||||||
@Override
|
@Override
|
||||||
public void reset()
|
public void reset()
|
||||||
{
|
{
|
||||||
//No actions neeeded
|
//No actions needed
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void start()
|
public void start()
|
||||||
{
|
{
|
||||||
//No actions neeeded
|
//No actions needed
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -61,7 +61,7 @@ public abstract class RecurringAction extends AliasAction
|
|||||||
break;
|
break;
|
||||||
case UNTIL_DISMISSED:
|
case UNTIL_DISMISSED:
|
||||||
mPerpetualAction = ThreadPool.SCHEDULED.scheduleAtFixedRate(
|
mPerpetualAction = ThreadPool.SCHEDULED.scheduleAtFixedRate(
|
||||||
new PerformActionTask(alias, message), mPeriod, mPeriod, TimeUnit.SECONDS);
|
new PerformActionTask(alias, message), 0, mPeriod, TimeUnit.SECONDS);
|
||||||
|
|
||||||
StringBuilder sb = new StringBuilder();
|
StringBuilder sb = new StringBuilder();
|
||||||
sb.append("<html><div width='250'>Alias [");
|
sb.append("<html><div width='250'>Alias [");
|
||||||
@@ -120,6 +120,7 @@ public abstract class RecurringAction extends AliasAction
|
|||||||
public void setPeriod(int period)
|
public void setPeriod(int period)
|
||||||
{
|
{
|
||||||
mPeriod = period;
|
mPeriod = period;
|
||||||
|
updateValueProperty();
|
||||||
}
|
}
|
||||||
|
|
||||||
@JacksonXmlProperty(isAttribute = true, localName = "interval")
|
@JacksonXmlProperty(isAttribute = true, localName = "interval")
|
||||||
@@ -131,14 +132,14 @@ public abstract class RecurringAction extends AliasAction
|
|||||||
public void setInterval(Interval interval)
|
public void setInterval(Interval interval)
|
||||||
{
|
{
|
||||||
mInterval = interval;
|
mInterval = interval;
|
||||||
|
|
||||||
mRunning.set(false);
|
mRunning.set(false);
|
||||||
|
updateValueProperty();
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum Interval
|
public enum Interval
|
||||||
{
|
{
|
||||||
ONCE("Once"),
|
ONCE("Once"),
|
||||||
DELAYED_RESET("Once, Reset After Delay"),
|
DELAYED_RESET("Once - Reset After Delay"),
|
||||||
UNTIL_DISMISSED("Until Dismissed");
|
UNTIL_DISMISSED("Until Dismissed");
|
||||||
|
|
||||||
private String mLabel;
|
private String mLabel;
|
||||||
|
|||||||
@@ -24,10 +24,43 @@ import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
|
|||||||
import io.github.dsheirer.alias.Alias;
|
import io.github.dsheirer.alias.Alias;
|
||||||
import io.github.dsheirer.alias.action.AliasActionType;
|
import io.github.dsheirer.alias.action.AliasActionType;
|
||||||
import io.github.dsheirer.alias.action.RecurringAction;
|
import io.github.dsheirer.alias.action.RecurringAction;
|
||||||
|
import io.github.dsheirer.audio.AudioFormats;
|
||||||
|
import io.github.dsheirer.gui.preference.playback.ToneFrequency;
|
||||||
|
import io.github.dsheirer.gui.preference.playback.ToneUtil;
|
||||||
|
import io.github.dsheirer.gui.preference.playback.ToneVolume;
|
||||||
import io.github.dsheirer.message.IMessage;
|
import io.github.dsheirer.message.IMessage;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import javax.sound.sampled.AudioSystem;
|
||||||
|
import javax.sound.sampled.Clip;
|
||||||
|
import javax.sound.sampled.DataLine;
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
import java.nio.ByteOrder;
|
||||||
|
import java.nio.ShortBuffer;
|
||||||
|
|
||||||
public class BeepAction extends RecurringAction
|
public class BeepAction extends RecurringAction
|
||||||
{
|
{
|
||||||
|
private static final Logger mLog = LoggerFactory.getLogger(BeepAction.class);
|
||||||
|
private byte[] mToneBytes;
|
||||||
|
|
||||||
|
public BeepAction()
|
||||||
|
{
|
||||||
|
setInterval(Interval.ONCE);
|
||||||
|
float[] tone = ToneUtil.getTone(ToneFrequency.F700, ToneVolume.V10, 1000);
|
||||||
|
|
||||||
|
/* Little-endian byte buffer */
|
||||||
|
ByteBuffer buffer = ByteBuffer.allocate(tone.length * 2).order(ByteOrder.LITTLE_ENDIAN);
|
||||||
|
|
||||||
|
ShortBuffer shortBuffer = buffer.asShortBuffer();
|
||||||
|
|
||||||
|
for(float sample : tone)
|
||||||
|
{
|
||||||
|
shortBuffer.put((short) (sample * Short.MAX_VALUE));
|
||||||
|
}
|
||||||
|
|
||||||
|
mToneBytes = buffer.array();
|
||||||
|
}
|
||||||
|
|
||||||
@JacksonXmlProperty(isAttribute = true, localName = "type", namespace = "http://www.w3.org/2001/XMLSchema-instance")
|
@JacksonXmlProperty(isAttribute = true, localName = "type", namespace = "http://www.w3.org/2001/XMLSchema-instance")
|
||||||
@Override
|
@Override
|
||||||
@@ -39,12 +72,48 @@ public class BeepAction extends RecurringAction
|
|||||||
@Override
|
@Override
|
||||||
public void performAction(Alias alias, IMessage message )
|
public void performAction(Alias alias, IMessage message )
|
||||||
{
|
{
|
||||||
System.out.println( "\007" );
|
DataLine.Info info = new DataLine.Info(Clip.class, AudioFormats.PCM_SIGNED_8KHZ_16BITS_MONO);
|
||||||
|
|
||||||
|
if(!AudioSystem.isLineSupported(info))
|
||||||
|
{
|
||||||
|
mLog.error("Audio clip playback is not supported on this system");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Clip clip = (Clip)AudioSystem.getLine(info);
|
||||||
|
clip.open(AudioFormats.PCM_SIGNED_8KHZ_16BITS_MONO, mToneBytes, 0, mToneBytes.length);
|
||||||
|
clip.start();
|
||||||
|
}
|
||||||
|
catch(Exception e)
|
||||||
|
{
|
||||||
|
mLog.error("Error attempting to play audio test tone");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString()
|
public String toString()
|
||||||
{
|
{
|
||||||
return "Beep";
|
StringBuilder sb = new StringBuilder();
|
||||||
|
sb.append("Beep");
|
||||||
|
|
||||||
|
if(getInterval() != null)
|
||||||
|
{
|
||||||
|
switch(getInterval())
|
||||||
|
{
|
||||||
|
case ONCE:
|
||||||
|
sb.append(" Once");
|
||||||
|
break;
|
||||||
|
case DELAYED_RESET:
|
||||||
|
sb.append(" Once, Reset After ").append(getPeriod()).append(" Seconds");
|
||||||
|
break;
|
||||||
|
case UNTIL_DISMISSED:
|
||||||
|
sb.append(" Every ").append(getPeriod()).append(" Seconds Until Dismissed");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return sb.toString();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,185 +0,0 @@
|
|||||||
/*******************************************************************************
|
|
||||||
* SDR Trunk
|
|
||||||
* Copyright (C) 2014-2016 Dennis Sheirer
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
|
||||||
******************************************************************************/
|
|
||||||
package io.github.dsheirer.alias.action.beep;
|
|
||||||
|
|
||||||
import io.github.dsheirer.alias.action.AliasAction;
|
|
||||||
import io.github.dsheirer.alias.action.RecurringAction;
|
|
||||||
import io.github.dsheirer.gui.editor.DocumentListenerEditor;
|
|
||||||
import net.miginfocom.swing.MigLayout;
|
|
||||||
|
|
||||||
import javax.swing.*;
|
|
||||||
import javax.swing.event.ChangeEvent;
|
|
||||||
import javax.swing.event.ChangeListener;
|
|
||||||
import java.awt.*;
|
|
||||||
import java.awt.event.ActionEvent;
|
|
||||||
import java.awt.event.ActionListener;
|
|
||||||
import java.awt.event.MouseAdapter;
|
|
||||||
import java.awt.event.MouseEvent;
|
|
||||||
|
|
||||||
public class BeepActionEditor extends DocumentListenerEditor<AliasAction>
|
|
||||||
{
|
|
||||||
private static final long serialVersionUID = 1L;
|
|
||||||
|
|
||||||
private static final String HELP_TEXT =
|
|
||||||
"<html><h3>Beep Action</h3>"
|
|
||||||
+ "This action will sound a beep according to the interval<br>"
|
|
||||||
+ "that you select:<br><br>"
|
|
||||||
+ "<b>Once:</b> Beep the first time the alias is active and<br>"
|
|
||||||
+ "never again<br><br>"
|
|
||||||
+ "<b>Once, Reset After Delay:</b> Beep once and suppress<br>"
|
|
||||||
+ "subsequent beeps for the specified period in seconds.<br>"
|
|
||||||
+ "After the reset period, beep again when the alias is active.<br><br>"
|
|
||||||
+ "<b>Until Dismissed:</b> Beep every period seconds until you<br>"
|
|
||||||
+ "click OK on the dialog that appears. Alerting is suppressed<br>"
|
|
||||||
+ "for 15 seconds after you click OK.<br>"
|
|
||||||
+ "</html>";
|
|
||||||
|
|
||||||
private JComboBox<RecurringAction.Interval> mComboInterval;
|
|
||||||
private JSlider mPeriodSlider;
|
|
||||||
private JLabel mPeriodSliderLabel;
|
|
||||||
|
|
||||||
public BeepActionEditor( AliasAction aliasAction )
|
|
||||||
{
|
|
||||||
init();
|
|
||||||
|
|
||||||
setItem( aliasAction );
|
|
||||||
}
|
|
||||||
|
|
||||||
public BeepAction getBeepAction()
|
|
||||||
{
|
|
||||||
if( getItem() instanceof BeepAction )
|
|
||||||
{
|
|
||||||
return (BeepAction)getItem();
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void init()
|
|
||||||
{
|
|
||||||
setLayout( new MigLayout( "fill,wrap 2", "[right][left]", "[][][]" ) );
|
|
||||||
|
|
||||||
add( new JLabel( "Beep Interval:" ) );
|
|
||||||
|
|
||||||
mComboInterval = new JComboBox<RecurringAction.Interval>( RecurringAction.Interval.values() );
|
|
||||||
mComboInterval.setToolTipText( HELP_TEXT );
|
|
||||||
mComboInterval.addActionListener( new ActionListener()
|
|
||||||
{
|
|
||||||
@Override
|
|
||||||
public void actionPerformed( ActionEvent e )
|
|
||||||
{
|
|
||||||
RecurringAction.Interval selected = mComboInterval
|
|
||||||
.getItemAt( mComboInterval.getSelectedIndex() );
|
|
||||||
|
|
||||||
if( selected != null )
|
|
||||||
{
|
|
||||||
/* Enable/disable period spinner based on selection */
|
|
||||||
boolean enabled = selected != RecurringAction.Interval.ONCE;
|
|
||||||
|
|
||||||
mPeriodSlider.setEnabled( enabled );
|
|
||||||
mPeriodSliderLabel.setEnabled( enabled );
|
|
||||||
}
|
|
||||||
|
|
||||||
setModified( true );
|
|
||||||
}
|
|
||||||
});
|
|
||||||
add( mComboInterval, "wrap" );
|
|
||||||
|
|
||||||
final SpinnerModel model = new SpinnerNumberModel( 1, 1, 30, 1 );
|
|
||||||
model.addChangeListener( new ChangeListener()
|
|
||||||
{
|
|
||||||
@Override
|
|
||||||
public void stateChanged( ChangeEvent e )
|
|
||||||
{
|
|
||||||
setModified( true );
|
|
||||||
}
|
|
||||||
} );
|
|
||||||
|
|
||||||
mPeriodSlider = new JSlider( JSlider.HORIZONTAL, 1, 60, 1 );
|
|
||||||
mPeriodSlider.setMajorTickSpacing( 10 );
|
|
||||||
mPeriodSlider.setMinorTickSpacing( 2 );
|
|
||||||
mPeriodSlider.setPaintTicks( true );
|
|
||||||
mPeriodSlider.setLabelTable( mPeriodSlider.createStandardLabels( 10, 10 ) );
|
|
||||||
mPeriodSlider.setPaintLabels( true );
|
|
||||||
mPeriodSlider.addChangeListener( new ChangeListener()
|
|
||||||
{
|
|
||||||
@Override
|
|
||||||
public void stateChanged( ChangeEvent e )
|
|
||||||
{
|
|
||||||
mPeriodSliderLabel.setText( "Period: " + mPeriodSlider.getValue() );
|
|
||||||
setModified( true );
|
|
||||||
}
|
|
||||||
} );
|
|
||||||
mPeriodSlider.setToolTipText( HELP_TEXT );
|
|
||||||
|
|
||||||
mPeriodSliderLabel = new JLabel( "Period: " + mPeriodSlider.getValue() + " " );
|
|
||||||
add( mPeriodSliderLabel );
|
|
||||||
add( mPeriodSlider, "wrap,grow" );
|
|
||||||
|
|
||||||
JLabel help = new JLabel( "Help ..." );
|
|
||||||
help.setForeground( Color.BLUE.brighter() );
|
|
||||||
help.setCursor( new Cursor( Cursor.HAND_CURSOR ) );
|
|
||||||
help.addMouseListener( new MouseAdapter()
|
|
||||||
{
|
|
||||||
@Override
|
|
||||||
public void mouseClicked( MouseEvent e )
|
|
||||||
{
|
|
||||||
JOptionPane.showMessageDialog( BeepActionEditor.this,
|
|
||||||
HELP_TEXT, "Help", JOptionPane.INFORMATION_MESSAGE );
|
|
||||||
}
|
|
||||||
} );
|
|
||||||
add( help, "align left" );
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setItem( AliasAction item )
|
|
||||||
{
|
|
||||||
super.setItem( item );
|
|
||||||
|
|
||||||
if( hasItem() )
|
|
||||||
{
|
|
||||||
BeepAction beep = getBeepAction();
|
|
||||||
|
|
||||||
RecurringAction.Interval interval = beep.getInterval();
|
|
||||||
|
|
||||||
mComboInterval.setSelectedItem( interval );
|
|
||||||
|
|
||||||
boolean enabled = interval != RecurringAction.Interval.ONCE;
|
|
||||||
|
|
||||||
mPeriodSliderLabel.setEnabled( enabled );
|
|
||||||
mPeriodSlider.setEnabled( enabled );
|
|
||||||
mPeriodSlider.setValue( beep.getPeriod() );
|
|
||||||
}
|
|
||||||
|
|
||||||
setModified( false );
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void save()
|
|
||||||
{
|
|
||||||
if( hasItem() && isModified() )
|
|
||||||
{
|
|
||||||
BeepAction beep = getBeepAction();
|
|
||||||
|
|
||||||
beep.setInterval( (RecurringAction.Interval)mComboInterval.getSelectedItem() );
|
|
||||||
beep.setPeriod( mPeriodSlider.getValue() );
|
|
||||||
}
|
|
||||||
|
|
||||||
setModified( false );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -45,6 +45,7 @@ public class ClipAction extends RecurringAction
|
|||||||
|
|
||||||
public ClipAction()
|
public ClipAction()
|
||||||
{
|
{
|
||||||
|
updateValueProperty();
|
||||||
}
|
}
|
||||||
|
|
||||||
@JacksonXmlProperty(isAttribute = true, localName = "type", namespace = "http://www.w3.org/2001/XMLSchema-instance")
|
@JacksonXmlProperty(isAttribute = true, localName = "type", namespace = "http://www.w3.org/2001/XMLSchema-instance")
|
||||||
@@ -62,6 +63,7 @@ public class ClipAction extends RecurringAction
|
|||||||
public void setPath(String path)
|
public void setPath(String path)
|
||||||
{
|
{
|
||||||
mFilePath = path;
|
mFilePath = path;
|
||||||
|
updateValueProperty();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -87,8 +89,7 @@ public class ClipAction extends RecurringAction
|
|||||||
{
|
{
|
||||||
mClip = AudioSystem.getClip();
|
mClip = AudioSystem.getClip();
|
||||||
|
|
||||||
AudioInputStream ais =
|
AudioInputStream ais = AudioSystem.getAudioInputStream(new File(mFilePath));
|
||||||
AudioSystem.getAudioInputStream(new File(mFilePath));
|
|
||||||
|
|
||||||
mClip.open(ais);
|
mClip.open(ais);
|
||||||
}
|
}
|
||||||
@@ -113,17 +114,33 @@ public class ClipAction extends RecurringAction
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString()
|
public String toString()
|
||||||
{
|
{
|
||||||
if(mFilePath == null)
|
StringBuilder sb = new StringBuilder();
|
||||||
|
sb.append("Play Audio Clip");
|
||||||
|
|
||||||
|
if(getInterval() != null)
|
||||||
{
|
{
|
||||||
return "Play Clip";
|
switch(getInterval())
|
||||||
|
{
|
||||||
|
case ONCE:
|
||||||
|
sb.append(" Once");
|
||||||
|
break;
|
||||||
|
case DELAYED_RESET:
|
||||||
|
sb.append(" Once, Reset After ").append(getPeriod()).append(" Seconds");
|
||||||
|
break;
|
||||||
|
case UNTIL_DISMISSED:
|
||||||
|
sb.append(" Every ").append(getPeriod()).append(" Seconds Until Dismissed");
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
|
||||||
|
if(getPath() == null)
|
||||||
{
|
{
|
||||||
return "Play Clip: " + mFilePath;
|
sb.append(" - (audio file empty)");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return sb.toString();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,285 +0,0 @@
|
|||||||
/*******************************************************************************
|
|
||||||
* SDR Trunk
|
|
||||||
* Copyright (C) 2014-2016 Dennis Sheirer
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
|
||||||
******************************************************************************/
|
|
||||||
package io.github.dsheirer.alias.action.clip;
|
|
||||||
|
|
||||||
import io.github.dsheirer.alias.action.AliasAction;
|
|
||||||
import io.github.dsheirer.alias.action.RecurringAction;
|
|
||||||
import io.github.dsheirer.gui.editor.DocumentListenerEditor;
|
|
||||||
import net.miginfocom.swing.MigLayout;
|
|
||||||
|
|
||||||
import javax.swing.*;
|
|
||||||
import javax.swing.event.ChangeEvent;
|
|
||||||
import javax.swing.event.ChangeListener;
|
|
||||||
import java.awt.*;
|
|
||||||
import java.awt.event.ActionEvent;
|
|
||||||
import java.awt.event.ActionListener;
|
|
||||||
import java.awt.event.MouseAdapter;
|
|
||||||
import java.awt.event.MouseEvent;
|
|
||||||
import java.io.File;
|
|
||||||
|
|
||||||
public class ClipActionEditor extends DocumentListenerEditor<AliasAction>
|
|
||||||
{
|
|
||||||
private static final long serialVersionUID = 1L;
|
|
||||||
|
|
||||||
private static final String PLEASE_SELECT_A_FILE = "Please select a file";
|
|
||||||
private static final String HELP_TEXT =
|
|
||||||
"<html><h3>Clip Action</h3>"
|
|
||||||
+ "This action will play an audio clip according to the interval<br>"
|
|
||||||
+ "that you select:<br><br>"
|
|
||||||
+ "<b>Once:</b> play the first time the alias is active and<br>"
|
|
||||||
+ "never again<br><br>"
|
|
||||||
+ "<b>Once, Reset After Delay:</b> play once and suppress<br>"
|
|
||||||
+ "subsequent runs for the specified period in seconds.<br>"
|
|
||||||
+ "After the reset period, play again when the alias is active.<br><br>"
|
|
||||||
+ "<b>Until Dismissed:</b> play every period seconds until you<br>"
|
|
||||||
+ "click OK on the dialog that appears. Alerting is suppressed<br>"
|
|
||||||
+ "for 15 seconds after you click OK.<br>"
|
|
||||||
+ "</html>";
|
|
||||||
|
|
||||||
private JComboBox<RecurringAction.Interval> mComboInterval;
|
|
||||||
private JSlider mPeriodSlider;
|
|
||||||
private JLabel mPeriodSliderLabel;
|
|
||||||
private JTextField mTextFilePath;
|
|
||||||
|
|
||||||
public ClipActionEditor( AliasAction aliasAction )
|
|
||||||
{
|
|
||||||
init();
|
|
||||||
|
|
||||||
setItem( aliasAction );
|
|
||||||
}
|
|
||||||
|
|
||||||
public ClipAction getClipAction()
|
|
||||||
{
|
|
||||||
if( getItem() instanceof ClipAction )
|
|
||||||
{
|
|
||||||
return (ClipAction)getItem();
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void init()
|
|
||||||
{
|
|
||||||
setLayout( new MigLayout( "fill,wrap 2", "[right][left]", "[][][][][]" ) );
|
|
||||||
|
|
||||||
add( new JLabel( "Play Interval:" ) );
|
|
||||||
|
|
||||||
mComboInterval = new JComboBox<RecurringAction.Interval>( RecurringAction.Interval.values() );
|
|
||||||
mComboInterval.setToolTipText( HELP_TEXT );
|
|
||||||
mComboInterval.addActionListener( new ActionListener()
|
|
||||||
{
|
|
||||||
@Override
|
|
||||||
public void actionPerformed( ActionEvent e )
|
|
||||||
{
|
|
||||||
RecurringAction.Interval selected = mComboInterval
|
|
||||||
.getItemAt( mComboInterval.getSelectedIndex() );
|
|
||||||
|
|
||||||
if( selected != null )
|
|
||||||
{
|
|
||||||
/* Enable/disable period slider based on selection */
|
|
||||||
boolean enabled = selected != RecurringAction.Interval.ONCE;
|
|
||||||
|
|
||||||
mPeriodSlider.setEnabled( enabled );
|
|
||||||
mPeriodSliderLabel.setEnabled( enabled );
|
|
||||||
}
|
|
||||||
|
|
||||||
setModified( true );
|
|
||||||
}
|
|
||||||
});
|
|
||||||
add( mComboInterval );
|
|
||||||
|
|
||||||
final SpinnerModel model = new SpinnerNumberModel( 1, 1, 30, 1 );
|
|
||||||
model.addChangeListener( new ChangeListener()
|
|
||||||
{
|
|
||||||
@Override
|
|
||||||
public void stateChanged( ChangeEvent e )
|
|
||||||
{
|
|
||||||
setModified( true );
|
|
||||||
}
|
|
||||||
} );
|
|
||||||
|
|
||||||
mPeriodSlider = new JSlider( JSlider.HORIZONTAL, 1, 60, 1 );
|
|
||||||
mPeriodSlider.setMajorTickSpacing( 10 );
|
|
||||||
mPeriodSlider.setMinorTickSpacing( 2 );
|
|
||||||
mPeriodSlider.setPaintTicks( true );
|
|
||||||
mPeriodSlider.setLabelTable( mPeriodSlider.createStandardLabels( 10, 10 ) );
|
|
||||||
mPeriodSlider.setPaintLabels( true );
|
|
||||||
mPeriodSlider.addChangeListener( new ChangeListener()
|
|
||||||
{
|
|
||||||
@Override
|
|
||||||
public void stateChanged( ChangeEvent e )
|
|
||||||
{
|
|
||||||
mPeriodSliderLabel.setText( "Period: " + mPeriodSlider.getValue() );
|
|
||||||
setModified( true );
|
|
||||||
}
|
|
||||||
} );
|
|
||||||
mPeriodSlider.setToolTipText( HELP_TEXT );
|
|
||||||
|
|
||||||
mPeriodSliderLabel = new JLabel( "Period: " + mPeriodSlider.getValue() + " " );
|
|
||||||
add( mPeriodSliderLabel );
|
|
||||||
add( mPeriodSlider, "wrap,grow" );
|
|
||||||
|
|
||||||
mTextFilePath = new JTextField( PLEASE_SELECT_A_FILE );
|
|
||||||
add( mTextFilePath, "growx,span" );
|
|
||||||
|
|
||||||
JButton fileButton = new JButton( "File" );
|
|
||||||
|
|
||||||
fileButton.addActionListener( new ActionListener()
|
|
||||||
{
|
|
||||||
@Override
|
|
||||||
public void actionPerformed( ActionEvent e )
|
|
||||||
{
|
|
||||||
final JFileChooser chooser = new JFileChooser();
|
|
||||||
|
|
||||||
int value = chooser.showOpenDialog( ClipActionEditor.this );
|
|
||||||
|
|
||||||
if( value == JFileChooser.APPROVE_OPTION )
|
|
||||||
{
|
|
||||||
File file = chooser.getSelectedFile();
|
|
||||||
|
|
||||||
mTextFilePath.setText( file.getAbsolutePath() );
|
|
||||||
|
|
||||||
setModified( true );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} );
|
|
||||||
|
|
||||||
add( fileButton, "grow" );
|
|
||||||
|
|
||||||
JButton testButton = new JButton( "Test" );
|
|
||||||
|
|
||||||
testButton.addActionListener( new ActionListener()
|
|
||||||
{
|
|
||||||
@Override
|
|
||||||
public void actionPerformed( ActionEvent e )
|
|
||||||
{
|
|
||||||
if( hasItem() )
|
|
||||||
{
|
|
||||||
String path = mTextFilePath.getText();
|
|
||||||
|
|
||||||
if( path == null || path.trim().isEmpty() )
|
|
||||||
{
|
|
||||||
JOptionPane.showMessageDialog( ClipActionEditor.this,
|
|
||||||
"Please select an audio file", PLEASE_SELECT_A_FILE,
|
|
||||||
JOptionPane.ERROR_MESSAGE );
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if( isModified() )
|
|
||||||
{
|
|
||||||
int option = JOptionPane.showConfirmDialog(
|
|
||||||
ClipActionEditor.this,
|
|
||||||
"Settings have changed. Do you want to save these changes?",
|
|
||||||
"Save Changes?",
|
|
||||||
JOptionPane.YES_NO_OPTION,
|
|
||||||
JOptionPane.QUESTION_MESSAGE );
|
|
||||||
|
|
||||||
if( option == JOptionPane.YES_OPTION )
|
|
||||||
{
|
|
||||||
save();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
new Thread( new Runnable()
|
|
||||||
{
|
|
||||||
@Override
|
|
||||||
public void run()
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
getClipAction().play();
|
|
||||||
}
|
|
||||||
catch( Exception e1 )
|
|
||||||
{
|
|
||||||
JOptionPane.showMessageDialog( ClipActionEditor.this,
|
|
||||||
"Couldn't play audio clip [" + e1.getMessage() + "]",
|
|
||||||
"Error", JOptionPane.ERROR_MESSAGE );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}).start();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} );
|
|
||||||
|
|
||||||
add( testButton, "grow,wrap" );
|
|
||||||
|
|
||||||
JLabel help = new JLabel( "Help ..." );
|
|
||||||
help.setForeground( Color.BLUE.brighter() );
|
|
||||||
help.setCursor( new Cursor( Cursor.HAND_CURSOR ) );
|
|
||||||
help.addMouseListener( new MouseAdapter()
|
|
||||||
{
|
|
||||||
@Override
|
|
||||||
public void mouseClicked( MouseEvent e )
|
|
||||||
{
|
|
||||||
JOptionPane.showMessageDialog( ClipActionEditor.this,
|
|
||||||
HELP_TEXT, "Help", JOptionPane.INFORMATION_MESSAGE );
|
|
||||||
}
|
|
||||||
} );
|
|
||||||
add( help, "align left" );
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setItem( AliasAction item )
|
|
||||||
{
|
|
||||||
super.setItem( item );
|
|
||||||
|
|
||||||
if( hasItem() )
|
|
||||||
{
|
|
||||||
ClipAction clip = getClipAction();
|
|
||||||
|
|
||||||
RecurringAction.Interval interval = clip.getInterval();
|
|
||||||
|
|
||||||
mComboInterval.setSelectedItem( interval );
|
|
||||||
|
|
||||||
boolean enabled = interval != RecurringAction.Interval.ONCE;
|
|
||||||
|
|
||||||
mPeriodSliderLabel.setEnabled( enabled );
|
|
||||||
mPeriodSlider.setEnabled( enabled );
|
|
||||||
mPeriodSlider.setValue( clip.getPeriod() );
|
|
||||||
|
|
||||||
String filepath = clip.getPath();
|
|
||||||
|
|
||||||
if( filepath != null && !filepath.isEmpty() )
|
|
||||||
{
|
|
||||||
mTextFilePath.setText( filepath );
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
mTextFilePath.setText( PLEASE_SELECT_A_FILE );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
setModified( false );
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void save()
|
|
||||||
{
|
|
||||||
if( hasItem() && isModified() )
|
|
||||||
{
|
|
||||||
ClipAction script = getClipAction();
|
|
||||||
|
|
||||||
script.setInterval( (RecurringAction.Interval)mComboInterval.getSelectedItem() );
|
|
||||||
script.setPeriod( mPeriodSlider.getValue() );
|
|
||||||
script.setPath( mTextFilePath.getText() );
|
|
||||||
}
|
|
||||||
|
|
||||||
setModified( false );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -39,6 +39,7 @@ public class ScriptAction extends RecurringAction
|
|||||||
|
|
||||||
public ScriptAction()
|
public ScriptAction()
|
||||||
{
|
{
|
||||||
|
updateValueProperty();
|
||||||
}
|
}
|
||||||
|
|
||||||
@JacksonXmlProperty(isAttribute = true, localName = "type", namespace = "http://www.w3.org/2001/XMLSchema-instance")
|
@JacksonXmlProperty(isAttribute = true, localName = "type", namespace = "http://www.w3.org/2001/XMLSchema-instance")
|
||||||
@@ -56,6 +57,7 @@ public class ScriptAction extends RecurringAction
|
|||||||
public void setScript(String script)
|
public void setScript(String script)
|
||||||
{
|
{
|
||||||
mScript = script;
|
mScript = script;
|
||||||
|
updateValueProperty();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -104,13 +106,30 @@ public class ScriptAction extends RecurringAction
|
|||||||
@Override
|
@Override
|
||||||
public String toString()
|
public String toString()
|
||||||
{
|
{
|
||||||
if(mScript == null)
|
StringBuilder sb = new StringBuilder();
|
||||||
|
sb.append("Run Script");
|
||||||
|
|
||||||
|
if(getInterval() != null)
|
||||||
{
|
{
|
||||||
return "Run Script";
|
switch(getInterval())
|
||||||
|
{
|
||||||
|
case ONCE:
|
||||||
|
sb.append(" Once");
|
||||||
|
break;
|
||||||
|
case DELAYED_RESET:
|
||||||
|
sb.append(" Once, Reset After ").append(getPeriod()).append(" Seconds");
|
||||||
|
break;
|
||||||
|
case UNTIL_DISMISSED:
|
||||||
|
sb.append(" Every ").append(getPeriod()).append(" Seconds Until Dismissed");
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
|
||||||
|
if(getScript() == null)
|
||||||
{
|
{
|
||||||
return "Run Script: " + mScript;
|
sb.append(" - (script file empty)");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return sb.toString();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,281 +0,0 @@
|
|||||||
/*******************************************************************************
|
|
||||||
* SDR Trunk
|
|
||||||
* Copyright (C) 2014-2016 Dennis Sheirer
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
|
||||||
******************************************************************************/
|
|
||||||
package io.github.dsheirer.alias.action.script;
|
|
||||||
|
|
||||||
import io.github.dsheirer.alias.action.AliasAction;
|
|
||||||
import io.github.dsheirer.alias.action.RecurringAction;
|
|
||||||
import io.github.dsheirer.gui.editor.DocumentListenerEditor;
|
|
||||||
import net.miginfocom.swing.MigLayout;
|
|
||||||
|
|
||||||
import javax.swing.*;
|
|
||||||
import javax.swing.event.ChangeEvent;
|
|
||||||
import javax.swing.event.ChangeListener;
|
|
||||||
import java.awt.*;
|
|
||||||
import java.awt.event.ActionEvent;
|
|
||||||
import java.awt.event.ActionListener;
|
|
||||||
import java.awt.event.MouseAdapter;
|
|
||||||
import java.awt.event.MouseEvent;
|
|
||||||
import java.io.File;
|
|
||||||
|
|
||||||
public class ScriptActionEditor extends DocumentListenerEditor<AliasAction>
|
|
||||||
{
|
|
||||||
private static final long serialVersionUID = 1L;
|
|
||||||
|
|
||||||
private static final String PLEASE_SELECT_A_FILE = "Please select a file";
|
|
||||||
private static final String HELP_TEXT =
|
|
||||||
"<html><h3>Script Action</h3>"
|
|
||||||
+ "This action will run a script according to the interval<br>"
|
|
||||||
+ "that you select:<br><br>"
|
|
||||||
+ "<b>Once:</b> run the first time the alias is active and<br>"
|
|
||||||
+ "never again<br><br>"
|
|
||||||
+ "<b>Once, Reset After Delay:</b> run once and suppress<br>"
|
|
||||||
+ "subsequent runs for the specified period in seconds.<br>"
|
|
||||||
+ "After the reset period, run again when the alias is active.<br><br>"
|
|
||||||
+ "<b>Until Dismissed:</b> run every period seconds until you<br>"
|
|
||||||
+ "click OK on the dialog that appears. Alerting is suppressed<br>"
|
|
||||||
+ "for 15 seconds after you click OK.<br>"
|
|
||||||
+ "</html>";
|
|
||||||
|
|
||||||
private JComboBox<RecurringAction.Interval> mComboInterval;
|
|
||||||
private JSlider mPeriodSlider;
|
|
||||||
private JLabel mPeriodSliderLabel;
|
|
||||||
private JTextField mTextFilePath;
|
|
||||||
|
|
||||||
public ScriptActionEditor( AliasAction aliasAction )
|
|
||||||
{
|
|
||||||
init();
|
|
||||||
|
|
||||||
setItem( aliasAction );
|
|
||||||
}
|
|
||||||
|
|
||||||
public ScriptAction getScriptAction()
|
|
||||||
{
|
|
||||||
if( getItem() instanceof ScriptAction )
|
|
||||||
{
|
|
||||||
return (ScriptAction)getItem();
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void init()
|
|
||||||
{
|
|
||||||
setLayout( new MigLayout( "fill,wrap 2", "[right][left]", "[][][][][]" ) );
|
|
||||||
|
|
||||||
add( new JLabel( "Run Interval:" ) );
|
|
||||||
|
|
||||||
mComboInterval = new JComboBox<RecurringAction.Interval>( RecurringAction.Interval.values() );
|
|
||||||
mComboInterval.setToolTipText( HELP_TEXT );
|
|
||||||
mComboInterval.addActionListener( new ActionListener()
|
|
||||||
{
|
|
||||||
@Override
|
|
||||||
public void actionPerformed( ActionEvent e )
|
|
||||||
{
|
|
||||||
RecurringAction.Interval selected = mComboInterval
|
|
||||||
.getItemAt( mComboInterval.getSelectedIndex() );
|
|
||||||
|
|
||||||
if( selected != null )
|
|
||||||
{
|
|
||||||
/* Enable/disable period slider based on selection */
|
|
||||||
boolean enabled = selected != RecurringAction.Interval.ONCE;
|
|
||||||
|
|
||||||
mPeriodSlider.setEnabled( enabled );
|
|
||||||
mPeriodSliderLabel.setEnabled( enabled );
|
|
||||||
}
|
|
||||||
|
|
||||||
setModified( true );
|
|
||||||
}
|
|
||||||
});
|
|
||||||
add( mComboInterval );
|
|
||||||
|
|
||||||
final SpinnerModel model = new SpinnerNumberModel( 1, 1, 30, 1 );
|
|
||||||
model.addChangeListener( new ChangeListener()
|
|
||||||
{
|
|
||||||
@Override
|
|
||||||
public void stateChanged( ChangeEvent e )
|
|
||||||
{
|
|
||||||
setModified( true );
|
|
||||||
}
|
|
||||||
} );
|
|
||||||
|
|
||||||
mPeriodSlider = new JSlider( JSlider.HORIZONTAL, 1, 60, 1 );
|
|
||||||
mPeriodSlider.setMajorTickSpacing( 10 );
|
|
||||||
mPeriodSlider.setMinorTickSpacing( 2 );
|
|
||||||
mPeriodSlider.setPaintTicks( true );
|
|
||||||
mPeriodSlider.setLabelTable( mPeriodSlider.createStandardLabels( 10, 10 ) );
|
|
||||||
mPeriodSlider.setPaintLabels( true );
|
|
||||||
mPeriodSlider.addChangeListener( new ChangeListener()
|
|
||||||
{
|
|
||||||
@Override
|
|
||||||
public void stateChanged( ChangeEvent e )
|
|
||||||
{
|
|
||||||
mPeriodSliderLabel.setText( "Period: " + mPeriodSlider.getValue() );
|
|
||||||
setModified( true );
|
|
||||||
}
|
|
||||||
} );
|
|
||||||
mPeriodSlider.setToolTipText( HELP_TEXT );
|
|
||||||
|
|
||||||
mPeriodSliderLabel = new JLabel( "Period: " + mPeriodSlider.getValue() + " " );
|
|
||||||
add( mPeriodSliderLabel );
|
|
||||||
add( mPeriodSlider, "wrap,grow" );
|
|
||||||
|
|
||||||
mTextFilePath = new JTextField( PLEASE_SELECT_A_FILE );
|
|
||||||
add( mTextFilePath, "growx,span" );
|
|
||||||
|
|
||||||
JButton fileButton = new JButton( "File" );
|
|
||||||
|
|
||||||
fileButton.addActionListener( new ActionListener()
|
|
||||||
{
|
|
||||||
@Override
|
|
||||||
public void actionPerformed( ActionEvent e )
|
|
||||||
{
|
|
||||||
final JFileChooser chooser = new JFileChooser();
|
|
||||||
|
|
||||||
int value = chooser.showOpenDialog( ScriptActionEditor.this );
|
|
||||||
|
|
||||||
if( value == JFileChooser.APPROVE_OPTION )
|
|
||||||
{
|
|
||||||
File file = chooser.getSelectedFile();
|
|
||||||
|
|
||||||
mTextFilePath.setText( file.getAbsolutePath() );
|
|
||||||
|
|
||||||
setModified( true );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} );
|
|
||||||
|
|
||||||
add( fileButton, "grow" );
|
|
||||||
|
|
||||||
JButton testButton = new JButton( "Test" );
|
|
||||||
testButton.addActionListener( new ActionListener()
|
|
||||||
{
|
|
||||||
@Override
|
|
||||||
public void actionPerformed( ActionEvent actionEvent )
|
|
||||||
{
|
|
||||||
String script = mTextFilePath.getText();
|
|
||||||
|
|
||||||
if( script == null || script.trim().isEmpty() )
|
|
||||||
{
|
|
||||||
JOptionPane.showMessageDialog( ScriptActionEditor.this,
|
|
||||||
"Please select a script file", PLEASE_SELECT_A_FILE,
|
|
||||||
JOptionPane.ERROR_MESSAGE );
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if( isModified() )
|
|
||||||
{
|
|
||||||
int option = JOptionPane.showConfirmDialog(
|
|
||||||
ScriptActionEditor.this,
|
|
||||||
"Settings have changed. Do you want to save these changes?",
|
|
||||||
"Save Changes?",
|
|
||||||
JOptionPane.YES_NO_OPTION,
|
|
||||||
JOptionPane.QUESTION_MESSAGE );
|
|
||||||
|
|
||||||
if( option == JOptionPane.YES_OPTION )
|
|
||||||
{
|
|
||||||
save();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
new Thread( new Runnable()
|
|
||||||
{
|
|
||||||
@Override
|
|
||||||
public void run()
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
getScriptAction().play();
|
|
||||||
}
|
|
||||||
catch( Exception e1 )
|
|
||||||
{
|
|
||||||
JOptionPane.showMessageDialog( ScriptActionEditor.this,
|
|
||||||
e1.getMessage(), "Error", JOptionPane.ERROR_MESSAGE );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}).start();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
} );
|
|
||||||
|
|
||||||
add( testButton, "grow,wrap" );
|
|
||||||
|
|
||||||
JLabel help = new JLabel( "Help ..." );
|
|
||||||
help.setForeground( Color.BLUE.brighter() );
|
|
||||||
help.setCursor( new Cursor( Cursor.HAND_CURSOR ) );
|
|
||||||
help.addMouseListener( new MouseAdapter()
|
|
||||||
{
|
|
||||||
@Override
|
|
||||||
public void mouseClicked( MouseEvent e )
|
|
||||||
{
|
|
||||||
JOptionPane.showMessageDialog( ScriptActionEditor.this,
|
|
||||||
HELP_TEXT, "Help", JOptionPane.INFORMATION_MESSAGE );
|
|
||||||
}
|
|
||||||
} );
|
|
||||||
add( help, "align left" );
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setItem( AliasAction item )
|
|
||||||
{
|
|
||||||
super.setItem( item );
|
|
||||||
|
|
||||||
if( hasItem() )
|
|
||||||
{
|
|
||||||
ScriptAction script = getScriptAction();
|
|
||||||
|
|
||||||
RecurringAction.Interval interval = script.getInterval();
|
|
||||||
|
|
||||||
mComboInterval.setSelectedItem( interval );
|
|
||||||
|
|
||||||
boolean enabled = interval != RecurringAction.Interval.ONCE;
|
|
||||||
|
|
||||||
mPeriodSliderLabel.setEnabled( enabled );
|
|
||||||
mPeriodSlider.setEnabled( enabled );
|
|
||||||
mPeriodSlider.setValue( script.getPeriod() );
|
|
||||||
|
|
||||||
String filepath = script.getScript();
|
|
||||||
|
|
||||||
if( filepath != null && !filepath.isEmpty() )
|
|
||||||
{
|
|
||||||
mTextFilePath.setText( filepath );
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
mTextFilePath.setText( PLEASE_SELECT_A_FILE );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
setModified( false );
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void save()
|
|
||||||
{
|
|
||||||
if( hasItem() && isModified() )
|
|
||||||
{
|
|
||||||
ScriptAction script = getScriptAction();
|
|
||||||
|
|
||||||
script.setInterval( (RecurringAction.Interval)mComboInterval.getSelectedItem() );
|
|
||||||
script.setPeriod( mPeriodSlider.getValue() );
|
|
||||||
script.setScript( mTextFilePath.getText() );
|
|
||||||
}
|
|
||||||
|
|
||||||
setModified( false );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -40,43 +40,126 @@ import io.github.dsheirer.alias.id.priority.Priority;
|
|||||||
import io.github.dsheirer.alias.id.radio.Radio;
|
import io.github.dsheirer.alias.id.radio.Radio;
|
||||||
import io.github.dsheirer.alias.id.radio.RadioRange;
|
import io.github.dsheirer.alias.id.radio.RadioRange;
|
||||||
import io.github.dsheirer.alias.id.record.Record;
|
import io.github.dsheirer.alias.id.record.Record;
|
||||||
import io.github.dsheirer.alias.id.status.StatusID;
|
import io.github.dsheirer.alias.id.status.UnitStatusID;
|
||||||
|
import io.github.dsheirer.alias.id.status.UserStatusID;
|
||||||
import io.github.dsheirer.alias.id.talkgroup.Talkgroup;
|
import io.github.dsheirer.alias.id.talkgroup.Talkgroup;
|
||||||
import io.github.dsheirer.alias.id.talkgroup.TalkgroupRange;
|
import io.github.dsheirer.alias.id.talkgroup.TalkgroupRange;
|
||||||
|
import io.github.dsheirer.alias.id.tone.TonesID;
|
||||||
|
import javafx.beans.Observable;
|
||||||
|
import javafx.beans.property.BooleanProperty;
|
||||||
|
import javafx.beans.property.SimpleBooleanProperty;
|
||||||
|
import javafx.beans.property.SimpleStringProperty;
|
||||||
|
import javafx.util.Callback;
|
||||||
|
|
||||||
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "type")
|
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "type")
|
||||||
@JsonSubTypes({
|
@JsonSubTypes({
|
||||||
@JsonSubTypes.Type(value = BroadcastChannel.class, name = "broadcastChannel"),
|
@JsonSubTypes.Type(value = BroadcastChannel.class, name = "broadcastChannel"),
|
||||||
@JsonSubTypes.Type(value = Esn.class, name = "esn"),
|
@JsonSubTypes.Type(value = Esn.class, name = "esn"),
|
||||||
@JsonSubTypes.Type(value = FleetsyncID.class, name = "fleetsyncID"),
|
@JsonSubTypes.Type(value = FleetsyncID.class, name = "fleetsyncID"),
|
||||||
|
@JsonSubTypes.Type(value = LegacyTalkgroupID.class, name = "talkgroupID"),
|
||||||
@JsonSubTypes.Type(value = LoJackFunctionAndID.class, name = "loJackFunctionAndID"),
|
@JsonSubTypes.Type(value = LoJackFunctionAndID.class, name = "loJackFunctionAndID"),
|
||||||
@JsonSubTypes.Type(value = MDC1200ID.class, name = "mdc1200ID"),
|
@JsonSubTypes.Type(value = MDC1200ID.class, name = "mdc1200ID"),
|
||||||
@JsonSubTypes.Type(value = Min.class, name = "min"),
|
@JsonSubTypes.Type(value = Min.class, name = "min"),
|
||||||
@JsonSubTypes.Type(value = MPT1327ID.class, name = "mpt1327ID"),
|
@JsonSubTypes.Type(value = MPT1327ID.class, name = "mpt1327ID"),
|
||||||
@JsonSubTypes.Type(value = NonRecordable.class, name = "nonRecordable"),
|
@JsonSubTypes.Type(value = NonRecordable.class, name = "nonRecordable"),
|
||||||
|
@JsonSubTypes.Type(value = Priority.class, name = "priority"),
|
||||||
@JsonSubTypes.Type(value = Radio.class, name = "radio"),
|
@JsonSubTypes.Type(value = Radio.class, name = "radio"),
|
||||||
@JsonSubTypes.Type(value = RadioRange.class, name = "radioRange"),
|
@JsonSubTypes.Type(value = RadioRange.class, name = "radioRange"),
|
||||||
@JsonSubTypes.Type(value = Talkgroup.class, name = "talkgroup"),
|
|
||||||
@JsonSubTypes.Type(value = TalkgroupRange.class, name = "talkgroupRange"),
|
|
||||||
@JsonSubTypes.Type(value = Priority.class, name = "priority"),
|
|
||||||
@JsonSubTypes.Type(value = Record.class, name = "record"),
|
@JsonSubTypes.Type(value = Record.class, name = "record"),
|
||||||
@JsonSubTypes.Type(value = SiteID.class, name = "siteID"),
|
@JsonSubTypes.Type(value = SiteID.class, name = "siteID"),
|
||||||
@JsonSubTypes.Type(value = StatusID.class, name = "statusID"),
|
@JsonSubTypes.Type(value = Talkgroup.class, name = "talkgroup"),
|
||||||
@JsonSubTypes.Type(value = LegacyTalkgroupID.class, name = "talkgroupID"),
|
@JsonSubTypes.Type(value = TalkgroupRange.class, name = "talkgroupRange"),
|
||||||
|
@JsonSubTypes.Type(value = TonesID.class, name = "tones"),
|
||||||
|
@JsonSubTypes.Type(value = UserStatusID.class, name = "statusID"),
|
||||||
|
@JsonSubTypes.Type(value = UnitStatusID.class, name = "unitStatusID"),
|
||||||
@JsonSubTypes.Type(value = UniqueID.class, name = "uniqueID")
|
@JsonSubTypes.Type(value = UniqueID.class, name = "uniqueID")
|
||||||
})
|
})
|
||||||
@JacksonXmlRootElement(localName = "id")
|
@JacksonXmlRootElement(localName = "id")
|
||||||
public abstract class AliasID
|
public abstract class AliasID
|
||||||
{
|
{
|
||||||
|
private SimpleStringProperty mValueProperty = new SimpleStringProperty();
|
||||||
|
private BooleanProperty mOverlapProperty = new SimpleBooleanProperty();
|
||||||
|
|
||||||
public AliasID()
|
public AliasID()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* String property representation of this alias ID
|
||||||
|
*/
|
||||||
|
@JsonIgnore
|
||||||
|
public SimpleStringProperty valueProperty()
|
||||||
|
{
|
||||||
|
return mValueProperty;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicates if this alias identifier overlaps with any other alias identifier in the same alias list
|
||||||
|
*/
|
||||||
|
@JsonIgnore
|
||||||
|
public BooleanProperty overlapProperty()
|
||||||
|
{
|
||||||
|
return mOverlapProperty;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the overlap flag for this alias id. Overlap indicates that this identifier collides with another
|
||||||
|
* identifier within the same alias list.
|
||||||
|
*/
|
||||||
|
public void setOverlap(boolean overlap)
|
||||||
|
{
|
||||||
|
mOverlapProperty.set(overlap);
|
||||||
|
updateValueProperty();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates the value property for this alias ID. Note: this method is intended to be invoked by all subclasses
|
||||||
|
* each time that any of the subclass member variable values change.
|
||||||
|
*/
|
||||||
|
public void updateValueProperty()
|
||||||
|
{
|
||||||
|
if(overlapProperty().get())
|
||||||
|
{
|
||||||
|
valueProperty().set(toString() + " - Error: Overlap");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
valueProperty().set(toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
//Hack: harmless, but the list extractor does not consistently update unless we do this.
|
||||||
|
valueProperty().get();
|
||||||
|
}
|
||||||
|
|
||||||
@JsonIgnore
|
@JsonIgnore
|
||||||
public abstract AliasIDType getType();
|
public abstract AliasIDType getType();
|
||||||
|
|
||||||
public abstract boolean matches(AliasID id);
|
public abstract boolean matches(AliasID id);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicates if this alias ID overlaps with another alias ID.
|
||||||
|
* Note: this method is intended to be overridden by certain subclass implementations.
|
||||||
|
*/
|
||||||
|
@JsonIgnore
|
||||||
|
public boolean overlaps(AliasID id)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
@JsonIgnore
|
@JsonIgnore
|
||||||
public abstract boolean isValid();
|
public abstract boolean isValid();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicates if the identifier is an audio identifier (monitor, record, stream)
|
||||||
|
*/
|
||||||
|
@JsonIgnore
|
||||||
|
public abstract boolean isAudioIdentifier();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates an observable property extractor for use with observable lists to detect changes internal to this object.
|
||||||
|
*/
|
||||||
|
public static Callback<AliasID,Observable[]> extractor()
|
||||||
|
{
|
||||||
|
return (AliasID aid) -> new Observable[] {aid.valueProperty(), aid.overlapProperty()};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,6 +21,8 @@
|
|||||||
*/
|
*/
|
||||||
package io.github.dsheirer.alias.id;
|
package io.github.dsheirer.alias.id;
|
||||||
|
|
||||||
|
import java.util.EnumSet;
|
||||||
|
|
||||||
public enum AliasIDType
|
public enum AliasIDType
|
||||||
{
|
{
|
||||||
BROADCAST_CHANNEL("Audio Broadcast Channel"),
|
BROADCAST_CHANNEL("Audio Broadcast Channel"),
|
||||||
@@ -34,7 +36,9 @@ public enum AliasIDType
|
|||||||
RADIO_ID_RANGE("Radio ID Range"),
|
RADIO_ID_RANGE("Radio ID Range"),
|
||||||
RECORD("Record"),
|
RECORD("Record"),
|
||||||
SITE("Site"),
|
SITE("Site"),
|
||||||
STATUS("Status"),
|
STATUS("User Status"),
|
||||||
|
TONES("Tone Sequence"),
|
||||||
|
UNIT_STATUS("Unit Status"),
|
||||||
TALKGROUP("Talkgroup"),
|
TALKGROUP("Talkgroup"),
|
||||||
TALKGROUP_RANGE("Talkgroup Range"),
|
TALKGROUP_RANGE("Talkgroup Range"),
|
||||||
|
|
||||||
@@ -52,6 +56,10 @@ public enum AliasIDType
|
|||||||
mLabel = label;
|
mLabel = label;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//Values used by the View-By alias editor
|
||||||
|
public static EnumSet<AliasIDType> VIEW_BY_VALUES = EnumSet.of(TALKGROUP, TALKGROUP_RANGE, RADIO_ID, RADIO_ID_RANGE,
|
||||||
|
UNIT_STATUS, STATUS, TONES);
|
||||||
|
|
||||||
public String toString()
|
public String toString()
|
||||||
{
|
{
|
||||||
return mLabel;
|
return mLabel;
|
||||||
|
|||||||
@@ -1,377 +0,0 @@
|
|||||||
/*
|
|
||||||
*
|
|
||||||
* * ******************************************************************************
|
|
||||||
* * Copyright (C) 2014-2019 Dennis Sheirer
|
|
||||||
* *
|
|
||||||
* * This program is free software: you can redistribute it and/or modify
|
|
||||||
* * it under the terms of the GNU General Public License as published by
|
|
||||||
* * the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* * (at your option) any later version.
|
|
||||||
* *
|
|
||||||
* * This program is distributed in the hope that it will be useful,
|
|
||||||
* * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* * GNU General Public License for more details.
|
|
||||||
* *
|
|
||||||
* * You should have received a copy of the GNU General Public License
|
|
||||||
* * along with this program. If not, see <http://www.gnu.org/licenses/>
|
|
||||||
* * *****************************************************************************
|
|
||||||
*
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
package io.github.dsheirer.alias.id;
|
|
||||||
|
|
||||||
import io.github.dsheirer.alias.Alias;
|
|
||||||
import io.github.dsheirer.alias.AliasEvent;
|
|
||||||
import io.github.dsheirer.alias.AliasFactory;
|
|
||||||
import io.github.dsheirer.alias.AliasModel;
|
|
||||||
import io.github.dsheirer.audio.broadcast.BroadcastModel;
|
|
||||||
import io.github.dsheirer.gui.editor.Editor;
|
|
||||||
import io.github.dsheirer.gui.editor.EmptyEditor;
|
|
||||||
import net.miginfocom.swing.MigLayout;
|
|
||||||
|
|
||||||
import javax.swing.DefaultListModel;
|
|
||||||
import javax.swing.JButton;
|
|
||||||
import javax.swing.JList;
|
|
||||||
import javax.swing.JMenuItem;
|
|
||||||
import javax.swing.JOptionPane;
|
|
||||||
import javax.swing.JPanel;
|
|
||||||
import javax.swing.JPopupMenu;
|
|
||||||
import javax.swing.JScrollPane;
|
|
||||||
import javax.swing.ListModel;
|
|
||||||
import javax.swing.ListSelectionModel;
|
|
||||||
import javax.swing.event.ListSelectionEvent;
|
|
||||||
import javax.swing.event.ListSelectionListener;
|
|
||||||
import java.awt.EventQueue;
|
|
||||||
import java.awt.event.ActionEvent;
|
|
||||||
import java.awt.event.ActionListener;
|
|
||||||
import java.awt.event.MouseAdapter;
|
|
||||||
import java.awt.event.MouseEvent;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.Comparator;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
public class AliasIdentifierEditor extends Editor<Alias>
|
|
||||||
{
|
|
||||||
private static final long serialVersionUID = 1L;
|
|
||||||
|
|
||||||
private static ListModel<AliasID> EMPTY_MODEL = new DefaultListModel<>();
|
|
||||||
private JList<AliasID> mAliasIDList = new JList<>(EMPTY_MODEL);
|
|
||||||
private JButton mNewIDButton;
|
|
||||||
private JButton mCloneIDButton;
|
|
||||||
private JButton mDeleteIDButton;
|
|
||||||
private EditorContainer mEditorContainer = new EditorContainer();
|
|
||||||
|
|
||||||
private AliasModel mAliasModel;
|
|
||||||
private BroadcastModel mBroadcastModel;
|
|
||||||
|
|
||||||
public AliasIdentifierEditor(AliasModel aliasModel, BroadcastModel broadcastModel)
|
|
||||||
{
|
|
||||||
mAliasModel = aliasModel;
|
|
||||||
mBroadcastModel = broadcastModel;
|
|
||||||
|
|
||||||
init();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void init()
|
|
||||||
{
|
|
||||||
setLayout(new MigLayout("fill,wrap 3",
|
|
||||||
"[grow,fill][grow,fill][grow,fill]", "[][grow,fill][]"));
|
|
||||||
|
|
||||||
mAliasIDList.setVisibleRowCount(6);
|
|
||||||
mAliasIDList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
|
|
||||||
mAliasIDList.setLayoutOrientation(JList.VERTICAL);
|
|
||||||
mAliasIDList.addListSelectionListener(new ListSelectionListener()
|
|
||||||
{
|
|
||||||
@Override
|
|
||||||
public void valueChanged(ListSelectionEvent event)
|
|
||||||
{
|
|
||||||
if(!event.getValueIsAdjusting())
|
|
||||||
{
|
|
||||||
JList<?> list = (JList<?>)event.getSource();
|
|
||||||
|
|
||||||
Object selectedItem = list.getSelectedValue();
|
|
||||||
|
|
||||||
if(selectedItem != null && selectedItem instanceof AliasID)
|
|
||||||
{
|
|
||||||
AliasID selected = (AliasID)selectedItem;
|
|
||||||
|
|
||||||
mEditorContainer.setAliasID(selected);
|
|
||||||
|
|
||||||
mCloneIDButton.setEnabled(true);
|
|
||||||
mDeleteIDButton.setEnabled(true);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
mCloneIDButton.setEnabled(false);
|
|
||||||
mDeleteIDButton.setEnabled(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
JScrollPane scroller = new JScrollPane(mAliasIDList);
|
|
||||||
add(scroller, "span,grow");
|
|
||||||
|
|
||||||
add(mEditorContainer, "span,grow");
|
|
||||||
|
|
||||||
mNewIDButton = new JButton("New ...");
|
|
||||||
mNewIDButton.setToolTipText("Create a new Alias Identifier");
|
|
||||||
mNewIDButton.addMouseListener(new MouseAdapter()
|
|
||||||
{
|
|
||||||
@Override
|
|
||||||
public void mouseClicked(MouseEvent e)
|
|
||||||
{
|
|
||||||
JPopupMenu menu = new JPopupMenu();
|
|
||||||
|
|
||||||
menu.add(new AddAliasIdentifierItem(AliasIDType.TALKGROUP));
|
|
||||||
menu.add(new AddAliasIdentifierItem(AliasIDType.TALKGROUP_RANGE));
|
|
||||||
menu.add(new AddAliasIdentifierItem(AliasIDType.RADIO_ID));
|
|
||||||
menu.add(new AddAliasIdentifierItem(AliasIDType.RADIO_ID_RANGE));
|
|
||||||
menu.add(new AddAliasIdentifierItem(AliasIDType.ESN));
|
|
||||||
// menu.add(new AddAliasIdentifierItem(AliasIDType.LOJACK));
|
|
||||||
menu.add(new AddAliasIdentifierItem(AliasIDType.STATUS));
|
|
||||||
|
|
||||||
menu.addSeparator();
|
|
||||||
|
|
||||||
menu.add(new AddAliasIdentifierItem(AliasIDType.BROADCAST_CHANNEL));
|
|
||||||
|
|
||||||
if(hasItem())
|
|
||||||
{
|
|
||||||
Alias alias = getItem();
|
|
||||||
|
|
||||||
if(!alias.isRecordable())
|
|
||||||
{
|
|
||||||
menu.add(new AddAliasIdentifierItem(AliasIDType.RECORD));
|
|
||||||
}
|
|
||||||
|
|
||||||
if(!alias.hasCallPriority())
|
|
||||||
{
|
|
||||||
menu.add(new AddAliasIdentifierItem(AliasIDType.PRIORITY));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
menu.show(e.getComponent(), e.getX(), e.getY());
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
add(mNewIDButton);
|
|
||||||
|
|
||||||
mCloneIDButton = new JButton("Clone");
|
|
||||||
mCloneIDButton.setToolTipText("Create a copy of the currently selected identifier");
|
|
||||||
mCloneIDButton.setEnabled(false);
|
|
||||||
mCloneIDButton.addActionListener(new ActionListener()
|
|
||||||
{
|
|
||||||
@Override
|
|
||||||
public void actionPerformed(ActionEvent e)
|
|
||||||
{
|
|
||||||
AliasID selected = mAliasIDList.getSelectedValue();
|
|
||||||
|
|
||||||
if(selected != null)
|
|
||||||
{
|
|
||||||
AliasID clone = AliasFactory.copyOf(selected);
|
|
||||||
|
|
||||||
if(clone != null)
|
|
||||||
{
|
|
||||||
addAliasIDToList(clone);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
add(mCloneIDButton);
|
|
||||||
|
|
||||||
mDeleteIDButton = new JButton("Delete");
|
|
||||||
mDeleteIDButton.setToolTipText("Delete the currently selected identifier");
|
|
||||||
mDeleteIDButton.setEnabled(false);
|
|
||||||
mDeleteIDButton.addActionListener(new ActionListener()
|
|
||||||
{
|
|
||||||
@Override
|
|
||||||
public void actionPerformed(ActionEvent e)
|
|
||||||
{
|
|
||||||
final AliasID selected = mAliasIDList.getSelectedValue();
|
|
||||||
|
|
||||||
if(selected != null)
|
|
||||||
{
|
|
||||||
int choice = JOptionPane.showConfirmDialog(mDeleteIDButton,
|
|
||||||
"Do you want to delete [" + selected.toString() + "]",
|
|
||||||
"Are you sure?", JOptionPane.YES_NO_OPTION);
|
|
||||||
|
|
||||||
if(choice == JOptionPane.YES_OPTION && hasItem())
|
|
||||||
{
|
|
||||||
Alias alias = getItem();
|
|
||||||
|
|
||||||
alias.removeAliasID(selected);
|
|
||||||
|
|
||||||
mAliasModel.broadcast(new AliasEvent(alias, AliasEvent.Event.CHANGE));
|
|
||||||
|
|
||||||
setItem(alias);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
add(mDeleteIDButton, "wrap");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setItem(Alias alias)
|
|
||||||
{
|
|
||||||
super.setItem(alias);
|
|
||||||
|
|
||||||
if(alias == null || alias.getId().isEmpty())
|
|
||||||
{
|
|
||||||
mAliasIDList.setModel(EMPTY_MODEL);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
DefaultListModel<AliasID> model = new DefaultListModel<AliasID>();
|
|
||||||
|
|
||||||
List<AliasID> ids = alias.getId();
|
|
||||||
|
|
||||||
Collections.sort(ids, new Comparator<AliasID>()
|
|
||||||
{
|
|
||||||
@Override
|
|
||||||
public int compare(AliasID o1, AliasID o2)
|
|
||||||
{
|
|
||||||
return o1.toString().compareTo(o2.toString());
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
for(AliasID id : ids)
|
|
||||||
{
|
|
||||||
model.addElement(id);
|
|
||||||
}
|
|
||||||
|
|
||||||
mAliasIDList.setModel(model);
|
|
||||||
}
|
|
||||||
|
|
||||||
mEditorContainer.setAliasID(null);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void save()
|
|
||||||
{
|
|
||||||
if(isModified() || mEditorContainer.isModified())
|
|
||||||
{
|
|
||||||
if(mEditorContainer.isModified())
|
|
||||||
{
|
|
||||||
mEditorContainer.save();
|
|
||||||
}
|
|
||||||
|
|
||||||
setModified(false);
|
|
||||||
|
|
||||||
if(hasItem())
|
|
||||||
{
|
|
||||||
mAliasModel.broadcast(new AliasEvent(getItem(), AliasEvent.Event.CHANGE));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class EditorContainer extends JPanel
|
|
||||||
{
|
|
||||||
private static final long serialVersionUID = 1L;
|
|
||||||
|
|
||||||
private Editor<AliasID> mEditor = new EmptyEditor<>();
|
|
||||||
|
|
||||||
public EditorContainer()
|
|
||||||
{
|
|
||||||
setLayout(new MigLayout("", "[grow,fill]", "[grow,fill]"));
|
|
||||||
|
|
||||||
add(mEditor);
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isModified()
|
|
||||||
{
|
|
||||||
if(mEditor != null)
|
|
||||||
{
|
|
||||||
return mEditor.isModified();
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void save()
|
|
||||||
{
|
|
||||||
if(mEditor != null)
|
|
||||||
{
|
|
||||||
mEditor.save();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setAliasID(AliasID aliasID)
|
|
||||||
{
|
|
||||||
if(mEditor != null)
|
|
||||||
{
|
|
||||||
if(mEditor.isModified())
|
|
||||||
{
|
|
||||||
int option = JOptionPane.showConfirmDialog(
|
|
||||||
EditorContainer.this,
|
|
||||||
"Identifier settings have changed. Do you want to save these changes?",
|
|
||||||
"Save Changes?",
|
|
||||||
JOptionPane.YES_NO_OPTION,
|
|
||||||
JOptionPane.QUESTION_MESSAGE);
|
|
||||||
|
|
||||||
if(option == JOptionPane.YES_OPTION)
|
|
||||||
{
|
|
||||||
mEditor.save();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
removeAll();
|
|
||||||
|
|
||||||
//This will always give us an editor
|
|
||||||
mEditor = AliasFactory.getEditor(aliasID, mBroadcastModel);
|
|
||||||
|
|
||||||
add(mEditor);
|
|
||||||
|
|
||||||
revalidate();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void addAliasIDToList(final AliasID id)
|
|
||||||
{
|
|
||||||
if(id != null && hasItem())
|
|
||||||
{
|
|
||||||
final Alias alias = getItem();
|
|
||||||
|
|
||||||
alias.addAliasID(id);
|
|
||||||
|
|
||||||
EventQueue.invokeLater(new Runnable()
|
|
||||||
{
|
|
||||||
@Override
|
|
||||||
public void run()
|
|
||||||
{
|
|
||||||
setItem(alias);
|
|
||||||
|
|
||||||
mAliasIDList.setSelectedValue(id, true);
|
|
||||||
|
|
||||||
AliasIdentifierEditor.this.setModified(true);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class AddAliasIdentifierItem extends JMenuItem
|
|
||||||
{
|
|
||||||
private static final long serialVersionUID = 1L;
|
|
||||||
|
|
||||||
private AliasIDType mAliasIDType;
|
|
||||||
|
|
||||||
public AddAliasIdentifierItem(AliasIDType type)
|
|
||||||
{
|
|
||||||
super(type.toString());
|
|
||||||
|
|
||||||
mAliasIDType = type;
|
|
||||||
|
|
||||||
addActionListener(new ActionListener()
|
|
||||||
{
|
|
||||||
@Override
|
|
||||||
public void actionPerformed(ActionEvent e)
|
|
||||||
{
|
|
||||||
final AliasID id = AliasFactory.getAliasID(mAliasIDType);
|
|
||||||
|
|
||||||
addAliasIDToList(id);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,113 +0,0 @@
|
|||||||
/*******************************************************************************
|
|
||||||
* sdrtrunk
|
|
||||||
* Copyright (C) 2014-2016 Dennis Sheirer
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
|
||||||
*
|
|
||||||
******************************************************************************/
|
|
||||||
package io.github.dsheirer.alias.id;
|
|
||||||
|
|
||||||
import org.apache.commons.lang3.Validate;
|
|
||||||
|
|
||||||
import java.util.regex.Pattern;
|
|
||||||
|
|
||||||
public class WildcardID implements Comparable<WildcardID>
|
|
||||||
{
|
|
||||||
public static final String WILDCARD = "*";
|
|
||||||
public static final String REGEX_WILDCARD = ".";
|
|
||||||
|
|
||||||
private String mValue;
|
|
||||||
private Pattern mPattern;
|
|
||||||
private int mWeight;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Wildcard identifier for matching to string identifiers containing single-character (*) wildcard values.
|
|
||||||
*
|
|
||||||
* Supports ordering from most-specific to least-specific using the build-in weighting calculation where identifier
|
|
||||||
* patterns containing fewer wildcards closer to the least significant digit will be weighted more heavily than
|
|
||||||
* identifier patterns containing more wildcards, or wildcard characters in the most significant digits.
|
|
||||||
*
|
|
||||||
* @param value is a string value containing one or more asterisk (*) single character wildcard with a maximum
|
|
||||||
* length of 10 characters.
|
|
||||||
*
|
|
||||||
* @throws AssertionError if the value is null, longer than 10 characters, or doesn't contain at least 1 asterisk
|
|
||||||
* @throws IllegalArgumentException if the value cannot be compiled as a regular expression after converting the
|
|
||||||
* asterisk characters to the regex single-character wildcard value (.)
|
|
||||||
*/
|
|
||||||
public WildcardID(String value)
|
|
||||||
{
|
|
||||||
Validate.isTrue(value != null && value.contains(WILDCARD) && value.length() < 10);
|
|
||||||
|
|
||||||
mValue = value;
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
mPattern = Pattern.compile(value.replace(WILDCARD, REGEX_WILDCARD));
|
|
||||||
}
|
|
||||||
catch(Exception e)
|
|
||||||
{
|
|
||||||
throw new IllegalArgumentException("Invalid regex pattern for alias ID value [" + value + "]", e);
|
|
||||||
}
|
|
||||||
|
|
||||||
mWeight = calculateWeight();
|
|
||||||
}
|
|
||||||
|
|
||||||
public String value()
|
|
||||||
{
|
|
||||||
return mValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Indicates if the id matches the internal regex pattern for this wildcard alias ID
|
|
||||||
* @param id
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
public boolean matches(String id)
|
|
||||||
{
|
|
||||||
return id != null && mPattern.matcher(id).matches();
|
|
||||||
}
|
|
||||||
|
|
||||||
public int weight()
|
|
||||||
{
|
|
||||||
return mWeight;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Calculates a weighting value for wildcard character quantity and significant digit location
|
|
||||||
*/
|
|
||||||
private int calculateWeight()
|
|
||||||
{
|
|
||||||
int weight = 0;
|
|
||||||
int characterCount = -1;
|
|
||||||
|
|
||||||
for( int x = 0; x < mValue.length(); x++)
|
|
||||||
{
|
|
||||||
if(mValue.substring(x, x + 1).equals(WILDCARD))
|
|
||||||
{
|
|
||||||
weight += (int)(Math.pow(2, mValue.length() - x - 1)); //Position weight
|
|
||||||
characterCount++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
weight += (int)(Math.pow(2, characterCount)) * 1000; //Character count weight
|
|
||||||
|
|
||||||
return weight;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int compareTo(WildcardID otherWildcardID)
|
|
||||||
{
|
|
||||||
return Integer.compare(this.weight(), otherWildcardID.weight());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,21 +1,24 @@
|
|||||||
/*******************************************************************************
|
/*
|
||||||
* sdrtrunk
|
|
||||||
* Copyright (C) 2014-2016 Dennis Sheirer
|
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* * ******************************************************************************
|
||||||
* it under the terms of the GNU General Public License as published by
|
* * Copyright (C) 2014-2020 Dennis Sheirer
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
* *
|
||||||
* (at your option) any later version.
|
* * This program is free software: you can redistribute it and/or modify
|
||||||
|
* * it under the terms of the GNU General Public License as published by
|
||||||
|
* * the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* * (at your option) any later version.
|
||||||
|
* *
|
||||||
|
* * This program is distributed in the hope that it will be useful,
|
||||||
|
* * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* * GNU General Public License for more details.
|
||||||
|
* *
|
||||||
|
* * You should have received a copy of the GNU General Public License
|
||||||
|
* * along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||||
|
* * *****************************************************************************
|
||||||
*
|
*
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
*
|
||||||
* You should have received a copy of the GNU General Public License
|
*/
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
|
||||||
*
|
|
||||||
******************************************************************************/
|
|
||||||
package io.github.dsheirer.alias.id.broadcast;
|
package io.github.dsheirer.alias.id.broadcast;
|
||||||
|
|
||||||
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
|
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
|
||||||
@@ -31,6 +34,12 @@ public class BroadcastChannel extends AliasID implements Comparable<BroadcastCha
|
|||||||
//JAXB Constructor
|
//JAXB Constructor
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isAudioIdentifier()
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int compareTo(BroadcastChannel other)
|
public int compareTo(BroadcastChannel other)
|
||||||
{
|
{
|
||||||
@@ -94,6 +103,7 @@ public class BroadcastChannel extends AliasID implements Comparable<BroadcastCha
|
|||||||
public void setChannelName(String channel)
|
public void setChannelName(String channel)
|
||||||
{
|
{
|
||||||
mChannelName = channel;
|
mChannelName = channel;
|
||||||
|
updateValueProperty();
|
||||||
}
|
}
|
||||||
|
|
||||||
@JacksonXmlProperty(isAttribute = true, localName = "type", namespace = "http://www.w3.org/2001/XMLSchema-instance")
|
@JacksonXmlProperty(isAttribute = true, localName = "type", namespace = "http://www.w3.org/2001/XMLSchema-instance")
|
||||||
@@ -118,13 +128,6 @@ public class BroadcastChannel extends AliasID implements Comparable<BroadcastCha
|
|||||||
@Override
|
@Override
|
||||||
public String toString()
|
public String toString()
|
||||||
{
|
{
|
||||||
if(isValid())
|
return isValid() ? mChannelName : "(invalid)";
|
||||||
{
|
|
||||||
return "Broadcast Channel: " + mChannelName;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return "Broadcast Channel: None Selected";
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,129 +0,0 @@
|
|||||||
/*******************************************************************************
|
|
||||||
* sdrtrunk
|
|
||||||
* Copyright (C) 2014-2016 Dennis Sheirer
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
|
||||||
*
|
|
||||||
******************************************************************************/
|
|
||||||
package io.github.dsheirer.alias.id.broadcast;
|
|
||||||
|
|
||||||
import io.github.dsheirer.alias.id.AliasID;
|
|
||||||
import io.github.dsheirer.audio.broadcast.BroadcastConfiguration;
|
|
||||||
import io.github.dsheirer.audio.broadcast.BroadcastModel;
|
|
||||||
import io.github.dsheirer.gui.editor.DocumentListenerEditor;
|
|
||||||
import net.miginfocom.swing.MigLayout;
|
|
||||||
|
|
||||||
import javax.swing.*;
|
|
||||||
import java.awt.*;
|
|
||||||
import java.awt.event.ActionEvent;
|
|
||||||
import java.awt.event.ActionListener;
|
|
||||||
import java.awt.event.MouseAdapter;
|
|
||||||
import java.awt.event.MouseEvent;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
public class BroadcastChannelEditor extends DocumentListenerEditor<AliasID>
|
|
||||||
{
|
|
||||||
private static final long serialVersionUID = 1L;
|
|
||||||
|
|
||||||
private static final String HELP_TEXT = "<html>"
|
|
||||||
+ "<h3>Audio Broadcast Channel</h3>"
|
|
||||||
+ "Name of a audio streaming broadcast configuration<br>"
|
|
||||||
+ "to be used for streaming audio to an external<br>"
|
|
||||||
+ "streaming server";
|
|
||||||
|
|
||||||
private JComboBox<String> mBroadcastConfigurations;
|
|
||||||
private BroadcastModel mBroadcastModel;
|
|
||||||
|
|
||||||
public BroadcastChannelEditor(AliasID aliasID, BroadcastModel broadcastModel)
|
|
||||||
{
|
|
||||||
mBroadcastModel = broadcastModel;
|
|
||||||
|
|
||||||
initGUI();
|
|
||||||
setItem( aliasID );
|
|
||||||
}
|
|
||||||
|
|
||||||
private void initGUI()
|
|
||||||
{
|
|
||||||
setLayout( new MigLayout( "fill,wrap 1", "[]", "[][]" ) );
|
|
||||||
|
|
||||||
add( new JLabel( "Broadcast Channel" ), "grow" );
|
|
||||||
|
|
||||||
List<String> channelNames = mBroadcastModel.getBroadcastConfigurationNames();
|
|
||||||
|
|
||||||
mBroadcastConfigurations = new JComboBox<String>(channelNames.toArray(new String[channelNames.size()]));
|
|
||||||
mBroadcastConfigurations.addActionListener(new ActionListener()
|
|
||||||
{
|
|
||||||
@Override
|
|
||||||
public void actionPerformed(ActionEvent e)
|
|
||||||
{
|
|
||||||
setModified(true);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
add(mBroadcastConfigurations, "grow");
|
|
||||||
|
|
||||||
JLabel help = new JLabel( "Help ..." );
|
|
||||||
help.setForeground( Color.BLUE.brighter() );
|
|
||||||
help.setCursor( new Cursor( Cursor.HAND_CURSOR ) );
|
|
||||||
help.addMouseListener( new MouseAdapter()
|
|
||||||
{
|
|
||||||
@Override
|
|
||||||
public void mouseClicked( MouseEvent e )
|
|
||||||
{
|
|
||||||
JOptionPane.showMessageDialog( BroadcastChannelEditor.this,
|
|
||||||
HELP_TEXT, "Help", JOptionPane.INFORMATION_MESSAGE );
|
|
||||||
}
|
|
||||||
} );
|
|
||||||
|
|
||||||
add( help, "align left" );
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setItem(AliasID item)
|
|
||||||
{
|
|
||||||
super.setItem(item);
|
|
||||||
|
|
||||||
if(item instanceof BroadcastChannel)
|
|
||||||
{
|
|
||||||
BroadcastChannel channel = (BroadcastChannel)item;
|
|
||||||
|
|
||||||
if(channel.isValid())
|
|
||||||
{
|
|
||||||
mBroadcastConfigurations.setSelectedItem(channel.getChannelName());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
setModified(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void save()
|
|
||||||
{
|
|
||||||
String channel = (String)mBroadcastConfigurations.getSelectedItem();
|
|
||||||
|
|
||||||
BroadcastConfiguration selectedConfiguration = mBroadcastModel.getBroadcastConfiguration(channel);
|
|
||||||
|
|
||||||
|
|
||||||
if(selectedConfiguration != null)
|
|
||||||
{
|
|
||||||
((BroadcastChannel)getItem()).setChannelName(selectedConfiguration.getName());
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
((BroadcastChannel)getItem()).setChannelName(null);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,113 +0,0 @@
|
|||||||
/*******************************************************************************
|
|
||||||
* SDR Trunk
|
|
||||||
* Copyright (C) 2014-2016 Dennis Sheirer
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
|
||||||
******************************************************************************/
|
|
||||||
package io.github.dsheirer.alias.id.esn;
|
|
||||||
|
|
||||||
import io.github.dsheirer.alias.id.AliasID;
|
|
||||||
import io.github.dsheirer.gui.editor.DocumentListenerEditor;
|
|
||||||
import net.miginfocom.swing.MigLayout;
|
|
||||||
|
|
||||||
import javax.swing.*;
|
|
||||||
import java.awt.*;
|
|
||||||
import java.awt.event.MouseAdapter;
|
|
||||||
import java.awt.event.MouseEvent;
|
|
||||||
|
|
||||||
public class ESNEditor extends DocumentListenerEditor<AliasID>
|
|
||||||
{
|
|
||||||
private static final long serialVersionUID = 1L;
|
|
||||||
|
|
||||||
private static final String HELP_TEXT =
|
|
||||||
"<html><h3>Electronic Serial Number (ESN)</h3>"
|
|
||||||
+ "<b>ESN:</b> hexadecimal 0-9, A-F (e.g. <u>ABCD1234</u> )<br>"
|
|
||||||
+ "<b>Wildcard:</b> use an asterisk (*) to wildcard individual<br>"
|
|
||||||
+ "digits (e.g. <u>ABCD123*</u> or <u>AB**1**4</u>)"
|
|
||||||
+ "</html>";
|
|
||||||
|
|
||||||
private JTextField mTextField;
|
|
||||||
|
|
||||||
public ESNEditor( AliasID aliasID )
|
|
||||||
{
|
|
||||||
initGUI();
|
|
||||||
|
|
||||||
setItem( aliasID );
|
|
||||||
}
|
|
||||||
|
|
||||||
private void initGUI()
|
|
||||||
{
|
|
||||||
setLayout( new MigLayout( "fill,wrap 2", "[right][left]", "[][]" ) );
|
|
||||||
|
|
||||||
add( new JLabel( "ESN:" ) );
|
|
||||||
mTextField = new JTextField();
|
|
||||||
mTextField.getDocument().addDocumentListener( this );
|
|
||||||
mTextField.setToolTipText( HELP_TEXT );
|
|
||||||
add( mTextField, "growx,push" );
|
|
||||||
|
|
||||||
JLabel help = new JLabel( "Help ..." );
|
|
||||||
help.setForeground( Color.BLUE.brighter() );
|
|
||||||
help.setCursor( new Cursor( Cursor.HAND_CURSOR ) );
|
|
||||||
help.addMouseListener( new MouseAdapter()
|
|
||||||
{
|
|
||||||
@Override
|
|
||||||
public void mouseClicked( MouseEvent e )
|
|
||||||
{
|
|
||||||
JOptionPane.showMessageDialog( ESNEditor.this,
|
|
||||||
HELP_TEXT, "Help", JOptionPane.INFORMATION_MESSAGE );
|
|
||||||
}
|
|
||||||
} );
|
|
||||||
add( help, "align left" );
|
|
||||||
}
|
|
||||||
|
|
||||||
public Esn getEsn()
|
|
||||||
{
|
|
||||||
if( getItem() instanceof Esn )
|
|
||||||
{
|
|
||||||
return (Esn)getItem();
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setItem( AliasID aliasID )
|
|
||||||
{
|
|
||||||
super.setItem( aliasID );
|
|
||||||
|
|
||||||
Esn esn = getEsn();
|
|
||||||
|
|
||||||
if( esn != null )
|
|
||||||
{
|
|
||||||
mTextField.setText( esn.getEsn() );
|
|
||||||
}
|
|
||||||
|
|
||||||
setModified( false );
|
|
||||||
|
|
||||||
repaint();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void save()
|
|
||||||
{
|
|
||||||
Esn esn = getEsn();
|
|
||||||
|
|
||||||
if( esn != null )
|
|
||||||
{
|
|
||||||
esn.setEsn( mTextField.getText() );
|
|
||||||
}
|
|
||||||
|
|
||||||
setModified( false );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -29,6 +29,12 @@ public class Esn extends AliasID
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isAudioIdentifier()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
@JacksonXmlProperty(isAttribute = true, localName = "esn")
|
@JacksonXmlProperty(isAttribute = true, localName = "esn")
|
||||||
public String getEsn()
|
public String getEsn()
|
||||||
{
|
{
|
||||||
@@ -44,6 +50,7 @@ public class Esn extends AliasID
|
|||||||
public void setEsn(String esn )
|
public void setEsn(String esn )
|
||||||
{
|
{
|
||||||
mEsn = esn;
|
mEsn = esn;
|
||||||
|
updateValueProperty();
|
||||||
}
|
}
|
||||||
|
|
||||||
public String toString()
|
public String toString()
|
||||||
|
|||||||
@@ -31,6 +31,12 @@ public class FleetsyncID extends AliasID
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isAudioIdentifier()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
@JacksonXmlProperty(isAttribute = true, localName = "ident")
|
@JacksonXmlProperty(isAttribute = true, localName = "ident")
|
||||||
public String getIdent()
|
public String getIdent()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,137 +0,0 @@
|
|||||||
/*
|
|
||||||
* ******************************************************************************
|
|
||||||
* sdrtrunk
|
|
||||||
* Copyright (C) 2014-2018 Dennis Sheirer
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
|
||||||
* *****************************************************************************
|
|
||||||
*/
|
|
||||||
package io.github.dsheirer.alias.id.legacy.fleetsync;
|
|
||||||
|
|
||||||
import io.github.dsheirer.alias.id.AliasID;
|
|
||||||
import io.github.dsheirer.gui.editor.DocumentListenerEditor;
|
|
||||||
import net.miginfocom.swing.MigLayout;
|
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
|
|
||||||
import javax.swing.JFormattedTextField;
|
|
||||||
import javax.swing.JLabel;
|
|
||||||
import javax.swing.JOptionPane;
|
|
||||||
import javax.swing.JTextField;
|
|
||||||
import javax.swing.text.MaskFormatter;
|
|
||||||
import java.awt.Color;
|
|
||||||
import java.awt.Cursor;
|
|
||||||
import java.awt.event.MouseAdapter;
|
|
||||||
import java.awt.event.MouseEvent;
|
|
||||||
|
|
||||||
public class FleetsyncIDEditor extends DocumentListenerEditor<AliasID>
|
|
||||||
{
|
|
||||||
private final static Logger mLog = LoggerFactory.getLogger( FleetsyncIDEditor.class );
|
|
||||||
|
|
||||||
private static final long serialVersionUID = 1L;
|
|
||||||
|
|
||||||
private static final String HELP_TEXT = "<html>"
|
|
||||||
+ "<h3>Fleetsync Identifier</h3>"
|
|
||||||
+ "<b>Fleetsync:</b> ggg-uuuu where g=Group and u=Unit (e.g. <u>001-0001</u>)<br>"
|
|
||||||
+ "<b>Wildcard:</b> use an asterisk (*) for each digit (e.g. <u>001-****</u>)"
|
|
||||||
+ "</html>";
|
|
||||||
|
|
||||||
private JTextField mTextField;
|
|
||||||
|
|
||||||
public FleetsyncIDEditor( AliasID aliasID )
|
|
||||||
{
|
|
||||||
initGUI();
|
|
||||||
|
|
||||||
setItem( aliasID );
|
|
||||||
}
|
|
||||||
|
|
||||||
private void initGUI()
|
|
||||||
{
|
|
||||||
setLayout( new MigLayout( "fill,wrap 2", "[right][left]", "[][]" ) );
|
|
||||||
|
|
||||||
add( new JLabel( "Fleetsync ID:" ) );
|
|
||||||
|
|
||||||
MaskFormatter formatter = null;
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
//Mask: 3 digits - 4 digits
|
|
||||||
formatter = new MaskFormatter( "###-####" );
|
|
||||||
}
|
|
||||||
catch( Exception e )
|
|
||||||
{
|
|
||||||
//Do nothing, the mask was invalid
|
|
||||||
}
|
|
||||||
|
|
||||||
mTextField = new JFormattedTextField( formatter );
|
|
||||||
mTextField.getDocument().addDocumentListener( this );
|
|
||||||
mTextField.setToolTipText( HELP_TEXT );
|
|
||||||
|
|
||||||
add( mTextField, "growx,push" );
|
|
||||||
|
|
||||||
JLabel help = new JLabel( "Help ..." );
|
|
||||||
help.setForeground( Color.BLUE.brighter() );
|
|
||||||
help.setCursor( new Cursor( Cursor.HAND_CURSOR ) );
|
|
||||||
help.addMouseListener( new MouseAdapter()
|
|
||||||
{
|
|
||||||
@Override
|
|
||||||
public void mouseClicked( MouseEvent e )
|
|
||||||
{
|
|
||||||
JOptionPane.showMessageDialog( FleetsyncIDEditor.this,
|
|
||||||
HELP_TEXT, "Help", JOptionPane.INFORMATION_MESSAGE );
|
|
||||||
}
|
|
||||||
} );
|
|
||||||
add( help, "align left" );
|
|
||||||
}
|
|
||||||
|
|
||||||
public FleetsyncID getFleetsyncID()
|
|
||||||
{
|
|
||||||
if( getItem() instanceof FleetsyncID )
|
|
||||||
{
|
|
||||||
return (FleetsyncID)getItem();
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setItem( AliasID aliasID )
|
|
||||||
{
|
|
||||||
super.setItem( aliasID );
|
|
||||||
|
|
||||||
FleetsyncID fleetsync = getFleetsyncID();
|
|
||||||
|
|
||||||
if( fleetsync != null )
|
|
||||||
{
|
|
||||||
mTextField.setText( fleetsync.getIdent() );
|
|
||||||
}
|
|
||||||
|
|
||||||
setModified( false );
|
|
||||||
|
|
||||||
repaint();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void save()
|
|
||||||
{
|
|
||||||
FleetsyncID fleetsync = getFleetsyncID();
|
|
||||||
|
|
||||||
if( fleetsync != null )
|
|
||||||
{
|
|
||||||
fleetsync.setIdent( mTextField.getText() );
|
|
||||||
}
|
|
||||||
|
|
||||||
setModified( false );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -32,6 +32,12 @@ public class MDC1200ID extends AliasID
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isAudioIdentifier()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
@JacksonXmlProperty(isAttribute = true, localName = "ident")
|
@JacksonXmlProperty(isAttribute = true, localName = "ident")
|
||||||
public String getIdent()
|
public String getIdent()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,133 +0,0 @@
|
|||||||
/*
|
|
||||||
* ******************************************************************************
|
|
||||||
* sdrtrunk
|
|
||||||
* Copyright (C) 2014-2018 Dennis Sheirer
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
|
||||||
* *****************************************************************************
|
|
||||||
*/
|
|
||||||
package io.github.dsheirer.alias.id.legacy.mdc;
|
|
||||||
|
|
||||||
import io.github.dsheirer.alias.id.AliasID;
|
|
||||||
import io.github.dsheirer.gui.editor.DocumentListenerEditor;
|
|
||||||
import net.miginfocom.swing.MigLayout;
|
|
||||||
|
|
||||||
import javax.swing.JFormattedTextField;
|
|
||||||
import javax.swing.JLabel;
|
|
||||||
import javax.swing.JOptionPane;
|
|
||||||
import javax.swing.JTextField;
|
|
||||||
import javax.swing.text.MaskFormatter;
|
|
||||||
import java.awt.Color;
|
|
||||||
import java.awt.Cursor;
|
|
||||||
import java.awt.event.MouseAdapter;
|
|
||||||
import java.awt.event.MouseEvent;
|
|
||||||
|
|
||||||
public class MDC1200IDEditor extends DocumentListenerEditor<AliasID>
|
|
||||||
{
|
|
||||||
private static final long serialVersionUID = 1L;
|
|
||||||
|
|
||||||
private static final String HELP_TEXT = "<html>"
|
|
||||||
+ "<h3>MDC-1200 Identifier</h3>"
|
|
||||||
+ "<b>MDC-1200:</b> four digit [0-9] value (e.g. <u>1234</u>)<br>"
|
|
||||||
+ "<b>Wildcard:</b> use an asterisk (*) to wildcard individual<br>"
|
|
||||||
+ "digits (e.g. <u>123*</u> or <u>**34</u>)"
|
|
||||||
+ "</html>";
|
|
||||||
|
|
||||||
private JTextField mTextField;
|
|
||||||
|
|
||||||
public MDC1200IDEditor(AliasID aliasID)
|
|
||||||
{
|
|
||||||
initGUI();
|
|
||||||
|
|
||||||
setItem(aliasID);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void initGUI()
|
|
||||||
{
|
|
||||||
setLayout(new MigLayout("fill,wrap 2", "[right][left]", "[][]"));
|
|
||||||
|
|
||||||
add(new JLabel("MDC-1200 ID:"));
|
|
||||||
|
|
||||||
MaskFormatter formatter = null;
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
//Mask: 4 digits
|
|
||||||
formatter = new MaskFormatter("####");
|
|
||||||
}
|
|
||||||
catch(Exception e)
|
|
||||||
{
|
|
||||||
//Do nothing, the mask was invalid
|
|
||||||
}
|
|
||||||
|
|
||||||
mTextField = new JFormattedTextField(formatter);
|
|
||||||
mTextField.getDocument().addDocumentListener(this);
|
|
||||||
mTextField.setToolTipText(HELP_TEXT);
|
|
||||||
add(mTextField, "growx,push");
|
|
||||||
|
|
||||||
JLabel help = new JLabel("Help ...");
|
|
||||||
help.setForeground(Color.BLUE.brighter());
|
|
||||||
help.setCursor(new Cursor(Cursor.HAND_CURSOR));
|
|
||||||
help.addMouseListener(new MouseAdapter()
|
|
||||||
{
|
|
||||||
@Override
|
|
||||||
public void mouseClicked(MouseEvent e)
|
|
||||||
{
|
|
||||||
JOptionPane.showMessageDialog(MDC1200IDEditor.this,
|
|
||||||
HELP_TEXT, "Help", JOptionPane.INFORMATION_MESSAGE);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
add(help, "align left");
|
|
||||||
}
|
|
||||||
|
|
||||||
public MDC1200ID getMDC1200ID()
|
|
||||||
{
|
|
||||||
if(getItem() instanceof MDC1200ID)
|
|
||||||
{
|
|
||||||
return (MDC1200ID)getItem();
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setItem(AliasID aliasID)
|
|
||||||
{
|
|
||||||
super.setItem(aliasID);
|
|
||||||
|
|
||||||
MDC1200ID mdc = getMDC1200ID();
|
|
||||||
|
|
||||||
if(mdc != null)
|
|
||||||
{
|
|
||||||
mTextField.setText(mdc.getIdent());
|
|
||||||
}
|
|
||||||
|
|
||||||
setModified(false);
|
|
||||||
|
|
||||||
repaint();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void save()
|
|
||||||
{
|
|
||||||
MDC1200ID mdc = getMDC1200ID();
|
|
||||||
|
|
||||||
if(mdc != null)
|
|
||||||
{
|
|
||||||
mdc.setIdent(mTextField.getText());
|
|
||||||
}
|
|
||||||
|
|
||||||
setModified(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,127 +0,0 @@
|
|||||||
/*******************************************************************************
|
|
||||||
* SDR Trunk
|
|
||||||
* Copyright (C) 2014-2016 Dennis Sheirer
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
|
||||||
******************************************************************************/
|
|
||||||
package io.github.dsheirer.alias.id.legacy.mobileID;
|
|
||||||
|
|
||||||
import io.github.dsheirer.alias.id.AliasID;
|
|
||||||
import io.github.dsheirer.gui.editor.DocumentListenerEditor;
|
|
||||||
import net.miginfocom.swing.MigLayout;
|
|
||||||
|
|
||||||
import javax.swing.*;
|
|
||||||
import javax.swing.text.MaskFormatter;
|
|
||||||
import java.awt.*;
|
|
||||||
import java.awt.event.MouseAdapter;
|
|
||||||
import java.awt.event.MouseEvent;
|
|
||||||
|
|
||||||
public class MINEditor extends DocumentListenerEditor<AliasID>
|
|
||||||
{
|
|
||||||
private static final long serialVersionUID = 1L;
|
|
||||||
|
|
||||||
private static final String HELP_TEXT = "<html>"
|
|
||||||
+ "<h3>Passport Mobile ID (MIN)</h3>"
|
|
||||||
+ "<b>MIN:</b> six character hex value (e.g. <u>AB12CD</u>)<br>"
|
|
||||||
+ "<b>Wildcard:</b> use an asterisk (*) to wildcard individual<br>"
|
|
||||||
+ "digits (e.g. <u>AB**CD</u> or <u>AB12**</u>)"
|
|
||||||
+ "</html>";
|
|
||||||
|
|
||||||
private JTextField mTextField;
|
|
||||||
|
|
||||||
public MINEditor( AliasID aliasID )
|
|
||||||
{
|
|
||||||
initGUI();
|
|
||||||
|
|
||||||
setItem( aliasID );
|
|
||||||
}
|
|
||||||
|
|
||||||
private void initGUI()
|
|
||||||
{
|
|
||||||
setLayout( new MigLayout( "fill,wrap 2", "[right][left]", "[][]" ) );
|
|
||||||
|
|
||||||
add( new JLabel( "MIN:" ) );
|
|
||||||
|
|
||||||
MaskFormatter formatter = null;
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
//Mask: 6 hex characters
|
|
||||||
formatter = new MaskFormatter( "HHHHHH" );
|
|
||||||
}
|
|
||||||
catch( Exception e )
|
|
||||||
{
|
|
||||||
//Do nothing, the mask was invalid
|
|
||||||
}
|
|
||||||
|
|
||||||
mTextField = new JFormattedTextField( formatter );
|
|
||||||
mTextField.getDocument().addDocumentListener( this );
|
|
||||||
mTextField.setToolTipText( HELP_TEXT );
|
|
||||||
add( mTextField, "growx,push" );
|
|
||||||
|
|
||||||
JLabel help = new JLabel( "Help ..." );
|
|
||||||
help.setForeground( Color.BLUE.brighter() );
|
|
||||||
help.setCursor( new Cursor( Cursor.HAND_CURSOR ) );
|
|
||||||
help.addMouseListener( new MouseAdapter()
|
|
||||||
{
|
|
||||||
@Override
|
|
||||||
public void mouseClicked( MouseEvent e )
|
|
||||||
{
|
|
||||||
JOptionPane.showMessageDialog( MINEditor.this,
|
|
||||||
HELP_TEXT, "Help", JOptionPane.INFORMATION_MESSAGE );
|
|
||||||
}
|
|
||||||
} );
|
|
||||||
add( help, "align left" );
|
|
||||||
}
|
|
||||||
|
|
||||||
public Min getMin()
|
|
||||||
{
|
|
||||||
if( getItem() instanceof Min )
|
|
||||||
{
|
|
||||||
return (Min)getItem();
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setItem( AliasID aliasID )
|
|
||||||
{
|
|
||||||
super.setItem( aliasID );
|
|
||||||
|
|
||||||
Min min = getMin();
|
|
||||||
|
|
||||||
if( min != null )
|
|
||||||
{
|
|
||||||
mTextField.setText( min.getMin() );
|
|
||||||
}
|
|
||||||
|
|
||||||
setModified( false );
|
|
||||||
|
|
||||||
repaint();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void save()
|
|
||||||
{
|
|
||||||
Min min = getMin();
|
|
||||||
|
|
||||||
if( min != null )
|
|
||||||
{
|
|
||||||
min.setMin( mTextField.getText() );
|
|
||||||
}
|
|
||||||
|
|
||||||
setModified( false );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -33,6 +33,12 @@ public class Min extends AliasID
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isAudioIdentifier()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
@JacksonXmlProperty(isAttribute = true, localName = "min")
|
@JacksonXmlProperty(isAttribute = true, localName = "min")
|
||||||
public String getMin()
|
public String getMin()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -32,6 +32,12 @@ public class MPT1327ID extends AliasID
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isAudioIdentifier()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
@JacksonXmlProperty(isAttribute = true, localName = "ident")
|
@JacksonXmlProperty(isAttribute = true, localName = "ident")
|
||||||
public String getIdent()
|
public String getIdent()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,135 +0,0 @@
|
|||||||
/*
|
|
||||||
* ******************************************************************************
|
|
||||||
* sdrtrunk
|
|
||||||
* Copyright (C) 2014-2018 Dennis Sheirer
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
|
||||||
* *****************************************************************************
|
|
||||||
*/
|
|
||||||
package io.github.dsheirer.alias.id.legacy.mpt1327;
|
|
||||||
|
|
||||||
import io.github.dsheirer.alias.id.AliasID;
|
|
||||||
import io.github.dsheirer.gui.editor.DocumentListenerEditor;
|
|
||||||
import net.miginfocom.swing.MigLayout;
|
|
||||||
|
|
||||||
import javax.swing.JFormattedTextField;
|
|
||||||
import javax.swing.JLabel;
|
|
||||||
import javax.swing.JOptionPane;
|
|
||||||
import javax.swing.JTextField;
|
|
||||||
import javax.swing.text.MaskFormatter;
|
|
||||||
import java.awt.Color;
|
|
||||||
import java.awt.Cursor;
|
|
||||||
import java.awt.event.MouseAdapter;
|
|
||||||
import java.awt.event.MouseEvent;
|
|
||||||
|
|
||||||
public class MPT1327IDEditor extends DocumentListenerEditor<AliasID>
|
|
||||||
{
|
|
||||||
private static final long serialVersionUID = 1L;
|
|
||||||
|
|
||||||
private static final String HELP_TEXT = "<html>"
|
|
||||||
+ "<h3>MPT-1327 Identifier</h3>"
|
|
||||||
+ "<b>MPT-1327:</b> decimal (0-9) format ppp-iiii where<br>"
|
|
||||||
+ "p=Prefix and i=Ident (e.g. <u>123-0001</u>)<br>"
|
|
||||||
+ "<b>Wildcard:</b> use an asterisk (*) to wildcard individual<br>"
|
|
||||||
+ "digits (e.g. <u>ABCD123*</u> or <u>AB**1**4</u>)"
|
|
||||||
+ "</html>";
|
|
||||||
|
|
||||||
private JTextField mTextField;
|
|
||||||
|
|
||||||
public MPT1327IDEditor( AliasID aliasID )
|
|
||||||
{
|
|
||||||
initGUI();
|
|
||||||
|
|
||||||
setItem( aliasID );
|
|
||||||
}
|
|
||||||
|
|
||||||
private void initGUI()
|
|
||||||
{
|
|
||||||
setLayout( new MigLayout( "fill,wrap 2", "[right][left]", "[][]" ) );
|
|
||||||
|
|
||||||
add( new JLabel( "MPT-1327 ID:" ) );
|
|
||||||
|
|
||||||
MaskFormatter formatter = null;
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
//Mask: 3 digits - 4 digits
|
|
||||||
formatter = new MaskFormatter( "***-****" );
|
|
||||||
}
|
|
||||||
catch( Exception e )
|
|
||||||
{
|
|
||||||
//Do nothing, the mask was invalid
|
|
||||||
}
|
|
||||||
|
|
||||||
mTextField = new JFormattedTextField( formatter );
|
|
||||||
mTextField.getDocument().addDocumentListener( this );
|
|
||||||
mTextField.setToolTipText( HELP_TEXT );
|
|
||||||
|
|
||||||
add( mTextField, "growx,push" );
|
|
||||||
|
|
||||||
JLabel help = new JLabel( "Help ..." );
|
|
||||||
help.setForeground( Color.BLUE.brighter() );
|
|
||||||
help.setCursor( new Cursor( Cursor.HAND_CURSOR ) );
|
|
||||||
help.addMouseListener( new MouseAdapter()
|
|
||||||
{
|
|
||||||
@Override
|
|
||||||
public void mouseClicked( MouseEvent e )
|
|
||||||
{
|
|
||||||
JOptionPane.showMessageDialog( MPT1327IDEditor.this,
|
|
||||||
HELP_TEXT, "Help", JOptionPane.INFORMATION_MESSAGE );
|
|
||||||
}
|
|
||||||
} );
|
|
||||||
add( help, "align left" );
|
|
||||||
}
|
|
||||||
|
|
||||||
public MPT1327ID getMPT1327ID()
|
|
||||||
{
|
|
||||||
if( getItem() instanceof MPT1327ID )
|
|
||||||
{
|
|
||||||
return (MPT1327ID)getItem();
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setItem( AliasID aliasID )
|
|
||||||
{
|
|
||||||
super.setItem( aliasID );
|
|
||||||
|
|
||||||
MPT1327ID mpt = getMPT1327ID();
|
|
||||||
|
|
||||||
if( mpt != null )
|
|
||||||
{
|
|
||||||
mTextField.setText( mpt.getIdent() );
|
|
||||||
}
|
|
||||||
|
|
||||||
setModified( false );
|
|
||||||
|
|
||||||
repaint();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void save()
|
|
||||||
{
|
|
||||||
MPT1327ID mpt = getMPT1327ID();
|
|
||||||
|
|
||||||
if( mpt != null )
|
|
||||||
{
|
|
||||||
mpt.setIdent( mTextField.getText() );
|
|
||||||
}
|
|
||||||
|
|
||||||
setModified( false );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -33,6 +33,12 @@ public class NonRecordable extends AliasID
|
|||||||
return AliasIDType.NON_RECORDABLE;
|
return AliasIDType.NON_RECORDABLE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isAudioIdentifier()
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isValid()
|
public boolean isValid()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,78 +0,0 @@
|
|||||||
/*
|
|
||||||
* ******************************************************************************
|
|
||||||
* sdrtrunk
|
|
||||||
* Copyright (C) 2014-2018 Dennis Sheirer
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
|
||||||
* *****************************************************************************
|
|
||||||
*/
|
|
||||||
package io.github.dsheirer.alias.id.legacy.nonrecordable;
|
|
||||||
|
|
||||||
import io.github.dsheirer.alias.id.AliasID;
|
|
||||||
import io.github.dsheirer.gui.editor.DocumentListenerEditor;
|
|
||||||
import net.miginfocom.swing.MigLayout;
|
|
||||||
|
|
||||||
import javax.swing.JLabel;
|
|
||||||
import javax.swing.JOptionPane;
|
|
||||||
import java.awt.Color;
|
|
||||||
import java.awt.Cursor;
|
|
||||||
import java.awt.event.MouseAdapter;
|
|
||||||
import java.awt.event.MouseEvent;
|
|
||||||
|
|
||||||
public class NonRecordableEditor extends DocumentListenerEditor<AliasID>
|
|
||||||
{
|
|
||||||
private static final long serialVersionUID = 1L;
|
|
||||||
|
|
||||||
private static final String HELP_TEXT = "<html>"
|
|
||||||
+ "<h3>Audio Non-Recordable</h3>"
|
|
||||||
+ "This identifies an alias as non-recordable.<br><br>"
|
|
||||||
+ "The alias can still be monitored but associated audio will<br>"
|
|
||||||
+ "not be recorded.<br><br>"
|
|
||||||
+ "The default behavior when audio recording is enabled for a<br>"
|
|
||||||
+ "channel is to record all aliases except any alias with a<br>"
|
|
||||||
+ "non-recordable identifier";
|
|
||||||
|
|
||||||
public NonRecordableEditor(AliasID aliasID)
|
|
||||||
{
|
|
||||||
initGUI();
|
|
||||||
|
|
||||||
setItem(aliasID);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void initGUI()
|
|
||||||
{
|
|
||||||
setLayout(new MigLayout("fill,wrap 1", "[]", "[][]"));
|
|
||||||
|
|
||||||
add(new JLabel("Non-Recordable"), "wrap");
|
|
||||||
|
|
||||||
JLabel help = new JLabel("Help ...");
|
|
||||||
help.setForeground(Color.BLUE.brighter());
|
|
||||||
help.setCursor(new Cursor(Cursor.HAND_CURSOR));
|
|
||||||
help.addMouseListener(new MouseAdapter()
|
|
||||||
{
|
|
||||||
@Override
|
|
||||||
public void mouseClicked(MouseEvent e)
|
|
||||||
{
|
|
||||||
JOptionPane.showMessageDialog(NonRecordableEditor.this,
|
|
||||||
HELP_TEXT, "Help", JOptionPane.INFORMATION_MESSAGE);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
add(help, "align left");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void save()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -29,6 +29,12 @@ public class SiteID extends AliasID
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isAudioIdentifier()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
@JacksonXmlProperty(isAttribute = true, localName = "site")
|
@JacksonXmlProperty(isAttribute = true, localName = "site")
|
||||||
public String getSite()
|
public String getSite()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,116 +0,0 @@
|
|||||||
/*******************************************************************************
|
|
||||||
* SDR Trunk
|
|
||||||
* Copyright (C) 2014-2016 Dennis Sheirer
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
|
||||||
******************************************************************************/
|
|
||||||
package io.github.dsheirer.alias.id.legacy.siteID;
|
|
||||||
|
|
||||||
import io.github.dsheirer.alias.id.AliasID;
|
|
||||||
import io.github.dsheirer.gui.editor.DocumentListenerEditor;
|
|
||||||
import net.miginfocom.swing.MigLayout;
|
|
||||||
|
|
||||||
import javax.swing.*;
|
|
||||||
import java.awt.*;
|
|
||||||
import java.awt.event.MouseAdapter;
|
|
||||||
import java.awt.event.MouseEvent;
|
|
||||||
|
|
||||||
public class SiteIDEditor extends DocumentListenerEditor<AliasID>
|
|
||||||
{
|
|
||||||
private static final long serialVersionUID = 1L;
|
|
||||||
|
|
||||||
private static final String HELP_TEXT = "<html>"
|
|
||||||
+ "<h3>Site Identifier</h3>"
|
|
||||||
+ "<b>LTR-Net:</b> decimal (0-9) (e.g. <u>019</u>)<br>"
|
|
||||||
+ "<b>MPT-1327:</b> 5 digits (0-9) (e.g. <u>23619</u>)<br>"
|
|
||||||
+ "<b>Passport:</b> 3 digits (0-9) (e.g. <u>019</u>)<br>"
|
|
||||||
+ "<b>P25:</b> hex (0-9, A-F) format RR-SS where RR = RF Subsystem<br>"
|
|
||||||
+ "and SS = Site Number (e.g. RFSS 1 Site 1F: <u>01-1F</u>)"
|
|
||||||
+ "</html>";
|
|
||||||
|
|
||||||
private JTextField mTextField;
|
|
||||||
|
|
||||||
public SiteIDEditor( AliasID aliasID )
|
|
||||||
{
|
|
||||||
initGUI();
|
|
||||||
|
|
||||||
setItem( aliasID );
|
|
||||||
}
|
|
||||||
|
|
||||||
private void initGUI()
|
|
||||||
{
|
|
||||||
setLayout( new MigLayout( "fill,wrap 2", "[right][left]", "[][]" ) );
|
|
||||||
|
|
||||||
add( new JLabel( "Site ID:" ) );
|
|
||||||
mTextField = new JTextField();
|
|
||||||
mTextField.getDocument().addDocumentListener( this );
|
|
||||||
mTextField.setToolTipText( HELP_TEXT );
|
|
||||||
add( mTextField, "growx,push" );
|
|
||||||
|
|
||||||
JLabel help = new JLabel( "Help ..." );
|
|
||||||
help.setForeground( Color.BLUE.brighter() );
|
|
||||||
help.setCursor( new Cursor( Cursor.HAND_CURSOR ) );
|
|
||||||
help.addMouseListener( new MouseAdapter()
|
|
||||||
{
|
|
||||||
@Override
|
|
||||||
public void mouseClicked( MouseEvent e )
|
|
||||||
{
|
|
||||||
JOptionPane.showMessageDialog( SiteIDEditor.this,
|
|
||||||
HELP_TEXT, "Help", JOptionPane.INFORMATION_MESSAGE );
|
|
||||||
}
|
|
||||||
} );
|
|
||||||
|
|
||||||
add( help, "align left" );
|
|
||||||
}
|
|
||||||
|
|
||||||
public SiteID getSiteID()
|
|
||||||
{
|
|
||||||
if( getItem() instanceof SiteID )
|
|
||||||
{
|
|
||||||
return (SiteID)getItem();
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setItem( AliasID aliasID )
|
|
||||||
{
|
|
||||||
super.setItem( aliasID );
|
|
||||||
|
|
||||||
SiteID siteID = getSiteID();
|
|
||||||
|
|
||||||
if( siteID != null )
|
|
||||||
{
|
|
||||||
mTextField.setText( siteID.getSite() );
|
|
||||||
}
|
|
||||||
|
|
||||||
setModified( false );
|
|
||||||
|
|
||||||
repaint();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void save()
|
|
||||||
{
|
|
||||||
SiteID siteID = getSiteID();
|
|
||||||
|
|
||||||
if( siteID != null )
|
|
||||||
{
|
|
||||||
siteID.setSite( mTextField.getText() );
|
|
||||||
}
|
|
||||||
|
|
||||||
setModified( false );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -38,6 +38,12 @@ public class LegacyTalkgroupID extends AliasID
|
|||||||
mTalkgroup = talkgroup;
|
mTalkgroup = talkgroup;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isAudioIdentifier()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
@JacksonXmlProperty(isAttribute = true, localName = "talkgroup")
|
@JacksonXmlProperty(isAttribute = true, localName = "talkgroup")
|
||||||
public String getTalkgroup()
|
public String getTalkgroup()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,121 +0,0 @@
|
|||||||
/*
|
|
||||||
* ******************************************************************************
|
|
||||||
* sdrtrunk
|
|
||||||
* Copyright (C) 2014-2018 Dennis Sheirer
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
|
||||||
* *****************************************************************************
|
|
||||||
*/
|
|
||||||
package io.github.dsheirer.alias.id.legacy.talkgroup;
|
|
||||||
|
|
||||||
import io.github.dsheirer.alias.id.AliasID;
|
|
||||||
import io.github.dsheirer.gui.editor.DocumentListenerEditor;
|
|
||||||
import net.miginfocom.swing.MigLayout;
|
|
||||||
|
|
||||||
import javax.swing.JLabel;
|
|
||||||
import javax.swing.JOptionPane;
|
|
||||||
import javax.swing.JTextField;
|
|
||||||
import java.awt.Color;
|
|
||||||
import java.awt.Cursor;
|
|
||||||
import java.awt.event.MouseAdapter;
|
|
||||||
import java.awt.event.MouseEvent;
|
|
||||||
|
|
||||||
public class LegacyTalkgroupIDEditor extends DocumentListenerEditor<AliasID>
|
|
||||||
{
|
|
||||||
private static final long serialVersionUID = 1L;
|
|
||||||
|
|
||||||
private static final String HELP_TEXT =
|
|
||||||
"<html><h3>Talkgroup Identifier</h3>"
|
|
||||||
+ "<b>P25:</b> 4 or 6 hex characters (e.g. <u>AB12</u> or <u>ABC123</u>)<br>"
|
|
||||||
+ "<b>LTR:</b> A-HH-TTT where A=Area H=Home T=Talkgroup (<u>0-01-128</u>)<br>"
|
|
||||||
+ "<b>Passport</b>: 5-digit number (<u>12345</u> or <u>00023</u>)<br>"
|
|
||||||
+ "<br>"
|
|
||||||
+ "<b>Wildcard:</b> use an asterisk (*) in place of each talkgroup digit (e.g. <u>0*1*5</u>)"
|
|
||||||
+ "</html>";
|
|
||||||
|
|
||||||
private JTextField mTextField;
|
|
||||||
|
|
||||||
public LegacyTalkgroupIDEditor(AliasID aliasID)
|
|
||||||
{
|
|
||||||
initGUI();
|
|
||||||
|
|
||||||
setItem(aliasID);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void initGUI()
|
|
||||||
{
|
|
||||||
setLayout(new MigLayout("fill,wrap 2", "[right][left]", "[][]"));
|
|
||||||
|
|
||||||
add(new JLabel("Talkgroup:"));
|
|
||||||
|
|
||||||
mTextField = new JTextField();
|
|
||||||
mTextField.getDocument().addDocumentListener(this);
|
|
||||||
mTextField.setToolTipText(HELP_TEXT);
|
|
||||||
add(mTextField, "growx,push");
|
|
||||||
|
|
||||||
JLabel help = new JLabel("Help ...");
|
|
||||||
help.setForeground(Color.BLUE.brighter());
|
|
||||||
help.setCursor(new Cursor(Cursor.HAND_CURSOR));
|
|
||||||
help.addMouseListener(new MouseAdapter()
|
|
||||||
{
|
|
||||||
@Override
|
|
||||||
public void mouseClicked(MouseEvent e)
|
|
||||||
{
|
|
||||||
JOptionPane.showMessageDialog(LegacyTalkgroupIDEditor.this,
|
|
||||||
HELP_TEXT, "Help", JOptionPane.INFORMATION_MESSAGE);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
add(help, "align left");
|
|
||||||
}
|
|
||||||
|
|
||||||
public LegacyTalkgroupID getTalkgroupID()
|
|
||||||
{
|
|
||||||
if(getItem() instanceof LegacyTalkgroupID)
|
|
||||||
{
|
|
||||||
return (LegacyTalkgroupID)getItem();
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setItem(AliasID aliasID)
|
|
||||||
{
|
|
||||||
super.setItem(aliasID);
|
|
||||||
|
|
||||||
LegacyTalkgroupID talkgroup = getTalkgroupID();
|
|
||||||
|
|
||||||
if(talkgroup != null)
|
|
||||||
{
|
|
||||||
mTextField.setText(talkgroup.getTalkgroup());
|
|
||||||
}
|
|
||||||
|
|
||||||
setModified(false);
|
|
||||||
|
|
||||||
repaint();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void save()
|
|
||||||
{
|
|
||||||
LegacyTalkgroupID talkgroup = getTalkgroupID();
|
|
||||||
|
|
||||||
if(talkgroup != null)
|
|
||||||
{
|
|
||||||
talkgroup.setTalkgroup(mTextField.getText());
|
|
||||||
}
|
|
||||||
|
|
||||||
setModified(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -29,6 +29,12 @@ public class UniqueID extends AliasID
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isAudioIdentifier()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
@JacksonXmlProperty(isAttribute = true, localName = "uid")
|
@JacksonXmlProperty(isAttribute = true, localName = "uid")
|
||||||
public int getUid()
|
public int getUid()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,123 +0,0 @@
|
|||||||
/*******************************************************************************
|
|
||||||
* SDR Trunk
|
|
||||||
* Copyright (C) 2014-2016 Dennis Sheirer
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
|
||||||
******************************************************************************/
|
|
||||||
package io.github.dsheirer.alias.id.legacy.uniqueID;
|
|
||||||
|
|
||||||
import io.github.dsheirer.alias.id.AliasID;
|
|
||||||
import io.github.dsheirer.gui.editor.DocumentListenerEditor;
|
|
||||||
import net.miginfocom.swing.MigLayout;
|
|
||||||
|
|
||||||
import javax.swing.*;
|
|
||||||
import java.awt.*;
|
|
||||||
import java.awt.event.MouseAdapter;
|
|
||||||
import java.awt.event.MouseEvent;
|
|
||||||
|
|
||||||
public class UniqueIDEditor extends DocumentListenerEditor<AliasID>
|
|
||||||
{
|
|
||||||
private static final long serialVersionUID = 1L;
|
|
||||||
|
|
||||||
private static final String HELP_TEXT = "<html>"
|
|
||||||
+ "<h3>LTR-Net Unique ID (UID)</h3>"
|
|
||||||
+ "<b>UID:</b> identifier assigned to each radio in<br>"
|
|
||||||
+ "the range <u>1 - 2097152</u>"
|
|
||||||
+ "</html>";
|
|
||||||
|
|
||||||
private JTextField mTextField;
|
|
||||||
|
|
||||||
public UniqueIDEditor( AliasID aliasID )
|
|
||||||
{
|
|
||||||
initGUI();
|
|
||||||
|
|
||||||
setItem( aliasID );
|
|
||||||
}
|
|
||||||
|
|
||||||
private void initGUI()
|
|
||||||
{
|
|
||||||
setLayout( new MigLayout( "fill,wrap 2", "[right][left]", "[][]" ) );
|
|
||||||
|
|
||||||
add( new JLabel( "Unique ID:" ) );
|
|
||||||
mTextField = new JTextField();
|
|
||||||
mTextField.getDocument().addDocumentListener( this );
|
|
||||||
mTextField.setToolTipText( HELP_TEXT );
|
|
||||||
add( mTextField, "growx,push" );
|
|
||||||
|
|
||||||
JLabel help = new JLabel( "Help ..." );
|
|
||||||
help.setForeground( Color.BLUE.brighter() );
|
|
||||||
help.setCursor( new Cursor( Cursor.HAND_CURSOR ) );
|
|
||||||
help.addMouseListener( new MouseAdapter()
|
|
||||||
{
|
|
||||||
@Override
|
|
||||||
public void mouseClicked( MouseEvent e )
|
|
||||||
{
|
|
||||||
JOptionPane.showMessageDialog( UniqueIDEditor.this,
|
|
||||||
HELP_TEXT, "Help", JOptionPane.INFORMATION_MESSAGE );
|
|
||||||
}
|
|
||||||
} );
|
|
||||||
add( help, "align left" );
|
|
||||||
}
|
|
||||||
|
|
||||||
public UniqueID getUniqueID()
|
|
||||||
{
|
|
||||||
if( getItem() instanceof UniqueID )
|
|
||||||
{
|
|
||||||
return (UniqueID)getItem();
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setItem( AliasID aliasID )
|
|
||||||
{
|
|
||||||
super.setItem( aliasID );
|
|
||||||
|
|
||||||
UniqueID uid = getUniqueID();
|
|
||||||
|
|
||||||
if( uid != null )
|
|
||||||
{
|
|
||||||
mTextField.setText( String.valueOf( uid.getUid() ) );
|
|
||||||
}
|
|
||||||
|
|
||||||
setModified( false );
|
|
||||||
|
|
||||||
repaint();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void save()
|
|
||||||
{
|
|
||||||
UniqueID uid = getUniqueID();
|
|
||||||
|
|
||||||
if( uid != null )
|
|
||||||
{
|
|
||||||
int id = 0;
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
id = Integer.parseInt( mTextField.getText() );
|
|
||||||
}
|
|
||||||
catch( Exception e )
|
|
||||||
{
|
|
||||||
//Do nothing, we couldn't parse the value
|
|
||||||
}
|
|
||||||
|
|
||||||
uid.setUid( id );
|
|
||||||
}
|
|
||||||
|
|
||||||
setModified( false );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -32,6 +32,12 @@ public class LoJackFunctionAndID extends AliasID
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isAudioIdentifier()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
@JacksonXmlProperty(isAttribute = true, localName = "id")
|
@JacksonXmlProperty(isAttribute = true, localName = "id")
|
||||||
public String getID()
|
public String getID()
|
||||||
{
|
{
|
||||||
@@ -41,6 +47,7 @@ public class LoJackFunctionAndID extends AliasID
|
|||||||
public void setID(String id)
|
public void setID(String id)
|
||||||
{
|
{
|
||||||
mID = id;
|
mID = id;
|
||||||
|
updateValueProperty();
|
||||||
}
|
}
|
||||||
|
|
||||||
@JacksonXmlProperty(isAttribute = true, localName = "function")
|
@JacksonXmlProperty(isAttribute = true, localName = "function")
|
||||||
@@ -52,6 +59,7 @@ public class LoJackFunctionAndID extends AliasID
|
|||||||
public void setFunction(LJ1200Message.Function function)
|
public void setFunction(LJ1200Message.Function function)
|
||||||
{
|
{
|
||||||
mFunction = function;
|
mFunction = function;
|
||||||
|
updateValueProperty();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -1,160 +0,0 @@
|
|||||||
/*******************************************************************************
|
|
||||||
* SDR Trunk
|
|
||||||
* Copyright (C) 2014-2016 Dennis Sheirer
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
|
||||||
******************************************************************************/
|
|
||||||
package io.github.dsheirer.alias.id.lojack;
|
|
||||||
|
|
||||||
import io.github.dsheirer.alias.id.AliasID;
|
|
||||||
import io.github.dsheirer.gui.editor.DocumentListenerEditor;
|
|
||||||
import io.github.dsheirer.module.decode.lj1200.LJ1200Message;
|
|
||||||
import io.github.dsheirer.module.decode.lj1200.LJ1200Message.Function;
|
|
||||||
import net.miginfocom.swing.MigLayout;
|
|
||||||
|
|
||||||
import javax.swing.*;
|
|
||||||
import javax.swing.text.MaskFormatter;
|
|
||||||
import java.awt.*;
|
|
||||||
import java.awt.event.ActionEvent;
|
|
||||||
import java.awt.event.ActionListener;
|
|
||||||
import java.awt.event.MouseAdapter;
|
|
||||||
import java.awt.event.MouseEvent;
|
|
||||||
|
|
||||||
public class LoJackIDEditor extends DocumentListenerEditor<AliasID>
|
|
||||||
{
|
|
||||||
private static final long serialVersionUID = 1L;
|
|
||||||
|
|
||||||
private JTextField mTextField;
|
|
||||||
|
|
||||||
private JComboBox<LJ1200Message.Function> mFunctionCombo;
|
|
||||||
|
|
||||||
private static final String HELP_TEXT = "<html>"
|
|
||||||
+ "<h3>LoJack Function and Identifier</h3>"
|
|
||||||
+ "<b>Function Code:</b> <u>1Y-SITE ID</u><br>"
|
|
||||||
+ "<b>ID:</b> 5 numbers or characters (e.g. <u>1BN47</u>)<br>"
|
|
||||||
+ "<br>"
|
|
||||||
+ "<b>Wildcard:</b> use an asterisk (*) to wildcard ID<br>"
|
|
||||||
+ "characters (e.g. <u>AB*CD</u> or <u>***12</u> or <u>*****</u>)<br><br>"
|
|
||||||
+ "The middle character in a reply ID code identifies the<br>"
|
|
||||||
+ "entity. Valid ID middle characters are:<br>"
|
|
||||||
+ "<li><b>Tower:</b> X,Y</li>"
|
|
||||||
+ "<li><b>Transponder:</b> 0-9,A,C-H,J-N,P-W</li>"
|
|
||||||
+ "<li><b>Not Used:</b> B,I,O,Z</li>"
|
|
||||||
+ "</html>";
|
|
||||||
|
|
||||||
public LoJackIDEditor( AliasID aliasID )
|
|
||||||
{
|
|
||||||
initGUI();
|
|
||||||
|
|
||||||
setItem( aliasID );
|
|
||||||
}
|
|
||||||
|
|
||||||
private void initGUI()
|
|
||||||
{
|
|
||||||
setLayout( new MigLayout( "fill,wrap 2", "[right][left]", "[][][]" ) );
|
|
||||||
|
|
||||||
add( new JLabel( "Function:" ) );
|
|
||||||
mFunctionCombo = new JComboBox<LJ1200Message.Function>( Function.values() );
|
|
||||||
mFunctionCombo.addActionListener( new ActionListener()
|
|
||||||
{
|
|
||||||
@Override
|
|
||||||
public void actionPerformed( ActionEvent e )
|
|
||||||
{
|
|
||||||
setModified( true );
|
|
||||||
}
|
|
||||||
} );
|
|
||||||
mFunctionCombo.setToolTipText( HELP_TEXT );
|
|
||||||
add( mFunctionCombo, "growx, push" );
|
|
||||||
|
|
||||||
add( new JLabel( "ID:" ) );
|
|
||||||
|
|
||||||
MaskFormatter formatter = null;
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
//Mask: any character or number, 5 places
|
|
||||||
formatter = new MaskFormatter( "AAAAA" );
|
|
||||||
}
|
|
||||||
catch( Exception e )
|
|
||||||
{
|
|
||||||
//Do nothing, the mask was invalid
|
|
||||||
}
|
|
||||||
|
|
||||||
mTextField = new JFormattedTextField( formatter );
|
|
||||||
mTextField.getDocument().addDocumentListener( this );
|
|
||||||
mTextField.setToolTipText( HELP_TEXT );
|
|
||||||
add( mTextField, "growx,push" );
|
|
||||||
|
|
||||||
JLabel help = new JLabel( "Help ..." );
|
|
||||||
help.setForeground( Color.BLUE.brighter() );
|
|
||||||
help.setCursor( new Cursor( Cursor.HAND_CURSOR ) );
|
|
||||||
help.addMouseListener( new MouseAdapter()
|
|
||||||
{
|
|
||||||
@Override
|
|
||||||
public void mouseClicked( MouseEvent e )
|
|
||||||
{
|
|
||||||
JOptionPane.showMessageDialog( LoJackIDEditor.this,
|
|
||||||
HELP_TEXT, "Help", JOptionPane.INFORMATION_MESSAGE );
|
|
||||||
}
|
|
||||||
} );
|
|
||||||
add( help, "align left" );
|
|
||||||
}
|
|
||||||
|
|
||||||
public LoJackFunctionAndID getLoJackID()
|
|
||||||
{
|
|
||||||
if( getItem() instanceof LoJackFunctionAndID )
|
|
||||||
{
|
|
||||||
return (LoJackFunctionAndID)getItem();
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setItem( AliasID aliasID )
|
|
||||||
{
|
|
||||||
super.setItem( aliasID );
|
|
||||||
|
|
||||||
LoJackFunctionAndID lojack = getLoJackID();
|
|
||||||
|
|
||||||
if( lojack != null )
|
|
||||||
{
|
|
||||||
mFunctionCombo.setSelectedItem( lojack.getFunction() );
|
|
||||||
mTextField.setText( lojack.getID() );
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
mFunctionCombo.setSelectedItem( null );
|
|
||||||
mTextField.setText( null );
|
|
||||||
}
|
|
||||||
|
|
||||||
setModified( false );
|
|
||||||
|
|
||||||
repaint();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void save()
|
|
||||||
{
|
|
||||||
LoJackFunctionAndID lojack = getLoJackID();
|
|
||||||
|
|
||||||
if( lojack != null )
|
|
||||||
{
|
|
||||||
lojack.setID( mTextField.getText() );
|
|
||||||
lojack.setFunction( (Function)mFunctionCombo.getSelectedItem() );
|
|
||||||
}
|
|
||||||
|
|
||||||
setModified( false );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -39,6 +39,17 @@ public class Priority extends AliasID
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Priority(int priority)
|
||||||
|
{
|
||||||
|
mPriority = priority;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isAudioIdentifier()
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isValid()
|
public boolean isValid()
|
||||||
{
|
{
|
||||||
@@ -63,6 +74,7 @@ public class Priority extends AliasID
|
|||||||
public void setPriority(int priority)
|
public void setPriority(int priority)
|
||||||
{
|
{
|
||||||
mPriority = priority;
|
mPriority = priority;
|
||||||
|
updateValueProperty();
|
||||||
}
|
}
|
||||||
|
|
||||||
public String toString()
|
public String toString()
|
||||||
|
|||||||
@@ -1,163 +0,0 @@
|
|||||||
/*******************************************************************************
|
|
||||||
* SDR Trunk
|
|
||||||
* Copyright (C) 2014-2016 Dennis Sheirer
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
|
||||||
******************************************************************************/
|
|
||||||
package io.github.dsheirer.alias.id.priority;
|
|
||||||
|
|
||||||
import io.github.dsheirer.alias.id.AliasID;
|
|
||||||
import io.github.dsheirer.gui.editor.DocumentListenerEditor;
|
|
||||||
import net.miginfocom.swing.MigLayout;
|
|
||||||
|
|
||||||
import javax.swing.*;
|
|
||||||
import javax.swing.event.ChangeEvent;
|
|
||||||
import javax.swing.event.ChangeListener;
|
|
||||||
import java.awt.*;
|
|
||||||
import java.awt.event.MouseAdapter;
|
|
||||||
import java.awt.event.MouseEvent;
|
|
||||||
|
|
||||||
public class PriorityEditor extends DocumentListenerEditor<AliasID>
|
|
||||||
{
|
|
||||||
private static final long serialVersionUID = 1L;
|
|
||||||
|
|
||||||
public static final String DO_NOT_MONITOR = "Do Not Monitor";
|
|
||||||
|
|
||||||
private static final String HELP_TEXT = "<html>"
|
|
||||||
+ "<h3>Call Audio Priority</h3>"
|
|
||||||
+ "Priority determines which calls have priority for playback<br>"
|
|
||||||
+ "over your computer speakers, or designates an alias for<br>"
|
|
||||||
+ "no-monitoring if you don't want to hear calls from an alias.<br><br>"
|
|
||||||
+ "Lower values indicate higher priority levels.<br><br>"
|
|
||||||
+ "<b>Do Not Monitor: </b> slide priority all the way to the right"
|
|
||||||
+ "</html>";
|
|
||||||
|
|
||||||
private JSlider mPrioritySlider;
|
|
||||||
private JLabel mPrioritySliderLabel;
|
|
||||||
|
|
||||||
public PriorityEditor( AliasID aliasID )
|
|
||||||
{
|
|
||||||
initGUI();
|
|
||||||
|
|
||||||
setItem( aliasID );
|
|
||||||
}
|
|
||||||
|
|
||||||
private void initGUI()
|
|
||||||
{
|
|
||||||
setLayout( new MigLayout( "fill,wrap 2", "[right][left]", "[][]" ) );
|
|
||||||
|
|
||||||
mPrioritySlider = new JSlider( JSlider.HORIZONTAL,
|
|
||||||
Priority.MIN_PRIORITY,
|
|
||||||
Priority.MAX_PRIORITY + 1,
|
|
||||||
Priority.MIN_PRIORITY );
|
|
||||||
|
|
||||||
mPrioritySlider.setMajorTickSpacing( 20 );
|
|
||||||
mPrioritySlider.setMinorTickSpacing( 5 );
|
|
||||||
mPrioritySlider.setPaintTicks( true );
|
|
||||||
mPrioritySlider.setLabelTable( mPrioritySlider.createStandardLabels( 20, 20 ) );
|
|
||||||
mPrioritySlider.setPaintLabels( true );
|
|
||||||
mPrioritySlider.addChangeListener( new ChangeListener()
|
|
||||||
{
|
|
||||||
@Override
|
|
||||||
public void stateChanged( ChangeEvent e )
|
|
||||||
{
|
|
||||||
int priority = mPrioritySlider.getValue();
|
|
||||||
|
|
||||||
if( priority == Priority.MAX_PRIORITY + 1 )
|
|
||||||
{
|
|
||||||
mPrioritySliderLabel.setText( "Priority: Do Not Monitor" );
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
mPrioritySliderLabel.setText( "Priority: " + priority );
|
|
||||||
}
|
|
||||||
|
|
||||||
setModified( true );
|
|
||||||
}
|
|
||||||
} );
|
|
||||||
mPrioritySlider.setToolTipText( HELP_TEXT );
|
|
||||||
|
|
||||||
mPrioritySliderLabel = new JLabel( "Priority: " + mPrioritySlider.getValue() + " " );
|
|
||||||
add( mPrioritySliderLabel, "span,align center" );
|
|
||||||
add( mPrioritySlider, "span,grow" );
|
|
||||||
|
|
||||||
JLabel help = new JLabel( "Help ..." );
|
|
||||||
help.setForeground( Color.BLUE.brighter() );
|
|
||||||
help.setCursor( new Cursor( Cursor.HAND_CURSOR ) );
|
|
||||||
help.addMouseListener( new MouseAdapter()
|
|
||||||
{
|
|
||||||
@Override
|
|
||||||
public void mouseClicked( MouseEvent e )
|
|
||||||
{
|
|
||||||
JOptionPane.showMessageDialog( PriorityEditor.this,
|
|
||||||
HELP_TEXT, "Help", JOptionPane.INFORMATION_MESSAGE );
|
|
||||||
}
|
|
||||||
} );
|
|
||||||
add( help, "align left" );
|
|
||||||
}
|
|
||||||
|
|
||||||
public Priority getPriority()
|
|
||||||
{
|
|
||||||
if( getItem() instanceof Priority )
|
|
||||||
{
|
|
||||||
return (Priority)getItem();
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setItem( AliasID aliasID )
|
|
||||||
{
|
|
||||||
super.setItem( aliasID );
|
|
||||||
|
|
||||||
Priority priority = getPriority();
|
|
||||||
|
|
||||||
if( priority != null )
|
|
||||||
{
|
|
||||||
int value = priority.getPriority();
|
|
||||||
|
|
||||||
if( value == Priority.DO_NOT_MONITOR )
|
|
||||||
{
|
|
||||||
value = Priority.MAX_PRIORITY + 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
mPrioritySlider.setValue( value );
|
|
||||||
}
|
|
||||||
|
|
||||||
setModified( false );
|
|
||||||
|
|
||||||
repaint();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void save()
|
|
||||||
{
|
|
||||||
Priority priority = getPriority();
|
|
||||||
|
|
||||||
if( priority != null )
|
|
||||||
{
|
|
||||||
int value = mPrioritySlider.getValue();
|
|
||||||
|
|
||||||
if( value == Priority.MAX_PRIORITY + 1 )
|
|
||||||
{
|
|
||||||
value = Priority.DO_NOT_MONITOR;
|
|
||||||
}
|
|
||||||
|
|
||||||
priority.setPriority( value );
|
|
||||||
}
|
|
||||||
|
|
||||||
setModified( false );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -25,14 +25,13 @@ package io.github.dsheirer.alias.id.radio;
|
|||||||
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
|
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
|
||||||
import io.github.dsheirer.alias.id.AliasID;
|
import io.github.dsheirer.alias.id.AliasID;
|
||||||
import io.github.dsheirer.alias.id.AliasIDType;
|
import io.github.dsheirer.alias.id.AliasIDType;
|
||||||
import io.github.dsheirer.alias.id.talkgroup.TalkgroupFormat;
|
|
||||||
import io.github.dsheirer.alias.id.talkgroup.TalkgroupFormatter;
|
import io.github.dsheirer.alias.id.talkgroup.TalkgroupFormatter;
|
||||||
import io.github.dsheirer.protocol.Protocol;
|
import io.github.dsheirer.protocol.Protocol;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Integer radio identifier with protocol.
|
* Integer radio identifier with protocol.
|
||||||
*/
|
*/
|
||||||
public class Radio extends AliasID
|
public class Radio extends AliasID implements Comparable<Radio>
|
||||||
{
|
{
|
||||||
private Protocol mProtocol = Protocol.UNKNOWN;
|
private Protocol mProtocol = Protocol.UNKNOWN;
|
||||||
private int mValue;
|
private int mValue;
|
||||||
@@ -48,6 +47,12 @@ public class Radio extends AliasID
|
|||||||
mValue = value;
|
mValue = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isAudioIdentifier()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
@JacksonXmlProperty(isAttribute = true, localName = "value")
|
@JacksonXmlProperty(isAttribute = true, localName = "value")
|
||||||
public int getValue()
|
public int getValue()
|
||||||
{
|
{
|
||||||
@@ -57,6 +62,7 @@ public class Radio extends AliasID
|
|||||||
public void setValue(int value)
|
public void setValue(int value)
|
||||||
{
|
{
|
||||||
mValue = value;
|
mValue = value;
|
||||||
|
updateValueProperty();
|
||||||
}
|
}
|
||||||
|
|
||||||
@JacksonXmlProperty(isAttribute = true, localName = "protocol")
|
@JacksonXmlProperty(isAttribute = true, localName = "protocol")
|
||||||
@@ -68,6 +74,7 @@ public class Radio extends AliasID
|
|||||||
public void setProtocol(Protocol protocol)
|
public void setProtocol(Protocol protocol)
|
||||||
{
|
{
|
||||||
mProtocol = protocol;
|
mProtocol = protocol;
|
||||||
|
updateValueProperty();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -115,4 +122,22 @@ public class Radio extends AliasID
|
|||||||
{
|
{
|
||||||
return AliasIDType.RADIO_ID;
|
return AliasIDType.RADIO_ID;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int compareTo(Radio other)
|
||||||
|
{
|
||||||
|
if(other == null)
|
||||||
|
{
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(getProtocol().equals(other.getProtocol()))
|
||||||
|
{
|
||||||
|
return Integer.compare(getValue(), other.getValue());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return getProtocol().compareTo(other.getProtocol());
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,235 +0,0 @@
|
|||||||
/*
|
|
||||||
*
|
|
||||||
* * ******************************************************************************
|
|
||||||
* * Copyright (C) 2014-2019 Dennis Sheirer
|
|
||||||
* *
|
|
||||||
* * This program is free software: you can redistribute it and/or modify
|
|
||||||
* * it under the terms of the GNU General Public License as published by
|
|
||||||
* * the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* * (at your option) any later version.
|
|
||||||
* *
|
|
||||||
* * This program is distributed in the hope that it will be useful,
|
|
||||||
* * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* * GNU General Public License for more details.
|
|
||||||
* *
|
|
||||||
* * You should have received a copy of the GNU General Public License
|
|
||||||
* * along with this program. If not, see <http://www.gnu.org/licenses/>
|
|
||||||
* * *****************************************************************************
|
|
||||||
*
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
package io.github.dsheirer.alias.id.radio;
|
|
||||||
|
|
||||||
import io.github.dsheirer.alias.id.AliasID;
|
|
||||||
import io.github.dsheirer.gui.editor.DocumentListenerEditor;
|
|
||||||
import io.github.dsheirer.protocol.Protocol;
|
|
||||||
import net.miginfocom.swing.MigLayout;
|
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
|
|
||||||
import javax.swing.DefaultComboBoxModel;
|
|
||||||
import javax.swing.JComboBox;
|
|
||||||
import javax.swing.JFormattedTextField;
|
|
||||||
import javax.swing.JLabel;
|
|
||||||
import javax.swing.JOptionPane;
|
|
||||||
import javax.swing.text.MaskFormatter;
|
|
||||||
import java.awt.Color;
|
|
||||||
import java.awt.Cursor;
|
|
||||||
import java.awt.event.ActionEvent;
|
|
||||||
import java.awt.event.ActionListener;
|
|
||||||
import java.awt.event.MouseAdapter;
|
|
||||||
import java.awt.event.MouseEvent;
|
|
||||||
import java.text.ParseException;
|
|
||||||
|
|
||||||
public class RadioEditor extends DocumentListenerEditor<AliasID>
|
|
||||||
{
|
|
||||||
private final static Logger mLog = LoggerFactory.getLogger(RadioEditor.class);
|
|
||||||
private static final long serialVersionUID = 1L;
|
|
||||||
|
|
||||||
private static final String VALID_CHARACTERS_FOR_ASTERISK_MASKS = "0123456789 ";
|
|
||||||
private MaskFormatter mMaskFormatter = new MaskFormatter();
|
|
||||||
private JComboBox<Protocol> mComboProtocol;
|
|
||||||
private JFormattedTextField mRadioIdField;
|
|
||||||
|
|
||||||
|
|
||||||
public RadioEditor(AliasID aliasID)
|
|
||||||
{
|
|
||||||
initGUI();
|
|
||||||
setItem(aliasID);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void initGUI()
|
|
||||||
{
|
|
||||||
setLayout(new MigLayout("fill,wrap 2", "[right][left]", "[][]"));
|
|
||||||
|
|
||||||
add(new JLabel("Protocol:"));
|
|
||||||
|
|
||||||
mComboProtocol = new JComboBox<>();
|
|
||||||
|
|
||||||
DefaultComboBoxModel<Protocol> model = new DefaultComboBoxModel<>();
|
|
||||||
|
|
||||||
for(Protocol protocol : Protocol.RADIO_ID_PROTOCOLS)
|
|
||||||
{
|
|
||||||
model.addElement(protocol);
|
|
||||||
}
|
|
||||||
|
|
||||||
mComboProtocol.setModel(model);
|
|
||||||
mComboProtocol.addActionListener(new ActionListener()
|
|
||||||
{
|
|
||||||
@Override
|
|
||||||
public void actionPerformed(ActionEvent e)
|
|
||||||
{
|
|
||||||
Protocol protocol = (Protocol)mComboProtocol.getSelectedItem();
|
|
||||||
updateEditor(protocol);
|
|
||||||
setModified(true);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
add(mComboProtocol);
|
|
||||||
|
|
||||||
add(new JLabel("Value:"));
|
|
||||||
|
|
||||||
mRadioIdField = new JFormattedTextField(mMaskFormatter);
|
|
||||||
mRadioIdField.getDocument().addDocumentListener(this);
|
|
||||||
add(mRadioIdField, "growx,push");
|
|
||||||
|
|
||||||
JLabel help = new JLabel("Help ...");
|
|
||||||
help.setForeground(Color.BLUE.brighter());
|
|
||||||
help.setCursor(new Cursor(Cursor.HAND_CURSOR));
|
|
||||||
help.addMouseListener(new MouseAdapter()
|
|
||||||
{
|
|
||||||
@Override
|
|
||||||
public void mouseClicked(MouseEvent e)
|
|
||||||
{
|
|
||||||
RadioFormat radioFormat = RadioFormat.get(getCurrentProtocol());
|
|
||||||
JOptionPane.showMessageDialog(RadioEditor.this,
|
|
||||||
radioFormat.getValidRangeHelpText(), "Help", JOptionPane.INFORMATION_MESSAGE);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
add(help, "align left");
|
|
||||||
}
|
|
||||||
|
|
||||||
private void updateEditor(Protocol protocol)
|
|
||||||
{
|
|
||||||
RadioFormat mask = RadioFormat.get(protocol);
|
|
||||||
int currentValue = 0;
|
|
||||||
Radio radio = getRadio();
|
|
||||||
|
|
||||||
if(radio != null)
|
|
||||||
{
|
|
||||||
currentValue = radio.getValue();
|
|
||||||
}
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
mRadioIdField.setValue(null);
|
|
||||||
mRadioIdField.setToolTipText(mask.getValidRangeHelpText());
|
|
||||||
mMaskFormatter.setMask(mask.getMask());
|
|
||||||
if(mask.getMask().contains("*"))
|
|
||||||
{
|
|
||||||
mMaskFormatter.setValidCharacters(VALID_CHARACTERS_FOR_ASTERISK_MASKS);
|
|
||||||
}
|
|
||||||
mRadioIdField.setValue(RadioFormatter.format(protocol, currentValue));
|
|
||||||
}
|
|
||||||
catch(ParseException pe)
|
|
||||||
{
|
|
||||||
mLog.error("Error applying talkgroup editor mask to mask formatter [" + mask.getMask() + "]");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private Protocol getCurrentProtocol()
|
|
||||||
{
|
|
||||||
return mComboProtocol.getItemAt(mComboProtocol.getSelectedIndex());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Current talkgroup alias id
|
|
||||||
*/
|
|
||||||
public Radio getRadio()
|
|
||||||
{
|
|
||||||
if(getItem() instanceof Radio)
|
|
||||||
{
|
|
||||||
return (Radio)getItem();
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setItem(AliasID aliasID)
|
|
||||||
{
|
|
||||||
super.setItem(aliasID);
|
|
||||||
|
|
||||||
Radio radio = getRadio();
|
|
||||||
|
|
||||||
if(radio != null)
|
|
||||||
{
|
|
||||||
Protocol currentProtocol = getCurrentProtocol();
|
|
||||||
if(currentProtocol == radio.getProtocol())
|
|
||||||
{
|
|
||||||
updateEditor(radio.getProtocol());
|
|
||||||
}
|
|
||||||
mComboProtocol.getModel().setSelectedItem(radio.getProtocol());
|
|
||||||
|
|
||||||
String formatted = RadioFormatter.format(radio.getProtocol(), radio.getValue());
|
|
||||||
mRadioIdField.setValue(formatted);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
mComboProtocol.getModel().setSelectedItem(Protocol.UNKNOWN);
|
|
||||||
mRadioIdField.setValue(RadioFormatter.format(getCurrentProtocol(), 0));
|
|
||||||
}
|
|
||||||
|
|
||||||
setModified(false);
|
|
||||||
|
|
||||||
repaint();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void save()
|
|
||||||
{
|
|
||||||
Radio radio = getRadio();
|
|
||||||
|
|
||||||
if(radio != null)
|
|
||||||
{
|
|
||||||
Protocol protocol = mComboProtocol.getItemAt(mComboProtocol.getSelectedIndex());
|
|
||||||
|
|
||||||
if(protocol == null)
|
|
||||||
{
|
|
||||||
protocol = Protocol.UNKNOWN;
|
|
||||||
}
|
|
||||||
|
|
||||||
int value = -1;
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
value = RadioFormatter.parse(protocol, mRadioIdField.getText());
|
|
||||||
}
|
|
||||||
catch(ParseException pe)
|
|
||||||
{
|
|
||||||
//ignore ... value is still -1 and outside valid value range
|
|
||||||
}
|
|
||||||
|
|
||||||
RadioFormat mask = RadioFormat.get(protocol);
|
|
||||||
|
|
||||||
//Check for valid value within range ... notify user but allow value to persist
|
|
||||||
if(value < mask.getMinimumValidValue() || value > mask.getMaximumValidValue())
|
|
||||||
{
|
|
||||||
String message = "Invalid value [" + mRadioIdField.getText() + "]. " + protocol.name() +
|
|
||||||
" valid range is [" + mask.getValidRangeDescription() + "]";
|
|
||||||
|
|
||||||
JOptionPane.showMessageDialog(RadioEditor.this, message, "Invalid Radio ID Value",
|
|
||||||
JOptionPane.ERROR_MESSAGE);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
radio.setValue(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
radio.setProtocol(protocol);
|
|
||||||
}
|
|
||||||
|
|
||||||
setModified(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -36,6 +36,8 @@ public enum RadioFormat
|
|||||||
{
|
{
|
||||||
APCO25("********", 0, 0xFFFFFF, "0 to 16,777,215",
|
APCO25("********", 0, 0xFFFFFF, "0 to 16,777,215",
|
||||||
"<html>APCO25 valid range is 0 to 16,777,215"),
|
"<html>APCO25 valid range is 0 to 16,777,215"),
|
||||||
|
PASSPORT("********", 0, 0x7FFFFF, "0 to 8,388,607",
|
||||||
|
"<html>PASSPORT valid range is 0 to 8,388,607"),
|
||||||
UNKNOWN("********", 1, 0xFFFFFF, "1 to 16,777,215",
|
UNKNOWN("********", 1, 0xFFFFFF, "1 to 16,777,215",
|
||||||
"Unknown protocol valid value range is 1-16,777,215");
|
"Unknown protocol valid value range is 1-16,777,215");
|
||||||
|
|
||||||
@@ -110,6 +112,8 @@ public enum RadioFormat
|
|||||||
{
|
{
|
||||||
case APCO25:
|
case APCO25:
|
||||||
return APCO25;
|
return APCO25;
|
||||||
|
case PASSPORT:
|
||||||
|
return PASSPORT;
|
||||||
default:
|
default:
|
||||||
return UNKNOWN;
|
return UNKNOWN;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,13 +22,16 @@
|
|||||||
|
|
||||||
package io.github.dsheirer.alias.id.radio;
|
package io.github.dsheirer.alias.id.radio;
|
||||||
|
|
||||||
|
import io.github.dsheirer.preference.identifier.IntegerFormat;
|
||||||
import io.github.dsheirer.preference.identifier.talkgroup.APCO25TalkgroupFormatter;
|
import io.github.dsheirer.preference.identifier.talkgroup.APCO25TalkgroupFormatter;
|
||||||
import io.github.dsheirer.preference.identifier.talkgroup.IntegerFormatter;
|
import io.github.dsheirer.preference.identifier.talkgroup.AbstractIntegerFormatter;
|
||||||
|
import io.github.dsheirer.preference.identifier.talkgroup.UnknownTalkgroupFormatter;
|
||||||
import io.github.dsheirer.protocol.Protocol;
|
import io.github.dsheirer.protocol.Protocol;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import java.text.ParseException;
|
import java.text.ParseException;
|
||||||
|
import java.util.EnumMap;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
@@ -38,12 +41,12 @@ import java.util.Map;
|
|||||||
public class RadioFormatter
|
public class RadioFormatter
|
||||||
{
|
{
|
||||||
private final static Logger mLog = LoggerFactory.getLogger(RadioFormatter.class);
|
private final static Logger mLog = LoggerFactory.getLogger(RadioFormatter.class);
|
||||||
private static Map<Protocol,IntegerFormatter> mFormatterMap = new HashMap<>();
|
private static Map<Protocol,AbstractIntegerFormatter> mFormatterMap = new EnumMap<>(Protocol.class);
|
||||||
|
|
||||||
static
|
static
|
||||||
{
|
{
|
||||||
mFormatterMap.put(Protocol.APCO25, new APCO25TalkgroupFormatter());
|
mFormatterMap.put(Protocol.APCO25, new APCO25TalkgroupFormatter());
|
||||||
mFormatterMap.put(Protocol.UNKNOWN, new IntegerFormatter());
|
mFormatterMap.put(Protocol.UNKNOWN, new UnknownTalkgroupFormatter());
|
||||||
}
|
}
|
||||||
|
|
||||||
public RadioFormatter()
|
public RadioFormatter()
|
||||||
@@ -55,7 +58,7 @@ public class RadioFormatter
|
|||||||
*/
|
*/
|
||||||
public static int parse(Protocol protocol, String value) throws ParseException
|
public static int parse(Protocol protocol, String value) throws ParseException
|
||||||
{
|
{
|
||||||
IntegerFormatter formatter = mFormatterMap.get(protocol);
|
AbstractIntegerFormatter formatter = mFormatterMap.get(protocol);
|
||||||
|
|
||||||
if(formatter == null)
|
if(formatter == null)
|
||||||
{
|
{
|
||||||
@@ -70,7 +73,7 @@ public class RadioFormatter
|
|||||||
*/
|
*/
|
||||||
public static String format(Protocol protocol, int value)
|
public static String format(Protocol protocol, int value)
|
||||||
{
|
{
|
||||||
IntegerFormatter formatter = mFormatterMap.get(protocol);
|
AbstractIntegerFormatter formatter = mFormatterMap.get(protocol);
|
||||||
|
|
||||||
if(formatter == null)
|
if(formatter == null)
|
||||||
{
|
{
|
||||||
@@ -79,4 +82,19 @@ public class RadioFormatter
|
|||||||
|
|
||||||
return formatter.format(value);
|
return formatter.format(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Formats the integer value to the specified integer format using the protocol specific formatter
|
||||||
|
*/
|
||||||
|
public static String format(Protocol protocol, int value, IntegerFormat format)
|
||||||
|
{
|
||||||
|
AbstractIntegerFormatter formatter = mFormatterMap.get(protocol);
|
||||||
|
|
||||||
|
if(formatter == null)
|
||||||
|
{
|
||||||
|
formatter = mFormatterMap.get(Protocol.UNKNOWN);
|
||||||
|
}
|
||||||
|
|
||||||
|
return formatter.format(value, format);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ import io.github.dsheirer.protocol.Protocol;
|
|||||||
/**
|
/**
|
||||||
* Integer radio identifier range of values with protocol.
|
* Integer radio identifier range of values with protocol.
|
||||||
*/
|
*/
|
||||||
public class RadioRange extends AliasID
|
public class RadioRange extends AliasID implements Comparable<RadioRange>
|
||||||
{
|
{
|
||||||
private Protocol mProtocol = Protocol.UNKNOWN;
|
private Protocol mProtocol = Protocol.UNKNOWN;
|
||||||
private int mMinRadio;
|
private int mMinRadio;
|
||||||
@@ -41,6 +41,12 @@ public class RadioRange extends AliasID
|
|||||||
//No arg JAXB constructor
|
//No arg JAXB constructor
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isAudioIdentifier()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a radio range of from - to radio values (inclusive) for the specified protocol
|
* Creates a radio range of from - to radio values (inclusive) for the specified protocol
|
||||||
* @param protocol for the radio range
|
* @param protocol for the radio range
|
||||||
@@ -63,6 +69,7 @@ public class RadioRange extends AliasID
|
|||||||
public void setMinRadio(int minRadio)
|
public void setMinRadio(int minRadio)
|
||||||
{
|
{
|
||||||
mMinRadio = minRadio;
|
mMinRadio = minRadio;
|
||||||
|
updateValueProperty();
|
||||||
}
|
}
|
||||||
|
|
||||||
@JacksonXmlProperty(isAttribute = true, localName = "max")
|
@JacksonXmlProperty(isAttribute = true, localName = "max")
|
||||||
@@ -74,6 +81,7 @@ public class RadioRange extends AliasID
|
|||||||
public void setMaxRadio(int maxRadio)
|
public void setMaxRadio(int maxRadio)
|
||||||
{
|
{
|
||||||
mMaxRadio = maxRadio;
|
mMaxRadio = maxRadio;
|
||||||
|
updateValueProperty();
|
||||||
}
|
}
|
||||||
|
|
||||||
@JacksonXmlProperty(isAttribute = true, localName = "protocol")
|
@JacksonXmlProperty(isAttribute = true, localName = "protocol")
|
||||||
@@ -99,7 +107,8 @@ public class RadioRange extends AliasID
|
|||||||
return radioFormat.getMinimumValidValue() <= mMinRadio &&
|
return radioFormat.getMinimumValidValue() <= mMinRadio &&
|
||||||
mMinRadio <= radioFormat.getMaximumValidValue() &&
|
mMinRadio <= radioFormat.getMaximumValidValue() &&
|
||||||
radioFormat.getMinimumValidValue() <= mMaxRadio &&
|
radioFormat.getMinimumValidValue() <= mMaxRadio &&
|
||||||
mMaxRadio <= radioFormat.getMaximumValidValue();
|
mMaxRadio <= radioFormat.getMaximumValidValue() &&
|
||||||
|
mMinRadio < mMaxRadio;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String toString()
|
public String toString()
|
||||||
@@ -142,6 +151,13 @@ public class RadioRange extends AliasID
|
|||||||
return getMinRadio() <= radioValue && radioValue <= getMaxRadio();
|
return getMinRadio() <= radioValue && radioValue <= getMaxRadio();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean overlaps(AliasID other)
|
||||||
|
{
|
||||||
|
return other instanceof RadioRange && overlaps((RadioRange)other);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Indicates if this talkgroup range overlaps the talkgroup range argument.
|
* Indicates if this talkgroup range overlaps the talkgroup range argument.
|
||||||
* @param radioRange to check for overlap
|
* @param radioRange to check for overlap
|
||||||
@@ -163,4 +179,30 @@ public class RadioRange extends AliasID
|
|||||||
{
|
{
|
||||||
return AliasIDType.RADIO_ID_RANGE;
|
return AliasIDType.RADIO_ID_RANGE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int compareTo(RadioRange other)
|
||||||
|
{
|
||||||
|
if(other == null)
|
||||||
|
{
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(getProtocol().equals(other.getProtocol()))
|
||||||
|
{
|
||||||
|
if(getMinRadio() == other.getMinRadio())
|
||||||
|
{
|
||||||
|
return Integer.compare(getMaxRadio(), other.getMaxRadio());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return Integer.compare(getMinRadio(), other.getMinRadio());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return getProtocol().compareTo(other.getProtocol());
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,273 +0,0 @@
|
|||||||
/*
|
|
||||||
*
|
|
||||||
* * ******************************************************************************
|
|
||||||
* * Copyright (C) 2014-2019 Dennis Sheirer
|
|
||||||
* *
|
|
||||||
* * This program is free software: you can redistribute it and/or modify
|
|
||||||
* * it under the terms of the GNU General Public License as published by
|
|
||||||
* * the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* * (at your option) any later version.
|
|
||||||
* *
|
|
||||||
* * This program is distributed in the hope that it will be useful,
|
|
||||||
* * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* * GNU General Public License for more details.
|
|
||||||
* *
|
|
||||||
* * You should have received a copy of the GNU General Public License
|
|
||||||
* * along with this program. If not, see <http://www.gnu.org/licenses/>
|
|
||||||
* * *****************************************************************************
|
|
||||||
*
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
package io.github.dsheirer.alias.id.radio;
|
|
||||||
|
|
||||||
import io.github.dsheirer.alias.id.AliasID;
|
|
||||||
import io.github.dsheirer.gui.editor.DocumentListenerEditor;
|
|
||||||
import io.github.dsheirer.protocol.Protocol;
|
|
||||||
import net.miginfocom.swing.MigLayout;
|
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
|
|
||||||
import javax.swing.DefaultComboBoxModel;
|
|
||||||
import javax.swing.JComboBox;
|
|
||||||
import javax.swing.JFormattedTextField;
|
|
||||||
import javax.swing.JLabel;
|
|
||||||
import javax.swing.JOptionPane;
|
|
||||||
import javax.swing.text.MaskFormatter;
|
|
||||||
import java.awt.Color;
|
|
||||||
import java.awt.Cursor;
|
|
||||||
import java.awt.event.ActionEvent;
|
|
||||||
import java.awt.event.ActionListener;
|
|
||||||
import java.awt.event.MouseAdapter;
|
|
||||||
import java.awt.event.MouseEvent;
|
|
||||||
import java.text.ParseException;
|
|
||||||
|
|
||||||
public class RadioRangeEditor extends DocumentListenerEditor<AliasID>
|
|
||||||
{
|
|
||||||
private final static Logger mLog = LoggerFactory.getLogger(RadioRangeEditor.class);
|
|
||||||
private static final long serialVersionUID = 1L;
|
|
||||||
|
|
||||||
private static final String VALID_CHARACTERS_FOR_ASTERISK_MASKS = "0123456789 ";
|
|
||||||
private MaskFormatter mMaskFormatter = new MaskFormatter();
|
|
||||||
private JComboBox<Protocol> mComboProtocol;
|
|
||||||
private JFormattedTextField mMinRadioField;
|
|
||||||
private JFormattedTextField mMaxRadioField;
|
|
||||||
|
|
||||||
public RadioRangeEditor(AliasID aliasID)
|
|
||||||
{
|
|
||||||
initGUI();
|
|
||||||
|
|
||||||
setItem(aliasID);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void initGUI()
|
|
||||||
{
|
|
||||||
setLayout(new MigLayout("fill,wrap 2", "[right][left]", "[][]"));
|
|
||||||
|
|
||||||
add(new JLabel("Protocol:"));
|
|
||||||
|
|
||||||
mComboProtocol = new JComboBox<Protocol>();
|
|
||||||
|
|
||||||
DefaultComboBoxModel<Protocol> model = new DefaultComboBoxModel<>();
|
|
||||||
|
|
||||||
for(Protocol protocol : Protocol.RADIO_ID_PROTOCOLS)
|
|
||||||
{
|
|
||||||
model.addElement(protocol);
|
|
||||||
}
|
|
||||||
|
|
||||||
mComboProtocol.setModel(model);
|
|
||||||
mComboProtocol.addActionListener(new ActionListener()
|
|
||||||
{
|
|
||||||
@Override
|
|
||||||
public void actionPerformed(ActionEvent e)
|
|
||||||
{
|
|
||||||
Protocol protocol = (Protocol)mComboProtocol.getSelectedItem();
|
|
||||||
updateEditor(protocol);
|
|
||||||
setModified(true);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
add(mComboProtocol);
|
|
||||||
|
|
||||||
add(new JLabel("Min:"));
|
|
||||||
|
|
||||||
mMinRadioField = new JFormattedTextField(mMaskFormatter);
|
|
||||||
mMinRadioField.getDocument().addDocumentListener(this);
|
|
||||||
add(mMinRadioField, "growx,push");
|
|
||||||
|
|
||||||
add(new JLabel("Max:"));
|
|
||||||
|
|
||||||
mMaxRadioField = new JFormattedTextField(mMaskFormatter);
|
|
||||||
mMaxRadioField.getDocument().addDocumentListener(this);
|
|
||||||
add(mMaxRadioField, "growx,push");
|
|
||||||
|
|
||||||
JLabel help = new JLabel("Help ...");
|
|
||||||
help.setForeground(Color.BLUE.brighter());
|
|
||||||
help.setCursor(new Cursor(Cursor.HAND_CURSOR));
|
|
||||||
help.addMouseListener(new MouseAdapter()
|
|
||||||
{
|
|
||||||
@Override
|
|
||||||
public void mouseClicked(MouseEvent e)
|
|
||||||
{
|
|
||||||
RadioFormat radioFormat = RadioFormat.get(getCurrentProtocol());
|
|
||||||
JOptionPane.showMessageDialog(RadioRangeEditor.this,
|
|
||||||
radioFormat.getValidRangeHelpText(), "Help", JOptionPane.INFORMATION_MESSAGE);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
add(help, "align left");
|
|
||||||
}
|
|
||||||
|
|
||||||
private void updateEditor(Protocol protocol)
|
|
||||||
{
|
|
||||||
RadioFormat mask = RadioFormat.get(protocol);
|
|
||||||
int currentMinValue = 0;
|
|
||||||
int currentMaxValue = 0;
|
|
||||||
RadioRange radioRange = getRadioRange();
|
|
||||||
|
|
||||||
if(radioRange != null)
|
|
||||||
{
|
|
||||||
currentMinValue = radioRange.getMinRadio();
|
|
||||||
currentMaxValue = radioRange.getMaxRadio();
|
|
||||||
}
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
mMinRadioField.setValue(null);
|
|
||||||
mMaxRadioField.setValue(null);
|
|
||||||
|
|
||||||
mMinRadioField.setToolTipText(mask.getValidRangeHelpText());
|
|
||||||
mMaxRadioField.setToolTipText(mask.getValidRangeHelpText());
|
|
||||||
mMaskFormatter.setMask(mask.getMask());
|
|
||||||
if(mask.getMask().contains("*"))
|
|
||||||
{
|
|
||||||
mMaskFormatter.setValidCharacters(VALID_CHARACTERS_FOR_ASTERISK_MASKS);
|
|
||||||
}
|
|
||||||
|
|
||||||
mMinRadioField.setValue(RadioFormatter.format(protocol, currentMinValue));
|
|
||||||
mMaxRadioField.setValue(RadioFormatter.format(protocol, currentMaxValue));
|
|
||||||
}
|
|
||||||
catch(ParseException pe)
|
|
||||||
{
|
|
||||||
mLog.error("Error applying radio editor mask to mask formatter [" + mask.getMask() + "]");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private Protocol getCurrentProtocol()
|
|
||||||
{
|
|
||||||
return mComboProtocol.getItemAt(mComboProtocol.getSelectedIndex());
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public RadioRange getRadioRange()
|
|
||||||
{
|
|
||||||
if(getItem() instanceof RadioRange)
|
|
||||||
{
|
|
||||||
return (RadioRange)getItem();
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setItem(AliasID aliasID)
|
|
||||||
{
|
|
||||||
super.setItem(aliasID);
|
|
||||||
|
|
||||||
RadioRange radioRange = getRadioRange();
|
|
||||||
|
|
||||||
updateEditor(radioRange.getProtocol());
|
|
||||||
|
|
||||||
if(radioRange != null)
|
|
||||||
{
|
|
||||||
mComboProtocol.getModel().setSelectedItem(radioRange.getProtocol());
|
|
||||||
String minFormatted = RadioFormatter.format(radioRange.getProtocol(), radioRange.getMinRadio());
|
|
||||||
mMinRadioField.setValue(minFormatted);
|
|
||||||
String maxFormatted = RadioFormatter.format(radioRange.getProtocol(), radioRange.getMaxRadio());
|
|
||||||
mMaxRadioField.setValue(maxFormatted);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
mComboProtocol.getModel().setSelectedItem(Protocol.UNKNOWN);
|
|
||||||
mMinRadioField.setValue(RadioFormatter.format(getCurrentProtocol(), 0));
|
|
||||||
mMaxRadioField.setValue(RadioFormatter.format(getCurrentProtocol(), 0));
|
|
||||||
}
|
|
||||||
|
|
||||||
setModified(false);
|
|
||||||
|
|
||||||
repaint();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void save()
|
|
||||||
{
|
|
||||||
RadioRange radioRange = getRadioRange();
|
|
||||||
|
|
||||||
if(radioRange != null)
|
|
||||||
{
|
|
||||||
Protocol protocol = mComboProtocol.getItemAt(mComboProtocol.getSelectedIndex());
|
|
||||||
|
|
||||||
if(protocol == null)
|
|
||||||
{
|
|
||||||
protocol = Protocol.UNKNOWN;
|
|
||||||
}
|
|
||||||
|
|
||||||
radioRange.setProtocol(protocol);
|
|
||||||
|
|
||||||
int minValue = -1;
|
|
||||||
int maxValue = -1;
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
minValue = RadioFormatter.parse(protocol, mMinRadioField.getText());
|
|
||||||
}
|
|
||||||
catch(ParseException pe)
|
|
||||||
{
|
|
||||||
//ignore ... value is still -1 and outside valid value range
|
|
||||||
}
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
maxValue = RadioFormatter.parse(protocol, mMaxRadioField.getText());
|
|
||||||
}
|
|
||||||
catch(ParseException pe)
|
|
||||||
{
|
|
||||||
//ignore ... value is still -1 and outside valid value range
|
|
||||||
}
|
|
||||||
|
|
||||||
RadioFormat mask = RadioFormat.get(protocol);
|
|
||||||
|
|
||||||
//Check for valid value within range ... notify user but allow value to persist
|
|
||||||
if(minValue < mask.getMinimumValidValue() || minValue > mask.getMaximumValidValue())
|
|
||||||
{
|
|
||||||
String message = "Invalid minimum value [" + mMinRadioField.getText() + "]. " + protocol.name() +
|
|
||||||
" valid range is [" + mask.getValidRangeDescription() + "]";
|
|
||||||
|
|
||||||
JOptionPane.showMessageDialog(RadioRangeEditor.this, message,
|
|
||||||
"Invalid Radio Value", JOptionPane.ERROR_MESSAGE);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
radioRange.setMinRadio(minValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
//Check for valid value within range ... notify user but allow value to persist
|
|
||||||
if(maxValue < mask.getMinimumValidValue() || maxValue > mask.getMaximumValidValue())
|
|
||||||
{
|
|
||||||
String message = "Invalid maximum value [" + mMaxRadioField.getText() + "]. " + protocol.name() +
|
|
||||||
" valid range is [" + mask.getValidRangeDescription() + "]";
|
|
||||||
|
|
||||||
JOptionPane.showMessageDialog(RadioRangeEditor.this, message,
|
|
||||||
"Invalid Radio Value", JOptionPane.ERROR_MESSAGE);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
radioRange.setMaxRadio(maxValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
radioRange.setProtocol(protocol);
|
|
||||||
}
|
|
||||||
|
|
||||||
setModified(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -36,6 +36,12 @@ public class Record extends AliasID
|
|||||||
return AliasIDType.RECORD;
|
return AliasIDType.RECORD;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isAudioIdentifier()
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isValid()
|
public boolean isValid()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,73 +0,0 @@
|
|||||||
/*
|
|
||||||
* ******************************************************************************
|
|
||||||
* sdrtrunk
|
|
||||||
* Copyright (C) 2014-2018 Dennis Sheirer
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
|
||||||
* *****************************************************************************
|
|
||||||
*/
|
|
||||||
package io.github.dsheirer.alias.id.record;
|
|
||||||
|
|
||||||
import io.github.dsheirer.alias.id.AliasID;
|
|
||||||
import io.github.dsheirer.gui.editor.DocumentListenerEditor;
|
|
||||||
import net.miginfocom.swing.MigLayout;
|
|
||||||
|
|
||||||
import javax.swing.JLabel;
|
|
||||||
import javax.swing.JOptionPane;
|
|
||||||
import java.awt.Color;
|
|
||||||
import java.awt.Cursor;
|
|
||||||
import java.awt.event.MouseAdapter;
|
|
||||||
import java.awt.event.MouseEvent;
|
|
||||||
|
|
||||||
public class RecordEditor extends DocumentListenerEditor<AliasID>
|
|
||||||
{
|
|
||||||
private static final long serialVersionUID = 1L;
|
|
||||||
|
|
||||||
private static final String HELP_TEXT = "<html>"
|
|
||||||
+ "<h3>Record Audio</h3>"
|
|
||||||
+ "Any audio associated with this alias will be recorded.<br><br>";
|
|
||||||
|
|
||||||
public RecordEditor(AliasID aliasID )
|
|
||||||
{
|
|
||||||
initGUI();
|
|
||||||
|
|
||||||
setItem( aliasID );
|
|
||||||
}
|
|
||||||
|
|
||||||
private void initGUI()
|
|
||||||
{
|
|
||||||
setLayout( new MigLayout( "fill,wrap 1", "[]", "[][]" ) );
|
|
||||||
|
|
||||||
add( new JLabel( "Record Audio" ), "wrap" );
|
|
||||||
|
|
||||||
JLabel help = new JLabel( "Help ..." );
|
|
||||||
help.setForeground( Color.BLUE.brighter() );
|
|
||||||
help.setCursor( new Cursor( Cursor.HAND_CURSOR ) );
|
|
||||||
help.addMouseListener( new MouseAdapter()
|
|
||||||
{
|
|
||||||
@Override
|
|
||||||
public void mouseClicked( MouseEvent e )
|
|
||||||
{
|
|
||||||
JOptionPane.showMessageDialog( RecordEditor.this,
|
|
||||||
HELP_TEXT, "Help", JOptionPane.INFORMATION_MESSAGE );
|
|
||||||
}
|
|
||||||
} );
|
|
||||||
add( help, "align left" );
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void save()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,75 +0,0 @@
|
|||||||
/*******************************************************************************
|
|
||||||
* SDR Trunk
|
|
||||||
* Copyright (C) 2014 Dennis Sheirer
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
|
||||||
******************************************************************************/
|
|
||||||
package io.github.dsheirer.alias.id.status;
|
|
||||||
|
|
||||||
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
|
|
||||||
import io.github.dsheirer.alias.id.AliasID;
|
|
||||||
import io.github.dsheirer.alias.id.AliasIDType;
|
|
||||||
|
|
||||||
public class StatusID extends AliasID
|
|
||||||
{
|
|
||||||
private int mStatus;
|
|
||||||
|
|
||||||
public StatusID()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
@JacksonXmlProperty(isAttribute = true, localName = "status")
|
|
||||||
public int getStatus()
|
|
||||||
{
|
|
||||||
return mStatus;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setStatus(int status)
|
|
||||||
{
|
|
||||||
this.mStatus = status;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isValid()
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String toString()
|
|
||||||
{
|
|
||||||
return "Status: " + String.format("%03d", mStatus);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean matches(AliasID id)
|
|
||||||
{
|
|
||||||
boolean retVal = false;
|
|
||||||
|
|
||||||
if(id instanceof StatusID)
|
|
||||||
{
|
|
||||||
StatusID statusId = (StatusID)id;
|
|
||||||
|
|
||||||
retVal = (mStatus == statusId.getStatus());
|
|
||||||
}
|
|
||||||
|
|
||||||
return retVal;
|
|
||||||
}
|
|
||||||
|
|
||||||
@JacksonXmlProperty(isAttribute = true, localName = "type", namespace = "http://www.w3.org/2001/XMLSchema-instance")
|
|
||||||
@Override
|
|
||||||
public AliasIDType getType()
|
|
||||||
{
|
|
||||||
return AliasIDType.STATUS;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,140 +0,0 @@
|
|||||||
/*******************************************************************************
|
|
||||||
* SDR Trunk
|
|
||||||
* Copyright (C) 2014-2016 Dennis Sheirer
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
|
||||||
******************************************************************************/
|
|
||||||
package io.github.dsheirer.alias.id.status;
|
|
||||||
|
|
||||||
import io.github.dsheirer.alias.id.AliasID;
|
|
||||||
import io.github.dsheirer.gui.editor.DocumentListenerEditor;
|
|
||||||
import net.miginfocom.swing.MigLayout;
|
|
||||||
|
|
||||||
import javax.swing.*;
|
|
||||||
import javax.swing.text.MaskFormatter;
|
|
||||||
import java.awt.*;
|
|
||||||
import java.awt.event.MouseAdapter;
|
|
||||||
import java.awt.event.MouseEvent;
|
|
||||||
|
|
||||||
public class StatusIDEditor extends DocumentListenerEditor<AliasID>
|
|
||||||
{
|
|
||||||
private static final long serialVersionUID = 1L;
|
|
||||||
|
|
||||||
private static final String HELP_TEXT = "<html>"
|
|
||||||
+ "<h3>Status Identifier</h3>"
|
|
||||||
+ "Status numbers are used in some protocols like Fleetsync.<br><br>"
|
|
||||||
+ "The status number is assigned a meaning in the radio. You<br>"
|
|
||||||
+ "can assign a 3 digit status code (use leading zeros) to an<br>"
|
|
||||||
+ "alias where the alias contains the status meaning.<br><br>"
|
|
||||||
+ "<b>Status:</b> <u>001</u> engine start"
|
|
||||||
+ "</html>";
|
|
||||||
|
|
||||||
private JTextField mTextField;
|
|
||||||
|
|
||||||
public StatusIDEditor( AliasID aliasID )
|
|
||||||
{
|
|
||||||
initGUI();
|
|
||||||
|
|
||||||
setItem( aliasID );
|
|
||||||
}
|
|
||||||
|
|
||||||
private void initGUI()
|
|
||||||
{
|
|
||||||
setLayout( new MigLayout( "fill,wrap 2", "[right][left]", "[][]" ) );
|
|
||||||
|
|
||||||
add( new JLabel( "Status:" ) );
|
|
||||||
|
|
||||||
MaskFormatter formatter = null;
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
//Mask: 3 digits
|
|
||||||
formatter = new MaskFormatter( "###" );
|
|
||||||
}
|
|
||||||
catch( Exception e )
|
|
||||||
{
|
|
||||||
//Do nothing, the mask was invalid
|
|
||||||
}
|
|
||||||
|
|
||||||
mTextField = new JFormattedTextField( formatter );
|
|
||||||
mTextField.getDocument().addDocumentListener( this );
|
|
||||||
mTextField.setToolTipText( HELP_TEXT );
|
|
||||||
add( mTextField, "growx,push" );
|
|
||||||
|
|
||||||
JLabel help = new JLabel( "Help ..." );
|
|
||||||
help.setForeground( Color.BLUE.brighter() );
|
|
||||||
help.setCursor( new Cursor( Cursor.HAND_CURSOR ) );
|
|
||||||
help.addMouseListener( new MouseAdapter()
|
|
||||||
{
|
|
||||||
@Override
|
|
||||||
public void mouseClicked( MouseEvent e )
|
|
||||||
{
|
|
||||||
JOptionPane.showMessageDialog( StatusIDEditor.this,
|
|
||||||
HELP_TEXT, "Help", JOptionPane.INFORMATION_MESSAGE );
|
|
||||||
}
|
|
||||||
} );
|
|
||||||
add( help, "align left" );
|
|
||||||
}
|
|
||||||
|
|
||||||
public StatusID getStatusID()
|
|
||||||
{
|
|
||||||
if( getItem() instanceof StatusID )
|
|
||||||
{
|
|
||||||
return (StatusID)getItem();
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setItem( AliasID aliasID )
|
|
||||||
{
|
|
||||||
super.setItem( aliasID );
|
|
||||||
|
|
||||||
StatusID statusID = getStatusID();
|
|
||||||
|
|
||||||
if( statusID != null )
|
|
||||||
{
|
|
||||||
mTextField.setText( String.format( "%03d", statusID.getStatus() ) );
|
|
||||||
}
|
|
||||||
|
|
||||||
setModified( false );
|
|
||||||
|
|
||||||
repaint();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void save()
|
|
||||||
{
|
|
||||||
StatusID statusID = getStatusID();
|
|
||||||
|
|
||||||
if( statusID != null )
|
|
||||||
{
|
|
||||||
int status = 0;
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
status = Integer.valueOf( mTextField.getText() );
|
|
||||||
}
|
|
||||||
catch( Exception e )
|
|
||||||
{
|
|
||||||
//Do nothing, we couldn't parse the value
|
|
||||||
}
|
|
||||||
|
|
||||||
statusID.setStatus( status );
|
|
||||||
}
|
|
||||||
|
|
||||||
setModified( false );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,88 @@
|
|||||||
|
/*
|
||||||
|
* *****************************************************************************
|
||||||
|
* Copyright (C) 2014-2020 Dennis Sheirer
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||||
|
* ****************************************************************************
|
||||||
|
*/
|
||||||
|
package io.github.dsheirer.alias.id.status;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
|
||||||
|
import io.github.dsheirer.alias.id.AliasID;
|
||||||
|
import io.github.dsheirer.alias.id.AliasIDType;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unit Status
|
||||||
|
*/
|
||||||
|
public class UnitStatusID extends AliasID
|
||||||
|
{
|
||||||
|
private int mStatus;
|
||||||
|
|
||||||
|
public UnitStatusID()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isAudioIdentifier()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@JacksonXmlProperty(isAttribute = true, localName = "status")
|
||||||
|
public int getStatus()
|
||||||
|
{
|
||||||
|
return mStatus;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setStatus(int status)
|
||||||
|
{
|
||||||
|
mStatus = status;
|
||||||
|
updateValueProperty();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isValid()
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String toString()
|
||||||
|
{
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
sb.append("Unit Status: ").append(String.format("%03d", mStatus));
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean matches(AliasID id)
|
||||||
|
{
|
||||||
|
boolean retVal = false;
|
||||||
|
|
||||||
|
if(id instanceof UnitStatusID)
|
||||||
|
{
|
||||||
|
UnitStatusID userStatusId = (UnitStatusID)id;
|
||||||
|
|
||||||
|
retVal = (mStatus == userStatusId.getStatus());
|
||||||
|
}
|
||||||
|
|
||||||
|
return retVal;
|
||||||
|
}
|
||||||
|
|
||||||
|
@JacksonXmlProperty(isAttribute = true, localName = "type", namespace = "http://www.w3.org/2001/XMLSchema-instance")
|
||||||
|
@Override
|
||||||
|
public AliasIDType getType()
|
||||||
|
{
|
||||||
|
return AliasIDType.UNIT_STATUS;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,88 @@
|
|||||||
|
/*
|
||||||
|
* *****************************************************************************
|
||||||
|
* Copyright (C) 2014-2020 Dennis Sheirer
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||||
|
* ****************************************************************************
|
||||||
|
*/
|
||||||
|
package io.github.dsheirer.alias.id.status;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
|
||||||
|
import io.github.dsheirer.alias.id.AliasID;
|
||||||
|
import io.github.dsheirer.alias.id.AliasIDType;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* User Status
|
||||||
|
*/
|
||||||
|
public class UserStatusID extends AliasID
|
||||||
|
{
|
||||||
|
private int mStatus;
|
||||||
|
|
||||||
|
public UserStatusID()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isAudioIdentifier()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@JacksonXmlProperty(isAttribute = true, localName = "status")
|
||||||
|
public int getStatus()
|
||||||
|
{
|
||||||
|
return mStatus;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setStatus(int status)
|
||||||
|
{
|
||||||
|
mStatus = status;
|
||||||
|
updateValueProperty();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isValid()
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String toString()
|
||||||
|
{
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
sb.append("User Status: ").append(String.format("%03d", mStatus));
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean matches(AliasID id)
|
||||||
|
{
|
||||||
|
boolean retVal = false;
|
||||||
|
|
||||||
|
if(id instanceof UserStatusID)
|
||||||
|
{
|
||||||
|
UserStatusID userStatusId = (UserStatusID)id;
|
||||||
|
|
||||||
|
retVal = (mStatus == userStatusId.getStatus());
|
||||||
|
}
|
||||||
|
|
||||||
|
return retVal;
|
||||||
|
}
|
||||||
|
|
||||||
|
@JacksonXmlProperty(isAttribute = true, localName = "type", namespace = "http://www.w3.org/2001/XMLSchema-instance")
|
||||||
|
@Override
|
||||||
|
public AliasIDType getType()
|
||||||
|
{
|
||||||
|
return AliasIDType.STATUS;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -24,12 +24,15 @@ import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
|
|||||||
import io.github.dsheirer.alias.id.AliasID;
|
import io.github.dsheirer.alias.id.AliasID;
|
||||||
import io.github.dsheirer.alias.id.AliasIDType;
|
import io.github.dsheirer.alias.id.AliasIDType;
|
||||||
import io.github.dsheirer.protocol.Protocol;
|
import io.github.dsheirer.protocol.Protocol;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Integer talkgroup identifier with protocol.
|
* Integer talkgroup identifier with protocol.
|
||||||
*/
|
*/
|
||||||
public class Talkgroup extends AliasID
|
public class Talkgroup extends AliasID implements Comparable<Talkgroup>
|
||||||
{
|
{
|
||||||
|
private static final Logger mLog = LoggerFactory.getLogger(Talkgroup.class);
|
||||||
private Protocol mProtocol = Protocol.UNKNOWN;
|
private Protocol mProtocol = Protocol.UNKNOWN;
|
||||||
private int mValue;
|
private int mValue;
|
||||||
|
|
||||||
@@ -44,6 +47,12 @@ public class Talkgroup extends AliasID
|
|||||||
mValue = value;
|
mValue = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isAudioIdentifier()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
@JacksonXmlProperty(isAttribute = true, localName = "value")
|
@JacksonXmlProperty(isAttribute = true, localName = "value")
|
||||||
public int getValue()
|
public int getValue()
|
||||||
{
|
{
|
||||||
@@ -53,6 +62,7 @@ public class Talkgroup extends AliasID
|
|||||||
public void setValue(int value)
|
public void setValue(int value)
|
||||||
{
|
{
|
||||||
mValue = value;
|
mValue = value;
|
||||||
|
updateValueProperty();
|
||||||
}
|
}
|
||||||
|
|
||||||
@JacksonXmlProperty(isAttribute = true, localName = "protocol")
|
@JacksonXmlProperty(isAttribute = true, localName = "protocol")
|
||||||
@@ -64,6 +74,7 @@ public class Talkgroup extends AliasID
|
|||||||
public void setProtocol(Protocol protocol)
|
public void setProtocol(Protocol protocol)
|
||||||
{
|
{
|
||||||
mProtocol = protocol;
|
mProtocol = protocol;
|
||||||
|
updateValueProperty();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -82,7 +93,18 @@ public class Talkgroup extends AliasID
|
|||||||
{
|
{
|
||||||
StringBuilder sb = new StringBuilder();
|
StringBuilder sb = new StringBuilder();
|
||||||
|
|
||||||
sb.append("Talkgroup:").append(TalkgroupFormatter.format(mProtocol, mValue));
|
String talkgroup = null;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
talkgroup = TalkgroupFormatter.format(mProtocol, mValue);
|
||||||
|
}
|
||||||
|
catch(Exception e)
|
||||||
|
{
|
||||||
|
mLog.error("Error formatting Talkgroup Protocol [" + mProtocol + "] value [" + mValue + "]", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
sb.append("Talkgroup:").append(talkgroup != null ? talkgroup : "error");
|
||||||
sb.append(" Protocol:").append((mProtocol));
|
sb.append(" Protocol:").append((mProtocol));
|
||||||
|
|
||||||
if(!isValid())
|
if(!isValid())
|
||||||
@@ -111,4 +133,22 @@ public class Talkgroup extends AliasID
|
|||||||
{
|
{
|
||||||
return AliasIDType.TALKGROUP;
|
return AliasIDType.TALKGROUP;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int compareTo(Talkgroup other)
|
||||||
|
{
|
||||||
|
if(other == null)
|
||||||
|
{
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(getProtocol().equals(other.getProtocol()))
|
||||||
|
{
|
||||||
|
return Integer.compare(getValue(), other.getValue());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return getProtocol().compareTo(other.getProtocol());
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,233 +0,0 @@
|
|||||||
/*
|
|
||||||
* ******************************************************************************
|
|
||||||
* sdrtrunk
|
|
||||||
* Copyright (C) 2014-2019 Dennis Sheirer
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
|
||||||
* *****************************************************************************
|
|
||||||
*/
|
|
||||||
package io.github.dsheirer.alias.id.talkgroup;
|
|
||||||
|
|
||||||
import io.github.dsheirer.alias.id.AliasID;
|
|
||||||
import io.github.dsheirer.gui.editor.DocumentListenerEditor;
|
|
||||||
import io.github.dsheirer.protocol.Protocol;
|
|
||||||
import net.miginfocom.swing.MigLayout;
|
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
|
|
||||||
import javax.swing.DefaultComboBoxModel;
|
|
||||||
import javax.swing.JComboBox;
|
|
||||||
import javax.swing.JFormattedTextField;
|
|
||||||
import javax.swing.JLabel;
|
|
||||||
import javax.swing.JOptionPane;
|
|
||||||
import javax.swing.text.MaskFormatter;
|
|
||||||
import java.awt.Color;
|
|
||||||
import java.awt.Cursor;
|
|
||||||
import java.awt.event.ActionEvent;
|
|
||||||
import java.awt.event.ActionListener;
|
|
||||||
import java.awt.event.MouseAdapter;
|
|
||||||
import java.awt.event.MouseEvent;
|
|
||||||
import java.text.ParseException;
|
|
||||||
|
|
||||||
public class TalkgroupEditor extends DocumentListenerEditor<AliasID>
|
|
||||||
{
|
|
||||||
private final static Logger mLog = LoggerFactory.getLogger(TalkgroupEditor.class);
|
|
||||||
private static final long serialVersionUID = 1L;
|
|
||||||
|
|
||||||
private static final String VALID_CHARACTERS_FOR_ASTERISK_MASKS = "0123456789 ";
|
|
||||||
private MaskFormatter mMaskFormatter = new MaskFormatter();
|
|
||||||
private JComboBox<Protocol> mComboProtocol;
|
|
||||||
private JFormattedTextField mTalkgroupField;
|
|
||||||
|
|
||||||
|
|
||||||
public TalkgroupEditor(AliasID aliasID)
|
|
||||||
{
|
|
||||||
initGUI();
|
|
||||||
setItem(aliasID);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void initGUI()
|
|
||||||
{
|
|
||||||
setLayout(new MigLayout("fill,wrap 2", "[right][left]", "[][]"));
|
|
||||||
|
|
||||||
add(new JLabel("Protocol:"));
|
|
||||||
|
|
||||||
mComboProtocol = new JComboBox<>();
|
|
||||||
|
|
||||||
DefaultComboBoxModel<Protocol> model = new DefaultComboBoxModel<>();
|
|
||||||
|
|
||||||
for(Protocol protocol : Protocol.TALKGROUP_PROTOCOLS)
|
|
||||||
{
|
|
||||||
model.addElement(protocol);
|
|
||||||
}
|
|
||||||
|
|
||||||
mComboProtocol.setModel(model);
|
|
||||||
mComboProtocol.addActionListener(new ActionListener()
|
|
||||||
{
|
|
||||||
@Override
|
|
||||||
public void actionPerformed(ActionEvent e)
|
|
||||||
{
|
|
||||||
Protocol protocol = (Protocol)mComboProtocol.getSelectedItem();
|
|
||||||
updateEditor(protocol);
|
|
||||||
setModified(true);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
add(mComboProtocol);
|
|
||||||
|
|
||||||
add(new JLabel("Value:"));
|
|
||||||
|
|
||||||
mTalkgroupField = new JFormattedTextField(mMaskFormatter);
|
|
||||||
mTalkgroupField.getDocument().addDocumentListener(this);
|
|
||||||
add(mTalkgroupField, "growx,push");
|
|
||||||
|
|
||||||
JLabel help = new JLabel("Help ...");
|
|
||||||
help.setForeground(Color.BLUE.brighter());
|
|
||||||
help.setCursor(new Cursor(Cursor.HAND_CURSOR));
|
|
||||||
help.addMouseListener(new MouseAdapter()
|
|
||||||
{
|
|
||||||
@Override
|
|
||||||
public void mouseClicked(MouseEvent e)
|
|
||||||
{
|
|
||||||
TalkgroupFormat talkgroupFormat = TalkgroupFormat.get(getCurrentProtocol());
|
|
||||||
JOptionPane.showMessageDialog(TalkgroupEditor.this,
|
|
||||||
talkgroupFormat.getValidRangeHelpText(), "Help", JOptionPane.INFORMATION_MESSAGE);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
add(help, "align left");
|
|
||||||
}
|
|
||||||
|
|
||||||
private void updateEditor(Protocol protocol)
|
|
||||||
{
|
|
||||||
TalkgroupFormat mask = TalkgroupFormat.get(protocol);
|
|
||||||
int currentValue = 0;
|
|
||||||
Talkgroup talkgroup = getTalkgroup();
|
|
||||||
|
|
||||||
if(talkgroup != null)
|
|
||||||
{
|
|
||||||
currentValue = talkgroup.getValue();
|
|
||||||
}
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
mTalkgroupField.setValue(null);
|
|
||||||
mTalkgroupField.setToolTipText(mask.getValidRangeHelpText());
|
|
||||||
mMaskFormatter.setMask(mask.getMask());
|
|
||||||
if(mask.getMask().contains("*"))
|
|
||||||
{
|
|
||||||
mMaskFormatter.setValidCharacters(VALID_CHARACTERS_FOR_ASTERISK_MASKS);
|
|
||||||
}
|
|
||||||
mTalkgroupField.setValue(TalkgroupFormatter.format(protocol, currentValue));
|
|
||||||
}
|
|
||||||
catch(ParseException pe)
|
|
||||||
{
|
|
||||||
mLog.error("Error applying talkgroup editor mask to mask formatter [" + mask.getMask() + "]");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private Protocol getCurrentProtocol()
|
|
||||||
{
|
|
||||||
return mComboProtocol.getItemAt(mComboProtocol.getSelectedIndex());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Current talkgroup alias id
|
|
||||||
*/
|
|
||||||
public Talkgroup getTalkgroup()
|
|
||||||
{
|
|
||||||
if(getItem() instanceof Talkgroup)
|
|
||||||
{
|
|
||||||
return (Talkgroup)getItem();
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setItem(AliasID aliasID)
|
|
||||||
{
|
|
||||||
super.setItem(aliasID);
|
|
||||||
|
|
||||||
Talkgroup talkgroup = getTalkgroup();
|
|
||||||
|
|
||||||
if(talkgroup != null)
|
|
||||||
{
|
|
||||||
Protocol currentProtocol = getCurrentProtocol();
|
|
||||||
if(currentProtocol == talkgroup.getProtocol())
|
|
||||||
{
|
|
||||||
updateEditor(talkgroup.getProtocol());
|
|
||||||
}
|
|
||||||
mComboProtocol.getModel().setSelectedItem(talkgroup.getProtocol());
|
|
||||||
|
|
||||||
String formatted = TalkgroupFormatter.format(talkgroup.getProtocol(), talkgroup.getValue());
|
|
||||||
mTalkgroupField.setValue(formatted);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
mComboProtocol.getModel().setSelectedItem(Protocol.UNKNOWN);
|
|
||||||
mTalkgroupField.setValue(TalkgroupFormatter.format(getCurrentProtocol(), 0));
|
|
||||||
}
|
|
||||||
|
|
||||||
setModified(false);
|
|
||||||
|
|
||||||
repaint();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void save()
|
|
||||||
{
|
|
||||||
Talkgroup talkgroup = getTalkgroup();
|
|
||||||
|
|
||||||
if(talkgroup != null)
|
|
||||||
{
|
|
||||||
Protocol protocol = mComboProtocol.getItemAt(mComboProtocol.getSelectedIndex());
|
|
||||||
|
|
||||||
if(protocol == null)
|
|
||||||
{
|
|
||||||
protocol = Protocol.UNKNOWN;
|
|
||||||
}
|
|
||||||
|
|
||||||
int value = -1;
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
value = TalkgroupFormatter.parse(protocol, mTalkgroupField.getText());
|
|
||||||
}
|
|
||||||
catch(ParseException pe)
|
|
||||||
{
|
|
||||||
//ignore ... value is still -1 and outside valid value range
|
|
||||||
}
|
|
||||||
|
|
||||||
TalkgroupFormat mask = TalkgroupFormat.get(protocol);
|
|
||||||
|
|
||||||
//Check for valid value within range ... notify user but allow value to persist
|
|
||||||
if(value < mask.getMinimumValidValue() || value > mask.getMaximumValidValue())
|
|
||||||
{
|
|
||||||
String message = "Invalid value [" + mTalkgroupField.getText() + "]. " + protocol.name() +
|
|
||||||
" valid range is [" + mask.getValidRangeDescription() + "]";
|
|
||||||
|
|
||||||
JOptionPane.showMessageDialog(TalkgroupEditor.this, message, "Invalid Talkgroup Value",
|
|
||||||
JOptionPane.ERROR_MESSAGE);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
talkgroup.setValue(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
talkgroup.setProtocol(protocol);
|
|
||||||
}
|
|
||||||
|
|
||||||
setModified(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -36,9 +36,9 @@ public enum TalkgroupFormat
|
|||||||
{
|
{
|
||||||
APCO25("********", 0, 0xFFFFFF, "0 to 65,535",
|
APCO25("********", 0, 0xFFFFFF, "0 to 65,535",
|
||||||
"<html>APCO25 talkgroup valid range is 0 to 65,535"),
|
"<html>APCO25 talkgroup valid range is 0 to 65,535"),
|
||||||
FLEETSYNC("###-####", 1, 0x7FFFFF, "001-0001 to 127-8192",
|
FLEETSYNC("###-####", 0, 0x7FFFFF, "001-0001 to 127-8192",
|
||||||
"<html>Fleetsync valid ranges are 1-127(prefix)<br>and 1-8192(ident) (ie. 001-0001 to 127-8192)"),
|
"<html>Fleetsync valid ranges are 1-127(prefix)<br>and 1-8192(ident) (ie. 001-0001 to 127-8192)"),
|
||||||
LTR("##-###", 257, 5375, "01-001 to 20-255",
|
LTR("##-###", 0x101, 0x3FFF, "01-001 to 20-255",
|
||||||
"<html>LTR valid ranges are 1-20(repeater) and 1-255(talkgroup) (ie. 01-001 to 20-255)"),
|
"<html>LTR valid ranges are 1-20(repeater) and 1-255(talkgroup) (ie. 01-001 to 20-255)"),
|
||||||
MDC1200("*****", 1, 0xFFFF, "1 to 65,535",
|
MDC1200("*****", 1, 0xFFFF, "1 to 65,535",
|
||||||
"MDC-1200 valid value range is 1-65,535"),
|
"MDC-1200 valid value range is 1-65,535"),
|
||||||
@@ -124,7 +124,6 @@ public enum TalkgroupFormat
|
|||||||
return FLEETSYNC;
|
return FLEETSYNC;
|
||||||
case LTR:
|
case LTR:
|
||||||
case LTR_NET:
|
case LTR_NET:
|
||||||
case LTR_STANDARD:
|
|
||||||
return LTR;
|
return LTR;
|
||||||
case MDC1200:
|
case MDC1200:
|
||||||
return MDC1200;
|
return MDC1200;
|
||||||
|
|||||||
@@ -20,18 +20,21 @@
|
|||||||
|
|
||||||
package io.github.dsheirer.alias.id.talkgroup;
|
package io.github.dsheirer.alias.id.talkgroup;
|
||||||
|
|
||||||
|
import io.github.dsheirer.preference.identifier.IntegerFormat;
|
||||||
import io.github.dsheirer.preference.identifier.talkgroup.APCO25TalkgroupFormatter;
|
import io.github.dsheirer.preference.identifier.talkgroup.APCO25TalkgroupFormatter;
|
||||||
|
import io.github.dsheirer.preference.identifier.talkgroup.AbstractIntegerFormatter;
|
||||||
import io.github.dsheirer.preference.identifier.talkgroup.FleetsyncTalkgroupFormatter;
|
import io.github.dsheirer.preference.identifier.talkgroup.FleetsyncTalkgroupFormatter;
|
||||||
import io.github.dsheirer.preference.identifier.talkgroup.IntegerFormatter;
|
|
||||||
import io.github.dsheirer.preference.identifier.talkgroup.LTRTalkgroupFormatter;
|
import io.github.dsheirer.preference.identifier.talkgroup.LTRTalkgroupFormatter;
|
||||||
import io.github.dsheirer.preference.identifier.talkgroup.MDC1200TalkgroupFormatter;
|
import io.github.dsheirer.preference.identifier.talkgroup.MDC1200TalkgroupFormatter;
|
||||||
import io.github.dsheirer.preference.identifier.talkgroup.MPT1327TalkgroupFormatter;
|
import io.github.dsheirer.preference.identifier.talkgroup.MPT1327TalkgroupFormatter;
|
||||||
import io.github.dsheirer.preference.identifier.talkgroup.PassportTalkgroupFormatter;
|
import io.github.dsheirer.preference.identifier.talkgroup.PassportTalkgroupFormatter;
|
||||||
|
import io.github.dsheirer.preference.identifier.talkgroup.UnknownTalkgroupFormatter;
|
||||||
import io.github.dsheirer.protocol.Protocol;
|
import io.github.dsheirer.protocol.Protocol;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import java.text.ParseException;
|
import java.text.ParseException;
|
||||||
|
import java.util.EnumMap;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
@@ -41,7 +44,7 @@ import java.util.Map;
|
|||||||
public class TalkgroupFormatter
|
public class TalkgroupFormatter
|
||||||
{
|
{
|
||||||
private final static Logger mLog = LoggerFactory.getLogger(TalkgroupFormatter.class);
|
private final static Logger mLog = LoggerFactory.getLogger(TalkgroupFormatter.class);
|
||||||
private static Map<Protocol,IntegerFormatter> mFormatterMap = new HashMap<>();
|
private static Map<Protocol,AbstractIntegerFormatter> mFormatterMap = new EnumMap<>(Protocol.class);
|
||||||
|
|
||||||
static
|
static
|
||||||
{
|
{
|
||||||
@@ -50,11 +53,10 @@ public class TalkgroupFormatter
|
|||||||
LTRTalkgroupFormatter ltr = new LTRTalkgroupFormatter();
|
LTRTalkgroupFormatter ltr = new LTRTalkgroupFormatter();
|
||||||
mFormatterMap.put(Protocol.LTR, ltr);
|
mFormatterMap.put(Protocol.LTR, ltr);
|
||||||
mFormatterMap.put(Protocol.LTR_NET, ltr);
|
mFormatterMap.put(Protocol.LTR_NET, ltr);
|
||||||
mFormatterMap.put(Protocol.LTR_STANDARD, ltr);
|
|
||||||
mFormatterMap.put(Protocol.MDC1200, new MDC1200TalkgroupFormatter());
|
mFormatterMap.put(Protocol.MDC1200, new MDC1200TalkgroupFormatter());
|
||||||
mFormatterMap.put(Protocol.MPT1327, new MPT1327TalkgroupFormatter());
|
mFormatterMap.put(Protocol.MPT1327, new MPT1327TalkgroupFormatter());
|
||||||
mFormatterMap.put(Protocol.PASSPORT, new PassportTalkgroupFormatter());
|
mFormatterMap.put(Protocol.PASSPORT, new PassportTalkgroupFormatter());
|
||||||
mFormatterMap.put(Protocol.UNKNOWN, new IntegerFormatter());
|
mFormatterMap.put(Protocol.UNKNOWN, new UnknownTalkgroupFormatter());
|
||||||
}
|
}
|
||||||
|
|
||||||
public TalkgroupFormatter()
|
public TalkgroupFormatter()
|
||||||
@@ -66,7 +68,7 @@ public class TalkgroupFormatter
|
|||||||
*/
|
*/
|
||||||
public static int parse(Protocol protocol, String value) throws ParseException
|
public static int parse(Protocol protocol, String value) throws ParseException
|
||||||
{
|
{
|
||||||
IntegerFormatter formatter = mFormatterMap.get(protocol);
|
AbstractIntegerFormatter formatter = mFormatterMap.get(protocol);
|
||||||
|
|
||||||
if(formatter == null)
|
if(formatter == null)
|
||||||
{
|
{
|
||||||
@@ -81,7 +83,7 @@ public class TalkgroupFormatter
|
|||||||
*/
|
*/
|
||||||
public static String format(Protocol protocol, int value)
|
public static String format(Protocol protocol, int value)
|
||||||
{
|
{
|
||||||
IntegerFormatter formatter = mFormatterMap.get(protocol);
|
AbstractIntegerFormatter formatter = mFormatterMap.get(protocol);
|
||||||
|
|
||||||
if(formatter == null)
|
if(formatter == null)
|
||||||
{
|
{
|
||||||
@@ -90,4 +92,19 @@ public class TalkgroupFormatter
|
|||||||
|
|
||||||
return formatter.format(value);
|
return formatter.format(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Formats the integer value to the specified integer format using the protocol specific formatter
|
||||||
|
*/
|
||||||
|
public static String format(Protocol protocol, int value, IntegerFormat format)
|
||||||
|
{
|
||||||
|
AbstractIntegerFormatter formatter = mFormatterMap.get(protocol);
|
||||||
|
|
||||||
|
if(formatter == null)
|
||||||
|
{
|
||||||
|
formatter = mFormatterMap.get(Protocol.UNKNOWN);
|
||||||
|
}
|
||||||
|
|
||||||
|
return formatter.format(value, format);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ import io.github.dsheirer.protocol.Protocol;
|
|||||||
/**
|
/**
|
||||||
* Integer talkgroup identifier range of values with protocol.
|
* Integer talkgroup identifier range of values with protocol.
|
||||||
*/
|
*/
|
||||||
public class TalkgroupRange extends AliasID
|
public class TalkgroupRange extends AliasID implements Comparable<TalkgroupRange>
|
||||||
{
|
{
|
||||||
private Protocol mProtocol = Protocol.UNKNOWN;
|
private Protocol mProtocol = Protocol.UNKNOWN;
|
||||||
private int mMinTalkgroup;
|
private int mMinTalkgroup;
|
||||||
@@ -52,6 +52,12 @@ public class TalkgroupRange extends AliasID
|
|||||||
mMaxTalkgroup = maxTalkgroup;
|
mMaxTalkgroup = maxTalkgroup;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isAudioIdentifier()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
@JacksonXmlProperty(isAttribute = true, localName = "min")
|
@JacksonXmlProperty(isAttribute = true, localName = "min")
|
||||||
public int getMinTalkgroup()
|
public int getMinTalkgroup()
|
||||||
{
|
{
|
||||||
@@ -61,6 +67,7 @@ public class TalkgroupRange extends AliasID
|
|||||||
public void setMinTalkgroup(int minTalkgroup)
|
public void setMinTalkgroup(int minTalkgroup)
|
||||||
{
|
{
|
||||||
mMinTalkgroup = minTalkgroup;
|
mMinTalkgroup = minTalkgroup;
|
||||||
|
updateValueProperty();
|
||||||
}
|
}
|
||||||
|
|
||||||
@JacksonXmlProperty(isAttribute = true, localName = "max")
|
@JacksonXmlProperty(isAttribute = true, localName = "max")
|
||||||
@@ -72,6 +79,7 @@ public class TalkgroupRange extends AliasID
|
|||||||
public void setMaxTalkgroup(int maxTalkgroup)
|
public void setMaxTalkgroup(int maxTalkgroup)
|
||||||
{
|
{
|
||||||
mMaxTalkgroup = maxTalkgroup;
|
mMaxTalkgroup = maxTalkgroup;
|
||||||
|
updateValueProperty();
|
||||||
}
|
}
|
||||||
|
|
||||||
@JacksonXmlProperty(isAttribute = true, localName = "protocol")
|
@JacksonXmlProperty(isAttribute = true, localName = "protocol")
|
||||||
@@ -83,6 +91,7 @@ public class TalkgroupRange extends AliasID
|
|||||||
public void setProtocol(Protocol protocol)
|
public void setProtocol(Protocol protocol)
|
||||||
{
|
{
|
||||||
mProtocol = protocol;
|
mProtocol = protocol;
|
||||||
|
updateValueProperty();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -97,7 +106,8 @@ public class TalkgroupRange extends AliasID
|
|||||||
return talkgroupFormat.getMinimumValidValue() <= mMinTalkgroup &&
|
return talkgroupFormat.getMinimumValidValue() <= mMinTalkgroup &&
|
||||||
mMinTalkgroup <= talkgroupFormat.getMaximumValidValue() &&
|
mMinTalkgroup <= talkgroupFormat.getMaximumValidValue() &&
|
||||||
talkgroupFormat.getMinimumValidValue() <= mMaxTalkgroup &&
|
talkgroupFormat.getMinimumValidValue() <= mMaxTalkgroup &&
|
||||||
mMaxTalkgroup <= talkgroupFormat.getMaximumValidValue();
|
mMaxTalkgroup <= talkgroupFormat.getMaximumValidValue() &&
|
||||||
|
mMinTalkgroup < mMaxTalkgroup;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String toString()
|
public String toString()
|
||||||
@@ -140,6 +150,12 @@ public class TalkgroupRange extends AliasID
|
|||||||
return getMinTalkgroup() <= talkgroupValue && talkgroupValue <= getMaxTalkgroup();
|
return getMinTalkgroup() <= talkgroupValue && talkgroupValue <= getMaxTalkgroup();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean overlaps(AliasID other)
|
||||||
|
{
|
||||||
|
return other instanceof TalkgroupRange && overlaps((TalkgroupRange)other);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Indicates if this talkgroup range overlaps the talkgroup range argument.
|
* Indicates if this talkgroup range overlaps the talkgroup range argument.
|
||||||
* @param talkgroupRange to check for overlap
|
* @param talkgroupRange to check for overlap
|
||||||
@@ -161,4 +177,29 @@ public class TalkgroupRange extends AliasID
|
|||||||
{
|
{
|
||||||
return AliasIDType.TALKGROUP_RANGE;
|
return AliasIDType.TALKGROUP_RANGE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int compareTo(TalkgroupRange other)
|
||||||
|
{
|
||||||
|
if(other == null)
|
||||||
|
{
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(getProtocol().equals(other.getProtocol()))
|
||||||
|
{
|
||||||
|
if(getMinTalkgroup() == other.getMinTalkgroup())
|
||||||
|
{
|
||||||
|
return Integer.compare(getMaxTalkgroup(), other.getMaxTalkgroup());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return Integer.compare(getMinTalkgroup(), other.getMinTalkgroup());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return getProtocol().compareTo(other.getProtocol());
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,271 +0,0 @@
|
|||||||
/*
|
|
||||||
* ******************************************************************************
|
|
||||||
* sdrtrunk
|
|
||||||
* Copyright (C) 2014-2018 Dennis Sheirer
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
|
||||||
* *****************************************************************************
|
|
||||||
*/
|
|
||||||
package io.github.dsheirer.alias.id.talkgroup;
|
|
||||||
|
|
||||||
import io.github.dsheirer.alias.id.AliasID;
|
|
||||||
import io.github.dsheirer.gui.editor.DocumentListenerEditor;
|
|
||||||
import io.github.dsheirer.protocol.Protocol;
|
|
||||||
import net.miginfocom.swing.MigLayout;
|
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
|
|
||||||
import javax.swing.DefaultComboBoxModel;
|
|
||||||
import javax.swing.JComboBox;
|
|
||||||
import javax.swing.JFormattedTextField;
|
|
||||||
import javax.swing.JLabel;
|
|
||||||
import javax.swing.JOptionPane;
|
|
||||||
import javax.swing.text.MaskFormatter;
|
|
||||||
import java.awt.Color;
|
|
||||||
import java.awt.Cursor;
|
|
||||||
import java.awt.event.ActionEvent;
|
|
||||||
import java.awt.event.ActionListener;
|
|
||||||
import java.awt.event.MouseAdapter;
|
|
||||||
import java.awt.event.MouseEvent;
|
|
||||||
import java.text.ParseException;
|
|
||||||
|
|
||||||
public class TalkgroupRangeEditor extends DocumentListenerEditor<AliasID>
|
|
||||||
{
|
|
||||||
private final static Logger mLog = LoggerFactory.getLogger(TalkgroupRangeEditor.class);
|
|
||||||
private static final long serialVersionUID = 1L;
|
|
||||||
|
|
||||||
private static final String VALID_CHARACTERS_FOR_ASTERISK_MASKS = "0123456789 ";
|
|
||||||
private MaskFormatter mMaskFormatter = new MaskFormatter();
|
|
||||||
private JComboBox<Protocol> mComboProtocol;
|
|
||||||
private JFormattedTextField mMinTalkgroupField;
|
|
||||||
private JFormattedTextField mMaxTalkgroupField;
|
|
||||||
|
|
||||||
public TalkgroupRangeEditor(AliasID aliasID)
|
|
||||||
{
|
|
||||||
initGUI();
|
|
||||||
|
|
||||||
setItem(aliasID);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void initGUI()
|
|
||||||
{
|
|
||||||
setLayout(new MigLayout("fill,wrap 2", "[right][left]", "[][]"));
|
|
||||||
|
|
||||||
add(new JLabel("Protocol:"));
|
|
||||||
|
|
||||||
mComboProtocol = new JComboBox<Protocol>();
|
|
||||||
|
|
||||||
DefaultComboBoxModel<Protocol> model = new DefaultComboBoxModel<>();
|
|
||||||
|
|
||||||
for(Protocol protocol : Protocol.TALKGROUP_PROTOCOLS)
|
|
||||||
{
|
|
||||||
model.addElement(protocol);
|
|
||||||
}
|
|
||||||
|
|
||||||
mComboProtocol.setModel(model);
|
|
||||||
mComboProtocol.addActionListener(new ActionListener()
|
|
||||||
{
|
|
||||||
@Override
|
|
||||||
public void actionPerformed(ActionEvent e)
|
|
||||||
{
|
|
||||||
Protocol protocol = (Protocol)mComboProtocol.getSelectedItem();
|
|
||||||
updateEditor(protocol);
|
|
||||||
setModified(true);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
add(mComboProtocol);
|
|
||||||
|
|
||||||
add(new JLabel("Min:"));
|
|
||||||
|
|
||||||
mMinTalkgroupField = new JFormattedTextField(mMaskFormatter);
|
|
||||||
mMinTalkgroupField.getDocument().addDocumentListener(this);
|
|
||||||
add(mMinTalkgroupField, "growx,push");
|
|
||||||
|
|
||||||
add(new JLabel("Max:"));
|
|
||||||
|
|
||||||
mMaxTalkgroupField = new JFormattedTextField(mMaskFormatter);
|
|
||||||
mMaxTalkgroupField.getDocument().addDocumentListener(this);
|
|
||||||
add(mMaxTalkgroupField, "growx,push");
|
|
||||||
|
|
||||||
JLabel help = new JLabel("Help ...");
|
|
||||||
help.setForeground(Color.BLUE.brighter());
|
|
||||||
help.setCursor(new Cursor(Cursor.HAND_CURSOR));
|
|
||||||
help.addMouseListener(new MouseAdapter()
|
|
||||||
{
|
|
||||||
@Override
|
|
||||||
public void mouseClicked(MouseEvent e)
|
|
||||||
{
|
|
||||||
TalkgroupFormat talkgroupFormat = TalkgroupFormat.get(getCurrentProtocol());
|
|
||||||
JOptionPane.showMessageDialog(TalkgroupRangeEditor.this,
|
|
||||||
talkgroupFormat.getValidRangeHelpText(), "Help", JOptionPane.INFORMATION_MESSAGE);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
add(help, "align left");
|
|
||||||
}
|
|
||||||
|
|
||||||
private void updateEditor(Protocol protocol)
|
|
||||||
{
|
|
||||||
TalkgroupFormat mask = TalkgroupFormat.get(protocol);
|
|
||||||
int currentMinValue = 0;
|
|
||||||
int currentMaxValue = 0;
|
|
||||||
TalkgroupRange talkgroupRange = getTalkgroupRange();
|
|
||||||
|
|
||||||
if(talkgroupRange != null)
|
|
||||||
{
|
|
||||||
currentMinValue = talkgroupRange.getMinTalkgroup();
|
|
||||||
currentMaxValue = talkgroupRange.getMaxTalkgroup();
|
|
||||||
}
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
mMinTalkgroupField.setValue(null);
|
|
||||||
mMaxTalkgroupField.setValue(null);
|
|
||||||
|
|
||||||
mMinTalkgroupField.setToolTipText(mask.getValidRangeHelpText());
|
|
||||||
mMaxTalkgroupField.setToolTipText(mask.getValidRangeHelpText());
|
|
||||||
mMaskFormatter.setMask(mask.getMask());
|
|
||||||
if(mask.getMask().contains("*"))
|
|
||||||
{
|
|
||||||
mMaskFormatter.setValidCharacters(VALID_CHARACTERS_FOR_ASTERISK_MASKS);
|
|
||||||
}
|
|
||||||
|
|
||||||
mMinTalkgroupField.setValue(TalkgroupFormatter.format(protocol, currentMinValue));
|
|
||||||
mMaxTalkgroupField.setValue(TalkgroupFormatter.format(protocol, currentMaxValue));
|
|
||||||
}
|
|
||||||
catch(ParseException pe)
|
|
||||||
{
|
|
||||||
mLog.error("Error applying talkgroup editor mask to mask formatter [" + mask.getMask() + "]");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private Protocol getCurrentProtocol()
|
|
||||||
{
|
|
||||||
return mComboProtocol.getItemAt(mComboProtocol.getSelectedIndex());
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public TalkgroupRange getTalkgroupRange()
|
|
||||||
{
|
|
||||||
if(getItem() instanceof TalkgroupRange)
|
|
||||||
{
|
|
||||||
return (TalkgroupRange)getItem();
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setItem(AliasID aliasID)
|
|
||||||
{
|
|
||||||
super.setItem(aliasID);
|
|
||||||
|
|
||||||
TalkgroupRange talkgroupRange = getTalkgroupRange();
|
|
||||||
|
|
||||||
updateEditor(talkgroupRange.getProtocol());
|
|
||||||
|
|
||||||
if(talkgroupRange != null)
|
|
||||||
{
|
|
||||||
mComboProtocol.getModel().setSelectedItem(talkgroupRange.getProtocol());
|
|
||||||
String minFormatted = TalkgroupFormatter.format(talkgroupRange.getProtocol(), talkgroupRange.getMinTalkgroup());
|
|
||||||
mMinTalkgroupField.setValue(minFormatted);
|
|
||||||
String maxFormatted = TalkgroupFormatter.format(talkgroupRange.getProtocol(), talkgroupRange.getMaxTalkgroup());
|
|
||||||
mMaxTalkgroupField.setValue(maxFormatted);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
mComboProtocol.getModel().setSelectedItem(Protocol.UNKNOWN);
|
|
||||||
mMinTalkgroupField.setValue(TalkgroupFormatter.format(getCurrentProtocol(), 0));
|
|
||||||
mMaxTalkgroupField.setValue(TalkgroupFormatter.format(getCurrentProtocol(), 0));
|
|
||||||
}
|
|
||||||
|
|
||||||
setModified(false);
|
|
||||||
|
|
||||||
repaint();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void save()
|
|
||||||
{
|
|
||||||
TalkgroupRange talkgroupRange = getTalkgroupRange();
|
|
||||||
|
|
||||||
if(talkgroupRange != null)
|
|
||||||
{
|
|
||||||
Protocol protocol = mComboProtocol.getItemAt(mComboProtocol.getSelectedIndex());
|
|
||||||
|
|
||||||
if(protocol == null)
|
|
||||||
{
|
|
||||||
protocol = Protocol.UNKNOWN;
|
|
||||||
}
|
|
||||||
|
|
||||||
talkgroupRange.setProtocol(protocol);
|
|
||||||
|
|
||||||
int minValue = -1;
|
|
||||||
int maxValue = -1;
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
minValue = TalkgroupFormatter.parse(protocol, mMinTalkgroupField.getText());
|
|
||||||
}
|
|
||||||
catch(ParseException pe)
|
|
||||||
{
|
|
||||||
//ignore ... value is still -1 and outside valid value range
|
|
||||||
}
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
maxValue = TalkgroupFormatter.parse(protocol, mMaxTalkgroupField.getText());
|
|
||||||
}
|
|
||||||
catch(ParseException pe)
|
|
||||||
{
|
|
||||||
//ignore ... value is still -1 and outside valid value range
|
|
||||||
}
|
|
||||||
|
|
||||||
TalkgroupFormat mask = TalkgroupFormat.get(protocol);
|
|
||||||
|
|
||||||
//Check for valid value within range ... notify user but allow value to persist
|
|
||||||
if(minValue < mask.getMinimumValidValue() || minValue > mask.getMaximumValidValue())
|
|
||||||
{
|
|
||||||
String message = "Invalid minimum value [" + mMinTalkgroupField.getText() + "]. " + protocol.name() +
|
|
||||||
" valid range is [" + mask.getValidRangeDescription() + "]";
|
|
||||||
|
|
||||||
JOptionPane.showMessageDialog(TalkgroupRangeEditor.this, message,
|
|
||||||
"Invalid Talkgroup Value", JOptionPane.ERROR_MESSAGE);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
talkgroupRange.setMinTalkgroup(minValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
//Check for valid value within range ... notify user but allow value to persist
|
|
||||||
if(maxValue < mask.getMinimumValidValue() || maxValue > mask.getMaximumValidValue())
|
|
||||||
{
|
|
||||||
String message = "Invalid maximum value [" + mMaxTalkgroupField.getText() + "]. " + protocol.name() +
|
|
||||||
" valid range is [" + mask.getValidRangeDescription() + "]";
|
|
||||||
|
|
||||||
JOptionPane.showMessageDialog(TalkgroupRangeEditor.this, message,
|
|
||||||
"Invalid Talkgroup Value", JOptionPane.ERROR_MESSAGE);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
talkgroupRange.setMaxTalkgroup(maxValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
talkgroupRange.setProtocol(protocol);
|
|
||||||
}
|
|
||||||
|
|
||||||
setModified(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
150
src/main/java/io/github/dsheirer/alias/id/tone/TonesID.java
Normal file
150
src/main/java/io/github/dsheirer/alias/id/tone/TonesID.java
Normal file
@@ -0,0 +1,150 @@
|
|||||||
|
/*
|
||||||
|
* *****************************************************************************
|
||||||
|
* Copyright (C) 2014-2020 Dennis Sheirer
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||||
|
* ****************************************************************************
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.github.dsheirer.alias.id.tone;
|
||||||
|
|
||||||
|
import io.github.dsheirer.alias.id.AliasID;
|
||||||
|
import io.github.dsheirer.alias.id.AliasIDType;
|
||||||
|
import io.github.dsheirer.identifier.tone.Tone;
|
||||||
|
import io.github.dsheirer.identifier.tone.ToneSequence;
|
||||||
|
import javafx.collections.ListChangeListener;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tone sequence alias identifier. This is used for matching tone sequences produced by the AMBE audio CODEC. Some
|
||||||
|
* examples include DTMF dialed numbers, KNOX tones, 2-Tone, Tone-outs, etc.
|
||||||
|
*/
|
||||||
|
public class TonesID extends AliasID implements ListChangeListener<Tone>
|
||||||
|
{
|
||||||
|
private ToneSequence mToneSequence;
|
||||||
|
|
||||||
|
public TonesID()
|
||||||
|
{
|
||||||
|
setToneSequence(new ToneSequence());
|
||||||
|
//Empty serialization constructor
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs an instance with the specified tone sequence
|
||||||
|
*/
|
||||||
|
public TonesID(ToneSequence toneSequence)
|
||||||
|
{
|
||||||
|
setToneSequence(toneSequence);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AliasIDType getType()
|
||||||
|
{
|
||||||
|
return AliasIDType.TONES;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* List of tones that define this identifier
|
||||||
|
*/
|
||||||
|
|
||||||
|
public ToneSequence getToneSequence()
|
||||||
|
{
|
||||||
|
return mToneSequence;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the tone sequence(s) for this ID
|
||||||
|
*/
|
||||||
|
public void setToneSequence(ToneSequence toneSequence)
|
||||||
|
{
|
||||||
|
if(mToneSequence != null)
|
||||||
|
{
|
||||||
|
mToneSequence.tonesProperty().removeListener(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
mToneSequence = toneSequence;
|
||||||
|
mToneSequence.tonesProperty().addListener(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicates if the other id is a tones identifier and has the exact same sequence of tones. However, it does not
|
||||||
|
* compare each of the tone duration value.
|
||||||
|
* @param id to check for match
|
||||||
|
* @return true if the identifiers match
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public boolean matches(AliasID id)
|
||||||
|
{
|
||||||
|
boolean match = true;
|
||||||
|
|
||||||
|
if(mToneSequence.getTones().isEmpty() || !(id instanceof TonesID))
|
||||||
|
{
|
||||||
|
match = false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
List<Tone> otherTones = ((TonesID)id).getToneSequence().getTones();
|
||||||
|
List<Tone> thisTones = mToneSequence.getTones();
|
||||||
|
|
||||||
|
if(thisTones.size() == otherTones.size())
|
||||||
|
{
|
||||||
|
for(int x = 0; x < thisTones.size(); x++)
|
||||||
|
{
|
||||||
|
if(thisTones.get(x).getAmbeTone() != otherTones.get(x).getAmbeTone())
|
||||||
|
{
|
||||||
|
match = false;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
match = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return match;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isValid()
|
||||||
|
{
|
||||||
|
return getToneSequence() != null && getToneSequence().hasTones();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isAudioIdentifier()
|
||||||
|
{
|
||||||
|
//This is not an audio identifier in the context that this method is used for.
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString()
|
||||||
|
{
|
||||||
|
if(getToneSequence() != null)
|
||||||
|
{
|
||||||
|
return "Tones: " + getToneSequence().toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
return "Tones: (empty)";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onChanged(Change<? extends Tone> c)
|
||||||
|
{
|
||||||
|
updateValueProperty();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -125,8 +125,15 @@ public abstract class AbstractAudioModule extends Module implements IAudioSegmen
|
|||||||
audioSegment.linkTo(previous);
|
audioSegment.linkTo(previous);
|
||||||
}
|
}
|
||||||
|
|
||||||
audioSegment.addAudio(audioBuffer);
|
try
|
||||||
mAudioSampleCount += audioBuffer.length;
|
{
|
||||||
|
audioSegment.addAudio(audioBuffer);
|
||||||
|
mAudioSampleCount += audioBuffer.length;
|
||||||
|
}
|
||||||
|
catch(Exception e)
|
||||||
|
{
|
||||||
|
closeAudioSegment();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -384,10 +384,7 @@ public class AudioSegment implements Listener<IdentifierUpdateNotification>
|
|||||||
}
|
}
|
||||||
|
|
||||||
//Add all broadcast channels for the alias ... let the set handle duplication.
|
//Add all broadcast channels for the alias ... let the set handle duplication.
|
||||||
for(BroadcastChannel broadcastChannel: alias.getBroadcastChannels())
|
mBroadcastChannels.addAll(alias.getBroadcastChannels());
|
||||||
{
|
|
||||||
mBroadcastChannels.add(broadcastChannel);
|
|
||||||
}
|
|
||||||
|
|
||||||
//Only assign a playback priority if it is lower priority than the current setting.
|
//Only assign a playback priority if it is lower priority than the current setting.
|
||||||
int playbackPriority = alias.getPlaybackPriority();
|
int playbackPriority = alias.getPlaybackPriority();
|
||||||
|
|||||||
@@ -0,0 +1,177 @@
|
|||||||
|
/*
|
||||||
|
* *****************************************************************************
|
||||||
|
* Copyright (C) 2014-2020 Dennis Sheirer
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||||
|
* ****************************************************************************
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.github.dsheirer.audio.broadcast;
|
||||||
|
|
||||||
|
import io.github.dsheirer.sample.Listener;
|
||||||
|
import javafx.beans.property.ObjectProperty;
|
||||||
|
import javafx.beans.property.SimpleObjectProperty;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Base Audio Broadcaster class
|
||||||
|
*/
|
||||||
|
public abstract class AbstractAudioBroadcaster<T extends BroadcastConfiguration> implements Listener<AudioRecording>
|
||||||
|
{
|
||||||
|
private final static Logger mLog = LoggerFactory.getLogger(AbstractAudioBroadcaster.class);
|
||||||
|
private Listener<BroadcastEvent> mBroadcastEventListener;
|
||||||
|
private T mBroadcastConfiguration;
|
||||||
|
protected ObjectProperty<BroadcastState> mBroadcastState = new SimpleObjectProperty<>(BroadcastState.READY);
|
||||||
|
protected int mStreamedAudioCount = 0;
|
||||||
|
protected int mErrorAudioCount = 0;
|
||||||
|
protected int mAgedOffAudioCount = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs an instance
|
||||||
|
* @param broadcastConfiguration to use for this broadcaster
|
||||||
|
*/
|
||||||
|
public AbstractAudioBroadcaster(T broadcastConfiguration)
|
||||||
|
{
|
||||||
|
mBroadcastConfiguration = broadcastConfiguration;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Observable broadcast state property
|
||||||
|
*/
|
||||||
|
public ObjectProperty<BroadcastState> broadcastStateProperty()
|
||||||
|
{
|
||||||
|
return mBroadcastState;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Current state of the broadcastAudio connection
|
||||||
|
*/
|
||||||
|
public BroadcastState getBroadcastState()
|
||||||
|
{
|
||||||
|
return mBroadcastState.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets or changes the broadcast state
|
||||||
|
*/
|
||||||
|
public void setBroadcastState(BroadcastState broadcastState)
|
||||||
|
{
|
||||||
|
mBroadcastState.setValue(broadcastState);
|
||||||
|
broadcast(new BroadcastEvent(this, BroadcastEvent.Event.BROADCASTER_STATE_CHANGE));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Starts the broadcaster
|
||||||
|
*/
|
||||||
|
public abstract void start();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stops the broadcaster
|
||||||
|
*/
|
||||||
|
public abstract void stop();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Perform any shutdown related tasks.
|
||||||
|
*/
|
||||||
|
public abstract void dispose();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Access the configuration for the broadcaster
|
||||||
|
*/
|
||||||
|
public T getBroadcastConfiguration()
|
||||||
|
{
|
||||||
|
return mBroadcastConfiguration;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers the listener to receive broadcast events/state changes
|
||||||
|
*/
|
||||||
|
public void setListener(Listener<BroadcastEvent> listener)
|
||||||
|
{
|
||||||
|
mBroadcastEventListener = listener;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes the listener from receiving broadcast events/state changes
|
||||||
|
*/
|
||||||
|
public void removeListener()
|
||||||
|
{
|
||||||
|
mBroadcastEventListener = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Broadcasts the event to any registered listener
|
||||||
|
*/
|
||||||
|
protected void broadcast(BroadcastEvent event)
|
||||||
|
{
|
||||||
|
if(mBroadcastEventListener != null)
|
||||||
|
{
|
||||||
|
mBroadcastEventListener.receive(event);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Number of audio recordings streamed to remote server
|
||||||
|
*/
|
||||||
|
public int getStreamedAudioCount()
|
||||||
|
{
|
||||||
|
return mStreamedAudioCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Increments the streamed or uploaded audio count by one
|
||||||
|
*/
|
||||||
|
public void incrementStreamedAudioCount()
|
||||||
|
{
|
||||||
|
mStreamedAudioCount++;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Number of audio recordings that were removed for exceeding age limit
|
||||||
|
*/
|
||||||
|
public int getAgedOffAudioCount()
|
||||||
|
{
|
||||||
|
return mAgedOffAudioCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Increments the aged-off audio count by one
|
||||||
|
*/
|
||||||
|
public void incrementAgedOffAudioCount()
|
||||||
|
{
|
||||||
|
mAgedOffAudioCount++;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Number of audio recordings awaiting streaming or upload
|
||||||
|
*/
|
||||||
|
public abstract int getAudioQueueSize();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Total audio upload/stream error count
|
||||||
|
*/
|
||||||
|
public int getAudioErrorCount()
|
||||||
|
{
|
||||||
|
return mErrorAudioCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Increments the error audio count by one
|
||||||
|
*/
|
||||||
|
public void incrementErrorAudioCount()
|
||||||
|
{
|
||||||
|
mErrorAudioCount++;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -138,4 +138,10 @@ public class AudioRecording implements Comparable<AudioRecording>
|
|||||||
{
|
{
|
||||||
return mPendingReplayCount.get() > 0;
|
return mPendingReplayCount.get() > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
if (!(o instanceof AudioRecording)) return false;
|
||||||
|
return compareTo((AudioRecording) o) == 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,8 +23,8 @@ package io.github.dsheirer.audio.broadcast;
|
|||||||
|
|
||||||
import io.github.dsheirer.audio.convert.ISilenceGenerator;
|
import io.github.dsheirer.audio.convert.ISilenceGenerator;
|
||||||
import io.github.dsheirer.identifier.IdentifierCollection;
|
import io.github.dsheirer.identifier.IdentifierCollection;
|
||||||
import io.github.dsheirer.sample.Listener;
|
|
||||||
import io.github.dsheirer.util.ThreadPool;
|
import io.github.dsheirer.util.ThreadPool;
|
||||||
|
import org.apache.commons.math3.util.FastMath;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
@@ -37,25 +37,17 @@ import java.util.concurrent.ScheduledFuture;
|
|||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
import java.util.concurrent.atomic.AtomicBoolean;
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
|
|
||||||
public abstract class AudioBroadcaster implements Listener<AudioRecording>
|
public abstract class AudioStreamingBroadcaster<T extends BroadcastConfiguration> extends AbstractAudioBroadcaster<T>
|
||||||
{
|
{
|
||||||
private final static Logger mLog = LoggerFactory.getLogger(AudioBroadcaster.class);
|
private final static Logger mLog = LoggerFactory.getLogger(AudioStreamingBroadcaster.class);
|
||||||
|
|
||||||
public static final int PROCESSOR_RUN_INTERVAL_MS = 1000;
|
public static final int PROCESSOR_RUN_INTERVAL_MS = 1000;
|
||||||
|
|
||||||
private ScheduledFuture mRecordingQueueProcessorFuture;
|
private ScheduledFuture mRecordingQueueProcessorFuture;
|
||||||
|
|
||||||
private RecordingQueueProcessor mRecordingQueueProcessor = new RecordingQueueProcessor();
|
private RecordingQueueProcessor mRecordingQueueProcessor = new RecordingQueueProcessor();
|
||||||
private Queue<AudioRecording> mAudioRecordingQueue = new LinkedTransferQueue<>();
|
private Queue<AudioRecording> mAudioRecordingQueue = new LinkedTransferQueue<>();
|
||||||
|
|
||||||
private ISilenceGenerator mSilenceGenerator;
|
private ISilenceGenerator mSilenceGenerator;
|
||||||
|
|
||||||
private Listener<BroadcastEvent> mBroadcastEventListener;
|
|
||||||
private BroadcastState mBroadcastState = BroadcastState.READY;
|
|
||||||
|
|
||||||
private int mStreamedAudioCount = 0;
|
|
||||||
private int mAgedOffAudioCount = 0;
|
|
||||||
private BroadcastConfiguration mBroadcastConfiguration;
|
|
||||||
private long mDelay;
|
private long mDelay;
|
||||||
private long mMaximumRecordingAge;
|
private long mMaximumRecordingAge;
|
||||||
private AtomicBoolean mStreaming = new AtomicBoolean();
|
private AtomicBoolean mStreaming = new AtomicBoolean();
|
||||||
@@ -81,14 +73,18 @@ public abstract class AudioBroadcaster implements Listener<AudioRecording>
|
|||||||
* The last audio packet's metadata is automatically attached to the closed audio recording when it is enqueued for
|
* The last audio packet's metadata is automatically attached to the closed audio recording when it is enqueued for
|
||||||
* broadcast. That metadata will be updated on the remote server once the audio recording is opened for streaming.
|
* broadcast. That metadata will be updated on the remote server once the audio recording is opened for streaming.
|
||||||
*/
|
*/
|
||||||
public AudioBroadcaster(BroadcastConfiguration broadcastConfiguration)
|
public AudioStreamingBroadcaster(T broadcastConfiguration)
|
||||||
{
|
{
|
||||||
mBroadcastConfiguration = broadcastConfiguration;
|
super(broadcastConfiguration);
|
||||||
mDelay = mBroadcastConfiguration.getDelay();
|
mDelay = getBroadcastConfiguration().getDelay();
|
||||||
mMaximumRecordingAge = mBroadcastConfiguration.getMaximumRecordingAge();
|
mMaximumRecordingAge = getBroadcastConfiguration().getMaximumRecordingAge();
|
||||||
mSilenceGenerator = BroadcastFactory.getSilenceGenerator(broadcastConfiguration.getBroadcastFormat());
|
mSilenceGenerator = BroadcastFactory.getSilenceGenerator(broadcastConfiguration.getBroadcastFormat());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void dispose()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Broadcast binary audio data frames or sequences.
|
* Broadcast binary audio data frames or sequences.
|
||||||
*/
|
*/
|
||||||
@@ -171,27 +167,11 @@ public abstract class AudioBroadcaster implements Listener<AudioRecording>
|
|||||||
/**
|
/**
|
||||||
* Size of recording queue for recordings awaiting streaming
|
* Size of recording queue for recordings awaiting streaming
|
||||||
*/
|
*/
|
||||||
public int getQueueSize()
|
public int getAudioQueueSize()
|
||||||
{
|
{
|
||||||
return mAudioRecordingQueue.size();
|
return mAudioRecordingQueue.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Number of audio recordings streamed to remote server
|
|
||||||
*/
|
|
||||||
public int getStreamedAudioCount()
|
|
||||||
{
|
|
||||||
return mStreamedAudioCount;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Number of audio recordings that were removed for exceeding age limit
|
|
||||||
*/
|
|
||||||
public int getAgedOffAudioCount()
|
|
||||||
{
|
|
||||||
return mAgedOffAudioCount;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Primary insert method for the stream manager to nominate completed audio recordings for broadcast.
|
* Primary insert method for the stream manager to nominate completed audio recordings for broadcast.
|
||||||
*
|
*
|
||||||
@@ -210,58 +190,21 @@ public abstract class AudioBroadcaster implements Listener<AudioRecording>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Broadcast configuration used by this broadcaster
|
|
||||||
*/
|
|
||||||
public BroadcastConfiguration getBroadcastConfiguration()
|
|
||||||
{
|
|
||||||
return mBroadcastConfiguration;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Registers the listener to receive broadcast events/state changes
|
|
||||||
*/
|
|
||||||
public void setListener(Listener<BroadcastEvent> listener)
|
|
||||||
{
|
|
||||||
mBroadcastEventListener = listener;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Removes the listener from receiving broadcast events/state changes
|
|
||||||
*/
|
|
||||||
public void removeListener()
|
|
||||||
{
|
|
||||||
mBroadcastEventListener = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Broadcasts the event to any registered listener
|
|
||||||
*/
|
|
||||||
public void broadcast(BroadcastEvent event)
|
|
||||||
{
|
|
||||||
if(mBroadcastEventListener != null)
|
|
||||||
{
|
|
||||||
mBroadcastEventListener.receive(event);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the state of the broadcastAudio connection
|
* Sets the state of the broadcastAudio connection
|
||||||
*/
|
*/
|
||||||
protected void setBroadcastState(BroadcastState state)
|
public void setBroadcastState(BroadcastState state)
|
||||||
{
|
{
|
||||||
if(mBroadcastState != state)
|
if(mBroadcastState.get() != state)
|
||||||
{
|
{
|
||||||
if(state == BroadcastState.CONNECTED || state == BroadcastState.DISCONNECTED)
|
if(state == BroadcastState.CONNECTED || state == BroadcastState.DISCONNECTED)
|
||||||
{
|
{
|
||||||
mLog.info("[" + getStreamName() + "] status: " + state);
|
mLog.info("[" + getStreamName() + "] status: " + state);
|
||||||
}
|
}
|
||||||
|
|
||||||
mBroadcastState = state;
|
super.setBroadcastState(state);
|
||||||
|
|
||||||
broadcast(new BroadcastEvent(this, BroadcastEvent.Event.BROADCASTER_STATE_CHANGE));
|
if(mBroadcastState.get() instanceof BroadcastState && ((BroadcastState)mBroadcastState.get()).isErrorState())
|
||||||
|
|
||||||
if(mBroadcastState.isErrorState())
|
|
||||||
{
|
{
|
||||||
stop();
|
stop();
|
||||||
}
|
}
|
||||||
@@ -285,14 +228,6 @@ public abstract class AudioBroadcaster implements Listener<AudioRecording>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Current state of the broadcastAudio connection
|
|
||||||
*/
|
|
||||||
public BroadcastState getBroadcastState()
|
|
||||||
{
|
|
||||||
return mBroadcastState;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Indicates if the broadcaster is currently connected to the remote server
|
* Indicates if the broadcaster is currently connected to the remote server
|
||||||
*/
|
*/
|
||||||
@@ -360,7 +295,7 @@ public abstract class AudioBroadcaster implements Listener<AudioRecording>
|
|||||||
//Trim length to whole-frame intervals (144 byte frame)
|
//Trim length to whole-frame intervals (144 byte frame)
|
||||||
bytesToStream -= (bytesToStream % 144);
|
bytesToStream -= (bytesToStream % 144);
|
||||||
|
|
||||||
int length = Math.min(bytesToStream, mInputStream.available());
|
int length = FastMath.min(bytesToStream, mInputStream.available());
|
||||||
|
|
||||||
byte[] audio = new byte[length];
|
byte[] audio = new byte[length];
|
||||||
|
|
||||||
@@ -402,7 +337,7 @@ public abstract class AudioBroadcaster implements Listener<AudioRecording>
|
|||||||
if(mInputStream != null)
|
if(mInputStream != null)
|
||||||
{
|
{
|
||||||
mStreamedAudioCount++;
|
mStreamedAudioCount++;
|
||||||
broadcast(new BroadcastEvent(AudioBroadcaster.this,
|
broadcast(new BroadcastEvent(AudioStreamingBroadcaster.this,
|
||||||
BroadcastEvent.Event.BROADCASTER_STREAMED_COUNT_CHANGE));
|
BroadcastEvent.Event.BROADCASTER_STREAMED_COUNT_CHANGE));
|
||||||
metadataUpdateRequired = true;
|
metadataUpdateRequired = true;
|
||||||
}
|
}
|
||||||
@@ -420,7 +355,7 @@ public abstract class AudioBroadcaster implements Listener<AudioRecording>
|
|||||||
nextRecording = mAudioRecordingQueue.remove();
|
nextRecording = mAudioRecordingQueue.remove();
|
||||||
nextRecording.removePendingReplay();
|
nextRecording.removePendingReplay();
|
||||||
mAgedOffAudioCount++;
|
mAgedOffAudioCount++;
|
||||||
broadcast(new BroadcastEvent(AudioBroadcaster.this,
|
broadcast(new BroadcastEvent(AudioStreamingBroadcaster.this,
|
||||||
BroadcastEvent.Event.BROADCASTER_AGED_OFF_COUNT_CHANGE));
|
BroadcastEvent.Event.BROADCASTER_AGED_OFF_COUNT_CHANGE));
|
||||||
nextRecording = mAudioRecordingQueue.peek();
|
nextRecording = mAudioRecordingQueue.peek();
|
||||||
}
|
}
|
||||||
@@ -467,7 +402,7 @@ public abstract class AudioBroadcaster implements Listener<AudioRecording>
|
|||||||
|
|
||||||
nextRecording.removePendingReplay();
|
nextRecording.removePendingReplay();
|
||||||
|
|
||||||
broadcast(new BroadcastEvent(AudioBroadcaster.this, BroadcastEvent.Event.BROADCASTER_QUEUE_CHANGE));
|
broadcast(new BroadcastEvent(AudioStreamingBroadcaster.this, BroadcastEvent.Event.BROADCASTER_QUEUE_CHANGE));
|
||||||
}
|
}
|
||||||
|
|
||||||
//If we closed out a recording and don't have a new/next recording, send an empty metadata update
|
//If we closed out a recording and don't have a new/next recording, send an empty metadata update
|
||||||
@@ -1,21 +1,24 @@
|
|||||||
/*******************************************************************************
|
/*
|
||||||
* sdrtrunk
|
|
||||||
* Copyright (C) 2014-2016 Dennis Sheirer
|
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* * ******************************************************************************
|
||||||
* it under the terms of the GNU General Public License as published by
|
* * Copyright (C) 2014-2020 Dennis Sheirer
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
* *
|
||||||
* (at your option) any later version.
|
* * This program is free software: you can redistribute it and/or modify
|
||||||
|
* * it under the terms of the GNU General Public License as published by
|
||||||
|
* * the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* * (at your option) any later version.
|
||||||
|
* *
|
||||||
|
* * This program is distributed in the hope that it will be useful,
|
||||||
|
* * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* * GNU General Public License for more details.
|
||||||
|
* *
|
||||||
|
* * You should have received a copy of the GNU General Public License
|
||||||
|
* * along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||||
|
* * *****************************************************************************
|
||||||
*
|
*
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
*
|
||||||
* You should have received a copy of the GNU General Public License
|
*/
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
|
||||||
*
|
|
||||||
******************************************************************************/
|
|
||||||
package io.github.dsheirer.audio.broadcast;
|
package io.github.dsheirer.audio.broadcast;
|
||||||
|
|
||||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||||
@@ -23,15 +26,29 @@ import com.fasterxml.jackson.annotation.JsonSubTypes;
|
|||||||
import com.fasterxml.jackson.annotation.JsonTypeInfo;
|
import com.fasterxml.jackson.annotation.JsonTypeInfo;
|
||||||
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
|
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
|
||||||
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlRootElement;
|
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlRootElement;
|
||||||
|
import io.github.dsheirer.audio.broadcast.broadcastify.BroadcastifyCallConfiguration;
|
||||||
import io.github.dsheirer.audio.broadcast.icecast.IcecastConfiguration;
|
import io.github.dsheirer.audio.broadcast.icecast.IcecastConfiguration;
|
||||||
import io.github.dsheirer.audio.broadcast.shoutcast.v1.ShoutcastV1Configuration;
|
import io.github.dsheirer.audio.broadcast.shoutcast.v1.ShoutcastV1Configuration;
|
||||||
import io.github.dsheirer.audio.broadcast.shoutcast.v2.ShoutcastV2Configuration;
|
import io.github.dsheirer.audio.broadcast.shoutcast.v2.ShoutcastV2Configuration;
|
||||||
|
import javafx.beans.Observable;
|
||||||
|
import javafx.beans.property.BooleanProperty;
|
||||||
|
import javafx.beans.property.IntegerProperty;
|
||||||
|
import javafx.beans.property.LongProperty;
|
||||||
|
import javafx.beans.property.SimpleBooleanProperty;
|
||||||
|
import javafx.beans.property.SimpleIntegerProperty;
|
||||||
|
import javafx.beans.property.SimpleLongProperty;
|
||||||
|
import javafx.beans.property.SimpleStringProperty;
|
||||||
|
import javafx.beans.property.StringProperty;
|
||||||
|
import javafx.util.Callback;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import java.net.InetSocketAddress;
|
import java.net.InetSocketAddress;
|
||||||
import java.net.SocketAddress;
|
import java.net.SocketAddress;
|
||||||
|
|
||||||
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "type")
|
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "type")
|
||||||
@JsonSubTypes({
|
@JsonSubTypes({
|
||||||
|
@JsonSubTypes.Type(value = BroadcastifyCallConfiguration.class, name="broadcastifyCallConfiguration"),
|
||||||
@JsonSubTypes.Type(value = IcecastConfiguration.class, name="icecastConfiguration"),
|
@JsonSubTypes.Type(value = IcecastConfiguration.class, name="icecastConfiguration"),
|
||||||
@JsonSubTypes.Type(value = ShoutcastV1Configuration.class, name="shoutcastV1Configuration"),
|
@JsonSubTypes.Type(value = ShoutcastV1Configuration.class, name="shoutcastV1Configuration"),
|
||||||
@JsonSubTypes.Type(value = ShoutcastV2Configuration.class, name="shoutcastV2Configuration"),
|
@JsonSubTypes.Type(value = ShoutcastV2Configuration.class, name="shoutcastV2Configuration"),
|
||||||
@@ -39,18 +56,33 @@ import java.net.SocketAddress;
|
|||||||
@JacksonXmlRootElement(localName = "stream")
|
@JacksonXmlRootElement(localName = "stream")
|
||||||
public abstract class BroadcastConfiguration
|
public abstract class BroadcastConfiguration
|
||||||
{
|
{
|
||||||
|
private final static Logger mLog = LoggerFactory.getLogger(BroadcastConfiguration.class);
|
||||||
|
// Static unique channel identifier tracking
|
||||||
|
private static int UNIQUE_ID = 0;
|
||||||
|
|
||||||
private BroadcastFormat mBroadcastFormat = BroadcastFormat.MP3;
|
private BroadcastFormat mBroadcastFormat = BroadcastFormat.MP3;
|
||||||
private String mName;
|
protected StringProperty mName = new SimpleStringProperty();
|
||||||
private String mHost;
|
protected StringProperty mHost = new SimpleStringProperty();
|
||||||
private int mPort;
|
protected IntegerProperty mPort = new SimpleIntegerProperty(80);
|
||||||
private String mPassword;
|
protected StringProperty mPassword = new SimpleStringProperty();
|
||||||
private long mDelay;
|
protected LongProperty mDelay = new SimpleLongProperty();
|
||||||
private long mMaximumRecordingAge = 10 * 60 * 1000; //10 minutes default
|
protected LongProperty mMaximumRecordingAge = new SimpleLongProperty(10 * 60 * 1000); //10 minutes default
|
||||||
private boolean mEnabled = true;
|
protected BooleanProperty mEnabled = new SimpleBooleanProperty(false);
|
||||||
|
protected BooleanProperty mValid = new SimpleBooleanProperty();
|
||||||
|
private int mId = ++UNIQUE_ID;
|
||||||
|
|
||||||
public BroadcastConfiguration()
|
public BroadcastConfiguration()
|
||||||
{
|
{
|
||||||
//No-arg constructor required for JAXB
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unique identifier for this configuration.
|
||||||
|
* Note: unique ids are generated at runtime and have no persistent context across sessions.
|
||||||
|
*/
|
||||||
|
@JsonIgnore
|
||||||
|
public int getId()
|
||||||
|
{
|
||||||
|
return mId;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -66,6 +98,70 @@ public abstract class BroadcastConfiguration
|
|||||||
mBroadcastFormat = format;
|
mBroadcastFormat = format;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stream name
|
||||||
|
*/
|
||||||
|
public StringProperty nameProperty()
|
||||||
|
{
|
||||||
|
return mName;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Server host name
|
||||||
|
*/
|
||||||
|
public StringProperty hostProperty()
|
||||||
|
{
|
||||||
|
return mHost;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Feed port
|
||||||
|
*/
|
||||||
|
public IntegerProperty portProperty()
|
||||||
|
{
|
||||||
|
return mPort;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Feed password
|
||||||
|
*/
|
||||||
|
public StringProperty passwordProperty()
|
||||||
|
{
|
||||||
|
return mPassword;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delay introduced for each recording before streaming
|
||||||
|
*/
|
||||||
|
public LongProperty delayProperty()
|
||||||
|
{
|
||||||
|
return mDelay;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Maximum allowable age of a recording to stream. Recordings that exceed this threshold will be aged off.
|
||||||
|
*/
|
||||||
|
public LongProperty maximumRecordingAgeProperty()
|
||||||
|
{
|
||||||
|
return mMaximumRecordingAge;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stream enabled property
|
||||||
|
*/
|
||||||
|
public BooleanProperty enabledProperty()
|
||||||
|
{
|
||||||
|
return mEnabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Configuration valid property
|
||||||
|
*/
|
||||||
|
public BooleanProperty validProperty()
|
||||||
|
{
|
||||||
|
return mValid;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Broadcast server type
|
* Broadcast server type
|
||||||
*/
|
*/
|
||||||
@@ -83,7 +179,7 @@ public abstract class BroadcastConfiguration
|
|||||||
@JacksonXmlProperty(isAttribute = true, localName = "name")
|
@JacksonXmlProperty(isAttribute = true, localName = "name")
|
||||||
public String getName()
|
public String getName()
|
||||||
{
|
{
|
||||||
return mName;
|
return mName.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -93,7 +189,7 @@ public abstract class BroadcastConfiguration
|
|||||||
*/
|
*/
|
||||||
public void setName(String name)
|
public void setName(String name)
|
||||||
{
|
{
|
||||||
mName = name;
|
mName.set(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -110,7 +206,7 @@ public abstract class BroadcastConfiguration
|
|||||||
@JacksonXmlProperty(isAttribute = true, localName = "host")
|
@JacksonXmlProperty(isAttribute = true, localName = "host")
|
||||||
public String getHost()
|
public String getHost()
|
||||||
{
|
{
|
||||||
return mHost;
|
return mHost.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -120,7 +216,7 @@ public abstract class BroadcastConfiguration
|
|||||||
*/
|
*/
|
||||||
public void setHost(String host)
|
public void setHost(String host)
|
||||||
{
|
{
|
||||||
mHost = host;
|
mHost.set(host);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -137,7 +233,7 @@ public abstract class BroadcastConfiguration
|
|||||||
@JacksonXmlProperty(isAttribute = true, localName = "port")
|
@JacksonXmlProperty(isAttribute = true, localName = "port")
|
||||||
public int getPort()
|
public int getPort()
|
||||||
{
|
{
|
||||||
return mPort;
|
return mPort.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -147,7 +243,7 @@ public abstract class BroadcastConfiguration
|
|||||||
*/
|
*/
|
||||||
public void setPort(int port)
|
public void setPort(int port)
|
||||||
{
|
{
|
||||||
mPort = port;
|
mPort.set(port);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -155,7 +251,7 @@ public abstract class BroadcastConfiguration
|
|||||||
*/
|
*/
|
||||||
public boolean hasPort()
|
public boolean hasPort()
|
||||||
{
|
{
|
||||||
return mPort > 0;
|
return mPort.get() > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@JsonIgnore
|
@JsonIgnore
|
||||||
@@ -170,7 +266,7 @@ public abstract class BroadcastConfiguration
|
|||||||
@JacksonXmlProperty(isAttribute = true, localName = "password")
|
@JacksonXmlProperty(isAttribute = true, localName = "password")
|
||||||
public String getPassword()
|
public String getPassword()
|
||||||
{
|
{
|
||||||
return mPassword;
|
return mPassword.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -180,7 +276,7 @@ public abstract class BroadcastConfiguration
|
|||||||
*/
|
*/
|
||||||
public void setPassword(String password)
|
public void setPassword(String password)
|
||||||
{
|
{
|
||||||
mPassword = password;
|
mPassword.set(password);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -211,7 +307,7 @@ public abstract class BroadcastConfiguration
|
|||||||
@JacksonXmlProperty(isAttribute = true, localName = "delay")
|
@JacksonXmlProperty(isAttribute = true, localName = "delay")
|
||||||
public long getDelay()
|
public long getDelay()
|
||||||
{
|
{
|
||||||
return mDelay;
|
return mDelay.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -220,7 +316,7 @@ public abstract class BroadcastConfiguration
|
|||||||
*/
|
*/
|
||||||
public void setDelay(long delay)
|
public void setDelay(long delay)
|
||||||
{
|
{
|
||||||
mDelay = delay;
|
mDelay.set(delay);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -231,7 +327,7 @@ public abstract class BroadcastConfiguration
|
|||||||
@JacksonXmlProperty(isAttribute = true, localName = "maximum_recording_age")
|
@JacksonXmlProperty(isAttribute = true, localName = "maximum_recording_age")
|
||||||
public long getMaximumRecordingAge()
|
public long getMaximumRecordingAge()
|
||||||
{
|
{
|
||||||
return mMaximumRecordingAge;
|
return mMaximumRecordingAge.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -241,7 +337,7 @@ public abstract class BroadcastConfiguration
|
|||||||
*/
|
*/
|
||||||
public void setMaximumRecordingAge(long age)
|
public void setMaximumRecordingAge(long age)
|
||||||
{
|
{
|
||||||
mMaximumRecordingAge = age;
|
mMaximumRecordingAge.set(age);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -250,12 +346,12 @@ public abstract class BroadcastConfiguration
|
|||||||
@JacksonXmlProperty(isAttribute = true, localName = "enabled")
|
@JacksonXmlProperty(isAttribute = true, localName = "enabled")
|
||||||
public boolean isEnabled()
|
public boolean isEnabled()
|
||||||
{
|
{
|
||||||
return mEnabled;
|
return mEnabled.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setEnabled(boolean enabled)
|
public void setEnabled(boolean enabled)
|
||||||
{
|
{
|
||||||
mEnabled = enabled;
|
mEnabled.set(enabled);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -302,6 +398,15 @@ public abstract class BroadcastConfiguration
|
|||||||
@JsonIgnore
|
@JsonIgnore
|
||||||
public boolean isValid()
|
public boolean isValid()
|
||||||
{
|
{
|
||||||
return mHost != null && mPort > 0;
|
return mValid.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates an observable property extractor for use with observable lists to detect changes internal to this object.
|
||||||
|
*/
|
||||||
|
public static Callback<BroadcastConfiguration, Observable[]> extractor()
|
||||||
|
{
|
||||||
|
return (BroadcastConfiguration b) -> new Observable[] {b.nameProperty(), b.hostProperty(), b.portProperty(),
|
||||||
|
b.passwordProperty(), b.maximumRecordingAgeProperty(), b.delayProperty(), b.enabledProperty()};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,126 +0,0 @@
|
|||||||
/*******************************************************************************
|
|
||||||
* sdrtrunk
|
|
||||||
* Copyright (C) 2014-2016 Dennis Sheirer
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
|
||||||
*
|
|
||||||
******************************************************************************/
|
|
||||||
package io.github.dsheirer.audio.broadcast;
|
|
||||||
|
|
||||||
import io.github.dsheirer.alias.AliasModel;
|
|
||||||
import io.github.dsheirer.gui.editor.DocumentListenerEditor;
|
|
||||||
import io.github.dsheirer.icon.IconManager;
|
|
||||||
|
|
||||||
import javax.swing.*;
|
|
||||||
|
|
||||||
public abstract class BroadcastConfigurationEditor extends DocumentListenerEditor<BroadcastConfiguration>
|
|
||||||
{
|
|
||||||
protected IconManager mIconManager;
|
|
||||||
protected BroadcastModel mBroadcastModel;
|
|
||||||
protected AliasModel mAliasModel;
|
|
||||||
|
|
||||||
public BroadcastConfigurationEditor(BroadcastModel broadcastModel, AliasModel aliasModel, IconManager iconManager)
|
|
||||||
{
|
|
||||||
mBroadcastModel = broadcastModel;
|
|
||||||
mAliasModel = aliasModel;
|
|
||||||
mIconManager = iconManager;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Updates the configuration with the new name and prompts the user to update any aliases that have a broadcast
|
|
||||||
* channel alias id with the old name to update to the new name.
|
|
||||||
*
|
|
||||||
* @param broadcastConfiguration to assign a new channel name
|
|
||||||
* @param newName to assign to the broadcast configuration. Note: assumes that validateConfiguration() has been
|
|
||||||
* invoked to verify that newName is non-null and non-empty
|
|
||||||
*/
|
|
||||||
protected void updateConfigurationName(BroadcastConfiguration broadcastConfiguration, String newName)
|
|
||||||
{
|
|
||||||
String previousName = broadcastConfiguration.getName();
|
|
||||||
|
|
||||||
if(previousName == null || previousName.isEmpty())
|
|
||||||
{
|
|
||||||
broadcastConfiguration.setName(newName);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(!previousName.equals(newName))
|
|
||||||
{
|
|
||||||
mAliasModel.renameBroadcastChannel(previousName, newName);
|
|
||||||
broadcastConfiguration.setName(newName);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Validates a text field control for a non-null, non-empty value
|
|
||||||
* @param field to validate
|
|
||||||
* @param title to use for error dialog
|
|
||||||
* @param message to use for error dialog
|
|
||||||
* @return true if field contains a non-null, non-empty value
|
|
||||||
*/
|
|
||||||
protected boolean validateTextField(JTextField field, String title, String message)
|
|
||||||
{
|
|
||||||
String text = field.getText();
|
|
||||||
|
|
||||||
if(text == null || text.isEmpty())
|
|
||||||
{
|
|
||||||
JOptionPane.showMessageDialog(BroadcastConfigurationEditor.this, message, title,
|
|
||||||
JOptionPane.ERROR_MESSAGE);
|
|
||||||
|
|
||||||
field.requestFocus();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Validates the text field control that contains an integer value for non-null, non-empty and within the
|
|
||||||
* specified min/max valid range.
|
|
||||||
* @param field to validate
|
|
||||||
* @param title to use for error dialog
|
|
||||||
* @param message to use for error dialog
|
|
||||||
* @param minValid value
|
|
||||||
* @param maxValid value
|
|
||||||
* @return true if field contains a non-null, non-empty value within the valid min/max range
|
|
||||||
*/
|
|
||||||
protected boolean validateIntegerTextField(JTextField field, String title, String message, int minValid, int maxValid)
|
|
||||||
{
|
|
||||||
if(validateTextField(field, title, message))
|
|
||||||
{
|
|
||||||
String text = field.getText();
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
int value = Integer.parseInt(text);
|
|
||||||
|
|
||||||
if(minValid <= value && value <= maxValid)
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch(Exception e)
|
|
||||||
{
|
|
||||||
//Do nothing, we couldn't parse the number value
|
|
||||||
}
|
|
||||||
|
|
||||||
JOptionPane.showMessageDialog(BroadcastConfigurationEditor.this, message, title,
|
|
||||||
JOptionPane.ERROR_MESSAGE);
|
|
||||||
|
|
||||||
field.requestFocus();
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -21,7 +21,7 @@ package io.github.dsheirer.audio.broadcast;
|
|||||||
public class BroadcastEvent
|
public class BroadcastEvent
|
||||||
{
|
{
|
||||||
private BroadcastConfiguration mBroadcastConfiguration;
|
private BroadcastConfiguration mBroadcastConfiguration;
|
||||||
private AudioBroadcaster mAudioBroadcaster;
|
private AbstractAudioBroadcaster mAudioBroadcaster;
|
||||||
private Event mEvent;
|
private Event mEvent;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -36,7 +36,7 @@ public class BroadcastEvent
|
|||||||
mEvent = event;
|
mEvent = event;
|
||||||
}
|
}
|
||||||
|
|
||||||
public BroadcastEvent(AudioBroadcaster audioBroadcaster, Event event)
|
public BroadcastEvent(AbstractAudioBroadcaster audioBroadcaster, Event event)
|
||||||
{
|
{
|
||||||
mAudioBroadcaster = audioBroadcaster;
|
mAudioBroadcaster = audioBroadcaster;
|
||||||
mEvent = event;
|
mEvent = event;
|
||||||
@@ -47,7 +47,7 @@ public class BroadcastEvent
|
|||||||
return mBroadcastConfiguration;
|
return mBroadcastConfiguration;
|
||||||
}
|
}
|
||||||
|
|
||||||
public AudioBroadcaster getAudioBroadcaster()
|
public AbstractAudioBroadcaster getAudioBroadcaster()
|
||||||
{
|
{
|
||||||
return mAudioBroadcaster;
|
return mAudioBroadcaster;
|
||||||
}
|
}
|
||||||
@@ -83,6 +83,7 @@ public class BroadcastEvent
|
|||||||
BROADCASTER_STATE_CHANGE,
|
BROADCASTER_STATE_CHANGE,
|
||||||
BROADCASTER_STREAMED_COUNT_CHANGE,
|
BROADCASTER_STREAMED_COUNT_CHANGE,
|
||||||
BROADCASTER_AGED_OFF_COUNT_CHANGE,
|
BROADCASTER_AGED_OFF_COUNT_CHANGE,
|
||||||
|
BROADCASTER_ERROR_COUNT_CHANGE,
|
||||||
BROADCASTER_DELETE,
|
BROADCASTER_DELETE,
|
||||||
|
|
||||||
CONFIGURATION_ADD,
|
CONFIGURATION_ADD,
|
||||||
|
|||||||
@@ -22,28 +22,21 @@
|
|||||||
package io.github.dsheirer.audio.broadcast;
|
package io.github.dsheirer.audio.broadcast;
|
||||||
|
|
||||||
import io.github.dsheirer.alias.AliasModel;
|
import io.github.dsheirer.alias.AliasModel;
|
||||||
import io.github.dsheirer.audio.broadcast.broadcastify.BroadcastifyConfiguration;
|
import io.github.dsheirer.audio.broadcast.broadcastify.BroadcastifyCallBroadcaster;
|
||||||
import io.github.dsheirer.audio.broadcast.broadcastify.BroadcastifyConfigurationEditor;
|
import io.github.dsheirer.audio.broadcast.broadcastify.BroadcastifyCallConfiguration;
|
||||||
|
import io.github.dsheirer.audio.broadcast.broadcastify.BroadcastifyFeedConfiguration;
|
||||||
import io.github.dsheirer.audio.broadcast.icecast.IcecastHTTPAudioBroadcaster;
|
import io.github.dsheirer.audio.broadcast.icecast.IcecastHTTPAudioBroadcaster;
|
||||||
import io.github.dsheirer.audio.broadcast.icecast.IcecastHTTPConfiguration;
|
import io.github.dsheirer.audio.broadcast.icecast.IcecastHTTPConfiguration;
|
||||||
import io.github.dsheirer.audio.broadcast.icecast.IcecastHTTPConfigurationEditor;
|
|
||||||
import io.github.dsheirer.audio.broadcast.icecast.IcecastTCPAudioBroadcaster;
|
import io.github.dsheirer.audio.broadcast.icecast.IcecastTCPAudioBroadcaster;
|
||||||
import io.github.dsheirer.audio.broadcast.icecast.IcecastTCPConfiguration;
|
import io.github.dsheirer.audio.broadcast.icecast.IcecastTCPConfiguration;
|
||||||
import io.github.dsheirer.audio.broadcast.icecast.IcecastTCPConfigurationEditor;
|
|
||||||
import io.github.dsheirer.audio.broadcast.shoutcast.v1.ShoutcastV1AudioBroadcaster;
|
import io.github.dsheirer.audio.broadcast.shoutcast.v1.ShoutcastV1AudioBroadcaster;
|
||||||
import io.github.dsheirer.audio.broadcast.shoutcast.v1.ShoutcastV1Configuration;
|
import io.github.dsheirer.audio.broadcast.shoutcast.v1.ShoutcastV1Configuration;
|
||||||
import io.github.dsheirer.audio.broadcast.shoutcast.v1.ShoutcastV1ConfigurationEditor;
|
import io.github.dsheirer.audio.broadcast.shoutcast.v2.ShoutcastV2AudioStreamingBroadcaster;
|
||||||
import io.github.dsheirer.audio.broadcast.shoutcast.v2.ShoutcastV2AudioBroadcaster;
|
|
||||||
import io.github.dsheirer.audio.broadcast.shoutcast.v2.ShoutcastV2Configuration;
|
import io.github.dsheirer.audio.broadcast.shoutcast.v2.ShoutcastV2Configuration;
|
||||||
import io.github.dsheirer.audio.broadcast.shoutcast.v2.ShoutcastV2ConfigurationEditor;
|
|
||||||
import io.github.dsheirer.audio.convert.IAudioConverter;
|
import io.github.dsheirer.audio.convert.IAudioConverter;
|
||||||
import io.github.dsheirer.audio.convert.ISilenceGenerator;
|
import io.github.dsheirer.audio.convert.ISilenceGenerator;
|
||||||
import io.github.dsheirer.audio.convert.MP3AudioConverter;
|
import io.github.dsheirer.audio.convert.MP3AudioConverter;
|
||||||
import io.github.dsheirer.audio.convert.MP3SilenceGenerator;
|
import io.github.dsheirer.audio.convert.MP3SilenceGenerator;
|
||||||
import io.github.dsheirer.gui.editor.Editor;
|
|
||||||
import io.github.dsheirer.gui.editor.EmptyEditor;
|
|
||||||
import io.github.dsheirer.icon.IconManager;
|
|
||||||
import io.github.dsheirer.preference.UserPreferences;
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
@@ -60,7 +53,7 @@ public class BroadcastFactory
|
|||||||
* @param configuration describing the server and audio types
|
* @param configuration describing the server and audio types
|
||||||
* @return configured broadcaster or null
|
* @return configured broadcaster or null
|
||||||
*/
|
*/
|
||||||
public static AudioBroadcaster getBroadcaster(BroadcastConfiguration configuration, AliasModel aliasModel)
|
public static AbstractAudioBroadcaster getBroadcaster(BroadcastConfiguration configuration, AliasModel aliasModel)
|
||||||
{
|
{
|
||||||
if(configuration != null)
|
if(configuration != null)
|
||||||
{
|
{
|
||||||
@@ -70,8 +63,10 @@ public class BroadcastFactory
|
|||||||
{
|
{
|
||||||
switch(configuration.getBroadcastServerType())
|
switch(configuration.getBroadcastServerType())
|
||||||
{
|
{
|
||||||
|
case BROADCASTIFY_CALL:
|
||||||
|
return new BroadcastifyCallBroadcaster((BroadcastifyCallConfiguration)configuration, aliasModel);
|
||||||
case BROADCASTIFY:
|
case BROADCASTIFY:
|
||||||
return new IcecastTCPAudioBroadcaster((BroadcastifyConfiguration) configuration, aliasModel);
|
return new IcecastTCPAudioBroadcaster((BroadcastifyFeedConfiguration) configuration, aliasModel);
|
||||||
case ICECAST_TCP:
|
case ICECAST_TCP:
|
||||||
return new IcecastTCPAudioBroadcaster((IcecastTCPConfiguration) configuration, aliasModel);
|
return new IcecastTCPAudioBroadcaster((IcecastTCPConfiguration) configuration, aliasModel);
|
||||||
case ICECAST_HTTP:
|
case ICECAST_HTTP:
|
||||||
@@ -79,7 +74,7 @@ public class BroadcastFactory
|
|||||||
case SHOUTCAST_V1:
|
case SHOUTCAST_V1:
|
||||||
return new ShoutcastV1AudioBroadcaster((ShoutcastV1Configuration) configuration, aliasModel);
|
return new ShoutcastV1AudioBroadcaster((ShoutcastV1Configuration) configuration, aliasModel);
|
||||||
case SHOUTCAST_V2:
|
case SHOUTCAST_V2:
|
||||||
return new ShoutcastV2AudioBroadcaster((ShoutcastV2Configuration) configuration, aliasModel);
|
return new ShoutcastV2AudioStreamingBroadcaster((ShoutcastV2Configuration) configuration, aliasModel);
|
||||||
case UNKNOWN:
|
case UNKNOWN:
|
||||||
default:
|
default:
|
||||||
mLog.info("Unrecognized broadcastAudio configuration: " + configuration.getBroadcastFormat().name());
|
mLog.info("Unrecognized broadcastAudio configuration: " + configuration.getBroadcastFormat().name());
|
||||||
@@ -121,8 +116,10 @@ public class BroadcastFactory
|
|||||||
{
|
{
|
||||||
switch(serverType)
|
switch(serverType)
|
||||||
{
|
{
|
||||||
|
case BROADCASTIFY_CALL:
|
||||||
|
return new BroadcastifyCallConfiguration(format);
|
||||||
case BROADCASTIFY:
|
case BROADCASTIFY:
|
||||||
return new BroadcastifyConfiguration(format);
|
return new BroadcastifyFeedConfiguration(format);
|
||||||
case ICECAST_HTTP:
|
case ICECAST_HTTP:
|
||||||
return new IcecastHTTPConfiguration(format);
|
return new IcecastHTTPConfiguration(format);
|
||||||
case ICECAST_TCP:
|
case ICECAST_TCP:
|
||||||
@@ -140,48 +137,6 @@ public class BroadcastFactory
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructs an editor for the specified broadcastAudio configuration
|
|
||||||
*
|
|
||||||
* @param configuration to modify or view
|
|
||||||
* @param broadcastModel model for broadcastAudio configurations
|
|
||||||
* @return an editor for the specified broadcastAudio configuration
|
|
||||||
*/
|
|
||||||
public static Editor<BroadcastConfiguration> getEditor(UserPreferences userPreferences,
|
|
||||||
BroadcastConfiguration configuration,
|
|
||||||
BroadcastModel broadcastModel,
|
|
||||||
AliasModel aliasModel,
|
|
||||||
IconManager iconManager)
|
|
||||||
{
|
|
||||||
Editor<BroadcastConfiguration> editor;
|
|
||||||
|
|
||||||
switch(configuration.getBroadcastServerType())
|
|
||||||
{
|
|
||||||
case BROADCASTIFY:
|
|
||||||
editor = new BroadcastifyConfigurationEditor(userPreferences, broadcastModel, aliasModel, iconManager);
|
|
||||||
break;
|
|
||||||
case ICECAST_TCP:
|
|
||||||
editor = new IcecastTCPConfigurationEditor(broadcastModel, aliasModel, iconManager);
|
|
||||||
break;
|
|
||||||
case ICECAST_HTTP:
|
|
||||||
editor = new IcecastHTTPConfigurationEditor(broadcastModel, aliasModel, iconManager);
|
|
||||||
break;
|
|
||||||
case SHOUTCAST_V1:
|
|
||||||
editor = new ShoutcastV1ConfigurationEditor(broadcastModel, aliasModel, iconManager);
|
|
||||||
break;
|
|
||||||
case SHOUTCAST_V2:
|
|
||||||
editor = new ShoutcastV2ConfigurationEditor(broadcastModel, aliasModel, iconManager);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
editor = new EmptyEditor<BroadcastConfiguration>();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
editor.setItem(configuration);
|
|
||||||
|
|
||||||
return editor;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static ISilenceGenerator getSilenceGenerator(BroadcastFormat format)
|
public static ISilenceGenerator getSilenceGenerator(BroadcastFormat format)
|
||||||
{
|
{
|
||||||
switch(format)
|
switch(format)
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
*
|
*
|
||||||
* * ******************************************************************************
|
* * ******************************************************************************
|
||||||
* * Copyright (C) 2014-2019 Dennis Sheirer
|
* * Copyright (C) 2014-2020 Dennis Sheirer
|
||||||
* *
|
* *
|
||||||
* * This program is free software: you can redistribute it and/or modify
|
* * This program is free software: you can redistribute it and/or modify
|
||||||
* * it under the terms of the GNU General Public License as published by
|
* * it under the terms of the GNU General Public License as published by
|
||||||
@@ -23,12 +23,14 @@ package io.github.dsheirer.audio.broadcast;
|
|||||||
|
|
||||||
import io.github.dsheirer.alias.AliasModel;
|
import io.github.dsheirer.alias.AliasModel;
|
||||||
import io.github.dsheirer.alias.id.broadcast.BroadcastChannel;
|
import io.github.dsheirer.alias.id.broadcast.BroadcastChannel;
|
||||||
import io.github.dsheirer.icon.IconManager;
|
import io.github.dsheirer.icon.IconModel;
|
||||||
import io.github.dsheirer.preference.UserPreferences;
|
import io.github.dsheirer.preference.UserPreferences;
|
||||||
import io.github.dsheirer.properties.SystemProperties;
|
import io.github.dsheirer.properties.SystemProperties;
|
||||||
import io.github.dsheirer.sample.Broadcaster;
|
import io.github.dsheirer.sample.Broadcaster;
|
||||||
import io.github.dsheirer.sample.Listener;
|
import io.github.dsheirer.sample.Listener;
|
||||||
import io.github.dsheirer.util.ThreadPool;
|
import io.github.dsheirer.util.ThreadPool;
|
||||||
|
import javafx.collections.FXCollections;
|
||||||
|
import javafx.collections.ObservableList;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
@@ -39,6 +41,7 @@ import java.nio.file.DirectoryStream;
|
|||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@@ -64,26 +67,27 @@ public class BroadcastModel extends AbstractTableModel implements Listener<Audio
|
|||||||
public static final int COLUMN_BROADCASTER_QUEUE_SIZE = 3;
|
public static final int COLUMN_BROADCASTER_QUEUE_SIZE = 3;
|
||||||
public static final int COLUMN_BROADCASTER_STREAMED_COUNT = 4;
|
public static final int COLUMN_BROADCASTER_STREAMED_COUNT = 4;
|
||||||
public static final int COLUMN_BROADCASTER_AGED_OFF_COUNT = 5;
|
public static final int COLUMN_BROADCASTER_AGED_OFF_COUNT = 5;
|
||||||
|
public static final int COLUMN_BROADCASTER_ERROR_COUNT = 6;
|
||||||
|
|
||||||
public static final String[] COLUMN_NAMES = new String[]
|
public static final String[] COLUMN_NAMES = new String[]
|
||||||
{"Streaming", "Name", "Status", "Queued", "Streamed", "Aged Off"};
|
{"Streaming", "Name", "Status", "Queued", "Streamed/Uploaded", "Aged Off", "Upload Error"};
|
||||||
|
|
||||||
private List<BroadcastConfiguration> mBroadcastConfigurations = new CopyOnWriteArrayList<>();
|
private ObservableList<ConfiguredBroadcast> mConfiguredBroadcasts =
|
||||||
|
FXCollections.observableArrayList(ConfiguredBroadcast.extractor());
|
||||||
private List<AudioRecording> mRecordingQueue = new CopyOnWriteArrayList<>();
|
private List<AudioRecording> mRecordingQueue = new CopyOnWriteArrayList<>();
|
||||||
|
private Map<Integer,AbstractAudioBroadcaster> mBroadcasterMap = new HashMap<>();
|
||||||
private Map<String,BroadcastConfiguration> mBroadcastConfigurationMap = new HashMap<>();
|
private IconModel mIconModel;
|
||||||
private Map<String,AudioBroadcaster> mBroadcasterMap = new HashMap<>();
|
|
||||||
private IconManager mIconManager;
|
|
||||||
private AliasModel mAliasModel;
|
private AliasModel mAliasModel;
|
||||||
private Broadcaster<BroadcastEvent> mBroadcastEventBroadcaster = new Broadcaster<>();
|
private Broadcaster<BroadcastEvent> mBroadcastEventBroadcaster = new Broadcaster<>();
|
||||||
|
private BroadcastEventListener mBroadcastEventListener = new BroadcastEventListener();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Model for managing Broadcast configurations and any associated broadcaster instances.
|
* Model for managing Broadcast configurations and any associated broadcaster instances.
|
||||||
*/
|
*/
|
||||||
public BroadcastModel(AliasModel aliasModel, IconManager iconManager, UserPreferences userPreferences)
|
public BroadcastModel(AliasModel aliasModel, IconModel iconModel, UserPreferences userPreferences)
|
||||||
{
|
{
|
||||||
mAliasModel = aliasModel;
|
mAliasModel = aliasModel;
|
||||||
mIconManager = iconManager;
|
mIconModel = iconModel;
|
||||||
|
|
||||||
//Monitor to remove temporary recording files that have been streamed by all audio broadcasters
|
//Monitor to remove temporary recording files that have been streamed by all audio broadcasters
|
||||||
ThreadPool.SCHEDULED.scheduleAtFixedRate(new RecordingDeletionMonitor(), 15l, 15l, TimeUnit.SECONDS);
|
ThreadPool.SCHEDULED.scheduleAtFixedRate(new RecordingDeletionMonitor(), 15l, 15l, TimeUnit.SECONDS);
|
||||||
@@ -91,6 +95,27 @@ public class BroadcastModel extends AbstractTableModel implements Listener<Audio
|
|||||||
removeOrphanedTemporaryRecordings();
|
removeOrphanedTemporaryRecordings();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* List of broadcast configurations with (optional) audio broadcasters created from each configuration
|
||||||
|
*/
|
||||||
|
public ObservableList<ConfiguredBroadcast> getConfiguredBroadcasts()
|
||||||
|
{
|
||||||
|
return mConfiguredBroadcasts;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes all broadcast configurations and shuts down any running broadcasters.
|
||||||
|
*/
|
||||||
|
public void clear()
|
||||||
|
{
|
||||||
|
List<ConfiguredBroadcast> configuredBroadcasts = new ArrayList<>(mConfiguredBroadcasts);
|
||||||
|
|
||||||
|
for(ConfiguredBroadcast configuredBroadcast: configuredBroadcasts)
|
||||||
|
{
|
||||||
|
removeBroadcastConfiguration(configuredBroadcast.getBroadcastConfiguration());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* List of broadcastAudio configuration names
|
* List of broadcastAudio configuration names
|
||||||
*/
|
*/
|
||||||
@@ -98,9 +123,9 @@ public class BroadcastModel extends AbstractTableModel implements Listener<Audio
|
|||||||
{
|
{
|
||||||
List<String> names = new ArrayList<>();
|
List<String> names = new ArrayList<>();
|
||||||
|
|
||||||
for(BroadcastConfiguration configuration : mBroadcastConfigurations)
|
for(ConfiguredBroadcast configuredBroadcast: mConfiguredBroadcasts)
|
||||||
{
|
{
|
||||||
names.add(configuration.getName());
|
names.add(configuredBroadcast.getBroadcastConfiguration().getName());
|
||||||
}
|
}
|
||||||
|
|
||||||
return names;
|
return names;
|
||||||
@@ -111,7 +136,14 @@ public class BroadcastModel extends AbstractTableModel implements Listener<Audio
|
|||||||
*/
|
*/
|
||||||
public List<BroadcastConfiguration> getBroadcastConfigurations()
|
public List<BroadcastConfiguration> getBroadcastConfigurations()
|
||||||
{
|
{
|
||||||
return mBroadcastConfigurations;
|
List<BroadcastConfiguration> configs = new ArrayList<>();
|
||||||
|
|
||||||
|
for(ConfiguredBroadcast configuredBroadcast: mConfiguredBroadcasts)
|
||||||
|
{
|
||||||
|
configs.add(configuredBroadcast.getBroadcastConfiguration());
|
||||||
|
}
|
||||||
|
|
||||||
|
return Collections.unmodifiableList(configs);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -128,38 +160,24 @@ public class BroadcastModel extends AbstractTableModel implements Listener<Audio
|
|||||||
/**
|
/**
|
||||||
* Adds the broadcastAudio configuration to this model
|
* Adds the broadcastAudio configuration to this model
|
||||||
*/
|
*/
|
||||||
public void addBroadcastConfiguration(BroadcastConfiguration configuration)
|
public ConfiguredBroadcast addBroadcastConfiguration(BroadcastConfiguration configuration)
|
||||||
{
|
{
|
||||||
if(configuration != null)
|
if(configuration != null)
|
||||||
{
|
{
|
||||||
ensureUniqueName(configuration);
|
ensureUniqueName(configuration);
|
||||||
|
|
||||||
if(!mBroadcastConfigurations.contains(configuration))
|
ConfiguredBroadcast configuredBroadcast = getConfiguredBroadcast(configuration);
|
||||||
|
|
||||||
|
if(configuredBroadcast == null)
|
||||||
{
|
{
|
||||||
mBroadcastConfigurations.add(configuration);
|
configuredBroadcast = new ConfiguredBroadcast(configuration);
|
||||||
|
mConfiguredBroadcasts.add(configuredBroadcast);
|
||||||
int index = mBroadcastConfigurations.size() - 1;
|
int index = mConfiguredBroadcasts.indexOf(configuredBroadcast);
|
||||||
|
|
||||||
fireTableRowsInserted(index, index);
|
fireTableRowsInserted(index, index);
|
||||||
|
|
||||||
mBroadcastConfigurationMap.put(configuration.getName(), configuration);
|
|
||||||
|
|
||||||
process(new BroadcastEvent(configuration, BroadcastEvent.Event.CONFIGURATION_ADD));
|
process(new BroadcastEvent(configuration, BroadcastEvent.Event.CONFIGURATION_ADD));
|
||||||
|
return configuredBroadcast;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Clones the configuration and adds it this model with a unique configuration name
|
|
||||||
*/
|
|
||||||
public BroadcastConfiguration cloneBroadcastConfiguration(BroadcastConfiguration configuration)
|
|
||||||
{
|
|
||||||
if(configuration != null)
|
|
||||||
{
|
|
||||||
BroadcastConfiguration clone = configuration.copyOf();
|
|
||||||
addBroadcastConfiguration(clone);
|
|
||||||
return clone;
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@@ -238,11 +256,11 @@ public class BroadcastModel extends AbstractTableModel implements Listener<Audio
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
for(BroadcastConfiguration configurationToCompare : mBroadcastConfigurations)
|
for(ConfiguredBroadcast configuredBroadcast: mConfiguredBroadcasts)
|
||||||
{
|
{
|
||||||
if(configurationToCompare != configuration &&
|
BroadcastConfiguration toCompare = configuredBroadcast.getBroadcastConfiguration();
|
||||||
configurationToCompare.getName() != null &&
|
|
||||||
configurationToCompare.getName().equals(name))
|
if(toCompare != configuration && toCompare.getName() != null && toCompare.getName().equals(name))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -253,13 +271,20 @@ public class BroadcastModel extends AbstractTableModel implements Listener<Audio
|
|||||||
|
|
||||||
public void removeBroadcastConfiguration(BroadcastConfiguration broadcastConfiguration)
|
public void removeBroadcastConfiguration(BroadcastConfiguration broadcastConfiguration)
|
||||||
{
|
{
|
||||||
if(broadcastConfiguration != null && mBroadcastConfigurations.contains(broadcastConfiguration))
|
ConfiguredBroadcast configuredBroadcast = getConfiguredBroadcast(broadcastConfiguration);
|
||||||
|
|
||||||
|
if(configuredBroadcast != null)
|
||||||
{
|
{
|
||||||
int index = mBroadcastConfigurations.indexOf(broadcastConfiguration);
|
int index = mConfiguredBroadcasts.indexOf(configuredBroadcast);
|
||||||
|
|
||||||
mBroadcastConfigurations.remove(broadcastConfiguration);
|
mConfiguredBroadcasts.remove(configuredBroadcast);
|
||||||
|
|
||||||
mBroadcastConfigurationMap.remove(broadcastConfiguration.getName());
|
if(configuredBroadcast.hasAudioBroadcaster())
|
||||||
|
{
|
||||||
|
mBroadcasterMap.remove(broadcastConfiguration.getId());
|
||||||
|
configuredBroadcast.getAudioBroadcaster().stop();;
|
||||||
|
configuredBroadcast.setAudioBroadcaster(null);
|
||||||
|
}
|
||||||
|
|
||||||
process(new BroadcastEvent(broadcastConfiguration, BroadcastEvent.Event.CONFIGURATION_DELETE));
|
process(new BroadcastEvent(broadcastConfiguration, BroadcastEvent.Event.CONFIGURATION_DELETE));
|
||||||
|
|
||||||
@@ -270,9 +295,16 @@ public class BroadcastModel extends AbstractTableModel implements Listener<Audio
|
|||||||
/**
|
/**
|
||||||
* Returns the broadcaster associated with the stream name or null if there is no broadcaster setup for the name.
|
* Returns the broadcaster associated with the stream name or null if there is no broadcaster setup for the name.
|
||||||
*/
|
*/
|
||||||
public AudioBroadcaster getBroadcaster(String streamName)
|
public AbstractAudioBroadcaster getBroadcaster(String streamName)
|
||||||
{
|
{
|
||||||
return mBroadcasterMap.get(streamName);
|
BroadcastConfiguration broadcastConfiguration = getBroadcastConfiguration(streamName);
|
||||||
|
|
||||||
|
if(broadcastConfiguration != null)
|
||||||
|
{
|
||||||
|
return mBroadcasterMap.get(broadcastConfiguration.getId());
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -286,7 +318,7 @@ public class BroadcastModel extends AbstractTableModel implements Listener<Audio
|
|||||||
|
|
||||||
if(channelName != null)
|
if(channelName != null)
|
||||||
{
|
{
|
||||||
AudioBroadcaster audioBroadcaster = getBroadcaster(channelName);
|
AbstractAudioBroadcaster audioBroadcaster = getBroadcaster(channelName);
|
||||||
|
|
||||||
if(audioBroadcaster != null)
|
if(audioBroadcaster != null)
|
||||||
{
|
{
|
||||||
@@ -305,17 +337,25 @@ public class BroadcastModel extends AbstractTableModel implements Listener<Audio
|
|||||||
*/
|
*/
|
||||||
private void createBroadcaster(BroadcastConfiguration broadcastConfiguration)
|
private void createBroadcaster(BroadcastConfiguration broadcastConfiguration)
|
||||||
{
|
{
|
||||||
if(broadcastConfiguration != null && broadcastConfiguration.isEnabled() && broadcastConfiguration.isValid() &&
|
ConfiguredBroadcast configuredBroadcast = getConfiguredBroadcast(broadcastConfiguration);
|
||||||
!mBroadcasterMap.containsKey(broadcastConfiguration.getName()))
|
|
||||||
|
if(configuredBroadcast != null && broadcastConfiguration.isEnabled())
|
||||||
{
|
{
|
||||||
AudioBroadcaster audioBroadcaster = BroadcastFactory.getBroadcaster(broadcastConfiguration, mAliasModel);
|
if(configuredBroadcast.hasAudioBroadcaster())
|
||||||
|
{
|
||||||
|
deleteBroadcaster(configuredBroadcast);
|
||||||
|
}
|
||||||
|
|
||||||
|
AbstractAudioBroadcaster audioBroadcaster = BroadcastFactory.getBroadcaster(broadcastConfiguration, mAliasModel);
|
||||||
|
|
||||||
if(audioBroadcaster != null)
|
if(audioBroadcaster != null)
|
||||||
{
|
{
|
||||||
audioBroadcaster.setListener(broadcastEvent -> process(broadcastEvent));
|
configuredBroadcast.setAudioBroadcaster(audioBroadcaster);
|
||||||
|
audioBroadcaster.setListener(mBroadcastEventListener);
|
||||||
audioBroadcaster.start();
|
audioBroadcaster.start();
|
||||||
mBroadcasterMap.put(audioBroadcaster.getBroadcastConfiguration().getName(), audioBroadcaster);
|
mBroadcasterMap.put(audioBroadcaster.getBroadcastConfiguration().getId(), audioBroadcaster);
|
||||||
int index = mBroadcastConfigurations.indexOf(audioBroadcaster.getBroadcastConfiguration());
|
|
||||||
|
int index = mConfiguredBroadcasts.indexOf(configuredBroadcast);
|
||||||
|
|
||||||
if(index >= 0)
|
if(index >= 0)
|
||||||
{
|
{
|
||||||
@@ -330,26 +370,27 @@ public class BroadcastModel extends AbstractTableModel implements Listener<Audio
|
|||||||
/**
|
/**
|
||||||
* Shut down a broadcaster created from the configuration and remove it from this model
|
* Shut down a broadcaster created from the configuration and remove it from this model
|
||||||
*/
|
*/
|
||||||
private void deleteBroadcaster(String name)
|
private void deleteBroadcaster(ConfiguredBroadcast configuredBroadcast)
|
||||||
{
|
{
|
||||||
if(name != null && mBroadcasterMap.containsKey(name))
|
if(configuredBroadcast != null && configuredBroadcast.hasAudioBroadcaster())
|
||||||
{
|
{
|
||||||
AudioBroadcaster audioBroadcaster = mBroadcasterMap.remove(name);
|
mBroadcasterMap.remove(configuredBroadcast.getBroadcastConfiguration().getId());
|
||||||
|
|
||||||
if(audioBroadcaster != null)
|
AbstractAudioBroadcaster broadcaster = configuredBroadcast.getAudioBroadcaster();
|
||||||
|
configuredBroadcast.setAudioBroadcaster(null);
|
||||||
|
|
||||||
|
broadcaster.stop();
|
||||||
|
broadcaster.removeListener();
|
||||||
|
broadcaster.dispose();
|
||||||
|
|
||||||
|
int index = mConfiguredBroadcasts.indexOf(configuredBroadcast);
|
||||||
|
|
||||||
|
if(index >= 0)
|
||||||
{
|
{
|
||||||
audioBroadcaster.stop();
|
fireTableRowsUpdated(index, index);
|
||||||
audioBroadcaster.removeListener();
|
|
||||||
|
|
||||||
int index = mBroadcastConfigurations.indexOf(audioBroadcaster.getBroadcastConfiguration());
|
|
||||||
|
|
||||||
if(index >= 0)
|
|
||||||
{
|
|
||||||
fireTableRowsUpdated(index, index);
|
|
||||||
}
|
|
||||||
|
|
||||||
broadcast(new BroadcastEvent(audioBroadcaster, BroadcastEvent.Event.BROADCASTER_DELETE));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
broadcast(new BroadcastEvent(broadcaster, BroadcastEvent.Event.BROADCASTER_DELETE));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -358,7 +399,16 @@ public class BroadcastModel extends AbstractTableModel implements Listener<Audio
|
|||||||
*/
|
*/
|
||||||
public BroadcastConfiguration getBroadcastConfiguration(String streamName)
|
public BroadcastConfiguration getBroadcastConfiguration(String streamName)
|
||||||
{
|
{
|
||||||
return mBroadcastConfigurationMap.get(streamName);
|
for(ConfiguredBroadcast configuredBroadcast: mConfiguredBroadcasts)
|
||||||
|
{
|
||||||
|
if(configuredBroadcast.getBroadcastConfiguration().getName() != null &&
|
||||||
|
configuredBroadcast.getBroadcastConfiguration().getName().equals(streamName))
|
||||||
|
{
|
||||||
|
return configuredBroadcast.getBroadcastConfiguration();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -377,6 +427,43 @@ public class BroadcastModel extends AbstractTableModel implements Listener<Audio
|
|||||||
mBroadcastEventBroadcaster.removeListener(listener);
|
mBroadcastEventBroadcaster.removeListener(listener);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the configured broadcast that contains the specified configuration
|
||||||
|
* @param configuration to search for
|
||||||
|
* @return configured broadcast that matches, or null
|
||||||
|
*/
|
||||||
|
private ConfiguredBroadcast getConfiguredBroadcast(BroadcastConfiguration configuration)
|
||||||
|
{
|
||||||
|
for(ConfiguredBroadcast configuredBroadcast: mConfiguredBroadcasts)
|
||||||
|
{
|
||||||
|
if(configuredBroadcast.getBroadcastConfiguration().equals(configuration))
|
||||||
|
{
|
||||||
|
return configuredBroadcast;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the configured broadcast that contains the specified broadcaster
|
||||||
|
* @param broadcaster to search for
|
||||||
|
* @return configured broadcast that matches, or null
|
||||||
|
*/
|
||||||
|
private ConfiguredBroadcast getConfiguredBroadcast(AbstractAudioBroadcaster broadcaster)
|
||||||
|
{
|
||||||
|
for(ConfiguredBroadcast configuredBroadcast: mConfiguredBroadcasts)
|
||||||
|
{
|
||||||
|
if(configuredBroadcast.getAudioBroadcaster() != null &&
|
||||||
|
configuredBroadcast.getAudioBroadcaster().equals(broadcaster))
|
||||||
|
{
|
||||||
|
return configuredBroadcast;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Broadcasts the broadcastAudio configuration change event
|
* Broadcasts the broadcastAudio configuration change event
|
||||||
*/
|
*/
|
||||||
@@ -399,30 +486,31 @@ public class BroadcastModel extends AbstractTableModel implements Listener<Audio
|
|||||||
break;
|
break;
|
||||||
case CONFIGURATION_CHANGE:
|
case CONFIGURATION_CHANGE:
|
||||||
BroadcastConfiguration broadcastConfiguration = broadcastEvent.getBroadcastConfiguration();
|
BroadcastConfiguration broadcastConfiguration = broadcastEvent.getBroadcastConfiguration();
|
||||||
int index = mBroadcastConfigurations.indexOf(broadcastConfiguration);
|
ConfiguredBroadcast configuredBroadcast = getConfiguredBroadcast(broadcastConfiguration);
|
||||||
|
|
||||||
//Delete the existing broadcaster for any broadcast configuration changes
|
//Delete the broadcaster if it exists
|
||||||
String previousChannelName = cleanupMapAssociations(broadcastConfiguration);
|
deleteBroadcaster(configuredBroadcast);
|
||||||
deleteBroadcaster(previousChannelName);
|
|
||||||
|
|
||||||
//If the configuration is enabled, create a new broadcaster after a brief delay
|
//If the configuration is enabled, create a new broadcaster after a brief delay
|
||||||
if(broadcastConfiguration.isEnabled())
|
if(broadcastConfiguration.isEnabled())
|
||||||
{
|
{
|
||||||
//Delay restarting the broadcaster to allow remote server time to cleanup
|
//Delay restarting the broadcaster to allow remote server time to cleanup
|
||||||
ThreadPool.SCHEDULED.schedule(new DelayedBroadcasterStartup(broadcastConfiguration),
|
ThreadPool.SCHEDULED.schedule(new DelayedBroadcasterStartup(broadcastConfiguration),
|
||||||
3, TimeUnit.SECONDS);
|
1, TimeUnit.SECONDS);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int index = mConfiguredBroadcasts.indexOf(configuredBroadcast);
|
||||||
fireTableRowsUpdated(index, index);
|
fireTableRowsUpdated(index, index);
|
||||||
break;
|
break;
|
||||||
case CONFIGURATION_DELETE:
|
case CONFIGURATION_DELETE:
|
||||||
deleteBroadcaster(broadcastEvent.getBroadcastConfiguration().getName());
|
deleteBroadcaster(getConfiguredBroadcast(broadcastEvent.getBroadcastConfiguration()));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if(broadcastEvent.isAudioBroadcasterEvent())
|
else if(broadcastEvent.isAudioBroadcasterEvent())
|
||||||
{
|
{
|
||||||
int row = mBroadcastConfigurations.indexOf(broadcastEvent.getAudioBroadcaster().getBroadcastConfiguration());
|
ConfiguredBroadcast configuredBroadcast = getConfiguredBroadcast(broadcastEvent.getAudioBroadcaster());
|
||||||
|
int row = mConfiguredBroadcasts.indexOf(configuredBroadcast);
|
||||||
|
|
||||||
switch(broadcastEvent.getEvent())
|
switch(broadcastEvent.getEvent())
|
||||||
{
|
{
|
||||||
@@ -449,6 +537,11 @@ public class BroadcastModel extends AbstractTableModel implements Listener<Audio
|
|||||||
{
|
{
|
||||||
fireTableCellUpdated(row, COLUMN_BROADCASTER_AGED_OFF_COUNT);
|
fireTableCellUpdated(row, COLUMN_BROADCASTER_AGED_OFF_COUNT);
|
||||||
}
|
}
|
||||||
|
case BROADCASTER_ERROR_COUNT_CHANGE:
|
||||||
|
if(row >= 0)
|
||||||
|
{
|
||||||
|
fireTableCellUpdated(row, COLUMN_BROADCASTER_ERROR_COUNT);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -457,41 +550,10 @@ public class BroadcastModel extends AbstractTableModel implements Listener<Audio
|
|||||||
broadcast(broadcastEvent);
|
broadcast(broadcastEvent);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* When the name of a broadcast configuration changes, remove the old map associations so that they can be replaced
|
|
||||||
* with the new map associations. Remove any broadcaster that is associated with the old configuration name.
|
|
||||||
*
|
|
||||||
* @param broadcastConfiguration
|
|
||||||
* @return previous or current channel name that has been replaced with the current configuration channel name so
|
|
||||||
* that anything else associated with the previous name can be cleaned up.
|
|
||||||
*/
|
|
||||||
private String cleanupMapAssociations(BroadcastConfiguration broadcastConfiguration)
|
|
||||||
{
|
|
||||||
String oldName = null;
|
|
||||||
|
|
||||||
for(Map.Entry<String,BroadcastConfiguration> entry : mBroadcastConfigurationMap.entrySet())
|
|
||||||
{
|
|
||||||
if(entry.getValue() == broadcastConfiguration)
|
|
||||||
{
|
|
||||||
oldName = entry.getKey();
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(oldName != null)
|
|
||||||
{
|
|
||||||
mBroadcastConfigurationMap.remove(oldName);
|
|
||||||
}
|
|
||||||
|
|
||||||
mBroadcastConfigurationMap.put(broadcastConfiguration.getName(), broadcastConfiguration);
|
|
||||||
|
|
||||||
return oldName;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getRowCount()
|
public int getRowCount()
|
||||||
{
|
{
|
||||||
return mBroadcastConfigurations.size();
|
return mConfiguredBroadcasts.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -505,61 +567,64 @@ public class BroadcastModel extends AbstractTableModel implements Listener<Audio
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if(rowIndex <= mBroadcastConfigurations.size())
|
if(rowIndex <= mConfiguredBroadcasts.size())
|
||||||
{
|
{
|
||||||
BroadcastConfiguration configuration = mBroadcastConfigurations.get(rowIndex);
|
ConfiguredBroadcast configuredBroadcast = mConfiguredBroadcasts.get(rowIndex);
|
||||||
|
|
||||||
if(configuration != null)
|
if(configuredBroadcast != null)
|
||||||
{
|
{
|
||||||
switch(columnIndex)
|
switch(columnIndex)
|
||||||
{
|
{
|
||||||
case COLUMN_SERVER_ICON:
|
case COLUMN_SERVER_ICON:
|
||||||
String iconPath = configuration.getBroadcastServerType().getIconPath();
|
String iconPath = configuredBroadcast.getBroadcastConfiguration()
|
||||||
|
.getBroadcastServerType().getIconPath();
|
||||||
|
|
||||||
if(iconPath != null && mIconManager != null)
|
if(iconPath != null && mIconModel != null)
|
||||||
{
|
{
|
||||||
return mIconManager.getScaledIcon(iconPath, 14);
|
return mIconModel.getScaledIcon(iconPath, 14);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case COLUMN_STREAM_NAME:
|
case COLUMN_STREAM_NAME:
|
||||||
return configuration.getName();
|
return configuredBroadcast.getBroadcastConfiguration().getName();
|
||||||
case COLUMN_BROADCASTER_STATUS:
|
case COLUMN_BROADCASTER_STATUS:
|
||||||
AudioBroadcaster audioBroadcasterA = mBroadcasterMap.get(configuration.getName());
|
if(configuredBroadcast.hasAudioBroadcaster())
|
||||||
|
|
||||||
if(audioBroadcasterA != null)
|
|
||||||
{
|
{
|
||||||
return audioBroadcasterA.getBroadcastState();
|
return configuredBroadcast.getAudioBroadcaster().getBroadcastState();
|
||||||
}
|
}
|
||||||
else if(!configuration.isEnabled())
|
else if(!configuredBroadcast.getBroadcastConfiguration().isEnabled())
|
||||||
{
|
{
|
||||||
return BroadcastState.DISABLED;
|
return BroadcastState.DISABLED;
|
||||||
}
|
}
|
||||||
else if(!configuration.isValid())
|
else if(!configuredBroadcast.getBroadcastConfiguration().isValid())
|
||||||
{
|
{
|
||||||
return BroadcastState.INVALID_SETTINGS;
|
return BroadcastState.INVALID_SETTINGS;
|
||||||
}
|
}
|
||||||
case COLUMN_BROADCASTER_QUEUE_SIZE:
|
else
|
||||||
AudioBroadcaster audioBroadcasterB = mBroadcasterMap.get(configuration.getName());
|
|
||||||
|
|
||||||
if(audioBroadcasterB != null)
|
|
||||||
{
|
{
|
||||||
return audioBroadcasterB.getQueueSize();
|
return BroadcastState.ERROR;
|
||||||
|
}
|
||||||
|
case COLUMN_BROADCASTER_QUEUE_SIZE:
|
||||||
|
if(configuredBroadcast.hasAudioBroadcaster())
|
||||||
|
{
|
||||||
|
return configuredBroadcast.getAudioBroadcaster().getAudioQueueSize();
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case COLUMN_BROADCASTER_STREAMED_COUNT:
|
case COLUMN_BROADCASTER_STREAMED_COUNT:
|
||||||
AudioBroadcaster audioBroadcasterC = mBroadcasterMap.get(configuration.getName());
|
if(configuredBroadcast.hasAudioBroadcaster())
|
||||||
|
|
||||||
if(audioBroadcasterC != null)
|
|
||||||
{
|
{
|
||||||
return audioBroadcasterC.getStreamedAudioCount();
|
return configuredBroadcast.getAudioBroadcaster().getStreamedAudioCount();
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case COLUMN_BROADCASTER_AGED_OFF_COUNT:
|
case COLUMN_BROADCASTER_AGED_OFF_COUNT:
|
||||||
AudioBroadcaster audioBroadcasterD = mBroadcasterMap.get(configuration.getName());
|
if(configuredBroadcast.hasAudioBroadcaster())
|
||||||
|
|
||||||
if(audioBroadcasterD != null)
|
|
||||||
{
|
{
|
||||||
return audioBroadcasterD.getAgedOffAudioCount();
|
return configuredBroadcast.getAudioBroadcaster().getAgedOffAudioCount();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case COLUMN_BROADCASTER_ERROR_COUNT:
|
||||||
|
if(configuredBroadcast.hasAudioBroadcaster())
|
||||||
|
{
|
||||||
|
return configuredBroadcast.getAudioBroadcaster().getAudioErrorCount();
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
@@ -586,6 +651,7 @@ public class BroadcastModel extends AbstractTableModel implements Listener<Audio
|
|||||||
case COLUMN_BROADCASTER_AGED_OFF_COUNT:
|
case COLUMN_BROADCASTER_AGED_OFF_COUNT:
|
||||||
case COLUMN_BROADCASTER_QUEUE_SIZE:
|
case COLUMN_BROADCASTER_QUEUE_SIZE:
|
||||||
case COLUMN_BROADCASTER_STREAMED_COUNT:
|
case COLUMN_BROADCASTER_STREAMED_COUNT:
|
||||||
|
case COLUMN_BROADCASTER_ERROR_COUNT:
|
||||||
return Integer.class;
|
return Integer.class;
|
||||||
case COLUMN_SERVER_ICON:
|
case COLUMN_SERVER_ICON:
|
||||||
return ImageIcon.class;
|
return ImageIcon.class;
|
||||||
@@ -595,22 +661,6 @@ public class BroadcastModel extends AbstractTableModel implements Listener<Audio
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Broadcast configuration at the specified model row
|
|
||||||
*/
|
|
||||||
public BroadcastConfiguration getConfigurationAt(int rowIndex)
|
|
||||||
{
|
|
||||||
return mBroadcastConfigurations.get(rowIndex);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Model row number for the specified configuration
|
|
||||||
*/
|
|
||||||
public int getRowForConfiguration(BroadcastConfiguration configuration)
|
|
||||||
{
|
|
||||||
return mBroadcastConfigurations.indexOf(configuration);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getColumnName(int column)
|
public String getColumnName(int column)
|
||||||
{
|
{
|
||||||
@@ -648,37 +698,51 @@ public class BroadcastModel extends AbstractTableModel implements Listener<Audio
|
|||||||
*/
|
*/
|
||||||
private void removeOrphanedTemporaryRecordings()
|
private void removeOrphanedTemporaryRecordings()
|
||||||
{
|
{
|
||||||
Path path = SystemProperties.getInstance().getApplicationFolder(BroadcastModel.TEMPORARY_STREAM_DIRECTORY);
|
ThreadPool.SCHEDULED.submit(new Runnable()
|
||||||
|
|
||||||
if(path != null && Files.isDirectory(path))
|
|
||||||
{
|
{
|
||||||
StringBuilder sb = new StringBuilder();
|
@Override
|
||||||
sb.append(TEMPORARY_STREAM_FILE_SUFFIX);
|
public void run()
|
||||||
sb.append("*.*");
|
|
||||||
|
|
||||||
try(DirectoryStream<Path> directoryStream = Files.newDirectoryStream(path, sb.toString()))
|
|
||||||
{
|
{
|
||||||
directoryStream.forEach(new Consumer<Path>()
|
try
|
||||||
{
|
{
|
||||||
@Override
|
Path path = SystemProperties.getInstance().getApplicationFolder(BroadcastModel.TEMPORARY_STREAM_DIRECTORY);
|
||||||
public void accept(Path path)
|
|
||||||
|
if(path != null && Files.isDirectory(path))
|
||||||
{
|
{
|
||||||
try
|
StringBuilder sb = new StringBuilder();
|
||||||
|
sb.append(TEMPORARY_STREAM_FILE_SUFFIX);
|
||||||
|
sb.append("*.*");
|
||||||
|
|
||||||
|
try(DirectoryStream<Path> directoryStream = Files.newDirectoryStream(path, sb.toString()))
|
||||||
{
|
{
|
||||||
Files.delete(path);
|
directoryStream.forEach(new Consumer<Path>()
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
public void accept(Path path)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Files.delete(path);
|
||||||
|
}
|
||||||
|
catch(IOException ioe)
|
||||||
|
{
|
||||||
|
mLog.error("Couldn't delete orphaned temporary recording: " + path.toString(), ioe);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
catch(IOException ioe)
|
catch(IOException ioe)
|
||||||
{
|
{
|
||||||
mLog.error("Couldn't delete orphaned temporary recording: " + path.toString(), ioe);
|
mLog.error("Error discovering orphaned temporary stream recording files", ioe);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
|
catch(Throwable t)
|
||||||
|
{
|
||||||
|
mLog.error("Error during cleanup of orphaned temporary streaming recording files");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
catch(IOException ioe)
|
});
|
||||||
{
|
|
||||||
mLog.error("Error discovering orphaned temporary stream recording files", ioe);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -742,4 +806,16 @@ public class BroadcastModel extends AbstractTableModel implements Listener<Audio
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adapter to receive and process broadcast events from constructed audio broadcasters
|
||||||
|
*/
|
||||||
|
public class BroadcastEventListener implements Listener<BroadcastEvent>
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
public void receive(BroadcastEvent broadcastEvent)
|
||||||
|
{
|
||||||
|
process(broadcastEvent);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,270 +0,0 @@
|
|||||||
/*
|
|
||||||
*
|
|
||||||
* * ******************************************************************************
|
|
||||||
* * Copyright (C) 2014-2019 Dennis Sheirer
|
|
||||||
* *
|
|
||||||
* * This program is free software: you can redistribute it and/or modify
|
|
||||||
* * it under the terms of the GNU General Public License as published by
|
|
||||||
* * the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* * (at your option) any later version.
|
|
||||||
* *
|
|
||||||
* * This program is distributed in the hope that it will be useful,
|
|
||||||
* * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* * GNU General Public License for more details.
|
|
||||||
* *
|
|
||||||
* * You should have received a copy of the GNU General Public License
|
|
||||||
* * along with this program. If not, see <http://www.gnu.org/licenses/>
|
|
||||||
* * *****************************************************************************
|
|
||||||
*
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
package io.github.dsheirer.audio.broadcast;
|
|
||||||
|
|
||||||
import com.jidesoft.swing.JideSplitPane;
|
|
||||||
import io.github.dsheirer.alias.AliasModel;
|
|
||||||
import io.github.dsheirer.gui.editor.Editor;
|
|
||||||
import io.github.dsheirer.gui.editor.EmptyEditor;
|
|
||||||
import io.github.dsheirer.icon.IconManager;
|
|
||||||
import io.github.dsheirer.preference.UserPreferences;
|
|
||||||
import net.miginfocom.swing.MigLayout;
|
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
|
|
||||||
import javax.swing.JButton;
|
|
||||||
import javax.swing.JMenuItem;
|
|
||||||
import javax.swing.JOptionPane;
|
|
||||||
import javax.swing.JPanel;
|
|
||||||
import javax.swing.JPopupMenu;
|
|
||||||
import javax.swing.ListSelectionModel;
|
|
||||||
import javax.swing.event.ListSelectionEvent;
|
|
||||||
import javax.swing.event.ListSelectionListener;
|
|
||||||
import java.awt.event.ActionEvent;
|
|
||||||
import java.awt.event.ActionListener;
|
|
||||||
import java.awt.event.MouseAdapter;
|
|
||||||
import java.awt.event.MouseEvent;
|
|
||||||
|
|
||||||
public class BroadcastPanel extends JPanel implements ActionListener, ListSelectionListener
|
|
||||||
{
|
|
||||||
private final static Logger mLog = LoggerFactory.getLogger( BroadcastPanel.class );
|
|
||||||
|
|
||||||
private static final String NEW_BROADCAST_CONFIGURATION = "New";
|
|
||||||
private static final String COPY_BROADCAST_CONFIGURATION = "Copy";
|
|
||||||
private static final String DELETE_BROADCAST_CONFIGURATION = "Delete";
|
|
||||||
|
|
||||||
private BroadcastModel mBroadcastModel;
|
|
||||||
private AliasModel mAliasModel;
|
|
||||||
private IconManager mIconManager;
|
|
||||||
private UserPreferences mUserPreferences;
|
|
||||||
|
|
||||||
private BroadcastStatusPanel mBroadcastStatusPanel;
|
|
||||||
private JideSplitPane mSplitPane;
|
|
||||||
private Editor<BroadcastConfiguration> mEmptyEditor = new EmptyEditor<>("Configuration:");
|
|
||||||
private Editor<BroadcastConfiguration> mEditor;
|
|
||||||
private JButton mNewButton = new JButton(NEW_BROADCAST_CONFIGURATION);
|
|
||||||
private JButton mCopyButton = new JButton(COPY_BROADCAST_CONFIGURATION);
|
|
||||||
private JButton mDeleteButton = new JButton(DELETE_BROADCAST_CONFIGURATION);
|
|
||||||
|
|
||||||
public BroadcastPanel(BroadcastModel broadcastModel, AliasModel aliasModel, IconManager iconManager,
|
|
||||||
UserPreferences userPreferences)
|
|
||||||
{
|
|
||||||
mBroadcastModel = broadcastModel;
|
|
||||||
mAliasModel = aliasModel;
|
|
||||||
mIconManager = iconManager;
|
|
||||||
mEditor = mEmptyEditor;
|
|
||||||
mUserPreferences = userPreferences;
|
|
||||||
|
|
||||||
init();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void init()
|
|
||||||
{
|
|
||||||
setLayout(new MigLayout("insets 0 0 0 0 ", "[grow,fill]", "[grow,fill]"));
|
|
||||||
|
|
||||||
mBroadcastStatusPanel = new BroadcastStatusPanel(mBroadcastModel, mUserPreferences,
|
|
||||||
"broadcast.panel.broadcast.status.panel");
|
|
||||||
|
|
||||||
mBroadcastStatusPanel.getTable().setAutoCreateRowSorter(true);
|
|
||||||
mBroadcastStatusPanel.getTable().setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
|
|
||||||
mBroadcastStatusPanel.getTable().getSelectionModel().addListSelectionListener(BroadcastPanel.this);
|
|
||||||
|
|
||||||
JPanel buttonsPanel = new JPanel();
|
|
||||||
buttonsPanel.setLayout(new MigLayout("insets 0 0 0 0", "[grow,fill][grow,fill][grow,fill]", "[]"));
|
|
||||||
|
|
||||||
mNewButton = new JButton("New ...");
|
|
||||||
mNewButton.setToolTipText("Create a new broadcast audio configuration");
|
|
||||||
mNewButton.addMouseListener(new MouseAdapter()
|
|
||||||
{
|
|
||||||
@Override
|
|
||||||
public void mouseClicked(MouseEvent e)
|
|
||||||
{
|
|
||||||
JPopupMenu menu = new JPopupMenu();
|
|
||||||
|
|
||||||
menu.add(new AddBroadcastConfigurationItem(BroadcastServerType.BROADCASTIFY));
|
|
||||||
menu.add(new AddBroadcastConfigurationItem(BroadcastServerType.ICECAST_TCP));
|
|
||||||
menu.add(new AddBroadcastConfigurationItem(BroadcastServerType.ICECAST_HTTP));
|
|
||||||
menu.add(new AddBroadcastConfigurationItem(BroadcastServerType.SHOUTCAST_V1));
|
|
||||||
menu.add(new AddBroadcastConfigurationItem(BroadcastServerType.SHOUTCAST_V2));
|
|
||||||
|
|
||||||
menu.show(e.getComponent(), e.getX(), e.getY());
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
buttonsPanel.add(mNewButton);
|
|
||||||
|
|
||||||
mCopyButton.addActionListener(this);
|
|
||||||
mCopyButton.setEnabled(false);
|
|
||||||
mCopyButton.setToolTipText("Creates a copy of the currently selected streaming configuration and adds it");
|
|
||||||
buttonsPanel.add(mCopyButton);
|
|
||||||
|
|
||||||
mDeleteButton.addActionListener(this);
|
|
||||||
mDeleteButton.setEnabled(false);
|
|
||||||
mDeleteButton.setToolTipText("Deletes the currently selected streaming configuration");
|
|
||||||
buttonsPanel.add(mDeleteButton);
|
|
||||||
|
|
||||||
JPanel listAndButtonsPanel = new JPanel();
|
|
||||||
|
|
||||||
listAndButtonsPanel.setLayout(new MigLayout("insets 0 0 0 0", "[grow,fill]", "[grow,fill][]"));
|
|
||||||
|
|
||||||
listAndButtonsPanel.add(mBroadcastStatusPanel, "wrap");
|
|
||||||
listAndButtonsPanel.add(buttonsPanel);
|
|
||||||
|
|
||||||
mSplitPane = new JideSplitPane(JideSplitPane.HORIZONTAL_SPLIT);
|
|
||||||
mSplitPane.setDividerSize(5);
|
|
||||||
mSplitPane.setShowGripper(true);
|
|
||||||
mSplitPane.add(listAndButtonsPanel);
|
|
||||||
|
|
||||||
mSplitPane.add(mEditor);
|
|
||||||
|
|
||||||
add(mSplitPane);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void actionPerformed(ActionEvent e)
|
|
||||||
{
|
|
||||||
switch(e.getActionCommand())
|
|
||||||
{
|
|
||||||
case COPY_BROADCAST_CONFIGURATION:
|
|
||||||
BroadcastConfiguration copySelected = getSelectedBroadcastConfiguration();
|
|
||||||
mBroadcastModel.cloneBroadcastConfiguration(copySelected);
|
|
||||||
break;
|
|
||||||
case DELETE_BROADCAST_CONFIGURATION:
|
|
||||||
BroadcastConfiguration deleteSelected = getSelectedBroadcastConfiguration();
|
|
||||||
|
|
||||||
if(deleteSelected != null)
|
|
||||||
{
|
|
||||||
int choice = JOptionPane.showConfirmDialog(BroadcastPanel.this, "Do you want to delete broadcast\n" +
|
|
||||||
" configuration " + deleteSelected.getName() + "?", "Delete Broadcast Configuration?",
|
|
||||||
JOptionPane.YES_NO_OPTION);
|
|
||||||
|
|
||||||
if(choice == JOptionPane.YES_OPTION)
|
|
||||||
{
|
|
||||||
mBroadcastModel.removeBroadcastConfiguration(deleteSelected);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the selected configuration
|
|
||||||
*/
|
|
||||||
private BroadcastConfiguration getSelectedBroadcastConfiguration()
|
|
||||||
{
|
|
||||||
int viewRow = mBroadcastStatusPanel.getTable().getSelectedRow();
|
|
||||||
|
|
||||||
if(viewRow >= 0)
|
|
||||||
{
|
|
||||||
int modelRow = mBroadcastStatusPanel.getTable().convertRowIndexToModel(viewRow);
|
|
||||||
|
|
||||||
if(modelRow >= 0)
|
|
||||||
{
|
|
||||||
return mBroadcastModel.getConfigurationAt(modelRow);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void valueChanged(ListSelectionEvent e)
|
|
||||||
{
|
|
||||||
if (!e.getValueIsAdjusting())
|
|
||||||
{
|
|
||||||
BroadcastConfiguration selectedConfiguration = getSelectedBroadcastConfiguration();
|
|
||||||
|
|
||||||
//Enable buttons accordingly
|
|
||||||
mCopyButton.setEnabled(selectedConfiguration != null);
|
|
||||||
mDeleteButton.setEnabled(selectedConfiguration != null);
|
|
||||||
|
|
||||||
if (mEditor.isModified())
|
|
||||||
{
|
|
||||||
int option = JOptionPane.showConfirmDialog(
|
|
||||||
BroadcastPanel.this,
|
|
||||||
"Configuration settings have changed. Do you want to save these changes?",
|
|
||||||
"Save Changes?",
|
|
||||||
JOptionPane.YES_NO_OPTION,
|
|
||||||
JOptionPane.QUESTION_MESSAGE);
|
|
||||||
|
|
||||||
if (option == JOptionPane.YES_OPTION)
|
|
||||||
{
|
|
||||||
mEditor.save();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
mSplitPane.removePane(mEditor);
|
|
||||||
|
|
||||||
if (selectedConfiguration != null)
|
|
||||||
{
|
|
||||||
mEditor = BroadcastFactory.getEditor(mUserPreferences, selectedConfiguration, mBroadcastModel,
|
|
||||||
mAliasModel, mIconManager);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
mEditor = mEmptyEditor;
|
|
||||||
}
|
|
||||||
|
|
||||||
mSplitPane.add(mEditor);
|
|
||||||
|
|
||||||
revalidate();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class AddBroadcastConfigurationItem extends JMenuItem
|
|
||||||
{
|
|
||||||
private static final long serialVersionUID = 1L;
|
|
||||||
|
|
||||||
private BroadcastServerType mBroadcastServerType;
|
|
||||||
|
|
||||||
public AddBroadcastConfigurationItem(BroadcastServerType type)
|
|
||||||
{
|
|
||||||
super(type.toString(), mIconManager.getScaledIcon(type.getIconPath(), 14));
|
|
||||||
|
|
||||||
mBroadcastServerType = type;
|
|
||||||
|
|
||||||
setToolTipText("Add a new " + type.toString() + " audio stream configuration");
|
|
||||||
|
|
||||||
addActionListener(new ActionListener()
|
|
||||||
{
|
|
||||||
@Override
|
|
||||||
public void actionPerformed(ActionEvent e)
|
|
||||||
{
|
|
||||||
final BroadcastConfiguration configuration =
|
|
||||||
BroadcastFactory.getConfiguration(mBroadcastServerType, BroadcastFormat.MP3);
|
|
||||||
|
|
||||||
if(configuration != null)
|
|
||||||
{
|
|
||||||
mBroadcastModel.addBroadcastConfiguration(configuration);
|
|
||||||
|
|
||||||
int modelRow = mBroadcastModel.getRowForConfiguration(configuration);
|
|
||||||
int tableRow = mBroadcastStatusPanel.getTable().convertRowIndexToView(modelRow);
|
|
||||||
mBroadcastStatusPanel.getTable().changeSelection(tableRow, 0, false, false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -20,7 +20,16 @@ package io.github.dsheirer.audio.broadcast;
|
|||||||
|
|
||||||
public enum BroadcastServerType
|
public enum BroadcastServerType
|
||||||
{
|
{
|
||||||
BROADCASTIFY("Broadcastify", "/images/broadcastify.png"), //Icecast Server 2.3.2
|
/**
|
||||||
|
* Broadcastify feeds (ie streaming) service
|
||||||
|
*/
|
||||||
|
BROADCASTIFY("Broadcastify Feed", "/images/broadcastify.png"), //Icecast Server 2.3.2
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Broadcastify calls - completed audio recording push service
|
||||||
|
*/
|
||||||
|
BROADCASTIFY_CALL("Broadcastify Call", "/images/broadcastify.png"),
|
||||||
|
|
||||||
ICECAST_HTTP("Icecast 2 (v2.4+)", "/images/icecast.png"),
|
ICECAST_HTTP("Icecast 2 (v2.4+)", "/images/icecast.png"),
|
||||||
ICECAST_TCP("Icecast (v2.3)", "/images/icecast.png"),
|
ICECAST_TCP("Icecast (v2.3)", "/images/icecast.png"),
|
||||||
SHOUTCAST_V1("Shoutcast v1.x", "/images/shoutcast.png"),
|
SHOUTCAST_V1("Shoutcast v1.x", "/images/shoutcast.png"),
|
||||||
|
|||||||
@@ -0,0 +1,153 @@
|
|||||||
|
/*
|
||||||
|
*
|
||||||
|
* * ******************************************************************************
|
||||||
|
* * Copyright (C) 2014-2020 Dennis Sheirer
|
||||||
|
* *
|
||||||
|
* * This program is free software: you can redistribute it and/or modify
|
||||||
|
* * it under the terms of the GNU General Public License as published by
|
||||||
|
* * the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* * (at your option) any later version.
|
||||||
|
* *
|
||||||
|
* * This program is distributed in the hope that it will be useful,
|
||||||
|
* * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* * GNU General Public License for more details.
|
||||||
|
* *
|
||||||
|
* * You should have received a copy of the GNU General Public License
|
||||||
|
* * along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||||
|
* * *****************************************************************************
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.github.dsheirer.audio.broadcast;
|
||||||
|
|
||||||
|
import javafx.beans.Observable;
|
||||||
|
import javafx.beans.property.BooleanProperty;
|
||||||
|
import javafx.beans.property.ObjectProperty;
|
||||||
|
import javafx.beans.property.SimpleObjectProperty;
|
||||||
|
import javafx.beans.property.StringProperty;
|
||||||
|
import javafx.util.Callback;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Composite observable object that joins a broadcast configuration and (optional) constructed audio broadcaster
|
||||||
|
*/
|
||||||
|
public class ConfiguredBroadcast
|
||||||
|
{
|
||||||
|
private final static Logger mLog = LoggerFactory.getLogger(ConfiguredBroadcast.class);
|
||||||
|
private BroadcastConfiguration mBroadcastConfiguration;
|
||||||
|
private AbstractAudioBroadcaster mAudioBroadcaster;
|
||||||
|
private ObjectProperty<BroadcastState> mBroadcastState = new SimpleObjectProperty<>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs an instance
|
||||||
|
* @param broadcastConfiguration for the instance
|
||||||
|
*/
|
||||||
|
public ConfiguredBroadcast(BroadcastConfiguration broadcastConfiguration)
|
||||||
|
{
|
||||||
|
mBroadcastConfiguration = broadcastConfiguration;
|
||||||
|
mBroadcastConfiguration.validProperty().addListener((observable, oldValue, newValue) -> updateBroadcastState());
|
||||||
|
updateBroadcastState();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Configuration for this broadcast
|
||||||
|
*/
|
||||||
|
public BroadcastConfiguration getBroadcastConfiguration()
|
||||||
|
{
|
||||||
|
return mBroadcastConfiguration;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enabled state of the configuration
|
||||||
|
*/
|
||||||
|
public BooleanProperty enabledProperty()
|
||||||
|
{
|
||||||
|
return mBroadcastConfiguration.enabledProperty();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Name of the broadcast configuration
|
||||||
|
*/
|
||||||
|
public StringProperty nameProperty()
|
||||||
|
{
|
||||||
|
return mBroadcastConfiguration.nameProperty();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Server type for the broadcast configuration
|
||||||
|
*/
|
||||||
|
public BroadcastServerType getBroadcastServerType()
|
||||||
|
{
|
||||||
|
return mBroadcastConfiguration.getBroadcastServerType();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Broadcast state of the configured audio broadcaster (optional)
|
||||||
|
*/
|
||||||
|
public ObjectProperty<BroadcastState> broadcastStateProperty()
|
||||||
|
{
|
||||||
|
return mBroadcastState;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the audio broadcaster
|
||||||
|
* @param audioBroadcaster to use for this configuration
|
||||||
|
*/
|
||||||
|
public void setAudioBroadcaster(AbstractAudioBroadcaster audioBroadcaster)
|
||||||
|
{
|
||||||
|
mBroadcastState.unbind();
|
||||||
|
mAudioBroadcaster = audioBroadcaster;
|
||||||
|
|
||||||
|
if(audioBroadcaster != null)
|
||||||
|
{
|
||||||
|
mBroadcastState.bind(mAudioBroadcaster.broadcastStateProperty());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
updateBroadcastState();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateBroadcastState()
|
||||||
|
{
|
||||||
|
if(!mBroadcastState.isBound())
|
||||||
|
{
|
||||||
|
if(mBroadcastConfiguration.isValid())
|
||||||
|
{
|
||||||
|
mBroadcastState.setValue(BroadcastState.READY);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
mBroadcastState.setValue(BroadcastState.CONFIGURATION_ERROR);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Optional audio broadcaster created from the configuration
|
||||||
|
*/
|
||||||
|
public AbstractAudioBroadcaster getAudioBroadcaster()
|
||||||
|
{
|
||||||
|
return mAudioBroadcaster;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicates if this configured broadcast has a non-null audio broadcaster assigned
|
||||||
|
*/
|
||||||
|
public boolean hasAudioBroadcaster()
|
||||||
|
{
|
||||||
|
return mAudioBroadcaster != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates an observable property extractor for use with observable lists to detect changes internal to this object.
|
||||||
|
*/
|
||||||
|
public static Callback<ConfiguredBroadcast, Observable[]> extractor()
|
||||||
|
{
|
||||||
|
return (ConfiguredBroadcast b) -> new Observable[] {b.nameProperty(), b.enabledProperty(),
|
||||||
|
b.broadcastStateProperty(), b.getBroadcastConfiguration().validProperty()};
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,529 @@
|
|||||||
|
/*
|
||||||
|
* *****************************************************************************
|
||||||
|
* Copyright (C) 2014-2020 Dennis Sheirer
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||||
|
* ****************************************************************************
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.github.dsheirer.audio.broadcast.broadcastify;
|
||||||
|
|
||||||
|
import com.google.common.net.HttpHeaders;
|
||||||
|
import io.github.dsheirer.alias.AliasModel;
|
||||||
|
import io.github.dsheirer.audio.broadcast.AbstractAudioBroadcaster;
|
||||||
|
import io.github.dsheirer.audio.broadcast.AudioRecording;
|
||||||
|
import io.github.dsheirer.audio.broadcast.BroadcastEvent;
|
||||||
|
import io.github.dsheirer.audio.broadcast.BroadcastState;
|
||||||
|
import io.github.dsheirer.gui.playlist.radioreference.RadioReferenceDecoder;
|
||||||
|
import io.github.dsheirer.identifier.Form;
|
||||||
|
import io.github.dsheirer.identifier.Identifier;
|
||||||
|
import io.github.dsheirer.identifier.IdentifierClass;
|
||||||
|
import io.github.dsheirer.identifier.Role;
|
||||||
|
import io.github.dsheirer.identifier.configuration.ConfigurationLongIdentifier;
|
||||||
|
import io.github.dsheirer.identifier.patch.PatchGroup;
|
||||||
|
import io.github.dsheirer.identifier.patch.PatchGroupIdentifier;
|
||||||
|
import io.github.dsheirer.identifier.radio.RadioIdentifier;
|
||||||
|
import io.github.dsheirer.identifier.talkgroup.TalkgroupIdentifier;
|
||||||
|
import io.github.dsheirer.util.ThreadPool;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import java.io.FileNotFoundException;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.net.URI;
|
||||||
|
import java.net.http.HttpClient;
|
||||||
|
import java.net.http.HttpRequest;
|
||||||
|
import java.net.http.HttpResponse;
|
||||||
|
import java.time.Duration;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Queue;
|
||||||
|
import java.util.concurrent.CompletionException;
|
||||||
|
import java.util.concurrent.LinkedTransferQueue;
|
||||||
|
import java.util.concurrent.ScheduledFuture;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Audio broadcaster to push completed audio recordings to the Broadcastify call push API.
|
||||||
|
*
|
||||||
|
* Note: this is not the same as the Broadcastify Feeds (ie streaming) service
|
||||||
|
*/
|
||||||
|
public class BroadcastifyCallBroadcaster extends AbstractAudioBroadcaster<BroadcastifyCallConfiguration>
|
||||||
|
{
|
||||||
|
private final static Logger mLog = LoggerFactory.getLogger(BroadcastifyCallBroadcaster.class);
|
||||||
|
|
||||||
|
private static final String ENCODING_TYPE_MP3 = "mp3";
|
||||||
|
private static final String MULTIPART_TYPE = "multipart";
|
||||||
|
private static final String DEFAULT_SUBTYPE = "form-data";
|
||||||
|
private static final String MULTIPART_FORM_DATA = MULTIPART_TYPE + "/" + DEFAULT_SUBTYPE;
|
||||||
|
private Queue<AudioRecording> mAudioRecordingQueue = new LinkedTransferQueue<>();
|
||||||
|
private ScheduledFuture<?> mAudioRecordingProcessorFuture;
|
||||||
|
private HttpClient mHttpClient = HttpClient.newBuilder()
|
||||||
|
.version(HttpClient.Version.HTTP_2)
|
||||||
|
.followRedirects(HttpClient.Redirect.NORMAL)
|
||||||
|
.connectTimeout(Duration.ofSeconds(20))
|
||||||
|
.build();
|
||||||
|
private long mLastConnectionAttempt;
|
||||||
|
private long mConnectionAttemptInterval = 5000; //Every 5 seconds
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs an instance of the broadcaster
|
||||||
|
* @param config to use
|
||||||
|
* @param aliasModel for access to aliases
|
||||||
|
*/
|
||||||
|
public BroadcastifyCallBroadcaster(BroadcastifyCallConfiguration config, AliasModel aliasModel)
|
||||||
|
{
|
||||||
|
super(config);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Starts the audio recording processor thread
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void start()
|
||||||
|
{
|
||||||
|
setBroadcastState(BroadcastState.CONNECTING);
|
||||||
|
String response = testConnection(getBroadcastConfiguration());
|
||||||
|
mLastConnectionAttempt = System.currentTimeMillis();
|
||||||
|
|
||||||
|
if(response != null && response.toLowerCase().startsWith("ok"))
|
||||||
|
{
|
||||||
|
setBroadcastState(BroadcastState.CONNECTED);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
mLog.error("Error connecting to Broadcastify calls server on startup [" + response + "]");
|
||||||
|
setBroadcastState(BroadcastState.ERROR);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(mAudioRecordingProcessorFuture == null)
|
||||||
|
{
|
||||||
|
mAudioRecordingProcessorFuture = ThreadPool.SCHEDULED.scheduleAtFixedRate(new AudioRecordingProcessor(),
|
||||||
|
0, 500, TimeUnit.MILLISECONDS);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stops the audio recording processor thread
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void stop()
|
||||||
|
{
|
||||||
|
if(mAudioRecordingProcessorFuture != null)
|
||||||
|
{
|
||||||
|
mAudioRecordingProcessorFuture.cancel(true);
|
||||||
|
mAudioRecordingProcessorFuture = null;
|
||||||
|
dispose();
|
||||||
|
setBroadcastState(BroadcastState.DISCONNECTED);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prepares for disposal
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void dispose()
|
||||||
|
{
|
||||||
|
AudioRecording audioRecording = mAudioRecordingQueue.poll();
|
||||||
|
|
||||||
|
while(audioRecording != null)
|
||||||
|
{
|
||||||
|
audioRecording.removePendingReplay();
|
||||||
|
audioRecording = mAudioRecordingQueue.poll();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicates if this broadcaster continues to have successful connections to and transactions with the remote
|
||||||
|
* server. If there is a connectivity or other issue, the broadcast state is set to temporary error and
|
||||||
|
* the audio processor thread will persistently invoke this method to attempt a reconnect.
|
||||||
|
*/
|
||||||
|
private boolean connected()
|
||||||
|
{
|
||||||
|
if(getBroadcastState() != BroadcastState.CONNECTED &&
|
||||||
|
(System.currentTimeMillis() - mLastConnectionAttempt > mConnectionAttemptInterval))
|
||||||
|
{
|
||||||
|
setBroadcastState(BroadcastState.CONNECTING);
|
||||||
|
|
||||||
|
String response = testConnection(getBroadcastConfiguration());
|
||||||
|
mLastConnectionAttempt = System.currentTimeMillis();
|
||||||
|
|
||||||
|
if(response != null && response.toLowerCase().startsWith("ok"))
|
||||||
|
{
|
||||||
|
setBroadcastState(BroadcastState.CONNECTED);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
setBroadcastState(BroadcastState.ERROR);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return getBroadcastState() == BroadcastState.CONNECTED;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getAudioQueueSize()
|
||||||
|
{
|
||||||
|
return mAudioRecordingQueue.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void receive(AudioRecording audioRecording)
|
||||||
|
{
|
||||||
|
mAudioRecordingQueue.offer(audioRecording);
|
||||||
|
broadcast(new BroadcastEvent(this, BroadcastEvent.Event.BROADCASTER_QUEUE_CHANGE));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicates if the audio recording is non-null and not too old, meaning that the age of the recording has not
|
||||||
|
* exceeded the max age value indicated in the broadcast configuration. Audio recordings that are too old will be
|
||||||
|
* deleted to ensure that the in-memory queue size doesn't blow up.
|
||||||
|
* @param audioRecording to test
|
||||||
|
* @return true if the recording is valid
|
||||||
|
*/
|
||||||
|
private boolean isValid(AudioRecording audioRecording)
|
||||||
|
{
|
||||||
|
return audioRecording != null && System.currentTimeMillis() - audioRecording.getStartTime() <=
|
||||||
|
getBroadcastConfiguration().getMaximumRecordingAge();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Processes any enqueued audio recordings. The broadcastify calls API uses a two-step process that includes
|
||||||
|
* requesting an upload URL and then uploading the audio recording to that URL. This method employs asynchronous
|
||||||
|
* interaction with the server, so multiple audio recording uploads can occur simultaneously.
|
||||||
|
*/
|
||||||
|
private void processRecordingQueue()
|
||||||
|
{
|
||||||
|
while(connected() && !mAudioRecordingQueue.isEmpty())
|
||||||
|
{
|
||||||
|
final AudioRecording audioRecording = mAudioRecordingQueue.poll();
|
||||||
|
broadcast(new BroadcastEvent(this, BroadcastEvent.Event.BROADCASTER_QUEUE_CHANGE));
|
||||||
|
|
||||||
|
if(isValid(audioRecording) && audioRecording.getRecordingLength() > 0)
|
||||||
|
{
|
||||||
|
float durationSeconds = (float)(audioRecording.getRecordingLength() / 1E3f);
|
||||||
|
long timestampSeconds = (int)(audioRecording.getStartTime() / 1E3);
|
||||||
|
String talkgroup = getTo(audioRecording);
|
||||||
|
String radioId = getFrom(audioRecording);
|
||||||
|
float frequency = getFrequency(audioRecording);
|
||||||
|
|
||||||
|
BroadcastifyCallBuilder bodyBuilder = new BroadcastifyCallBuilder();
|
||||||
|
bodyBuilder.addPart(FormField.API_KEY, getBroadcastConfiguration().getApiKey())
|
||||||
|
.addPart(FormField.SYSTEM_ID, getBroadcastConfiguration().getSystemID())
|
||||||
|
.addPart(FormField.CALL_DURATION, durationSeconds)
|
||||||
|
.addPart(FormField.TIMESTAMP, timestampSeconds)
|
||||||
|
.addPart(FormField.TALKGROUP_ID, talkgroup)
|
||||||
|
.addPart(FormField.RADIO_ID, radioId)
|
||||||
|
.addPart(FormField.FREQUENCY, frequency)
|
||||||
|
.addPart(FormField.ENCODING, ENCODING_TYPE_MP3);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
HttpRequest request = HttpRequest.newBuilder()
|
||||||
|
.uri(URI.create(getBroadcastConfiguration().getHost()))
|
||||||
|
.header(HttpHeaders.CONTENT_TYPE, MULTIPART_FORM_DATA + "; boundary=" + bodyBuilder.getBoundary())
|
||||||
|
.header(HttpHeaders.USER_AGENT, "sdrtrunk")
|
||||||
|
.header(HttpHeaders.ACCEPT, "*/*")
|
||||||
|
.POST(bodyBuilder.build())
|
||||||
|
.build();
|
||||||
|
|
||||||
|
mHttpClient.sendAsync(request, HttpResponse.BodyHandlers.ofString())
|
||||||
|
.whenComplete((stringHttpResponse, throwable) -> {
|
||||||
|
if(throwable != null || stringHttpResponse.statusCode() != 200)
|
||||||
|
{
|
||||||
|
if(throwable instanceof IOException || throwable instanceof CompletionException)
|
||||||
|
{
|
||||||
|
//We get socket reset exceptions occasionally when the remote server doesn't
|
||||||
|
//fully read our request and immediately responds.
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
mLog.error("Error while sending upload URL request" + throwable.getLocalizedMessage());
|
||||||
|
setBroadcastState(BroadcastState.TEMPORARY_BROADCAST_ERROR);
|
||||||
|
}
|
||||||
|
incrementErrorAudioCount();
|
||||||
|
broadcast(new BroadcastEvent(BroadcastifyCallBroadcaster.this,
|
||||||
|
BroadcastEvent.Event.BROADCASTER_ERROR_COUNT_CHANGE));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
String urlResponse = stringHttpResponse.body();
|
||||||
|
|
||||||
|
if(urlResponse.startsWith("0 "))
|
||||||
|
{
|
||||||
|
HttpRequest.BodyPublisher filePublisher = null;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
filePublisher = HttpRequest.BodyPublishers.ofFile(audioRecording.getPath());
|
||||||
|
}
|
||||||
|
catch(FileNotFoundException fnfe)
|
||||||
|
{
|
||||||
|
mLog.error("Broadcastify calls API - audio recording file not found - ignoring upload");
|
||||||
|
}
|
||||||
|
|
||||||
|
if(filePublisher != null)
|
||||||
|
{
|
||||||
|
HttpRequest fileRequest = HttpRequest.newBuilder()
|
||||||
|
.uri(URI.create(urlResponse.substring(2)))
|
||||||
|
.header(HttpHeaders.USER_AGENT, "sdrtrunk")
|
||||||
|
.header(HttpHeaders.CONTENT_TYPE, "audio/mpeg")
|
||||||
|
.PUT(filePublisher)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
mHttpClient.sendAsync(fileRequest, HttpResponse.BodyHandlers.ofString())
|
||||||
|
.whenComplete((fileResponse, throwable1) -> {
|
||||||
|
if(throwable1 != null || fileResponse.statusCode() != 200)
|
||||||
|
{
|
||||||
|
if(throwable1 instanceof IOException || throwable1 instanceof CompletionException)
|
||||||
|
{
|
||||||
|
//We get socket reset exceptions occasionally when the remote server doesn't
|
||||||
|
//fully read our request and immediately responds.
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
setBroadcastState(BroadcastState.TEMPORARY_BROADCAST_ERROR);
|
||||||
|
mLog.error("Broadcastify calls API file upload fail [" +
|
||||||
|
fileResponse.statusCode() + "] response [" +
|
||||||
|
fileResponse.body() + "]");
|
||||||
|
}
|
||||||
|
|
||||||
|
incrementErrorAudioCount();
|
||||||
|
broadcast(new BroadcastEvent(BroadcastifyCallBroadcaster.this,
|
||||||
|
BroadcastEvent.Event.BROADCASTER_ERROR_COUNT_CHANGE));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
incrementStreamedAudioCount();
|
||||||
|
broadcast(new BroadcastEvent(BroadcastifyCallBroadcaster.this,
|
||||||
|
BroadcastEvent.Event.BROADCASTER_STREAMED_COUNT_CHANGE));
|
||||||
|
}
|
||||||
|
|
||||||
|
audioRecording.removePendingReplay();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
//Register an error for the file not found exception
|
||||||
|
mLog.error("Broadcastify calls API - upload file not found [" +
|
||||||
|
audioRecording.getPath().toString() + "]");
|
||||||
|
incrementErrorAudioCount();
|
||||||
|
broadcast(new BroadcastEvent(BroadcastifyCallBroadcaster.this,
|
||||||
|
BroadcastEvent.Event.BROADCASTER_ERROR_COUNT_CHANGE));
|
||||||
|
audioRecording.removePendingReplay();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
mLog.error("Broadcastify calls API upload URL request failed [" + urlResponse + "]");
|
||||||
|
setBroadcastState(BroadcastState.TEMPORARY_BROADCAST_ERROR);
|
||||||
|
incrementErrorAudioCount();
|
||||||
|
broadcast(new BroadcastEvent(BroadcastifyCallBroadcaster.this,
|
||||||
|
BroadcastEvent.Event.BROADCASTER_ERROR_COUNT_CHANGE));
|
||||||
|
audioRecording.removePendingReplay();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
catch(Exception e)
|
||||||
|
{
|
||||||
|
mLog.error("Unknown Error", e);
|
||||||
|
setBroadcastState(BroadcastState.ERROR);
|
||||||
|
incrementErrorAudioCount();
|
||||||
|
broadcast(new BroadcastEvent(this, BroadcastEvent.Event.BROADCASTER_ERROR_COUNT_CHANGE));
|
||||||
|
audioRecording.removePendingReplay();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//If we're not connected and there are recordings in the queue, check the recording at the head of the queue
|
||||||
|
// and start age-off once the recordings become too old. The recordings should be time ordered in the queue.
|
||||||
|
AudioRecording audioRecording = mAudioRecordingQueue.peek();
|
||||||
|
|
||||||
|
while(audioRecording != null)
|
||||||
|
{
|
||||||
|
if(isValid(audioRecording))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
//Remove the recording from the queue, remove a replay, and peek at the next recording in the queue
|
||||||
|
mAudioRecordingQueue.poll();
|
||||||
|
audioRecording.removePendingReplay();
|
||||||
|
incrementAgedOffAudioCount();
|
||||||
|
broadcast(new BroadcastEvent(this, BroadcastEvent.Event.BROADCASTER_AGED_OFF_COUNT_CHANGE));
|
||||||
|
audioRecording = mAudioRecordingQueue.peek();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a frequency value from the audio recording identifier collection.
|
||||||
|
*/
|
||||||
|
private static float getFrequency(AudioRecording audioRecording)
|
||||||
|
{
|
||||||
|
Identifier identifier = audioRecording.getIdentifierCollection().getIdentifier(IdentifierClass.CONFIGURATION,
|
||||||
|
Form.CHANNEL_FREQUENCY, Role.ANY);
|
||||||
|
|
||||||
|
if(identifier instanceof ConfigurationLongIdentifier)
|
||||||
|
{
|
||||||
|
Long value = ((ConfigurationLongIdentifier)identifier).getValue();
|
||||||
|
|
||||||
|
if(value != null)
|
||||||
|
{
|
||||||
|
return value / 1E6f;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a formatted string with the FROM identifier or uses a default of zero(0)
|
||||||
|
*/
|
||||||
|
private static String getFrom(AudioRecording audioRecording)
|
||||||
|
{
|
||||||
|
for(Identifier identifier: audioRecording.getIdentifierCollection().getIdentifiers(Role.FROM))
|
||||||
|
{
|
||||||
|
if(identifier instanceof RadioIdentifier)
|
||||||
|
{
|
||||||
|
return ((RadioIdentifier)identifier).getValue().toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return "0";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a formatted string with the TO identifiers or uses a default of zero (0)
|
||||||
|
*/
|
||||||
|
private static String getTo(AudioRecording audioRecording)
|
||||||
|
{
|
||||||
|
List<Identifier> toIdentifiers = audioRecording.getIdentifierCollection().getIdentifiers(Role.TO);
|
||||||
|
|
||||||
|
if(toIdentifiers.size() >= 1)
|
||||||
|
{
|
||||||
|
Identifier to = toIdentifiers.get(0);
|
||||||
|
|
||||||
|
if(to instanceof TalkgroupIdentifier)
|
||||||
|
{
|
||||||
|
TalkgroupIdentifier talkgroupIdentifier = (TalkgroupIdentifier)to;
|
||||||
|
return String.valueOf(RadioReferenceDecoder.convertToRadioReferenceTalkgroup(talkgroupIdentifier.getValue(),
|
||||||
|
talkgroupIdentifier.getProtocol()));
|
||||||
|
}
|
||||||
|
else if(to instanceof PatchGroupIdentifier)
|
||||||
|
{
|
||||||
|
PatchGroup patchGroup = ((PatchGroupIdentifier)to).getValue();
|
||||||
|
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
sb.append(patchGroup.getPatchGroup().getValue().toString());
|
||||||
|
for(TalkgroupIdentifier patched: patchGroup.getPatchedGroupIdentifiers())
|
||||||
|
{
|
||||||
|
sb.append(",").append(patched.getValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
else if(to instanceof RadioIdentifier)
|
||||||
|
{
|
||||||
|
return ((RadioIdentifier)to).getValue().toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return "0";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests both the connection and configuration against the Broadcastify Call API service
|
||||||
|
* @param configuration containing API key and system id
|
||||||
|
* @return error string or null if test is successful
|
||||||
|
*/
|
||||||
|
public static String testConnection(BroadcastifyCallConfiguration configuration)
|
||||||
|
{
|
||||||
|
HttpClient httpClient = HttpClient.newBuilder()
|
||||||
|
.version(HttpClient.Version.HTTP_1_1)
|
||||||
|
.followRedirects(HttpClient.Redirect.NORMAL)
|
||||||
|
.connectTimeout(Duration.ofSeconds(20))
|
||||||
|
.build();
|
||||||
|
|
||||||
|
BroadcastifyCallBuilder bodyBuilder = new BroadcastifyCallBuilder();
|
||||||
|
bodyBuilder.addPart(FormField.API_KEY, configuration.getApiKey())
|
||||||
|
.addPart(FormField.SYSTEM_ID, configuration.getSystemID())
|
||||||
|
.addPart(FormField.TEST, 1);
|
||||||
|
|
||||||
|
HttpRequest request = HttpRequest.newBuilder()
|
||||||
|
.uri(URI.create(configuration.getHost()))
|
||||||
|
.header(HttpHeaders.CONTENT_TYPE, MULTIPART_FORM_DATA + "; boundary=" + bodyBuilder.getBoundary())
|
||||||
|
.header(HttpHeaders.USER_AGENT, "sdrtrunk")
|
||||||
|
.header(HttpHeaders.ACCEPT, "*/*")
|
||||||
|
.POST(bodyBuilder.build())
|
||||||
|
.build();
|
||||||
|
|
||||||
|
HttpResponse.BodyHandler<String> responseHandler = HttpResponse.BodyHandlers.ofString();
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
HttpResponse<String> response = httpClient.send(request, responseHandler);
|
||||||
|
String responseBody = response.body();
|
||||||
|
return (responseBody != null ? responseBody : "(no response)") + " Status Code:" + response.statusCode();
|
||||||
|
}
|
||||||
|
catch(Exception e)
|
||||||
|
{
|
||||||
|
return e.getLocalizedMessage();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class AudioRecordingProcessor implements Runnable
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
public void run()
|
||||||
|
{
|
||||||
|
processRecordingQueue();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void main(String[] args)
|
||||||
|
{
|
||||||
|
mLog.debug("Starting ...");
|
||||||
|
|
||||||
|
BroadcastifyCallConfiguration config = new BroadcastifyCallConfiguration();
|
||||||
|
config.setHost("https://api.broadcastify.com/call-upload-dev");
|
||||||
|
config.setApiKey("c33aae37-8572-11ea-bd8b-0ecc8ab9ccec");
|
||||||
|
config.setSystemID(11);
|
||||||
|
|
||||||
|
String response = testConnection(config);
|
||||||
|
|
||||||
|
if(response == null)
|
||||||
|
{
|
||||||
|
mLog.debug("Test Successful!");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if(response.contains("1 Invalid-API-Key"))
|
||||||
|
{
|
||||||
|
mLog.error("Invalid API Key");
|
||||||
|
}
|
||||||
|
else if(response.contains("1 API-Key-Access-Denied"))
|
||||||
|
{
|
||||||
|
mLog.error("System ID not valid for API Key");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
mLog.debug("Response: " + response);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mLog.debug("Finished!");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,159 @@
|
|||||||
|
/*
|
||||||
|
* *****************************************************************************
|
||||||
|
* Copyright (C) 2014-2020 Dennis Sheirer
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||||
|
* ****************************************************************************
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.github.dsheirer.audio.broadcast.broadcastify;
|
||||||
|
|
||||||
|
import java.net.http.HttpRequest;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Builder for an HTTP body publisher to produce a broadcastify call event
|
||||||
|
*/
|
||||||
|
public class BroadcastifyCallBuilder
|
||||||
|
{
|
||||||
|
private static final String DASH_DASH = "--";
|
||||||
|
private static final String BOUNDARY = "--sdrtrunk-sdrtrunk-sdrtrunk";
|
||||||
|
private List<Part> mParts = new ArrayList<>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs an instance
|
||||||
|
*/
|
||||||
|
public BroadcastifyCallBuilder()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Access the static multi-part boundary string
|
||||||
|
*/
|
||||||
|
public String getBoundary()
|
||||||
|
{
|
||||||
|
return BOUNDARY;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a string part to the call
|
||||||
|
*/
|
||||||
|
public BroadcastifyCallBuilder addPart(FormField key, String value)
|
||||||
|
{
|
||||||
|
if(key != null && value != null)
|
||||||
|
{
|
||||||
|
mParts.add(new Part(key.getHeader(), value));
|
||||||
|
}
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a number part to the call
|
||||||
|
*/
|
||||||
|
public BroadcastifyCallBuilder addPart(FormField key, Number value)
|
||||||
|
{
|
||||||
|
if(key != null && value != null)
|
||||||
|
{
|
||||||
|
mParts.add(new Part(key.getHeader(), value.toString()));
|
||||||
|
}
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a form-data item
|
||||||
|
*/
|
||||||
|
private static String formatPart(Part part, String boundary)
|
||||||
|
{
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
sb.append(DASH_DASH).append(boundary).append("\r\n");
|
||||||
|
sb.append("Content-Disposition: form-data; name=\"").append(part.mKey).append("\"\r\n\r\n");
|
||||||
|
sb.append(part.getValue()).append("\r\n");
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates the boundary closing item
|
||||||
|
*/
|
||||||
|
private static String getClosingBoundary(String boundary)
|
||||||
|
{
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
sb.append(DASH_DASH).append(boundary).append(DASH_DASH).append("\r\n");
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a BodyPublisher for accessing the call form data
|
||||||
|
*/
|
||||||
|
public HttpRequest.BodyPublisher build()
|
||||||
|
{
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
|
||||||
|
for(Part part: mParts)
|
||||||
|
{
|
||||||
|
sb.append(formatPart(part, BOUNDARY));
|
||||||
|
}
|
||||||
|
|
||||||
|
sb.append(getClosingBoundary(BOUNDARY));
|
||||||
|
|
||||||
|
return HttpRequest.BodyPublishers.ofByteArray(sb.toString().getBytes());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString()
|
||||||
|
{
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
|
||||||
|
for(Part part: mParts)
|
||||||
|
{
|
||||||
|
sb.append(formatPart(part, BOUNDARY));
|
||||||
|
}
|
||||||
|
|
||||||
|
sb.append(getClosingBoundary(BOUNDARY));
|
||||||
|
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Key:Value pair holder
|
||||||
|
*/
|
||||||
|
public class Part
|
||||||
|
{
|
||||||
|
private String mKey;
|
||||||
|
private String mValue;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a new part
|
||||||
|
* @param key value
|
||||||
|
* @param value item
|
||||||
|
*/
|
||||||
|
public Part(String key, String value)
|
||||||
|
{
|
||||||
|
mKey = key;
|
||||||
|
mValue = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getKey()
|
||||||
|
{
|
||||||
|
return mKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getValue()
|
||||||
|
{
|
||||||
|
return mValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,138 @@
|
|||||||
|
/*
|
||||||
|
* *****************************************************************************
|
||||||
|
* Copyright (C) 2014-2020 Dennis Sheirer
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||||
|
* ****************************************************************************
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.github.dsheirer.audio.broadcast.broadcastify;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
|
||||||
|
import io.github.dsheirer.audio.broadcast.BroadcastConfiguration;
|
||||||
|
import io.github.dsheirer.audio.broadcast.BroadcastFormat;
|
||||||
|
import io.github.dsheirer.audio.broadcast.BroadcastServerType;
|
||||||
|
import javafx.beans.binding.Bindings;
|
||||||
|
import javafx.beans.property.IntegerProperty;
|
||||||
|
import javafx.beans.property.SimpleIntegerProperty;
|
||||||
|
import javafx.beans.property.SimpleStringProperty;
|
||||||
|
import javafx.beans.property.StringProperty;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Streaming configuration for Broadcastify Calls API.
|
||||||
|
*
|
||||||
|
* Note: this API is not a streaming audio service, rather a completed call push service. However, it fits nicely
|
||||||
|
* with the structure of the audio streaming subsystem in sdrtrunk
|
||||||
|
*/
|
||||||
|
public class BroadcastifyCallConfiguration extends BroadcastConfiguration
|
||||||
|
{
|
||||||
|
public static final String DEVELOPMENT_ENDPOINT = "https://api.broadcastify.com/call-upload-dev";
|
||||||
|
public static final String PRODUCTION_ENDPOINT = "https://api.broadcastify.com/call-upload";
|
||||||
|
|
||||||
|
private IntegerProperty mSystemID = new SimpleIntegerProperty();
|
||||||
|
private StringProperty mApiKey = new SimpleStringProperty();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor for faster jackson
|
||||||
|
*/
|
||||||
|
public BroadcastifyCallConfiguration()
|
||||||
|
{
|
||||||
|
this(BroadcastFormat.MP3);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Public constructor.
|
||||||
|
* @param format to use for audio recording (MP3)
|
||||||
|
*/
|
||||||
|
public BroadcastifyCallConfiguration(BroadcastFormat format)
|
||||||
|
{
|
||||||
|
super(format);
|
||||||
|
|
||||||
|
if(getHost() == null)
|
||||||
|
{
|
||||||
|
setHost(PRODUCTION_ENDPOINT);
|
||||||
|
}
|
||||||
|
|
||||||
|
//The parent class binds this property, so we unbind it and rebind it here
|
||||||
|
mValid.unbind();
|
||||||
|
mValid.bind(Bindings.and(Bindings.and(Bindings.greaterThan(mSystemID, 0), Bindings.isNotNull(mApiKey)),
|
||||||
|
Bindings.isNotNull(mHost)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* System ID as a property
|
||||||
|
*/
|
||||||
|
public IntegerProperty systemIDProperty()
|
||||||
|
{
|
||||||
|
return mSystemID;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* API key as a property
|
||||||
|
*/
|
||||||
|
public StringProperty apiKeyProperty()
|
||||||
|
{
|
||||||
|
return mApiKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* API Key
|
||||||
|
*/
|
||||||
|
@JacksonXmlProperty(isAttribute = true, localName = "api_key")
|
||||||
|
public String getApiKey()
|
||||||
|
{
|
||||||
|
return mApiKey.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the api key
|
||||||
|
* @param apiKey
|
||||||
|
*/
|
||||||
|
public void setApiKey(String apiKey)
|
||||||
|
{
|
||||||
|
mApiKey.setValue(apiKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* System ID as provided by broadcastify.com
|
||||||
|
*/
|
||||||
|
@JacksonXmlProperty(isAttribute = true, localName = "system_id")
|
||||||
|
public int getSystemID()
|
||||||
|
{
|
||||||
|
return mSystemID.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the system ID provided by broadcastify.com
|
||||||
|
*/
|
||||||
|
public void setSystemID(int systemID)
|
||||||
|
{
|
||||||
|
mSystemID.set(systemID);
|
||||||
|
}
|
||||||
|
|
||||||
|
@JacksonXmlProperty(isAttribute = true, localName = "type", namespace = "http://www.w3.org/2001/XMLSchema-instance")
|
||||||
|
@Override
|
||||||
|
public BroadcastServerType getBroadcastServerType()
|
||||||
|
{
|
||||||
|
return BroadcastServerType.BROADCASTIFY_CALL;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BroadcastConfiguration copyOf()
|
||||||
|
{
|
||||||
|
BroadcastifyCallConfiguration copy = new BroadcastifyCallConfiguration();
|
||||||
|
copy.setSystemID(getSystemID());
|
||||||
|
return copy;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,96 +0,0 @@
|
|||||||
/*******************************************************************************
|
|
||||||
* sdrtrunk
|
|
||||||
* Copyright (C) 2014-2016 Dennis Sheirer
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
|
||||||
*
|
|
||||||
******************************************************************************/
|
|
||||||
package io.github.dsheirer.audio.broadcast.broadcastify;
|
|
||||||
|
|
||||||
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
|
|
||||||
import io.github.dsheirer.audio.broadcast.BroadcastConfiguration;
|
|
||||||
import io.github.dsheirer.audio.broadcast.BroadcastFormat;
|
|
||||||
import io.github.dsheirer.audio.broadcast.BroadcastServerType;
|
|
||||||
import io.github.dsheirer.audio.broadcast.icecast.IcecastTCPConfiguration;
|
|
||||||
|
|
||||||
public class BroadcastifyConfiguration extends IcecastTCPConfiguration
|
|
||||||
{
|
|
||||||
private int mFeedID;
|
|
||||||
|
|
||||||
public BroadcastifyConfiguration()
|
|
||||||
{
|
|
||||||
//No-arg constructor for JAXB
|
|
||||||
this(BroadcastFormat.MP3);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Broadcastify configuration for Icecast 2.3.2 compatible servers
|
|
||||||
*
|
|
||||||
* @param format of output audio (MP3)
|
|
||||||
*/
|
|
||||||
public BroadcastifyConfiguration(BroadcastFormat format)
|
|
||||||
{
|
|
||||||
super(format);
|
|
||||||
|
|
||||||
setBitRate(16);
|
|
||||||
setChannels(1);
|
|
||||||
setSampleRate(8000);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public BroadcastConfiguration copyOf()
|
|
||||||
{
|
|
||||||
BroadcastifyConfiguration copy = new BroadcastifyConfiguration(getBroadcastFormat());
|
|
||||||
|
|
||||||
//Broadcast Configuration Parameters
|
|
||||||
copy.setName(getName());
|
|
||||||
copy.setHost(getHost());
|
|
||||||
copy.setPort(getPort());
|
|
||||||
copy.setPassword(getPassword());
|
|
||||||
copy.setDelay(getDelay());
|
|
||||||
|
|
||||||
copy.setEnabled(false);
|
|
||||||
|
|
||||||
//Icecast Configuration Parameters
|
|
||||||
copy.setUserName(getUserName());
|
|
||||||
copy.setMountPoint(getMountPoint());
|
|
||||||
copy.setDescription(getDescription());
|
|
||||||
copy.setGenre(getGenre());
|
|
||||||
copy.setPublic(isPublic());
|
|
||||||
copy.setURL(getURL());
|
|
||||||
|
|
||||||
//Broadcastify Configuration Parameters
|
|
||||||
copy.setFeedID(getFeedID());
|
|
||||||
|
|
||||||
return copy;
|
|
||||||
}
|
|
||||||
|
|
||||||
@JacksonXmlProperty(isAttribute = true, localName = "type", namespace = "http://www.w3.org/2001/XMLSchema-instance")
|
|
||||||
@Override
|
|
||||||
public BroadcastServerType getBroadcastServerType()
|
|
||||||
{
|
|
||||||
return BroadcastServerType.BROADCASTIFY;
|
|
||||||
}
|
|
||||||
|
|
||||||
@JacksonXmlProperty(isAttribute = true, localName = "feed_id")
|
|
||||||
public int getFeedID()
|
|
||||||
{
|
|
||||||
return mFeedID;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setFeedID(int feedID)
|
|
||||||
{
|
|
||||||
mFeedID = feedID;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,408 +0,0 @@
|
|||||||
/*
|
|
||||||
*
|
|
||||||
* * ******************************************************************************
|
|
||||||
* * Copyright (C) 2014-2019 Dennis Sheirer
|
|
||||||
* *
|
|
||||||
* * This program is free software: you can redistribute it and/or modify
|
|
||||||
* * it under the terms of the GNU General Public License as published by
|
|
||||||
* * the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* * (at your option) any later version.
|
|
||||||
* *
|
|
||||||
* * This program is distributed in the hope that it will be useful,
|
|
||||||
* * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* * GNU General Public License for more details.
|
|
||||||
* *
|
|
||||||
* * You should have received a copy of the GNU General Public License
|
|
||||||
* * along with this program. If not, see <http://www.gnu.org/licenses/>
|
|
||||||
* * *****************************************************************************
|
|
||||||
*
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
package io.github.dsheirer.audio.broadcast.broadcastify;
|
|
||||||
|
|
||||||
import io.github.dsheirer.alias.AliasModel;
|
|
||||||
import io.github.dsheirer.audio.broadcast.BroadcastConfiguration;
|
|
||||||
import io.github.dsheirer.audio.broadcast.BroadcastConfigurationEditor;
|
|
||||||
import io.github.dsheirer.audio.broadcast.BroadcastEvent;
|
|
||||||
import io.github.dsheirer.audio.broadcast.BroadcastModel;
|
|
||||||
import io.github.dsheirer.audio.broadcast.BroadcastServerType;
|
|
||||||
import io.github.dsheirer.icon.IconManager;
|
|
||||||
import io.github.dsheirer.preference.UserPreferences;
|
|
||||||
import io.github.dsheirer.rrapi.type.UserFeedBroadcast;
|
|
||||||
import io.github.dsheirer.sample.Listener;
|
|
||||||
import net.miginfocom.swing.MigLayout;
|
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
|
|
||||||
import javax.swing.ImageIcon;
|
|
||||||
import javax.swing.JButton;
|
|
||||||
import javax.swing.JCheckBox;
|
|
||||||
import javax.swing.JLabel;
|
|
||||||
import javax.swing.JOptionPane;
|
|
||||||
import javax.swing.JSlider;
|
|
||||||
import javax.swing.JTextField;
|
|
||||||
import javax.swing.event.ChangeEvent;
|
|
||||||
import javax.swing.event.ChangeListener;
|
|
||||||
import java.awt.Dimension;
|
|
||||||
import java.awt.EventQueue;
|
|
||||||
import java.awt.event.ActionEvent;
|
|
||||||
import java.awt.event.ActionListener;
|
|
||||||
|
|
||||||
public class BroadcastifyConfigurationEditor extends BroadcastConfigurationEditor implements Listener<UserFeedBroadcast>
|
|
||||||
{
|
|
||||||
private final static Logger mLog = LoggerFactory.getLogger(BroadcastifyConfigurationEditor.class);
|
|
||||||
|
|
||||||
private static final int ONE_MINUTE_MS = 60000;
|
|
||||||
|
|
||||||
private JTextField mName;
|
|
||||||
private JTextField mServer;
|
|
||||||
private JTextField mPort;
|
|
||||||
private JTextField mMountPoint;
|
|
||||||
private JTextField mPassword;
|
|
||||||
private JTextField mFeedID;
|
|
||||||
private JSlider mDelay;
|
|
||||||
private JLabel mDelayValue;
|
|
||||||
private JSlider mMaximumRecordingAge;
|
|
||||||
private JLabel mMaximumRecordingAgeValue;
|
|
||||||
private JCheckBox mEnabled;
|
|
||||||
private JButton mSaveButton;
|
|
||||||
private JButton mResetButton;
|
|
||||||
private UserPreferences mUserPreferences;
|
|
||||||
|
|
||||||
public BroadcastifyConfigurationEditor(UserPreferences userPreferences, BroadcastModel broadcastModel,
|
|
||||||
AliasModel aliasModel, IconManager iconManager)
|
|
||||||
{
|
|
||||||
super(broadcastModel, aliasModel, iconManager);
|
|
||||||
mUserPreferences = userPreferences;
|
|
||||||
|
|
||||||
init();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void init()
|
|
||||||
{
|
|
||||||
setLayout(new MigLayout("fill,wrap 2", "[align right][grow,fill]",
|
|
||||||
"[][][][][][][][][][][][][][][grow,fill]"));
|
|
||||||
setPreferredSize(new Dimension(150, 400));
|
|
||||||
|
|
||||||
|
|
||||||
JLabel channelLabel = new JLabel("Broadcastify Stream");
|
|
||||||
|
|
||||||
ImageIcon broadcastifyIcon = mIconManager.getScaledIcon(BroadcastServerType.BROADCASTIFY.getIconPath(), 25);
|
|
||||||
channelLabel.setIcon(broadcastifyIcon);
|
|
||||||
add(channelLabel, "span, align center");
|
|
||||||
|
|
||||||
JButton lookupButton = new JButton("Lookup ...");
|
|
||||||
lookupButton.addActionListener(new ActionListener()
|
|
||||||
{
|
|
||||||
@Override
|
|
||||||
public void actionPerformed(ActionEvent e)
|
|
||||||
{
|
|
||||||
final UserFeedSelectionDialog dialog = new UserFeedSelectionDialog(mUserPreferences,BroadcastifyConfigurationEditor.this);
|
|
||||||
|
|
||||||
EventQueue.invokeLater(new Runnable()
|
|
||||||
{
|
|
||||||
@Override
|
|
||||||
public void run()
|
|
||||||
{
|
|
||||||
dialog.setLocationRelativeTo(BroadcastifyConfigurationEditor.this);
|
|
||||||
dialog.setVisible(true);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
add(new JLabel(""));
|
|
||||||
add(lookupButton, "grow,wrap");
|
|
||||||
|
|
||||||
add(new JLabel("Name:"));
|
|
||||||
mName = new JTextField();
|
|
||||||
mName.getDocument().addDocumentListener(this);
|
|
||||||
mName.setToolTipText("Unique name for this stream");
|
|
||||||
add(mName);
|
|
||||||
|
|
||||||
add(new JLabel("Server:"));
|
|
||||||
mServer = new JTextField();
|
|
||||||
mServer.getDocument().addDocumentListener(this);
|
|
||||||
mServer.setToolTipText("Server address (e.g. audio3.broadcastify.com). See MyBCFY feed details, Technicals tab");
|
|
||||||
add(mServer);
|
|
||||||
|
|
||||||
add(new JLabel("Port:"));
|
|
||||||
mPort = new JTextField();
|
|
||||||
mPort.getDocument().addDocumentListener(this);
|
|
||||||
mPort.setToolTipText("Server port number (e.g. 80). See MyBCFY feed details, Technicals tab");
|
|
||||||
add(mPort);
|
|
||||||
|
|
||||||
add(new JLabel("Mount:"));
|
|
||||||
mMountPoint = new JTextField();
|
|
||||||
mMountPoint.getDocument().addDocumentListener(this);
|
|
||||||
mMountPoint.setToolTipText("Mount point (e.g. \\awlkejrlkjsd). See MyBCFY feed details, Technicals tab");
|
|
||||||
add(mMountPoint);
|
|
||||||
|
|
||||||
add(new JLabel("Password:"));
|
|
||||||
mPassword = new JTextField();
|
|
||||||
mPassword.getDocument().addDocumentListener(this);
|
|
||||||
mPassword.setToolTipText("Password. See MyBCFY feed details, Technicals tab");
|
|
||||||
add(mPassword);
|
|
||||||
|
|
||||||
add(new JLabel("Feed ID:"));
|
|
||||||
mFeedID = new JTextField();
|
|
||||||
mFeedID.getDocument().addDocumentListener(this);
|
|
||||||
mFeedID.setToolTipText("Feed ID (optional). See MyBCFY feed details");
|
|
||||||
add(mFeedID);
|
|
||||||
|
|
||||||
|
|
||||||
add(new JLabel());
|
|
||||||
mDelayValue = new JLabel("0 Minutes");
|
|
||||||
add(mDelayValue);
|
|
||||||
|
|
||||||
add(new JLabel("Delay:"));
|
|
||||||
mDelay = new JSlider(0, 60, 0);
|
|
||||||
mDelay.setMajorTickSpacing(10);
|
|
||||||
mDelay.setMinorTickSpacing(5);
|
|
||||||
mDelay.addChangeListener(new ChangeListener()
|
|
||||||
{
|
|
||||||
@Override
|
|
||||||
public void stateChanged(ChangeEvent e)
|
|
||||||
{
|
|
||||||
mDelayValue.setText(mDelay.getValue() + " Minutes");
|
|
||||||
setModified(true);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
mDelay.setToolTipText("Audio broadcast delay in minutes");
|
|
||||||
add(mDelay);
|
|
||||||
|
|
||||||
add(new JLabel());
|
|
||||||
mMaximumRecordingAgeValue = new JLabel("1 Minute");
|
|
||||||
add(mMaximumRecordingAgeValue);
|
|
||||||
|
|
||||||
add(new JLabel("Age Limit:"));
|
|
||||||
mMaximumRecordingAge = new JSlider(1,60,5);
|
|
||||||
mMaximumRecordingAge.setMajorTickSpacing(10);
|
|
||||||
mMaximumRecordingAge.setMinorTickSpacing(5);
|
|
||||||
mMaximumRecordingAge.addChangeListener(new ChangeListener()
|
|
||||||
{
|
|
||||||
@Override
|
|
||||||
public void stateChanged(ChangeEvent e)
|
|
||||||
{
|
|
||||||
int value = mMaximumRecordingAge.getValue();
|
|
||||||
|
|
||||||
mMaximumRecordingAgeValue.setText(value + " Minute" + (value > 1 ? "s" : ""));
|
|
||||||
setModified(true);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
mMaximumRecordingAge.setToolTipText("Maximum recording age (in addition to delay) to stream");
|
|
||||||
add(mMaximumRecordingAge);
|
|
||||||
|
|
||||||
mEnabled = new JCheckBox("Enabled");
|
|
||||||
mEnabled.setToolTipText("Enable (checked) or disable this stream");
|
|
||||||
mEnabled.addActionListener(new ActionListener()
|
|
||||||
{
|
|
||||||
@Override
|
|
||||||
public void actionPerformed(ActionEvent e)
|
|
||||||
{
|
|
||||||
setModified(true);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
add(new JLabel());
|
|
||||||
add(mEnabled);
|
|
||||||
|
|
||||||
mSaveButton = new JButton("Save");
|
|
||||||
mSaveButton.setEnabled(false);
|
|
||||||
mSaveButton.setToolTipText("Click to save configuration information");
|
|
||||||
mSaveButton.addActionListener(new ActionListener()
|
|
||||||
{
|
|
||||||
@Override
|
|
||||||
public void actionPerformed(ActionEvent e)
|
|
||||||
{
|
|
||||||
save();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
add(mSaveButton);
|
|
||||||
|
|
||||||
mResetButton = new JButton("Reset");
|
|
||||||
mResetButton.setEnabled(false);
|
|
||||||
mResetButton.setToolTipText("Click to reset any changes you've made\n since the last save");
|
|
||||||
mResetButton.addActionListener(new ActionListener()
|
|
||||||
{
|
|
||||||
@Override
|
|
||||||
public void actionPerformed(ActionEvent e)
|
|
||||||
{
|
|
||||||
setModified(false);
|
|
||||||
setItem(getItem());
|
|
||||||
}
|
|
||||||
});
|
|
||||||
add(mResetButton);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setModified(boolean modified)
|
|
||||||
{
|
|
||||||
super.setModified(modified);
|
|
||||||
|
|
||||||
mSaveButton.setEnabled(modified);
|
|
||||||
mResetButton.setEnabled(modified);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setItem(BroadcastConfiguration item)
|
|
||||||
{
|
|
||||||
super.setItem(item);
|
|
||||||
|
|
||||||
if(hasItem())
|
|
||||||
{
|
|
||||||
BroadcastifyConfiguration config = (BroadcastifyConfiguration) getItem();
|
|
||||||
|
|
||||||
mName.setText(config.getName());
|
|
||||||
mServer.setText(config.getHost());
|
|
||||||
mPort.setText(config.getPort() != 0 ? String.valueOf(config.getPort()) : null);
|
|
||||||
mMountPoint.setText(config.getMountPoint());
|
|
||||||
mPassword.setText(config.getPassword());
|
|
||||||
mFeedID.setText(String.valueOf(config.getFeedID()));
|
|
||||||
mDelay.setValue((int) (config.getDelay() / ONE_MINUTE_MS));
|
|
||||||
mMaximumRecordingAge.setValue((int)config.getMaximumRecordingAge() / ONE_MINUTE_MS);
|
|
||||||
mEnabled.setSelected(config.isEnabled());
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
mName.setText(null);
|
|
||||||
mServer.setText(null);
|
|
||||||
mPort.setText(null);
|
|
||||||
mMountPoint.setText(null);
|
|
||||||
mPassword.setText(null);
|
|
||||||
mFeedID.setText(null);
|
|
||||||
mDelay.setValue(0);
|
|
||||||
mMaximumRecordingAge.setValue(1);
|
|
||||||
mEnabled.setSelected(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
setModified(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void save()
|
|
||||||
{
|
|
||||||
BroadcastifyConfiguration config = (BroadcastifyConfiguration) getItem();
|
|
||||||
|
|
||||||
if(validateConfiguration())
|
|
||||||
{
|
|
||||||
updateConfigurationName(config, mName.getText());
|
|
||||||
config.setHost(mServer.getText());
|
|
||||||
config.setPort(getPort());
|
|
||||||
config.setMountPoint(mMountPoint.getText());
|
|
||||||
config.setPassword(mPassword.getText());
|
|
||||||
config.setFeedID(getFeedID());
|
|
||||||
config.setDelay(mDelay.getValue() * ONE_MINUTE_MS);
|
|
||||||
config.setMaximumRecordingAge(mMaximumRecordingAge.getValue() * ONE_MINUTE_MS);
|
|
||||||
config.setEnabled(mEnabled.isSelected());
|
|
||||||
|
|
||||||
setModified(false);
|
|
||||||
|
|
||||||
mBroadcastModel.process(new BroadcastEvent(getItem(), BroadcastEvent.Event.CONFIGURATION_CHANGE));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Validates the user specified values in the controls prior to saving them to the configuration
|
|
||||||
*/
|
|
||||||
private boolean validateConfiguration()
|
|
||||||
{
|
|
||||||
String name = mName.getText();
|
|
||||||
|
|
||||||
//A unique stream name is required
|
|
||||||
if(!mBroadcastModel.isUniqueName(name, getItem()))
|
|
||||||
{
|
|
||||||
String message = (name == null || name.isEmpty()) ?
|
|
||||||
"Please specify a stream name." : "Stream name " + name +
|
|
||||||
"is already in use.\nPlease choose another name.";
|
|
||||||
|
|
||||||
JOptionPane.showMessageDialog(BroadcastifyConfigurationEditor.this,
|
|
||||||
message, "Invalid Stream Name", JOptionPane.ERROR_MESSAGE);
|
|
||||||
|
|
||||||
mName.requestFocus();
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
//Server address is optional, but required
|
|
||||||
if(!validateTextField(mServer, "Invalid Server Address", "Please specify a server address."))
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
//Port is optional, but required
|
|
||||||
if(!validateIntegerTextField(mPort, "Invalid Port Number", "Please specify a port number (1 <> 65535)", 1, 65535))
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
//Mount Point is optional, but required
|
|
||||||
if(!validateTextField(mMountPoint, "Invalid Mount Point", "Please specify a mount point."))
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
//Password is optional, but required
|
|
||||||
if(!validateTextField(mPassword, "Invalid Password", "Please specify a password."))
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Parses an Integer from the port text control or returns 0 if the control doesn't contain an integer value.
|
|
||||||
*/
|
|
||||||
private int getPort()
|
|
||||||
{
|
|
||||||
String port = mPort.getText();
|
|
||||||
|
|
||||||
if(port != null && !port.isEmpty())
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
return Integer.parseInt(port);
|
|
||||||
}
|
|
||||||
catch(Exception e)
|
|
||||||
{
|
|
||||||
//Do nothing, we couldn't parse the number value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Parses an Integer from the port text control or returns 0 if the control doesn't contain an integer value.
|
|
||||||
*/
|
|
||||||
private int getFeedID()
|
|
||||||
{
|
|
||||||
String feedID = mFeedID.getText();
|
|
||||||
|
|
||||||
if(feedID != null && !feedID.isEmpty())
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
return Integer.parseInt(feedID);
|
|
||||||
}
|
|
||||||
catch(Exception e)
|
|
||||||
{
|
|
||||||
//Do nothing, we couldn't parse the number value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void receive(UserFeedBroadcast userFeedBroadcast)
|
|
||||||
{
|
|
||||||
mName.setText(userFeedBroadcast.getDescription());
|
|
||||||
mServer.setText(userFeedBroadcast.getHostname());
|
|
||||||
mPort.setText(userFeedBroadcast.getPort());
|
|
||||||
mMountPoint.setText(userFeedBroadcast.getMount());
|
|
||||||
mPassword.setText(userFeedBroadcast.getPassword());
|
|
||||||
mFeedID.setText(String.valueOf(userFeedBroadcast.getFeedId()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,125 @@
|
|||||||
|
/*
|
||||||
|
*
|
||||||
|
* * ******************************************************************************
|
||||||
|
* * Copyright (C) 2014-2020 Dennis Sheirer
|
||||||
|
* *
|
||||||
|
* * This program is free software: you can redistribute it and/or modify
|
||||||
|
* * it under the terms of the GNU General Public License as published by
|
||||||
|
* * the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* * (at your option) any later version.
|
||||||
|
* *
|
||||||
|
* * This program is distributed in the hope that it will be useful,
|
||||||
|
* * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* * GNU General Public License for more details.
|
||||||
|
* *
|
||||||
|
* * You should have received a copy of the GNU General Public License
|
||||||
|
* * along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||||
|
* * *****************************************************************************
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
package io.github.dsheirer.audio.broadcast.broadcastify;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
|
||||||
|
import io.github.dsheirer.audio.broadcast.BroadcastConfiguration;
|
||||||
|
import io.github.dsheirer.audio.broadcast.BroadcastFormat;
|
||||||
|
import io.github.dsheirer.audio.broadcast.BroadcastServerType;
|
||||||
|
import io.github.dsheirer.audio.broadcast.icecast.IcecastTCPConfiguration;
|
||||||
|
import io.github.dsheirer.rrapi.type.UserFeedBroadcast;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
public class BroadcastifyFeedConfiguration extends IcecastTCPConfiguration
|
||||||
|
{
|
||||||
|
private final static Logger mLog = LoggerFactory.getLogger(BroadcastifyFeedConfiguration.class);
|
||||||
|
|
||||||
|
private int mFeedID;
|
||||||
|
|
||||||
|
public BroadcastifyFeedConfiguration()
|
||||||
|
{
|
||||||
|
//No-arg constructor for JAXB
|
||||||
|
this(BroadcastFormat.MP3);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Broadcastify configuration for Icecast 2.3.2 compatible servers
|
||||||
|
*
|
||||||
|
* @param format of output audio (MP3)
|
||||||
|
*/
|
||||||
|
public BroadcastifyFeedConfiguration(BroadcastFormat format)
|
||||||
|
{
|
||||||
|
super(format);
|
||||||
|
|
||||||
|
setBitRate(16);
|
||||||
|
setChannels(1);
|
||||||
|
setSampleRate(8000);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static BroadcastifyFeedConfiguration from(UserFeedBroadcast userFeedBroadcast)
|
||||||
|
{
|
||||||
|
BroadcastifyFeedConfiguration config = new BroadcastifyFeedConfiguration(BroadcastFormat.MP3);
|
||||||
|
config.setName(userFeedBroadcast.getDescription());
|
||||||
|
config.setHost(userFeedBroadcast.getHostname());
|
||||||
|
config.setMountPoint(userFeedBroadcast.getMount());
|
||||||
|
config.setFeedID(userFeedBroadcast.getFeedId());
|
||||||
|
config.setPassword(userFeedBroadcast.getPassword());
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
config.setPort(Integer.parseInt(userFeedBroadcast.getPort()));
|
||||||
|
}
|
||||||
|
catch(Exception e)
|
||||||
|
{
|
||||||
|
mLog.error("Error creating broadcastify configuration from radio reference user feed instance");
|
||||||
|
}
|
||||||
|
|
||||||
|
return config;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BroadcastConfiguration copyOf()
|
||||||
|
{
|
||||||
|
BroadcastifyFeedConfiguration copy = new BroadcastifyFeedConfiguration(getBroadcastFormat());
|
||||||
|
|
||||||
|
//Broadcast Configuration Parameters
|
||||||
|
copy.setName(getName());
|
||||||
|
copy.setHost(getHost());
|
||||||
|
copy.setPort(getPort());
|
||||||
|
copy.setPassword(getPassword());
|
||||||
|
copy.setDelay(getDelay());
|
||||||
|
|
||||||
|
copy.setEnabled(false);
|
||||||
|
|
||||||
|
//Icecast Configuration Parameters
|
||||||
|
copy.setUserName(getUserName());
|
||||||
|
copy.setMountPoint(getMountPoint());
|
||||||
|
copy.setDescription(getDescription());
|
||||||
|
copy.setGenre(getGenre());
|
||||||
|
copy.setPublic(isPublic());
|
||||||
|
copy.setURL(getURL());
|
||||||
|
|
||||||
|
//Broadcastify Configuration Parameters
|
||||||
|
copy.setFeedID(getFeedID());
|
||||||
|
|
||||||
|
return copy;
|
||||||
|
}
|
||||||
|
|
||||||
|
@JacksonXmlProperty(isAttribute = true, localName = "type", namespace = "http://www.w3.org/2001/XMLSchema-instance")
|
||||||
|
@Override
|
||||||
|
public BroadcastServerType getBroadcastServerType()
|
||||||
|
{
|
||||||
|
return BroadcastServerType.BROADCASTIFY;
|
||||||
|
}
|
||||||
|
|
||||||
|
@JacksonXmlProperty(isAttribute = true, localName = "feed_id")
|
||||||
|
public int getFeedID()
|
||||||
|
{
|
||||||
|
return mFeedID;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setFeedID(int feedID)
|
||||||
|
{
|
||||||
|
mFeedID = feedID;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
/*
|
||||||
|
* *****************************************************************************
|
||||||
|
* Copyright (C) 2014-2020 Dennis Sheirer
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||||
|
* ****************************************************************************
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.github.dsheirer.audio.broadcast.broadcastify;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* HTTP headers used for posting to Broadcastify calls api
|
||||||
|
*/
|
||||||
|
public enum FormField
|
||||||
|
{
|
||||||
|
API_KEY("apiKey"),
|
||||||
|
SYSTEM_ID("systemId"),
|
||||||
|
CALL_DURATION("callDuration"),
|
||||||
|
TIMESTAMP("ts"),
|
||||||
|
TALKGROUP_ID("tg"),
|
||||||
|
RADIO_ID("src"),
|
||||||
|
FREQUENCY("freq"),
|
||||||
|
ENCODING("enc"),
|
||||||
|
TEST("test");
|
||||||
|
|
||||||
|
private String mHeader;
|
||||||
|
|
||||||
|
FormField(String header)
|
||||||
|
{
|
||||||
|
mHeader = header;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getHeader()
|
||||||
|
{
|
||||||
|
return mHeader;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,343 +0,0 @@
|
|||||||
/*
|
|
||||||
*
|
|
||||||
* * ******************************************************************************
|
|
||||||
* * Copyright (C) 2014-2019 Dennis Sheirer
|
|
||||||
* *
|
|
||||||
* * This program is free software: you can redistribute it and/or modify
|
|
||||||
* * it under the terms of the GNU General Public License as published by
|
|
||||||
* * the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* * (at your option) any later version.
|
|
||||||
* *
|
|
||||||
* * This program is distributed in the hope that it will be useful,
|
|
||||||
* * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* * GNU General Public License for more details.
|
|
||||||
* *
|
|
||||||
* * You should have received a copy of the GNU General Public License
|
|
||||||
* * along with this program. If not, see <http://www.gnu.org/licenses/>
|
|
||||||
* * *****************************************************************************
|
|
||||||
*
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
package io.github.dsheirer.audio.broadcast.broadcastify;
|
|
||||||
|
|
||||||
import com.google.common.eventbus.Subscribe;
|
|
||||||
import io.github.dsheirer.eventbus.MyEventBus;
|
|
||||||
import io.github.dsheirer.preference.PreferenceType;
|
|
||||||
import io.github.dsheirer.preference.UserPreferences;
|
|
||||||
import io.github.dsheirer.rrapi.RadioReferenceException;
|
|
||||||
import io.github.dsheirer.rrapi.RadioReferenceService;
|
|
||||||
import io.github.dsheirer.rrapi.type.AuthorizationInformation;
|
|
||||||
import io.github.dsheirer.rrapi.type.UserFeedBroadcast;
|
|
||||||
import io.github.dsheirer.rrapi.type.UserInfo;
|
|
||||||
import io.github.dsheirer.sample.Listener;
|
|
||||||
import net.miginfocom.swing.MigLayout;
|
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
|
|
||||||
import javax.swing.JButton;
|
|
||||||
import javax.swing.JCheckBox;
|
|
||||||
import javax.swing.JDialog;
|
|
||||||
import javax.swing.JFrame;
|
|
||||||
import javax.swing.JLabel;
|
|
||||||
import javax.swing.JList;
|
|
||||||
import javax.swing.JOptionPane;
|
|
||||||
import javax.swing.JPanel;
|
|
||||||
import javax.swing.JScrollPane;
|
|
||||||
import javax.swing.JTextField;
|
|
||||||
import javax.swing.ListCellRenderer;
|
|
||||||
import javax.swing.event.DocumentEvent;
|
|
||||||
import javax.swing.event.DocumentListener;
|
|
||||||
import javax.swing.event.ListSelectionEvent;
|
|
||||||
import javax.swing.event.ListSelectionListener;
|
|
||||||
import java.awt.BorderLayout;
|
|
||||||
import java.awt.Component;
|
|
||||||
import java.awt.EventQueue;
|
|
||||||
import java.awt.event.ActionEvent;
|
|
||||||
import java.awt.event.ActionListener;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
public class UserFeedSelectionDialog extends JDialog implements DocumentListener
|
|
||||||
{
|
|
||||||
private final static Logger mLog = LoggerFactory.getLogger( UserFeedSelectionDialog.class );
|
|
||||||
|
|
||||||
public static final String SDRTRUNK_APP_KEY = "88969092";
|
|
||||||
|
|
||||||
private Listener<UserFeedBroadcast> mListener;
|
|
||||||
private UserPreferences mUserPreferences;
|
|
||||||
private JTextField mUserNameText;
|
|
||||||
private JTextField mPasswordText;
|
|
||||||
private JCheckBox mSaveCredentialsCheckBox;
|
|
||||||
private JButton mClearStoredCredentialsButton;
|
|
||||||
private JButton mLookupButton;
|
|
||||||
private JList mUserFeedBroadcastList;
|
|
||||||
private JButton mOKButton;
|
|
||||||
|
|
||||||
public UserFeedSelectionDialog(UserPreferences userPreferences, Listener<UserFeedBroadcast> listener)
|
|
||||||
{
|
|
||||||
mUserPreferences = userPreferences;
|
|
||||||
mListener = listener;
|
|
||||||
|
|
||||||
//Register to receive radio reference preferences updates
|
|
||||||
MyEventBus.getEventBus().register(this);
|
|
||||||
|
|
||||||
init();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void init()
|
|
||||||
{
|
|
||||||
JPanel panel = new JPanel();
|
|
||||||
panel.setLayout(new MigLayout("","[align right,grow,fill][align left,grow,fill][]","[][][][][][grow][]"));
|
|
||||||
|
|
||||||
panel.add(new JLabel("Please enter your credentials"), "align left,span");
|
|
||||||
|
|
||||||
panel.add(new JLabel("User Name:"));
|
|
||||||
mUserNameText = new JTextField();
|
|
||||||
String storedUserName = mUserPreferences.getRadioReferencePreference().getUserName();
|
|
||||||
if(storedUserName != null)
|
|
||||||
{
|
|
||||||
mUserNameText.setText(storedUserName);
|
|
||||||
}
|
|
||||||
mUserNameText.getDocument().addDocumentListener(this);
|
|
||||||
panel.add(mUserNameText, "wrap");
|
|
||||||
|
|
||||||
panel.add(new JLabel("Password:"));
|
|
||||||
mPasswordText = new JTextField();
|
|
||||||
String storedPassword = mUserPreferences.getRadioReferencePreference().getPassword();
|
|
||||||
if(storedPassword != null)
|
|
||||||
{
|
|
||||||
mPasswordText.setText(storedPassword);
|
|
||||||
}
|
|
||||||
mPasswordText.getDocument().addDocumentListener(this);
|
|
||||||
panel.add(mPasswordText, "wrap");
|
|
||||||
|
|
||||||
mSaveCredentialsCheckBox = new JCheckBox("Store Credentials");
|
|
||||||
mSaveCredentialsCheckBox.setSelected(mUserPreferences.getRadioReferencePreference().isStoreCredentials());
|
|
||||||
mSaveCredentialsCheckBox.addActionListener(new ActionListener()
|
|
||||||
{
|
|
||||||
@Override
|
|
||||||
public void actionPerformed(ActionEvent e)
|
|
||||||
{
|
|
||||||
//If user unchecks box and there are stored credentials, prompt to clear them
|
|
||||||
if(!mSaveCredentialsCheckBox.isSelected() && mUserPreferences.getRadioReferencePreference().hasStoredCredentials())
|
|
||||||
{
|
|
||||||
int choice = JOptionPane.showOptionDialog(UserFeedSelectionDialog.this,
|
|
||||||
"Would you like to remove previously stored credentials from this computer?",
|
|
||||||
"Clear Stored Credentials?", JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE,
|
|
||||||
null, null, 0);
|
|
||||||
|
|
||||||
if(choice == JOptionPane.YES_OPTION)
|
|
||||||
{
|
|
||||||
mUserPreferences.getRadioReferencePreference().removeStoreCredentials();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
panel.add(mSaveCredentialsCheckBox);
|
|
||||||
|
|
||||||
mClearStoredCredentialsButton = new JButton("Remove");
|
|
||||||
mClearStoredCredentialsButton.setToolTipText("Clear stored credentials");
|
|
||||||
mClearStoredCredentialsButton.setEnabled(mUserPreferences.getRadioReferencePreference().hasStoredCredentials());
|
|
||||||
mClearStoredCredentialsButton.addActionListener(new ActionListener()
|
|
||||||
{
|
|
||||||
@Override
|
|
||||||
public void actionPerformed(ActionEvent e)
|
|
||||||
{
|
|
||||||
mUserPreferences.getRadioReferencePreference().removeStoreCredentials();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
panel.add(mClearStoredCredentialsButton, "wrap");
|
|
||||||
|
|
||||||
mLookupButton = new JButton("Get Feeds");
|
|
||||||
mLookupButton.setEnabled(storedPassword != null && storedPassword != null);
|
|
||||||
mLookupButton.addActionListener(new ActionListener()
|
|
||||||
{
|
|
||||||
@Override
|
|
||||||
public void actionPerformed(ActionEvent e)
|
|
||||||
{
|
|
||||||
mLookupButton.setEnabled(false);
|
|
||||||
|
|
||||||
EventQueue.invokeLater(new Runnable()
|
|
||||||
{
|
|
||||||
@Override
|
|
||||||
public void run()
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
AuthorizationInformation authorizationInformation = new AuthorizationInformation(SDRTRUNK_APP_KEY,
|
|
||||||
mUserNameText.getText(), mPasswordText.getText());
|
|
||||||
|
|
||||||
mLog.debug("User:" + authorizationInformation.getUserName() + " Pass:" + authorizationInformation.getPassword() + " Key:" + authorizationInformation.getApplicationKey());
|
|
||||||
|
|
||||||
RadioReferenceService service = new RadioReferenceService(authorizationInformation);
|
|
||||||
UserInfo userInfo = service.getUserInfo();
|
|
||||||
mLog.info("RR User:" + userInfo.getUserName() + " Expiration Date:" + userInfo.getExpirationDate());
|
|
||||||
List<UserFeedBroadcast> feeds = service.getUserFeeds();
|
|
||||||
|
|
||||||
mUserFeedBroadcastList.setListData(feeds.toArray(new UserFeedBroadcast[feeds.size()]));
|
|
||||||
}
|
|
||||||
catch(RadioReferenceException rre)
|
|
||||||
{
|
|
||||||
JOptionPane.showMessageDialog(UserFeedSelectionDialog.this,
|
|
||||||
"Unable to create radio reference web service client. See log file.",
|
|
||||||
"Error", JOptionPane.ERROR_MESSAGE);
|
|
||||||
|
|
||||||
mLog.error("Error creating the radio reference web service client", rre);
|
|
||||||
}
|
|
||||||
|
|
||||||
mLookupButton.setEnabled(true);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
panel.add(mLookupButton,"grow,span");
|
|
||||||
|
|
||||||
mUserFeedBroadcastList = new JList();
|
|
||||||
mUserFeedBroadcastList.setVisibleRowCount(3);
|
|
||||||
mUserFeedBroadcastList.setLayoutOrientation(JList.VERTICAL);
|
|
||||||
mUserFeedBroadcastList.setCellRenderer(new CellRenderer());
|
|
||||||
mUserFeedBroadcastList.addListSelectionListener(new ListSelectionListener()
|
|
||||||
{
|
|
||||||
@Override
|
|
||||||
public void valueChanged(ListSelectionEvent e)
|
|
||||||
{
|
|
||||||
mOKButton.setEnabled(mUserFeedBroadcastList.getSelectedValue() != null);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
JScrollPane listScrollPane = new JScrollPane(mUserFeedBroadcastList);
|
|
||||||
panel.add(listScrollPane, "grow,span");
|
|
||||||
|
|
||||||
mOKButton = new JButton("Ok");
|
|
||||||
mOKButton.setEnabled(false);
|
|
||||||
mOKButton.addActionListener(new ActionListener()
|
|
||||||
{
|
|
||||||
@Override
|
|
||||||
public void actionPerformed(ActionEvent e)
|
|
||||||
{
|
|
||||||
if(mListener != null)
|
|
||||||
{
|
|
||||||
storeCredentials();
|
|
||||||
mListener.receive((UserFeedBroadcast)mUserFeedBroadcastList.getSelectedValue());
|
|
||||||
}
|
|
||||||
|
|
||||||
dispose();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
panel.add(mOKButton,"grow");
|
|
||||||
|
|
||||||
JButton cancelButton = new JButton("Cancel");
|
|
||||||
cancelButton.addActionListener(new ActionListener()
|
|
||||||
{
|
|
||||||
@Override
|
|
||||||
public void actionPerformed(ActionEvent e)
|
|
||||||
{
|
|
||||||
dispose();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
panel.add(cancelButton, "wrap");
|
|
||||||
|
|
||||||
setTitle("Broadcastify Feed Lookup");
|
|
||||||
setLayout(new BorderLayout());
|
|
||||||
add(panel, BorderLayout.CENTER);
|
|
||||||
setSize(400,450);
|
|
||||||
setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
|
|
||||||
setModalityType(ModalityType.APPLICATION_MODAL);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Stores login credentials in the user preferences
|
|
||||||
*/
|
|
||||||
private void storeCredentials()
|
|
||||||
{
|
|
||||||
boolean store = mSaveCredentialsCheckBox.isSelected();
|
|
||||||
mUserPreferences.getRadioReferencePreference().setStoreCredentials(store);
|
|
||||||
|
|
||||||
if(store)
|
|
||||||
{
|
|
||||||
String userName = mUserNameText.getText();
|
|
||||||
|
|
||||||
if(userName != null)
|
|
||||||
{
|
|
||||||
mUserPreferences.getRadioReferencePreference().setUserName(userName);
|
|
||||||
}
|
|
||||||
|
|
||||||
String password = mPasswordText.getText();
|
|
||||||
|
|
||||||
if(password != null)
|
|
||||||
{
|
|
||||||
mUserPreferences.getRadioReferencePreference().setPassword(password);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void updateButtons()
|
|
||||||
{
|
|
||||||
String username = mUserNameText.getText();
|
|
||||||
String password = mPasswordText.getText();
|
|
||||||
|
|
||||||
mLookupButton.setEnabled(username != null && !username.isEmpty() && password != null && !password.isEmpty());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void insertUpdate(DocumentEvent e)
|
|
||||||
{
|
|
||||||
updateButtons();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void removeUpdate(DocumentEvent e)
|
|
||||||
{
|
|
||||||
updateButtons();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void changedUpdate(DocumentEvent e)
|
|
||||||
{
|
|
||||||
updateButtons();
|
|
||||||
}
|
|
||||||
|
|
||||||
private class CellRenderer extends JLabel implements ListCellRenderer<UserFeedBroadcast>
|
|
||||||
{
|
|
||||||
public CellRenderer()
|
|
||||||
{
|
|
||||||
setOpaque(true);
|
|
||||||
setHorizontalAlignment(LEFT);
|
|
||||||
setVerticalAlignment(CENTER);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Component getListCellRendererComponent(JList<? extends UserFeedBroadcast> list,
|
|
||||||
UserFeedBroadcast value,
|
|
||||||
int index,
|
|
||||||
boolean isSelected,
|
|
||||||
boolean cellHasFocus)
|
|
||||||
{
|
|
||||||
if (isSelected)
|
|
||||||
{
|
|
||||||
setBackground(list.getSelectionBackground());
|
|
||||||
setForeground(list.getSelectionForeground());
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
setBackground(list.getBackground());
|
|
||||||
setForeground(list.getForeground());
|
|
||||||
}
|
|
||||||
|
|
||||||
setText(value.getDescription());
|
|
||||||
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Subscribe
|
|
||||||
public void preferenceUpdated(PreferenceType preferenceType)
|
|
||||||
{
|
|
||||||
if(preferenceType == PreferenceType.RADIO_REFERENCE)
|
|
||||||
{
|
|
||||||
//Update the store-credentials checkbox only, so we don't change the username or password currently displayed
|
|
||||||
mSaveCredentialsCheckBox.setSelected(mUserPreferences.getRadioReferencePreference().isStoreCredentials());
|
|
||||||
mClearStoredCredentialsButton.setEnabled(mUserPreferences.getRadioReferencePreference().hasStoredCredentials());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -19,11 +19,11 @@
|
|||||||
package io.github.dsheirer.audio.broadcast.icecast;
|
package io.github.dsheirer.audio.broadcast.icecast;
|
||||||
|
|
||||||
import io.github.dsheirer.alias.AliasModel;
|
import io.github.dsheirer.alias.AliasModel;
|
||||||
import io.github.dsheirer.audio.broadcast.AudioBroadcaster;
|
import io.github.dsheirer.audio.broadcast.AudioStreamingBroadcaster;
|
||||||
import io.github.dsheirer.audio.broadcast.BroadcastConfiguration;
|
import io.github.dsheirer.audio.broadcast.BroadcastConfiguration;
|
||||||
import io.github.dsheirer.audio.broadcast.IBroadcastMetadataUpdater;
|
import io.github.dsheirer.audio.broadcast.IBroadcastMetadataUpdater;
|
||||||
|
|
||||||
public abstract class IcecastAudioBroadcaster extends AudioBroadcaster
|
public abstract class IcecastAudioBroadcaster extends AudioStreamingBroadcaster
|
||||||
{
|
{
|
||||||
private IBroadcastMetadataUpdater mMetadataUpdater;
|
private IBroadcastMetadataUpdater mMetadataUpdater;
|
||||||
private AliasModel mAliasModel;
|
private AliasModel mAliasModel;
|
||||||
|
|||||||
@@ -24,6 +24,8 @@ import com.fasterxml.jackson.annotation.JsonTypeInfo;
|
|||||||
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
|
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
|
||||||
import io.github.dsheirer.audio.broadcast.BroadcastConfiguration;
|
import io.github.dsheirer.audio.broadcast.BroadcastConfiguration;
|
||||||
import io.github.dsheirer.audio.broadcast.BroadcastFormat;
|
import io.github.dsheirer.audio.broadcast.BroadcastFormat;
|
||||||
|
import javafx.beans.property.SimpleStringProperty;
|
||||||
|
import javafx.beans.property.StringProperty;
|
||||||
|
|
||||||
import java.util.Base64;
|
import java.util.Base64;
|
||||||
|
|
||||||
@@ -35,7 +37,7 @@ import java.util.Base64;
|
|||||||
public abstract class IcecastConfiguration extends BroadcastConfiguration
|
public abstract class IcecastConfiguration extends BroadcastConfiguration
|
||||||
{
|
{
|
||||||
protected String mUserName = "source";
|
protected String mUserName = "source";
|
||||||
private String mMountPoint = "/stream";
|
protected StringProperty mMountPoint = new SimpleStringProperty("/stream");
|
||||||
private String mDescription;
|
private String mDescription;
|
||||||
private String mGenre;
|
private String mGenre;
|
||||||
private boolean mPublic;
|
private boolean mPublic;
|
||||||
@@ -51,6 +53,7 @@ public abstract class IcecastConfiguration extends BroadcastConfiguration
|
|||||||
|
|
||||||
public IcecastConfiguration()
|
public IcecastConfiguration()
|
||||||
{
|
{
|
||||||
|
this(BroadcastFormat.MP3);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -99,7 +102,7 @@ public abstract class IcecastConfiguration extends BroadcastConfiguration
|
|||||||
@JacksonXmlProperty(isAttribute = true, localName = "mount_point")
|
@JacksonXmlProperty(isAttribute = true, localName = "mount_point")
|
||||||
public String getMountPoint()
|
public String getMountPoint()
|
||||||
{
|
{
|
||||||
return mMountPoint;
|
return mMountPoint.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -113,18 +116,18 @@ public abstract class IcecastConfiguration extends BroadcastConfiguration
|
|||||||
{
|
{
|
||||||
if(mountPoint.startsWith("/"))
|
if(mountPoint.startsWith("/"))
|
||||||
{
|
{
|
||||||
mMountPoint = mountPoint;
|
mMountPoint.setValue(mountPoint);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
mMountPoint = "/" + mountPoint;
|
mMountPoint.setValue("/" + mountPoint);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean hasMountPoint()
|
public boolean hasMountPoint()
|
||||||
{
|
{
|
||||||
return mMountPoint != null;
|
return mMountPoint.get() != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@JacksonXmlProperty(isAttribute = true, localName = "description")
|
@JacksonXmlProperty(isAttribute = true, localName = "description")
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
|
|||||||
import io.github.dsheirer.audio.broadcast.BroadcastConfiguration;
|
import io.github.dsheirer.audio.broadcast.BroadcastConfiguration;
|
||||||
import io.github.dsheirer.audio.broadcast.BroadcastFormat;
|
import io.github.dsheirer.audio.broadcast.BroadcastFormat;
|
||||||
import io.github.dsheirer.audio.broadcast.BroadcastServerType;
|
import io.github.dsheirer.audio.broadcast.BroadcastServerType;
|
||||||
|
import javafx.beans.binding.Bindings;
|
||||||
|
|
||||||
@JsonSubTypes.Type(value=IcecastHTTPConfiguration.class, name="icecastHTTPConfiguration")
|
@JsonSubTypes.Type(value=IcecastHTTPConfiguration.class, name="icecastHTTPConfiguration")
|
||||||
public class IcecastHTTPConfiguration extends IcecastConfiguration
|
public class IcecastHTTPConfiguration extends IcecastConfiguration
|
||||||
@@ -40,6 +41,9 @@ public class IcecastHTTPConfiguration extends IcecastConfiguration
|
|||||||
{
|
{
|
||||||
super(format);
|
super(format);
|
||||||
setPassword("change me!");
|
setPassword("change me!");
|
||||||
|
|
||||||
|
mValid.bind(Bindings.and(Bindings.and(Bindings.and(Bindings.isNotNull(mHost), Bindings.greaterThan(mPort, 0)),
|
||||||
|
Bindings.isNotNull(mMountPoint)), Bindings.isNotNull(mPassword)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -1,383 +0,0 @@
|
|||||||
/*******************************************************************************
|
|
||||||
* sdrtrunk
|
|
||||||
* Copyright (C) 2014-2016 Dennis Sheirer
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
|
||||||
*
|
|
||||||
******************************************************************************/
|
|
||||||
package io.github.dsheirer.audio.broadcast.icecast;
|
|
||||||
|
|
||||||
import io.github.dsheirer.alias.AliasModel;
|
|
||||||
import io.github.dsheirer.audio.broadcast.BroadcastConfiguration;
|
|
||||||
import io.github.dsheirer.audio.broadcast.BroadcastConfigurationEditor;
|
|
||||||
import io.github.dsheirer.audio.broadcast.BroadcastEvent;
|
|
||||||
import io.github.dsheirer.audio.broadcast.BroadcastModel;
|
|
||||||
import io.github.dsheirer.audio.broadcast.BroadcastServerType;
|
|
||||||
import io.github.dsheirer.icon.IconManager;
|
|
||||||
import net.miginfocom.swing.MigLayout;
|
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
|
|
||||||
import javax.swing.ImageIcon;
|
|
||||||
import javax.swing.JButton;
|
|
||||||
import javax.swing.JCheckBox;
|
|
||||||
import javax.swing.JLabel;
|
|
||||||
import javax.swing.JOptionPane;
|
|
||||||
import javax.swing.JSlider;
|
|
||||||
import javax.swing.JTextField;
|
|
||||||
import javax.swing.event.ChangeEvent;
|
|
||||||
import javax.swing.event.ChangeListener;
|
|
||||||
import java.awt.Dimension;
|
|
||||||
import java.awt.event.ActionEvent;
|
|
||||||
import java.awt.event.ActionListener;
|
|
||||||
|
|
||||||
public class IcecastHTTPConfigurationEditor extends BroadcastConfigurationEditor
|
|
||||||
{
|
|
||||||
private final static Logger mLog = LoggerFactory.getLogger( IcecastHTTPConfigurationEditor.class );
|
|
||||||
|
|
||||||
private static final int ONE_MINUTE_MS = 60000;
|
|
||||||
|
|
||||||
private JTextField mName;
|
|
||||||
private JTextField mServer;
|
|
||||||
private JTextField mPort;
|
|
||||||
private JTextField mMountPoint;
|
|
||||||
private JTextField mUsername;
|
|
||||||
private JTextField mPassword;
|
|
||||||
private JTextField mDescription;
|
|
||||||
private JTextField mGenre;
|
|
||||||
private JCheckBox mPublic;
|
|
||||||
private JSlider mDelay;
|
|
||||||
private JLabel mDelayValue;
|
|
||||||
private JSlider mMaximumRecordingAge;
|
|
||||||
private JLabel mMaximumRecordingAgeValue;
|
|
||||||
private JCheckBox mEnabled;
|
|
||||||
private JButton mSaveButton;
|
|
||||||
private JButton mResetButton;
|
|
||||||
|
|
||||||
public IcecastHTTPConfigurationEditor(BroadcastModel broadcastModel, AliasModel aliasModel, IconManager iconManager)
|
|
||||||
{
|
|
||||||
super(broadcastModel, aliasModel, iconManager);
|
|
||||||
init();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void init()
|
|
||||||
{
|
|
||||||
setLayout( new MigLayout( "fill,wrap 2", "[align right][grow,fill]",
|
|
||||||
"[][][][][][][][][][][][][][][][][grow,fill]" ) );
|
|
||||||
setPreferredSize(new Dimension(150,400));
|
|
||||||
|
|
||||||
JLabel channelLabel = new JLabel("Icecast 2 (v2.4+) Stream");
|
|
||||||
|
|
||||||
ImageIcon icon = mIconManager.getScaledIcon(BroadcastServerType.ICECAST_HTTP.getIconPath(), 25);
|
|
||||||
channelLabel.setIcon(icon);
|
|
||||||
|
|
||||||
add(channelLabel, "span, align center");
|
|
||||||
|
|
||||||
add(new JLabel("Name:"));
|
|
||||||
mName = new JTextField();
|
|
||||||
mName.getDocument().addDocumentListener(this);
|
|
||||||
mName.setToolTipText("Unique name for this stream");
|
|
||||||
add(mName);
|
|
||||||
|
|
||||||
add(new JLabel("Server:"));
|
|
||||||
mServer = new JTextField();
|
|
||||||
mServer.getDocument().addDocumentListener(this);
|
|
||||||
mServer.setToolTipText("Server address (e.g. audio1.icecast.com or localhost)");
|
|
||||||
add(mServer);
|
|
||||||
|
|
||||||
add(new JLabel("Port:"));
|
|
||||||
mPort = new JTextField();
|
|
||||||
mPort.getDocument().addDocumentListener(this);
|
|
||||||
mPort.setToolTipText("Server port number (e.g. 80)");
|
|
||||||
add(mPort);
|
|
||||||
|
|
||||||
add(new JLabel("Mount:"));
|
|
||||||
mMountPoint = new JTextField();
|
|
||||||
mMountPoint.getDocument().addDocumentListener(this);
|
|
||||||
mMountPoint.setToolTipText("Mount point (e.g. /12345)");
|
|
||||||
add(mMountPoint);
|
|
||||||
|
|
||||||
add(new JLabel("User Name:"));
|
|
||||||
mUsername = new JTextField();
|
|
||||||
mUsername.getDocument().addDocumentListener(this);
|
|
||||||
mUsername.setToolTipText("User name for the stream. Default: source");
|
|
||||||
add(mUsername);
|
|
||||||
|
|
||||||
add(new JLabel("Password:"));
|
|
||||||
mPassword = new JTextField();
|
|
||||||
mPassword.getDocument().addDocumentListener(this);
|
|
||||||
mPassword.setToolTipText("Password for the stream");
|
|
||||||
add(mPassword);
|
|
||||||
|
|
||||||
add(new JLabel("Description:"));
|
|
||||||
mDescription = new JTextField();
|
|
||||||
mDescription.getDocument().addDocumentListener(this);
|
|
||||||
mDescription.setToolTipText("Description of the stream contents");
|
|
||||||
add(mDescription);
|
|
||||||
|
|
||||||
add(new JLabel("Genre:"));
|
|
||||||
mGenre = new JTextField();
|
|
||||||
mGenre.getDocument().addDocumentListener(this);
|
|
||||||
mGenre.setToolTipText("Genre (e.g. public safety)");
|
|
||||||
add(mGenre);
|
|
||||||
|
|
||||||
add(new JLabel("Public:"));
|
|
||||||
mPublic = new JCheckBox();
|
|
||||||
mPublic.addActionListener(new ActionListener()
|
|
||||||
{
|
|
||||||
@Override
|
|
||||||
public void actionPerformed(ActionEvent e)
|
|
||||||
{
|
|
||||||
setModified(true);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
mPublic.setToolTipText("Designate stream as public (checked) or private");
|
|
||||||
add(mPublic);
|
|
||||||
|
|
||||||
add(new JLabel());
|
|
||||||
mDelayValue = new JLabel("0 Minutes");
|
|
||||||
add(mDelayValue);
|
|
||||||
|
|
||||||
add(new JLabel("Delay:"));
|
|
||||||
mDelay = new JSlider(0,60,0);
|
|
||||||
mDelay.setMajorTickSpacing(10);
|
|
||||||
mDelay.setMinorTickSpacing(5);
|
|
||||||
mDelay.addChangeListener(new ChangeListener()
|
|
||||||
{
|
|
||||||
@Override
|
|
||||||
public void stateChanged(ChangeEvent e)
|
|
||||||
{
|
|
||||||
mDelayValue.setText(mDelay.getValue() + " Minutes");
|
|
||||||
setModified(true);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
mDelay.setToolTipText("Audio broadcast delay in minutes");
|
|
||||||
add(mDelay);
|
|
||||||
|
|
||||||
add(new JLabel());
|
|
||||||
mMaximumRecordingAgeValue = new JLabel("1 Minute");
|
|
||||||
add(mMaximumRecordingAgeValue);
|
|
||||||
|
|
||||||
add(new JLabel("Age Limit:"));
|
|
||||||
mMaximumRecordingAge = new JSlider(1,60,5);
|
|
||||||
mMaximumRecordingAge.setMajorTickSpacing(10);
|
|
||||||
mMaximumRecordingAge.setMinorTickSpacing(5);
|
|
||||||
mMaximumRecordingAge.addChangeListener(new ChangeListener()
|
|
||||||
{
|
|
||||||
@Override
|
|
||||||
public void stateChanged(ChangeEvent e)
|
|
||||||
{
|
|
||||||
int value = mMaximumRecordingAge.getValue();
|
|
||||||
|
|
||||||
mMaximumRecordingAgeValue.setText(value + " Minute" + (value > 1 ? "s" : ""));
|
|
||||||
setModified(true);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
mMaximumRecordingAge.setToolTipText("Maximum recording age (in addition to delay) to stream");
|
|
||||||
add(mMaximumRecordingAge);
|
|
||||||
|
|
||||||
add(new JLabel("Enabled:"));
|
|
||||||
mEnabled = new JCheckBox();
|
|
||||||
mEnabled.setToolTipText("Enable (checked) or disable this stream");
|
|
||||||
mEnabled.addActionListener(new ActionListener()
|
|
||||||
{
|
|
||||||
@Override
|
|
||||||
public void actionPerformed(ActionEvent e)
|
|
||||||
{
|
|
||||||
setModified(true);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
add(mEnabled);
|
|
||||||
|
|
||||||
mSaveButton = new JButton("Save");
|
|
||||||
mSaveButton.setEnabled(false);
|
|
||||||
mSaveButton.setToolTipText("Click to save configuration information");
|
|
||||||
mSaveButton.addActionListener(new ActionListener()
|
|
||||||
{
|
|
||||||
@Override
|
|
||||||
public void actionPerformed(ActionEvent e)
|
|
||||||
{
|
|
||||||
save();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
add(mSaveButton);
|
|
||||||
|
|
||||||
mResetButton = new JButton("Reset");
|
|
||||||
mResetButton.setEnabled(false);
|
|
||||||
mResetButton.setToolTipText("Click to reset any changes you've made since the last save");
|
|
||||||
mResetButton.addActionListener(new ActionListener()
|
|
||||||
{
|
|
||||||
@Override
|
|
||||||
public void actionPerformed(ActionEvent e)
|
|
||||||
{
|
|
||||||
setModified(false);
|
|
||||||
setItem(getItem());
|
|
||||||
}
|
|
||||||
});
|
|
||||||
add(mResetButton);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setModified(boolean modified)
|
|
||||||
{
|
|
||||||
super.setModified(modified);
|
|
||||||
|
|
||||||
mSaveButton.setEnabled(modified);
|
|
||||||
mResetButton.setEnabled(modified);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setItem(BroadcastConfiguration item)
|
|
||||||
{
|
|
||||||
super.setItem(item);
|
|
||||||
|
|
||||||
if(hasItem())
|
|
||||||
{
|
|
||||||
IcecastHTTPConfiguration config = (IcecastHTTPConfiguration)getItem();
|
|
||||||
|
|
||||||
mName.setText(config.getName());
|
|
||||||
mServer.setText(config.getHost());
|
|
||||||
mPort.setText(config.getPort() != 0 ? String.valueOf(config.getPort()) : null);
|
|
||||||
mMountPoint.setText(config.getMountPoint());
|
|
||||||
mUsername.setText(config.getUserName());
|
|
||||||
mPassword.setText(config.getPassword());
|
|
||||||
mDescription.setText(config.getDescription());
|
|
||||||
mGenre.setText(config.getGenre());
|
|
||||||
mPublic.setSelected(config.isPublic());
|
|
||||||
mDelay.setValue((int)(config.getDelay() / ONE_MINUTE_MS));
|
|
||||||
mMaximumRecordingAge.setValue((int)config.getMaximumRecordingAge() / ONE_MINUTE_MS);
|
|
||||||
mEnabled.setSelected(config.isEnabled());
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
mName.setText(null);
|
|
||||||
mServer.setText(null);
|
|
||||||
mPort.setText(null);
|
|
||||||
mMountPoint.setText(null);
|
|
||||||
mUsername.setText(null);
|
|
||||||
mPassword.setText(null);
|
|
||||||
mDescription.setText(null);
|
|
||||||
mGenre.setText(null);
|
|
||||||
mPublic.setSelected(true);
|
|
||||||
mDelay.setValue(0);
|
|
||||||
mMaximumRecordingAge.setValue(1);
|
|
||||||
mEnabled.setSelected(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
setModified(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void save()
|
|
||||||
{
|
|
||||||
IcecastHTTPConfiguration config = (IcecastHTTPConfiguration)getItem();
|
|
||||||
|
|
||||||
if(validateConfiguration())
|
|
||||||
{
|
|
||||||
updateConfigurationName(config, mName.getText());
|
|
||||||
config.setHost(mServer.getText());
|
|
||||||
config.setPort(getPort());
|
|
||||||
config.setMountPoint(mMountPoint.getText());
|
|
||||||
config.setUserName(mUsername.getText());
|
|
||||||
config.setPassword(mPassword.getText());
|
|
||||||
config.setDescription(mDescription.getText());
|
|
||||||
config.setGenre(mGenre.getText());
|
|
||||||
config.setPublic(mPublic.isSelected());
|
|
||||||
config.setDelay(mDelay.getValue() * ONE_MINUTE_MS);
|
|
||||||
config.setMaximumRecordingAge(mMaximumRecordingAge.getValue() * ONE_MINUTE_MS);
|
|
||||||
config.setEnabled(mEnabled.isSelected());
|
|
||||||
|
|
||||||
setModified(false);
|
|
||||||
|
|
||||||
mBroadcastModel.process(new BroadcastEvent(getItem(), BroadcastEvent.Event.CONFIGURATION_CHANGE));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Validates the user specified values in the controls prior to saving them to the configuration
|
|
||||||
*/
|
|
||||||
private boolean validateConfiguration()
|
|
||||||
{
|
|
||||||
String name = mName.getText();
|
|
||||||
|
|
||||||
//A unique stream name is required
|
|
||||||
if(!mBroadcastModel.isUniqueName(name, getItem()))
|
|
||||||
{
|
|
||||||
String message = (name == null || name.isEmpty()) ?
|
|
||||||
"Please specify a stream name." : "Stream name " + name +
|
|
||||||
"is already in use.\nPlease choose another name.";
|
|
||||||
|
|
||||||
JOptionPane.showMessageDialog(IcecastHTTPConfigurationEditor.this,
|
|
||||||
message, "Invalid Stream Name", JOptionPane.ERROR_MESSAGE);
|
|
||||||
|
|
||||||
mName.requestFocus();
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
//Server address is optional, but required
|
|
||||||
if(!validateTextField(mServer, "Invalid Server Address", "Please specify a server address."))
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
//Port is optional, but required
|
|
||||||
if(!validateIntegerTextField(mPort, "Invalid Port Number", "Please specify a port number (1 <> 65535)", 1, 65535))
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
//Mount Point is required
|
|
||||||
if(!validateTextField(mMountPoint, "Invalid Mount Point", "Please specify a mount point."))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
//User name is required
|
|
||||||
if(!validateTextField(mUsername, "Invalid User Name", "Please specify a user name."))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
//Password is required
|
|
||||||
if(!validateTextField(mPassword, "Invalid Password", "Please specify a password."))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Parses an Integer from the port text control or returns 0 if the control doesn't contain an integer value.
|
|
||||||
*/
|
|
||||||
private int getPort()
|
|
||||||
{
|
|
||||||
String port = mPort.getText();
|
|
||||||
|
|
||||||
if(port != null && !port.isEmpty())
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
return Integer.parseInt(port);
|
|
||||||
}
|
|
||||||
catch(Exception e)
|
|
||||||
{
|
|
||||||
//Do nothing, we couldn't parse the number value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -24,13 +24,14 @@ import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
|
|||||||
import io.github.dsheirer.audio.broadcast.BroadcastConfiguration;
|
import io.github.dsheirer.audio.broadcast.BroadcastConfiguration;
|
||||||
import io.github.dsheirer.audio.broadcast.BroadcastFormat;
|
import io.github.dsheirer.audio.broadcast.BroadcastFormat;
|
||||||
import io.github.dsheirer.audio.broadcast.BroadcastServerType;
|
import io.github.dsheirer.audio.broadcast.BroadcastServerType;
|
||||||
import io.github.dsheirer.audio.broadcast.broadcastify.BroadcastifyConfiguration;
|
import io.github.dsheirer.audio.broadcast.broadcastify.BroadcastifyFeedConfiguration;
|
||||||
|
import javafx.beans.binding.Bindings;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "type")
|
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "type")
|
||||||
@JsonSubTypes({
|
@JsonSubTypes({
|
||||||
@JsonSubTypes.Type(value = BroadcastifyConfiguration.class, name="broadcastifyConfiguration"),
|
@JsonSubTypes.Type(value = BroadcastifyFeedConfiguration.class, name="broadcastifyConfiguration"),
|
||||||
})
|
})
|
||||||
public class IcecastTCPConfiguration extends IcecastConfiguration
|
public class IcecastTCPConfiguration extends IcecastConfiguration
|
||||||
{
|
{
|
||||||
@@ -48,6 +49,9 @@ public class IcecastTCPConfiguration extends IcecastConfiguration
|
|||||||
public IcecastTCPConfiguration(BroadcastFormat format)
|
public IcecastTCPConfiguration(BroadcastFormat format)
|
||||||
{
|
{
|
||||||
super(format);
|
super(format);
|
||||||
|
|
||||||
|
mValid.bind(Bindings.and(Bindings.and(Bindings.isNotNull(mHost), Bindings.greaterThan(mPort, 0)),
|
||||||
|
Bindings.isNotNull(mMountPoint)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user