Compare commits

...

18 Commits

Author SHA1 Message Date
Denny Sheirer
139227e46d Resolves #535 (#540)
Updates Icecast HTTP (2.4) streaming client to perpetually attempt to
reconnect to the server, even if there are (temp) errors.
2019-02-17 06:12:11 -05:00
Denny Sheirer
2394d51a51 Resolves #537 (#539)
Updates application title bar with currently displayed tuner.
2019-02-17 05:49:26 -05:00
Denny Sheirer
d9fa2bea5b Resolves #533. Updates broadcastify stream editor to use new (#538)
radio reference client library and adds support for storing user
credentials in user preferences.

Removes legacy apache axis SOAP and WSDL libraries calls to radio
reference web services -- not required by the new library.
2019-02-17 05:34:31 -05:00
Denny Sheirer
0cb360eeed Resovles #531 updates gradle build script to remove extra jfx runtimes (#532) 2019-02-02 15:17:11 +00:00
Denny Sheirer
ea472f873f Resolves #517 NPE thrown by TalkgroupRangeEditor (#530) 2019-02-02 14:24:23 +00:00
Denny Sheirer
8cbdbd527e Resolves #525 NPE in P25 activity summary report. (#529) 2019-02-02 13:40:57 +00:00
Denny Sheirer
ff0d893bd9 Updates build script to use latest beryx runtime zip plugin and adds (#528)
validation to check for each of the platform-specific JDKs before
executing the runtimeZip task.
2019-02-02 13:37:09 +00:00
Denny
7b924d0486 Removes debug logging 2019-01-27 20:21:46 -05:00
Denny
712eb07f4b Updates build to 0.4.0 alpha 8 2019-01-27 19:59:04 -05:00
Denny Sheirer
d5c3ecc7df Resolves #519 (#520)
- Clears stale patch groups
- Reworks the audio playback panel label update code to properly update
  talkgroups and aliases in concert with the audio playback.
- Removes code that was truncating playback aliases at 32 characters.
2019-01-28 00:58:01 +00:00
Denny Sheirer
c4bf089667 502 icecast http connect error (#518)
* Resolves #502

Resolves issue with connecting to icecast http streaming server.

The issue stems from Icecast HTTP (2.4+) servers not sending a full
HTTP OK response (missing header tokens) to a connection request.

Prior to the upgrade to apache mina 2.0.19, we received a specific
exception and handled it and set the status to connected.

After the upgrade, apache mina is now sending an array index out of
bounds exception when it decodes the incomplete http response.

So, we now catch that specific exception, inspect the http response
hex bytes and set the status to connected.
2019-01-27 14:00:57 +00:00
Denny Sheirer
378b03e06e 515 Patch Group Manager Cross Linking of Talk and Patch Groups (#516)
* Stores minimal main application window location and size/layout settings
to allow application to be restored to the same size/location on
restart.

* Removes talkgroup swapping from the patch group manager which may have
been causing talkgroup <> patch group cross-linking on certain systems.
2019-01-27 12:25:08 +00:00
Denny Sheirer
34235382fb 513 persist application layout across restarts (#514)
* Stores minimal main application window location and size/layout settings
to allow application to be restored to the same size/location on
restart.

* Adds column width monitor to all tables in application that stores the
column width settings in the user preferences so that on restarting
the application the table column widths are restored to the previous
widths.
2019-01-27 12:05:20 +00:00
Denny Sheirer
c05aff0768 Updates alias controller so that when adding an alias to a filtered (#512)
column view it doesn't throw an error.
2019-01-26 13:03:59 +00:00
Denny Sheirer
f0c3e0fd1a Adds logging/warning when alias list contains talkgroups or talkgroup (#511)
range collisions or overlaps.
2019-01-26 11:38:42 +00:00
Denny Sheirer
d601cbda0e Resolves issue with missing case statements in talkgroup format classes. (#509) 2019-01-26 11:11:41 +00:00
Denny Sheirer
2653dc6b38 Removes debug logging. (#508) 2019-01-26 11:02:43 +00:00
Denny Sheirer
48cbbfc656 Updates beryx gradle plugin to resolve issue where launcher script (#503)
did not like '&' ampersand in the execution path.
2019-01-23 09:21:16 +00:00
44 changed files with 1370 additions and 627 deletions

View File

@@ -1,37 +1,30 @@
/*
* ******************************************************************************
* 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.
* * ******************************************************************************
* * 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/>
* * *****************************************************************************
*
* 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/>
* *****************************************************************************
*/
buildscript {
repositories {
jcenter()
mavenCentral()
}
dependencies {
}
}
plugins {
id 'application'
id 'java'
id 'idea'
id 'org.openjfx.javafxplugin' version '0.0.6'
id 'org.beryx.runtime' version '1.1.1'
id 'org.beryx.runtime' version '1.1.5'
}
repositories {
@@ -39,7 +32,7 @@ repositories {
mavenCentral()
}
version = '0.4.0-alpha.7'
version = '0.4.0-alpha.9'
sourceCompatibility = '11'
sourceSets {
@@ -61,10 +54,9 @@ dependencies {
compile 'com.google.guava:guava:27.0.1-jre'
compile 'com.jidesoft:jide-oss:3.6.18'
compile 'com.miglayout:miglayout-swing:5.2'
compile 'io.github.dsheirer:radio-reference-api:15.0.1'
compile 'javax.usb:usb-api:1.0.2'
compile 'net.coderazzi:tablefilter-swing:5.4.0'
compile 'org.apache.axis:axis:1.4'
compile 'org.apache.axis:axis-jaxrpc:1.4'
compile 'org.apache.commons:commons-lang3:3.8.1'
compile 'org.apache.commons:commons-io:1.3.2'
compile 'org.apache.mina:mina-core:2.0.19'
@@ -103,15 +95,6 @@ javafx {
modules = ['javafx.base', 'javafx.controls', 'javafx.graphics', 'javafx.swing']
}
runtime {
targetPlatform('linux-x64', '/media/denny/WD250GB/java/linux/jdk-11.0.1')
targetPlatform('osx-x64', '/media/denny/WD250GB/java/osx/jdk-11.0.1.jdk/Contents/Home')
targetPlatform('windows-x64', '/media/denny/WD250GB/java/windows/jdk-11.0.1')
options = ['--strip-debug', '--compress', '2', '--no-header-files', '--no-man-pages']
modules = ['java.desktop', 'java.naming', 'jdk.unsupported', 'jdk.unsupported.desktop']
imageZip = file("$buildDir/image/sdr-trunk-" + version + ".zip")
}
task buildSdr(type: Jar) {
manifest {
attributes 'Implementation-Title': 'SdrTrunk project',
@@ -123,3 +106,77 @@ task buildSdr(type: Jar) {
from { configurations.compile.collect { it.isDirectory() ? it : zipTree(it) } }
with jar
}
/**
* Java Development Kit (JDK) locations. 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 tasks to produce platform-specific builds
*/
def jdkLinux_aarch64 = '/media/denny/WD250GB/java/bellsoft/linux-aarch64/jdk-11.0.2'
def jdkLinux_x64 = '/media/denny/WD250GB/java/bellsoft/linux-x64/jdk-11.0.2'
def jdkOsx_x64 = '/media/denny/WD250GB/java/bellsoft/osx-x64/jdk-11.0.2.jdk'
def jdkWindows_x64 = '/media/denny/WD250GB/java/bellsoft/windows-x64/jdk-11.0.2'
runtime {
targetPlatform('linux-aarch64', jdkLinux_aarch64)
targetPlatform('linux-x64', jdkLinux_x64)
targetPlatform('osx-x64', jdkOsx_x64)
targetPlatform('windows-x64', jdkWindows_x64)
options = ['--strip-debug', '--compress', '2', '--no-header-files', '--no-man-pages']
modules = ['java.desktop', 'java.naming', 'jdk.unsupported', 'jdk.unsupported.desktop']
imageZip = file("$buildDir/image/sdr-trunk-" + version + ".zip")
}
/**
* Check for existence of JDK folders for each supported platform before creating runtime images
*/
tasks.runtime.doFirst {
def linux_aarch64 = new File(jdkLinux_aarch64)
if(!linux_aarch64.exists())
{
println("Linux aarch64 JDK was not found at " + jdkLinux_aarch64)
throw new GradleException("Cannot find Java Development Kit (JDK) for linux-aarch64 architecture. " +
"Please update the build.gradle script to provide the correct path")
}
def linux_x64 = new File(jdkLinux_x64)
if(!linux_x64.exists())
{
println("Linux x64 JDK was not found at " + jdkLinux_x64)
throw new GradleException("Cannot find Java Development Kit (JDK) for linux-x64 architecture. " +
"Please update the build.gradle script to provide the correct path")
}
def osx_x64 = new File(jdkOsx_x64)
if(!osx_x64.exists())
{
println("OSX x64 JDK was not found at " + jdkOsx_x64);
throw new GradleException("Cannot find Java Development Kit (JDK) for osx-x64 architecture. " +
"Please update the build.gradle script to provide the correct path")
}
def windows_x64 = new File(jdkWindows_x64)
if(!windows_x64.exists())
{
println("Windows x64 JDK was not found at " + jdkWindows_x64)
throw new GradleException("Cannot find Java Development Kit (JDK) for windows-x64 architecture. " +
"Please update the build.gradle script to provide the correct path")
}
}
/**
* Since we have to list each of the platform-specific JavaFX libraries as dependencies,
* remove them from each build image
*/
tasks.runtime.doLast {
delete(fileTree('build/image/sdr-trunk-linux-aarch64/lib').include { it.name ==~ /javafx.*-(win|mac)\.jar/ })
delete(fileTree('build/image/sdr-trunk-linux-x64/lib').include { it.name ==~ /javafx.*-(win|mac)\.jar/ })
delete(fileTree('build/image/sdr-trunk-osx-x64/lib').include { it.name ==~ /javafx.*-(win|linux)\.jar/ })
delete(fileTree('build/image/sdr-trunk-windows-x64/lib').include { it.name ==~ /javafx.*-(linux|mac)\.jar/ })
}

Binary file not shown.

Binary file not shown.

View File

@@ -1,34 +1,67 @@
/*
* ******************************************************************************
* 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.*;
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.*;
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
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";
@@ -39,15 +72,16 @@ public class AliasController extends JPanel
private JButton mDeleteButton = new JButton(DELETE_ALIAS);
private IconCellRenderer mIconCellRenderer;
private JTableColumnWidthMonitor mColumnWidthMonitor;
public AliasController(AliasModel aliasModel, BroadcastModel broadcastModel, IconManager iconManager)
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();
@@ -56,9 +90,7 @@ public class AliasController extends JPanel
private void init()
{
setLayout(new MigLayout("insets 0 0 0 0",
"[grow,fill]",
"[grow,fill]"));
setLayout(new MigLayout("insets 0 0 0 0", "[grow,fill]", "[grow,fill]"));
//System Configuration View and Editor
mAliasTable = new JTable(mAliasModel);
@@ -66,11 +98,11 @@ public class AliasController extends JPanel
mAliasTable.getSelectionModel().addListSelectionListener(this);
mAliasTable.setAutoCreateRowSorter(true);
mAliasTable.getColumnModel().getColumn(AliasModel.COLUMN_COLOR)
.setCellRenderer(new ColorCellRenderer());
mAliasTable.getColumnModel().getColumn(AliasModel.COLUMN_COLOR).setCellRenderer(new ColorCellRenderer());
mAliasTable.getColumnModel().getColumn(AliasModel.COLUMN_ICON)
.setCellRenderer(mIconCellRenderer);
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);
@@ -79,8 +111,8 @@ public class AliasController extends JPanel
JPanel buttonsPanel = new JPanel();
buttonsPanel.setLayout(
new MigLayout("insets 0 0 0 0", "[grow,fill][grow,fill][grow,fill]", "[]"));
buttonsPanel.setLayout(new MigLayout("insets 0 0 0 0",
"[grow,fill][grow,fill][grow,fill]", "[]"));
mNewButton.addActionListener(this);
mNewButton.setToolTipText("Adds a new alias");
@@ -98,8 +130,8 @@ public class AliasController extends JPanel
JPanel listAndButtonsPanel = new JPanel();
listAndButtonsPanel.setLayout(
new MigLayout("insets 0 0 0 0", "[grow,fill]", "[grow,fill][]"));
listAndButtonsPanel.setLayout(new MigLayout("insets 0 0 0 0", "[grow,fill]",
"[grow,fill][]"));
listAndButtonsPanel.add(tableScroller, "wrap");
listAndButtonsPanel.add(buttonsPanel);
@@ -212,18 +244,11 @@ public class AliasController extends JPanel
*/
private void addAlias(Alias alias)
{
//HACK: when inserting a row to the model, the JTable gets
//notified and attempts to tell the coderazzi table filter
//adaptive choices filter to refresh before the table filter is
//notified of the row additions, causing an index out of bounds
//exception. We turn off adaptive choices temporarily, add the
//channel, and turn on adaptive choices again.
mTableFilterHeader.setAdaptiveChoices(false);
//Remove any column headers so that the newly added alias shows correctly.
mTableFilterHeader.resetFilter();
int index = mAliasModel.addAlias(alias);
mTableFilterHeader.setAdaptiveChoices(true);
if(index >= 0)
{
int translatedIndex = mAliasTable.convertRowIndexToView(index);

View File

@@ -505,11 +505,32 @@ public class AliasList implements Listener<AliasEvent>
public void add(Talkgroup talkgroup, Alias alias)
{
//Detect talkgroup collisions
if(mTalkgroupAliasMap.containsKey(talkgroup.getValue()))
{
Alias existing = mTalkgroupAliasMap.get(talkgroup.getValue());
mLog.warn("Alias [" + alias.getName() + "] talkgroup [" + talkgroup.getValue() +
"] has the same talkgroup value as alias [" + existing.getName() +
"] - alias [" + alias.getName() + "] will be used for alias list [" + getName() + "]");
}
mTalkgroupAliasMap.put(talkgroup.getValue(), alias);
}
public void add(TalkgroupRange talkgroupRange, Alias alias)
{
//Log warning if the new talkgroup range overlaps with any existing ranges
for(Map.Entry<TalkgroupRange,Alias> entry: mTalkgroupRangeAliasMap.entrySet())
{
if(talkgroupRange.overlaps(entry.getKey()))
{
mLog.warn("Alias [" + alias.getName() + "] with talkgroup range [" + talkgroupRange.toString() +
"] overlaps with alias [" + entry.getValue().getName() +
"] with talkgroup range [" + entry.getKey().toString() + "] for alias list [" + getName() + "]");
}
}
mTalkgroupRangeAliasMap.put(talkgroupRange, alias);
}

View File

@@ -193,6 +193,11 @@ public class TalkgroupEditor extends DocumentListenerEditor<AliasID>
{
Protocol protocol = mComboProtocol.getItemAt(mComboProtocol.getSelectedIndex());
if(protocol == null)
{
protocol = Protocol.UNKNOWN;
}
int value = -1;
try

View File

@@ -83,6 +83,14 @@ public enum TalkgroupFormat
return mMaximumValue;
}
/**
* Indicates if the value is valid for the protocol
*/
public boolean isValid(int value)
{
return getMinimumValidValue() <= value && value <= getMaximumValidValue();
}
/**
* Short description of the valid value range
*/
@@ -99,9 +107,14 @@ public enum TalkgroupFormat
return mValidRangeHelpText;
}
public static TalkgroupFormat get(Protocol prococol)
public static TalkgroupFormat get(Protocol protocol)
{
switch(prococol)
if(protocol == null)
{
return UNKNOWN;
}
switch(protocol)
{
case APCO25:
return APCO25;

View File

@@ -140,6 +140,21 @@ public class TalkgroupRange extends AliasID
return getMinTalkgroup() <= talkgroupValue && talkgroupValue <= getMaxTalkgroup();
}
/**
* Indicates if this talkgroup range overlaps the talkgroup range argument.
* @param talkgroupRange to check for overlap
* @return true if the ranges overlap
*/
public boolean overlaps(TalkgroupRange talkgroupRange)
{
return contains(talkgroupRange.getMinTalkgroup()) ||
contains(talkgroupRange.getMaxTalkgroup()) ||
(getMinTalkgroup() < talkgroupRange.getMinTalkgroup() &&
talkgroupRange.getMaxTalkgroup() < getMaxTalkgroup()) ||
(talkgroupRange.getMinTalkgroup() < getMinTalkgroup() &&
getMaxTalkgroup() < talkgroupRange.getMaxTalkgroup());
}
@JacksonXmlProperty(isAttribute = true, localName = "type", namespace = "http://www.w3.org/2001/XMLSchema-instance")
@Override
public AliasIDType getType()

View File

@@ -174,6 +174,8 @@ public class TalkgroupRangeEditor extends DocumentListenerEditor<AliasID>
TalkgroupRange talkgroupRange = getTalkgroupRange();
updateEditor(talkgroupRange.getProtocol());
if(talkgroupRange != null)
{
mComboProtocol.getModel().setSelectedItem(talkgroupRange.getProtocol());
@@ -202,6 +204,12 @@ public class TalkgroupRangeEditor extends DocumentListenerEditor<AliasID>
if(talkgroupRange != null)
{
Protocol protocol = mComboProtocol.getItemAt(mComboProtocol.getSelectedIndex());
if(protocol == null)
{
protocol = Protocol.UNKNOWN;
}
talkgroupRange.setProtocol(protocol);
int minValue = -1;

View File

@@ -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
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
* * ******************************************************************************
* * 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/>
* * *****************************************************************************
*
* 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;
@@ -40,6 +43,7 @@ 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 io.github.dsheirer.record.AudioRecorder;
import io.github.dsheirer.record.mp3.MP3Recorder;
import org.slf4j.Logger;
@@ -147,7 +151,8 @@ public class BroadcastFactory
* @param broadcastModel model for broadcastAudio configurations
* @return an editor for the specified broadcastAudio configuration
*/
public static Editor<BroadcastConfiguration> getEditor(BroadcastConfiguration configuration,
public static Editor<BroadcastConfiguration> getEditor(UserPreferences userPreferences,
BroadcastConfiguration configuration,
BroadcastModel broadcastModel,
AliasModel aliasModel,
IconManager iconManager)
@@ -157,7 +162,7 @@ public class BroadcastFactory
switch(configuration.getBroadcastServerType())
{
case BROADCASTIFY:
editor = new BroadcastifyConfigurationEditor(broadcastModel, aliasModel, iconManager);
editor = new BroadcastifyConfigurationEditor(userPreferences, broadcastModel, aliasModel, iconManager);
break;
case ICECAST_TCP:
editor = new IcecastTCPConfigurationEditor(broadcastModel, aliasModel, iconManager);

View File

@@ -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
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
* * ******************************************************************************
* * 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/>
* * *****************************************************************************
*
* 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;
@@ -586,12 +588,20 @@ public class BroadcastModel extends AbstractTableModel implements Listener<Reusa
@Override
public Class<?> getColumnClass(int columnIndex)
{
if(columnIndex == COLUMN_SERVER_ICON)
switch(columnIndex)
{
return ImageIcon.class;
case COLUMN_BROADCASTER_STATUS:
return BroadcastState.class;
case COLUMN_BROADCASTER_AGED_OFF_COUNT:
case COLUMN_BROADCASTER_QUEUE_SIZE:
case COLUMN_BROADCASTER_STREAMED_COUNT:
return Integer.class;
case COLUMN_SERVER_ICON:
return ImageIcon.class;
case COLUMN_STREAM_NAME:
default:
return String.class;
}
return String.class;
}
/**

View File

@@ -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
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
* * ******************************************************************************
* * 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/>
* * *****************************************************************************
*
* 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;
@@ -23,6 +26,7 @@ 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;
@@ -51,6 +55,7 @@ public class BroadcastPanel extends JPanel implements ActionListener, ListSelect
private BroadcastModel mBroadcastModel;
private AliasModel mAliasModel;
private IconManager mIconManager;
private UserPreferences mUserPreferences;
private BroadcastStatusPanel mBroadcastStatusPanel;
private JideSplitPane mSplitPane;
@@ -60,12 +65,14 @@ public class BroadcastPanel extends JPanel implements ActionListener, ListSelect
private JButton mCopyButton = new JButton(COPY_BROADCAST_CONFIGURATION);
private JButton mDeleteButton = new JButton(DELETE_BROADCAST_CONFIGURATION);
public BroadcastPanel(BroadcastModel broadcastModel, AliasModel aliasModel, IconManager iconManager)
public BroadcastPanel(BroadcastModel broadcastModel, AliasModel aliasModel, IconManager iconManager,
UserPreferences userPreferences)
{
mBroadcastModel = broadcastModel;
mAliasModel = aliasModel;
mIconManager = iconManager;
mEditor = mEmptyEditor;
mUserPreferences = userPreferences;
init();
}
@@ -74,7 +81,8 @@ public class BroadcastPanel extends JPanel implements ActionListener, ListSelect
{
setLayout(new MigLayout("insets 0 0 0 0 ", "[grow,fill]", "[grow,fill]"));
mBroadcastStatusPanel = new BroadcastStatusPanel(mBroadcastModel);
mBroadcastStatusPanel = new BroadcastStatusPanel(mBroadcastModel, mUserPreferences,
"broadcast.panel.broadcast.status.panel");
mBroadcastStatusPanel.getTable().setAutoCreateRowSorter(true);
mBroadcastStatusPanel.getTable().setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
@@ -210,8 +218,8 @@ public class BroadcastPanel extends JPanel implements ActionListener, ListSelect
if (selectedConfiguration != null)
{
mEditor = BroadcastFactory.getEditor(selectedConfiguration, mBroadcastModel, mAliasModel,
mIconManager);
mEditor = BroadcastFactory.getEditor(mUserPreferences, selectedConfiguration, mBroadcastModel,
mAliasModel, mIconManager);
}
else
{

View File

@@ -1,6 +1,7 @@
/*******************************************************************************
/*
* ******************************************************************************
* sdrtrunk
* Copyright (C) 2014-2017 Dennis Sheirer
* 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
@@ -14,10 +15,12 @@
*
* 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.preference.UserPreferences;
import io.github.dsheirer.preference.swing.JTableColumnWidthMonitor;
import net.miginfocom.swing.MigLayout;
import javax.swing.JLabel;
@@ -32,12 +35,17 @@ import java.awt.Component;
public class BroadcastStatusPanel extends JPanel
{
private JTable mTable;
private JTableColumnWidthMonitor mColumnWidthMonitor;
private JScrollPane mScrollPane;
private BroadcastModel mBroadcastModel;
private UserPreferences mUserPreferences;
private String mPreferenceKey;
public BroadcastStatusPanel(BroadcastModel broadcastModel)
public BroadcastStatusPanel(BroadcastModel broadcastModel, UserPreferences userPreferences, String preferenceKey)
{
mBroadcastModel = broadcastModel;
mUserPreferences = userPreferences;
mPreferenceKey = preferenceKey;
init();
}
@@ -57,6 +65,7 @@ public class BroadcastStatusPanel extends JPanel
renderer.setHorizontalAlignment(SwingConstants.CENTER);
mTable.getColumnModel().getColumn(BroadcastModel.COLUMN_BROADCASTER_STATUS).setCellRenderer(new StatusCellRenderer());
mColumnWidthMonitor = new JTableColumnWidthMonitor(mUserPreferences, mTable, mPreferenceKey);
mScrollPane = new JScrollPane(mTable);

View File

@@ -1,24 +1,26 @@
/*******************************************************************************
* 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.
* * ******************************************************************************
* * 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/>
* * *****************************************************************************
*
* 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.radioreference.api.soap2.UserFeedBroadcast;
import io.github.dsheirer.alias.AliasModel;
import io.github.dsheirer.audio.broadcast.BroadcastConfiguration;
import io.github.dsheirer.audio.broadcast.BroadcastConfigurationEditor;
@@ -26,6 +28,8 @@ 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;
@@ -64,10 +68,13 @@ public class BroadcastifyConfigurationEditor extends BroadcastConfigurationEdito
private JCheckBox mEnabled;
private JButton mSaveButton;
private JButton mResetButton;
private UserPreferences mUserPreferences;
public BroadcastifyConfigurationEditor(BroadcastModel broadcastModel, AliasModel aliasModel, IconManager iconManager)
public BroadcastifyConfigurationEditor(UserPreferences userPreferences, BroadcastModel broadcastModel,
AliasModel aliasModel, IconManager iconManager)
{
super(broadcastModel, aliasModel, iconManager);
mUserPreferences = userPreferences;
init();
}
@@ -91,7 +98,7 @@ public class BroadcastifyConfigurationEditor extends BroadcastConfigurationEdito
@Override
public void actionPerformed(ActionEvent e)
{
final UserFeedSelectionDialog dialog = new UserFeedSelectionDialog(BroadcastifyConfigurationEditor.this);
final UserFeedSelectionDialog dialog = new UserFeedSelectionDialog(mUserPreferences,BroadcastifyConfigurationEditor.this);
EventQueue.invokeLater(new Runnable()
{
@@ -391,7 +398,7 @@ public class BroadcastifyConfigurationEditor extends BroadcastConfigurationEdito
@Override
public void receive(UserFeedBroadcast userFeedBroadcast)
{
mName.setText(userFeedBroadcast.getDescr());
mName.setText(userFeedBroadcast.getDescription());
mServer.setText(userFeedBroadcast.getHostname());
mPort.setText(userFeedBroadcast.getPort());
mMountPoint.setText(userFeedBroadcast.getMount());

View File

@@ -1,78 +1,155 @@
/*******************************************************************************
* 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.
* * ******************************************************************************
* * 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/>
* * *****************************************************************************
*
* 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.radioreference.api.soap2.UserFeedBroadcast;
import io.github.dsheirer.radioreference.RadioReferenceService;
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.*;
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 javax.xml.rpc.ServiceException;
import java.awt.*;
import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.EventQueue;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.net.UnknownHostException;
import java.rmi.RemoteException;
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(Listener<UserFeedBroadcast> listener)
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.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(false);
mLookupButton.setEnabled(storedPassword != null && storedPassword != null);
mLookupButton.addActionListener(new ActionListener()
{
@Override
@@ -87,45 +164,25 @@ public class UserFeedSelectionDialog extends JDialog implements DocumentListener
{
try
{
RadioReferenceService service =
new RadioReferenceService(mUserNameText.getText(), mPasswordText.getText());
AuthorizationInformation authorizationInformation = new AuthorizationInformation(SDRTRUNK_APP_KEY,
mUserNameText.getText(), mPasswordText.getText());
UserFeedBroadcast[] feeds = service.getUserFeedBroadcasts();
mLog.debug("User:" + authorizationInformation.getUserName() + " Pass:" + authorizationInformation.getPassword() + " Key:" + authorizationInformation.getApplicationKey());
mUserFeedBroadcastList.setListData(feeds);
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(ServiceException se)
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", se);
}
catch(RemoteException re)
{
Throwable cause = re.getCause();
if(cause == null)
{
JOptionPane.showMessageDialog(UserFeedSelectionDialog.this,
re.getMessage(), "Error", JOptionPane.ERROR_MESSAGE);
}
else if(cause instanceof UnknownHostException)
{
JOptionPane.showMessageDialog(UserFeedSelectionDialog.this,
"Broadcastify server is unavailable", "No Server",
JOptionPane.ERROR_MESSAGE);
}
else
{
JOptionPane.showMessageDialog(UserFeedSelectionDialog.this,
"An unknown error has occurred. Please see log file.", "" +
"Unknown Error", JOptionPane.ERROR_MESSAGE);
mLog.error("Error retrieving user broadcast feed configurations from radio reference " +
"service: ", re);
}
mLog.error("Error creating the radio reference web service client", rre);
}
mLookupButton.setEnabled(true);
@@ -160,6 +217,7 @@ public class UserFeedSelectionDialog extends JDialog implements DocumentListener
{
if(mListener != null)
{
storeCredentials();
mListener.receive((UserFeedBroadcast)mUserFeedBroadcastList.getSelectedValue());
}
@@ -182,11 +240,37 @@ public class UserFeedSelectionDialog extends JDialog implements DocumentListener
setTitle("Broadcastify Feed Lookup");
setLayout(new BorderLayout());
add(panel, BorderLayout.CENTER);
setSize(300,250);
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();
@@ -240,8 +324,20 @@ public class UserFeedSelectionDialog extends JDialog implements DocumentListener
setForeground(list.getForeground());
}
setText(value.getDescr());
setText(value.getDescription());
return this; }
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());
}
}
}

View File

@@ -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
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
* * ******************************************************************************
* * 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/>
* * *****************************************************************************
*
* 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;
@@ -30,11 +33,9 @@ import org.apache.mina.core.service.IoHandlerAdapter;
import org.apache.mina.core.session.IoSession;
import org.apache.mina.filter.codec.ProtocolDecoderException;
import org.apache.mina.http.HttpClientCodec;
import org.apache.mina.http.HttpException;
import org.apache.mina.http.HttpRequestImpl;
import org.apache.mina.http.api.DefaultHttpResponse;
import org.apache.mina.http.api.HttpMethod;
import org.apache.mina.http.api.HttpStatus;
import org.apache.mina.http.api.HttpVersion;
import org.apache.mina.transport.socket.nio.NioSocketConnector;
import org.slf4j.Logger;
@@ -51,6 +52,7 @@ public class IcecastHTTPAudioBroadcaster extends IcecastAudioBroadcaster
{
private static final Logger mLog = LoggerFactory.getLogger(IcecastHTTPAudioBroadcaster.class);
private static final long RECONNECT_INTERVAL_MILLISECONDS = 30000; //30 seconds
private static final String HTTP_1_0_OK_HEX_DUMP = "48 54 54 50 2F 31 2E 30 20 32 30 30 20 4F 4B";
private NioSocketConnector mSocketConnector;
private IoSession mStreamingSession = null;
@@ -113,7 +115,7 @@ public class IcecastHTTPAudioBroadcaster extends IcecastAudioBroadcaster
mSocketConnector.setConnectTimeoutCheckInterval(10000);
// mSocketConnector.getFilterChain().addLast("logger",
// new LoggingFilter(IcecastTCPAudioBroadcaster.class));
// new LoggingFilter(IcecastHTTPAudioBroadcaster.class));
mSocketConnector.getFilterChain().addLast("codec", new HttpClientCodec());
mSocketConnector.setHandler(new IcecastHTTPIOHandler());
@@ -146,12 +148,12 @@ public class IcecastHTTPAudioBroadcaster extends IcecastAudioBroadcaster
}
else if(throwableCause != null)
{
setBroadcastState(BroadcastState.ERROR);
setBroadcastState(BroadcastState.DISCONNECTED);
mLog.debug("Failed to connect", rie);
}
else
{
setBroadcastState(BroadcastState.ERROR);
setBroadcastState(BroadcastState.DISCONNECTED);
mLog.debug("Failed to connect - no exception is available");
}
@@ -266,25 +268,30 @@ public class IcecastHTTPAudioBroadcaster extends IcecastAudioBroadcaster
{
if(throwable instanceof ProtocolDecoderException)
{
Throwable cause = ((ProtocolDecoderException) throwable).getCause();
Throwable cause = throwable.getCause();
if(cause instanceof HttpException &&
((HttpException) cause).getStatusCode() == HttpStatus.CLIENT_ERROR_LENGTH_REQUIRED.code())
/**
* Warning: Hack. Icecast 2.4.2+ doesn't like to send HTTP headers along with their HTTP/1.0 OK
* message which causes Apache Mina to think that it hasn't received the full HTTP response. So, we
* intercept the out of bounds exception and then inspect the message hex dump to see if it's an
* OK response and then play like everything is well and good.
*/
if(cause instanceof ArrayIndexOutOfBoundsException &&
((ProtocolDecoderException)throwable).getHexdump().startsWith(HTTP_1_0_OK_HEX_DUMP))
{
//Ignore - Icecast 2.4 sometimes doesn't send any headers with their HTTP responses. Mina expects a
//content-length for HTTP responses.
setBroadcastState(BroadcastState.CONNECTED);
}
else
{
mLog.error("HTTP protocol decoder error", throwable);
setBroadcastState(BroadcastState.TEMPORARY_BROADCAST_ERROR);
setBroadcastState(BroadcastState.DISCONNECTED);
disconnect();
}
}
else
{
mLog.error("Broadcast error", throwable);
setBroadcastState(BroadcastState.TEMPORARY_BROADCAST_ERROR);
setBroadcastState(BroadcastState.DISCONNECTED);
disconnect();
}

View File

@@ -83,8 +83,6 @@ public class AudioChannelPanel extends JPanel implements Listener<AudioEvent>, S
private JLabel mIdentifierLabel = new JLabel("-----");
private Identifier mIdentifier;
private List<Alias> mAliases = Collections.EMPTY_LIST;
private boolean mConfigured = false;
private AliasModel mAliasModel;
public AudioChannelPanel(IconManager iconManager, UserPreferences userPreferences, SettingsManager settingsManager,
@@ -191,31 +189,70 @@ public class AudioChannelPanel extends JPanel implements Listener<AudioEvent>, S
}
/**
* Resets the from and to labels. Note: this does not happen on the swing
* event thread. Only invoke from the swing thread.
* Resets the from and to labels.
*/
private void resetLabels()
{
boolean updated = mIdentifier != null;
mIdentifier = null;
mAliases = Collections.EMPTY_LIST;
updateLabels();
mConfigured = false;
if(updated)
{
updateLabels();
}
}
/**
* Truncates the string to the specified length using an ellipses at the end
* @param value to optionally truncate
* @param length of the final formatted string
* @return formatted string or null
*/
private static String truncate(String value, int length)
private void updateIdentifiers(IdentifierCollection identifierCollection)
{
if(value != null && value.length() > length)
if(identifierCollection == null || identifierCollection.isEmpty())
{
return value.substring(0, length - 3) + "...";
resetLabels();
return;
}
return value;
List<Identifier> toIds = identifierCollection.getIdentifiers(IdentifierClass.USER, Role.TO);
if(toIds.isEmpty())
{
resetLabels();
return;
}
boolean updated = false;
if(toIds.size() == 1)
{
Identifier currentIdentifier = mIdentifier;
if(currentIdentifier == null || currentIdentifier != toIds.get(0))
{
mIdentifier = toIds.get(0);
AliasList aliasList = mAliasModel.getAliasList(identifierCollection);
if(aliasList != null)
{
mAliases = aliasList.getAliases(mIdentifier);
}
updated = true;
}
}
else
{
mIdentifier = toIds.get(0);
AliasList aliasList = mAliasModel.getAliasList(identifierCollection);
if(aliasList != null)
{
mAliases = aliasList.getAliases(mIdentifier);
}
updated = true;
}
if(updated)
{
updateLabels();
}
}
/**
@@ -249,7 +286,8 @@ public class AudioChannelPanel extends JPanel implements Listener<AudioEvent>, S
}
final ImageIcon icon = iconName != null ? mIconManager.getIcon(iconName, 18) : null;
final String identifierText = truncate(identifier, 33);
final String identifierText = identifier;
EventQueue.invokeLater(new Runnable()
{
@@ -258,7 +296,6 @@ public class AudioChannelPanel extends JPanel implements Listener<AudioEvent>, S
{
mIdentifierLabel.setText(identifierText);
mIconLabel.setIcon(icon);
mConfigured = true;
}
});
@@ -272,23 +309,10 @@ public class AudioChannelPanel extends JPanel implements Listener<AudioEvent>, S
@Override
public void receive(final IdentifierCollection identifierCollection)
{
if(identifierCollection != null && (identifierCollection.isUpdated() || !mConfigured))
{
List<Identifier> toIds = identifierCollection.getIdentifiers(IdentifierClass.USER, Role.TO);
mIdentifier = !toIds.isEmpty() ? toIds.get(0) : null;
AliasList aliasList = mAliasModel.getAliasList(identifierCollection);
if(aliasList != null)
{
mAliases = aliasList.getAliases(mIdentifier);
}
updateLabels();
}
updateIdentifiers(identifierCollection);
}
}
@Override
public void settingChanged(Setting setting)
{

View File

@@ -1,7 +1,7 @@
/*
* ******************************************************************************
* sdrtrunk
* Copyright (C) 2014-2018 Dennis Sheirer
* 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
@@ -39,7 +39,8 @@ public class AudioChannelsPanel extends JPanel
public AudioChannelsPanel(IconManager iconManager, UserPreferences userPreferences, SettingsManager settingsManager,
IAudioController controller, AliasModel aliasModel)
{
setLayout(new MigLayout("insets 0 0 0 0", "[][sg abc,grow,fill][][sg abc,grow,fill]", "[grow,fill]"));
setLayout(new MigLayout("insets 0 0 0 0",
"[][sizegroup abc,grow,fill][][sizegroup abc,grow,fill]", "[grow,fill]"));
setBackground(Color.BLACK);

View File

@@ -1,7 +1,7 @@
/*
* ******************************************************************************
* sdrtrunk
* Copyright (C) 2014-2018 Dennis Sheirer
* 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
@@ -54,6 +54,11 @@ public abstract class AudioOutput implements Listener<ReusableAudioPacket>, Line
private LinkedTransferQueue<ReusableAudioPacket> mBuffer = new LinkedTransferQueue<>();
private int mBufferStartThreshold;
private int mBufferStopThreshold;
private static IdentifierCollection EMPTY_IDENTIFIER_COLLECTION = new IdentifierCollection();
static
{
EMPTY_IDENTIFIER_COLLECTION.setUpdated(true);
}
private Listener<IdentifierCollection> mIdentifierCollectionListener;
private Broadcaster<AudioEvent> mAudioEventBroadcaster = new Broadcaster<>();
@@ -380,12 +385,11 @@ public abstract class AudioOutput implements Listener<ReusableAudioPacket>, Line
*/
private void checkStop()
{
if(mCanProcessAudio &&
mOutput.isRunning() &&
mOutput.available() >= mBufferStopThreshold)
if(mCanProcessAudio && mOutput.isRunning() && mOutput.available() >= mBufferStopThreshold)
{
mOutput.drain();
mOutput.stop();
broadcast(EMPTY_IDENTIFIER_COLLECTION);
}
}
}

View File

@@ -33,6 +33,7 @@ import io.github.dsheirer.identifier.decoder.ChannelStateIdentifier;
import io.github.dsheirer.module.ProcessingChain;
import io.github.dsheirer.preference.UserPreferences;
import io.github.dsheirer.preference.identifier.TalkgroupFormatPreference;
import io.github.dsheirer.preference.swing.JTableColumnWidthMonitor;
import io.github.dsheirer.sample.Broadcaster;
import io.github.dsheirer.sample.Listener;
import net.miginfocom.swing.MigLayout;
@@ -58,6 +59,7 @@ import java.util.Map;
public class ChannelMetadataPanel extends JPanel implements ListSelectionListener
{
private static final String TABLE_PREFERENCE_KEY = "channel.metadata.panel";
private ChannelModel mChannelModel;
private ChannelProcessingManager mChannelProcessingManager;
private IconManager mIconManager;
@@ -66,6 +68,7 @@ public class ChannelMetadataPanel extends JPanel implements ListSelectionListene
private Broadcaster<ProcessingChain> mSelectedProcessingChainBroadcaster = new Broadcaster<>();
private Map<State,Color> mBackgroundColors = new HashMap<>();
private Map<State,Color> mForegroundColors = new HashMap<>();
private JTableColumnWidthMonitor mTableColumnMonitor;
/**
* Table view for currently decoding channel metadata
@@ -108,6 +111,9 @@ public class ChannelMetadataPanel extends JPanel implements ListSelectionListene
mTable.getColumnModel().getColumn(ChannelMetadataModel.COLUMN_CONFIGURATION_FREQUENCY)
.setCellRenderer(new FrequencyCellRenderer());
//Add a table column width monitor to store/restore column widths
mTableColumnMonitor = new JTableColumnWidthMonitor(mUserPreferences, mTable, TABLE_PREFERENCE_KEY);
JScrollPane scrollPane = new JScrollPane(mTable, JScrollPane.VERTICAL_SCROLLBAR_ALWAYS,
JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);

View File

@@ -1,6 +1,7 @@
/*******************************************************************************
/*
* ******************************************************************************
* sdrtrunk
* Copyright (C) 2014-2017 Dennis Sheirer
* 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
@@ -14,8 +15,8 @@
*
* 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.channel.metadata;
import com.jidesoft.swing.JideSplitPane;
@@ -33,10 +34,9 @@ import net.miginfocom.swing.MigLayout;
import javax.swing.JPanel;
import java.awt.Color;
public class ChannelMetadataViewer extends JPanel
public class NowPlayingPanel extends JPanel
{
private ChannelMetadataPanel mChannelMetadataPanel;
private ChannelDetailPanel mChannelDetailPanel;
private DecodeEventPanel mDecodeEventPanel;
private MessageActivityPanel mMessageActivityPanel;
@@ -45,12 +45,12 @@ public class ChannelMetadataViewer extends JPanel
* GUI panel that combines the currently decoding channels metadata table and viewers for channel details,
* messages, events, and spectral view.
*/
public ChannelMetadataViewer(ChannelModel channelModel, ChannelProcessingManager channelProcessingManager,
IconManager iconManager, AliasModel aliasModel, UserPreferences userPreferences)
public NowPlayingPanel(ChannelModel channelModel, ChannelProcessingManager channelProcessingManager,
IconManager iconManager, AliasModel aliasModel, UserPreferences userPreferences)
{
mChannelDetailPanel = new ChannelDetailPanel(channelProcessingManager);
mDecodeEventPanel = new DecodeEventPanel(iconManager, userPreferences, aliasModel);
mMessageActivityPanel = new MessageActivityPanel(channelProcessingManager);
mMessageActivityPanel = new MessageActivityPanel(userPreferences);
mChannelMetadataPanel = new ChannelMetadataPanel(channelModel, channelProcessingManager, iconManager, userPreferences);
init();
@@ -64,7 +64,6 @@ public class ChannelMetadataViewer extends JPanel
tabbedPane.addTab("Details", mChannelDetailPanel);
tabbedPane.addTab("Events", mDecodeEventPanel);
tabbedPane.addTab("Messages", mMessageActivityPanel);
// tabbedPane.addTab("Spectrum", mChannelSpectrumPanel);
tabbedPane.setFont(this.getFont());
tabbedPane.setForeground(Color.BLACK);

View File

@@ -28,6 +28,7 @@ import io.github.dsheirer.controller.channel.Channel;
import io.github.dsheirer.controller.channel.Channel.ChannelType;
import io.github.dsheirer.controller.channel.ChannelEvent;
import io.github.dsheirer.controller.channel.IChannelEventProvider;
import io.github.dsheirer.identifier.IdentifierClass;
import io.github.dsheirer.identifier.IdentifierUpdateListener;
import io.github.dsheirer.identifier.IdentifierUpdateNotification;
import io.github.dsheirer.identifier.IdentifierUpdateProvider;
@@ -203,6 +204,7 @@ public class ChannelState extends Module implements IChannelEventProvider, IDeco
{
mState = State.IDLE;
broadcast(new DecoderStateEvent(this, Event.RESET, State.IDLE));
mIdentifierCollection.remove(IdentifierClass.USER);
mIdentifierCollection.update(ChannelStateIdentifier.IDLE);
mSourceOverflow = false;
}

View File

@@ -1,6 +1,7 @@
/*******************************************************************************
/*
* ******************************************************************************
* sdrtrunk
* Copyright (C) 2014-2017 Dennis Sheirer
* 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
@@ -14,8 +15,8 @@
*
* 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.controller;
import com.jidesoft.swing.JideTabbedPane;
@@ -25,7 +26,7 @@ import io.github.dsheirer.audio.broadcast.BroadcastModel;
import io.github.dsheirer.audio.broadcast.BroadcastPanel;
import io.github.dsheirer.audio.playback.AudioPanel;
import io.github.dsheirer.audio.playback.AudioPlaybackManager;
import io.github.dsheirer.channel.metadata.ChannelMetadataViewer;
import io.github.dsheirer.channel.metadata.NowPlayingPanel;
import io.github.dsheirer.controller.channel.ChannelController;
import io.github.dsheirer.controller.channel.ChannelModel;
import io.github.dsheirer.controller.channel.ChannelProcessingManager;
@@ -41,7 +42,6 @@ import io.github.dsheirer.source.tuner.TunerViewPanel;
import net.miginfocom.swing.MigLayout;
import javax.swing.JPanel;
import javax.swing.JTable;
import java.awt.Color;
import java.awt.Dimension;
@@ -53,27 +53,18 @@ public class ControllerPanel extends JPanel
private AudioPanel mAudioPanel;
private BroadcastPanel mBroadcastPanel;
private ChannelController mChannelController;
private ChannelMetadataViewer mChannelMetadataViewer;
private NowPlayingPanel mNowPlayingPanel;
private ChannelModel mChannelModel;
private MapPanel mMapPanel;
private TunerViewPanel mTunerManagerPanel;
private BroadcastModel mBroadcastModel;
private JideTabbedPane mTabbedPane;
protected JTable mChannelActivityTable = new JTable();
public ControllerPanel(AudioPlaybackManager audioPlaybackManager,
AliasModel aliasModel,
BroadcastModel broadcastModel,
ChannelModel channelModel,
ChannelMapModel channelMapModel,
ChannelProcessingManager channelProcessingManager,
IconManager iconManager,
MapService mapService,
SettingsManager settingsManager,
SourceManager sourceManager,
TunerModel tunerModel,
UserPreferences userPreferences)
public ControllerPanel(AudioPlaybackManager audioPlaybackManager, AliasModel aliasModel, BroadcastModel broadcastModel,
ChannelModel channelModel, ChannelMapModel channelMapModel, ChannelProcessingManager channelProcessingManager,
IconManager iconManager, MapService mapService, SettingsManager settingsManager,
SourceManager sourceManager, TunerModel tunerModel, UserPreferences userPreferences)
{
mBroadcastModel = broadcastModel;
mChannelModel = channelModel;
@@ -81,19 +72,19 @@ public class ControllerPanel extends JPanel
mAudioPanel = new AudioPanel(iconManager, userPreferences, settingsManager, sourceManager, audioPlaybackManager,
aliasModel);
mChannelMetadataViewer = new ChannelMetadataViewer(channelModel, channelProcessingManager, iconManager,
mNowPlayingPanel = new NowPlayingPanel(channelModel, channelProcessingManager, iconManager,
aliasModel, userPreferences);
mMapPanel = new MapPanel(mapService, aliasModel, iconManager, settingsManager);
mBroadcastPanel = new BroadcastPanel(broadcastModel, aliasModel, iconManager);
mBroadcastPanel = new BroadcastPanel(broadcastModel, aliasModel, iconManager, userPreferences);
mChannelController = new ChannelController(channelModel, channelProcessingManager, channelMapModel,
sourceManager, aliasModel);
sourceManager, aliasModel, userPreferences);
mAliasController = new AliasController(aliasModel, broadcastModel, iconManager);
mAliasController = new AliasController(aliasModel, broadcastModel, iconManager, userPreferences);
mTunerManagerPanel = new TunerViewPanel(tunerModel);
mTunerManagerPanel = new TunerViewPanel(tunerModel, userPreferences);
init();
}
@@ -108,7 +99,7 @@ public class ControllerPanel extends JPanel
mTabbedPane = new JideTabbedPane();
mTabbedPane.setFont(this.getFont());
mTabbedPane.setForeground(Color.BLACK);
mTabbedPane.addTab("Now Playing", mChannelMetadataViewer);
mTabbedPane.addTab("Now Playing", mNowPlayingPanel);
mTabbedPane.addTab("Aliases", mAliasController);
mTabbedPane.addTab("Channels", mChannelController);
mTabbedPane.addTab("Map", mMapPanel);

View File

@@ -1,23 +1,29 @@
/*******************************************************************************
* sdr-trunk
* Copyright (C) 2014-2018 Dennis Sheirer
/*
* ******************************************************************************
* 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 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.
* 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/>
*
******************************************************************************/
* 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.controller.channel;
import com.jidesoft.swing.JideSplitPane;
import io.github.dsheirer.alias.AliasModel;
import io.github.dsheirer.controller.channel.map.ChannelMapModel;
import io.github.dsheirer.preference.UserPreferences;
import io.github.dsheirer.preference.swing.JTableColumnWidthMonitor;
import io.github.dsheirer.source.SourceManager;
import net.coderazzi.filters.gui.AutoChoices;
import net.coderazzi.filters.gui.TableFilterHeader;
@@ -43,11 +49,14 @@ public class ChannelController extends JPanel implements ActionListener, ListSel
{
private static final long serialVersionUID = 1L;
private static final String TABLE_PREFERENCE_KEY = "channel.controller";
private ChannelModel mChannelModel;
private ChannelProcessingManager mChannelProcessingManager;
private JTable mChannelTable;
private JTableColumnWidthMonitor mColumnWidthMonitor;
private TableFilterHeader mTableFilterHeader;
private ChannelEditor mEditor;
private UserPreferences mUserPreferences;
private static final String NEW_CHANNEL = "New";
private static final String COPY_CHANNEL = "Copy";
@@ -57,17 +66,16 @@ public class ChannelController extends JPanel implements ActionListener, ListSel
private JButton mCopyChannelButton = new JButton(COPY_CHANNEL);
private JButton mDeleteChannelButton = new JButton(DELETE_CHANNEL);
public ChannelController(ChannelModel channelModel,
ChannelProcessingManager channelProcessingManager,
ChannelMapModel channelMapModel,
SourceManager sourceManager,
AliasModel aliasModel)
public ChannelController(ChannelModel channelModel, ChannelProcessingManager channelProcessingManager,
ChannelMapModel channelMapModel, SourceManager sourceManager, AliasModel aliasModel,
UserPreferences userPreferences)
{
mChannelModel = channelModel;
mChannelProcessingManager = channelProcessingManager;
mEditor = new ChannelEditor(channelModel, channelProcessingManager, channelMapModel, sourceManager, aliasModel);
mChannelModel.addListener(mEditor);
mChannelProcessingManager.addChannelEventListener(mEditor);
mUserPreferences = userPreferences;
init();
}
@@ -82,6 +90,7 @@ public class ChannelController extends JPanel implements ActionListener, ListSel
mChannelTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
mChannelTable.getSelectionModel().addListSelectionListener(this);
mChannelTable.setAutoCreateRowSorter(true);
mColumnWidthMonitor = new JTableColumnWidthMonitor(mUserPreferences, mChannelTable, TABLE_PREFERENCE_KEY);
mTableFilterHeader = new TableFilterHeader(mChannelTable, AutoChoices.ENABLED);
mTableFilterHeader.setFilterOnUpdates(true);

View File

@@ -1,21 +1,23 @@
/*
* ******************************************************************************
* 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.
* * ******************************************************************************
* * 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/>
* * *****************************************************************************
*
* 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.gui;
@@ -78,6 +80,7 @@ import java.awt.AWTException;
import java.awt.Desktop;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Point;
import java.awt.Robot;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
@@ -97,8 +100,12 @@ public class SDRTrunk implements Listener<TunerEvent>
private final static Logger mLog = LoggerFactory.getLogger(SDRTrunk.class);
private static final String PROPERTY_BROADCAST_STATUS_VISIBLE = "main.broadcast.status.visible";
private boolean mBroadcastStatusVisible;
private static final String BASE_WINDOW_NAME = "sdrtrunk.main.window";
private static final String CONTROLLER_PANEL_IDENTIFIER = BASE_WINDOW_NAME + ".control.panel";
private static final String SPECTRAL_PANEL_IDENTIFIER = BASE_WINDOW_NAME + ".spectral.panel";
private static final String WINDOW_FRAME_IDENTIFIER = BASE_WINDOW_NAME + ".frame";
private boolean mBroadcastStatusVisible;
private AudioPacketManager mAudioPacketManager;
private IconManager mIconManager;
private BroadcastStatusPanel mBroadcastStatusPanel;
@@ -271,15 +278,45 @@ public class SDRTrunk implements Listener<TunerEvent>
*/
mTitle = SystemProperties.getInstance().getApplicationName();
mMainGui.setTitle(mTitle);
mMainGui.setSize(new Dimension(1280, 800));
mMainGui.setLocationRelativeTo(null);
Point location = mUserPreferences.getSwingPreference().getLocation(WINDOW_FRAME_IDENTIFIER);
if(location != null)
{
mMainGui.setLocation(location);
}
else
{
mMainGui.setLocationRelativeTo(null);
}
mMainGui.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
mMainGui.addWindowListener(new ShutdownMonitor());
//Set preferred sizes to influence the split
Dimension dimension = mUserPreferences.getSwingPreference().getDimension(WINDOW_FRAME_IDENTIFIER);
mSpectralPanel.setPreferredSize(new Dimension(1280, 300));
mControllerPanel.setPreferredSize(new Dimension(1280, 500));
if(dimension != null)
{
Dimension spectral = mUserPreferences.getSwingPreference().getDimension(SPECTRAL_PANEL_IDENTIFIER);
if(spectral != null)
{
mSpectralPanel.setSize(spectral);
}
Dimension controller = mUserPreferences.getSwingPreference().getDimension(CONTROLLER_PANEL_IDENTIFIER);
if(controller != null)
{
mControllerPanel.setSize(controller);
}
mMainGui.setSize(dimension);
}
else
{
mMainGui.setSize(new Dimension(1280, 800));
}
mSplitPane = new JideSplitPane(JideSplitPane.VERTICAL_SPLIT);
mSplitPane.setDividerSize(5);
mSplitPane.add(mSpectralPanel);
@@ -360,9 +397,9 @@ public class SDRTrunk implements Listener<TunerEvent>
viewMenu.add(new BroadcastStatusVisibleMenuItem(mControllerPanel));
viewMenu.add(new JSeparator());
for(Tuner tuner: mSourceManager.getTunerModel().getTuners())
for(Tuner tuner : mSourceManager.getTunerModel().getTuners())
{
viewMenu.add(new ShowTunerMenuItem(mSpectralPanel, tuner));
viewMenu.add(new ShowTunerMenuItem(mSourceManager.getTunerModel(), tuner));
}
viewMenu.add(new JSeparator());
@@ -371,7 +408,7 @@ public class SDRTrunk implements Listener<TunerEvent>
viewMenu.add(new JSeparator());
JMenuItem preferencesItem = new JMenuItem("Preferences");
preferencesItem.addActionListener(e -> MyEventBus.getEventBus()
.post(new PreferenceEditorViewRequest(PreferenceEditorType.CHANNEL_EVENT)));
.post(new PreferenceEditorViewRequest(PreferenceEditorType.CHANNEL_EVENT)));
viewMenu.add(preferencesItem);
menuBar.add(viewMenu);
@@ -424,6 +461,10 @@ public class SDRTrunk implements Listener<TunerEvent>
private void processShutdown()
{
mLog.info("Application shutdown started ...");
mUserPreferences.getSwingPreference().setLocation(WINDOW_FRAME_IDENTIFIER, mMainGui.getLocation());
mUserPreferences.getSwingPreference().setDimension(WINDOW_FRAME_IDENTIFIER, mMainGui.getSize());
mUserPreferences.getSwingPreference().setDimension(SPECTRAL_PANEL_IDENTIFIER, mSpectralPanel.getSize());
mUserPreferences.getSwingPreference().setDimension(CONTROLLER_PANEL_IDENTIFIER, mControllerPanel.getSize());
mJavaFxWindowManager.shutdown();
mLog.info("Stopping channels ...");
mChannelProcessingManager.shutdown();
@@ -443,7 +484,8 @@ public class SDRTrunk implements Listener<TunerEvent>
{
if(mBroadcastStatusPanel == null)
{
mBroadcastStatusPanel = new BroadcastStatusPanel(mBroadcastModel);
mBroadcastStatusPanel = new BroadcastStatusPanel(mBroadcastModel, mUserPreferences,
"application.broadcast.status.panel");
mBroadcastStatusPanel.setPreferredSize(new Dimension(880, 70));
mBroadcastStatusPanel.getTable().setEnabled(false);
}

View File

@@ -1,7 +1,7 @@
/*
* ******************************************************************************
* sdrtrunk
* Copyright (C) 2014-2018 Dennis Sheirer
* 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
@@ -84,7 +84,7 @@ public class IdentifierCollection
return mUpdated;
}
protected void setUpdated(boolean updated)
public void setUpdated(boolean updated)
{
mUpdated = updated;
}
@@ -114,6 +114,14 @@ public class IdentifierCollection
return Collections.unmodifiableList(mIdentifiers);
}
/**
* Indicates if this collection has no identifiers
*/
public boolean isEmpty()
{
return mIdentifiers.isEmpty();
}
/**
* Get a list of identifiers by identifier class from this collection.
*

View File

@@ -22,6 +22,8 @@ package io.github.dsheirer.identifier;
import io.github.dsheirer.identifier.configuration.AliasListConfigurationIdentifier;
import io.github.dsheirer.sample.Listener;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Collection;
import java.util.Iterator;
@@ -32,6 +34,7 @@ import java.util.Iterator;
public class MutableIdentifierCollection extends IdentifierCollection implements IdentifierUpdateProvider,
Listener<IdentifierUpdateNotification>
{
private final static Logger mLog = LoggerFactory.getLogger(MutableIdentifierCollection.class);
private Listener<IdentifierUpdateNotification> mListener;
public MutableIdentifierCollection()

View File

@@ -1,7 +1,7 @@
/*
* ******************************************************************************
* sdrtrunk
* Copyright (C) 2014-2018 Dennis Sheirer
* 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
@@ -23,7 +23,6 @@ package io.github.dsheirer.identifier.patch;
import io.github.dsheirer.identifier.Identifier;
import io.github.dsheirer.identifier.IdentifierClass;
import io.github.dsheirer.identifier.Role;
import io.github.dsheirer.identifier.talkgroup.TalkgroupIdentifier;
import java.util.ArrayList;
import java.util.HashMap;
@@ -32,6 +31,14 @@ import java.util.Map;
/**
* Manager for (temporary) patch groups and related identifiers.
*
* Patch groups are primarily used on Motorola P25 systems. This manager monitors patch group additions and deletions
* and maintains a map of the current patch groups.
*
* Traffic channel calls will sometimes reference a patch group by simply using the patch group identifier. This
* manager will replace that reference with the full patch group so that the call event has the full patch group
* including all of the patched groups, which may not have been included in the patch group reference on the
* traffic channel.
*/
public class PatchGroupManager
{
@@ -127,7 +134,7 @@ public class PatchGroupManager
}
/**
* Checks the TALKGROUP or PATCH GROUP identifier and replaces the identifier with the current patch group
* Checks the PATCH GROUP identifier and replaces the identifier with the current patch group
* if the identifier matches a currently managed patch group.
*
* @param identifier for a talkgroup or a patch group.
@@ -144,22 +151,6 @@ public class PatchGroupManager
{
switch(identifier.getForm())
{
case TALKGROUP:
if(identifier instanceof TalkgroupIdentifier)
{
TalkgroupIdentifier tgi = (TalkgroupIdentifier)identifier;
if(tgi.isGroup())
{
int talkgroupId = ((TalkgroupIdentifier)identifier).getValue();
if(mPatchGroupMap.containsKey(talkgroupId))
{
return mPatchGroupMap.get(talkgroupId);
}
}
}
break;
case PATCH_GROUP:
if(identifier instanceof PatchGroupIdentifier)
{

View File

@@ -1,7 +1,7 @@
/*
* ******************************************************************************
* sdrtrunk
* Copyright (C) 2014-2018 Dennis Sheirer
* 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
@@ -34,6 +34,7 @@ import io.github.dsheirer.identifier.Role;
import io.github.dsheirer.module.ProcessingChain;
import io.github.dsheirer.preference.PreferenceType;
import io.github.dsheirer.preference.UserPreferences;
import io.github.dsheirer.preference.swing.JTableColumnWidthMonitor;
import io.github.dsheirer.sample.Listener;
import net.miginfocom.swing.MigLayout;
import org.slf4j.Logger;
@@ -55,10 +56,12 @@ import java.util.List;
public class DecodeEventPanel extends JPanel implements Listener<ProcessingChain>
{
private final static Logger mLog = LoggerFactory.getLogger(DecodeEventPanel.class);
private static final long serialVersionUID = 1L;
private final static Logger mLog = LoggerFactory.getLogger(DecodeEventPanel.class);
private static final String TABLE_PREFERENCE_KEY = "decode.event.panel";
private JTable mTable;
private JTableColumnWidthMonitor mTableColumnWidthMonitor;
private DecodeEventModel mEmptyDecodeEventModel;
private JScrollPane mEmptyScroller;
private IconManager mIconManager;
@@ -83,6 +86,7 @@ public class DecodeEventPanel extends JPanel implements Listener<ProcessingChain
mTable = new JTable(mEmptyDecodeEventModel);
mTable.setAutoCreateRowSorter(true);
mTable.setAutoResizeMode(JTable.AUTO_RESIZE_LAST_COLUMN);
mTableColumnWidthMonitor = new JTableColumnWidthMonitor(mUserPreferences, mTable, TABLE_PREFERENCE_KEY);
updateCellRenderers();
mEmptyScroller = new JScrollPane(mTable);
@@ -127,12 +131,17 @@ public class DecodeEventPanel extends JPanel implements Listener<ProcessingChain
public void receive(ProcessingChain processingChain)
{
EventQueue.invokeLater(() -> {
//Dispose the current column width monitor and recreate after we swap out the table model
mTableColumnWidthMonitor.dispose();
mTable.setModel(processingChain != null ? processingChain.getDecodeEventModel() : mEmptyDecodeEventModel);
if(processingChain != null)
{
updateCellRenderers();
}
mTableColumnWidthMonitor = new JTableColumnWidthMonitor(mUserPreferences, mTable, TABLE_PREFERENCE_KEY);
});
}
@@ -162,17 +171,6 @@ public class DecodeEventPanel extends JPanel implements Listener<ProcessingChain
if(value instanceof IdentifierCollection)
{
List<Identifier> identifiers = ((IdentifierCollection)value).getIdentifiers(mRole);
// if(identifiers.size() > 1)
// {
// StringBuilder sb = new StringBuilder();
// sb.append("Multiple Identifiers:\n");
// for(Identifier identifier: identifiers)
// {
// sb.append("ID:").append(identifier.debug()).append("\n");
// }
// mLog.debug(sb.toString());
// }
label.setText(format(identifiers));
}
else

View File

@@ -1,6 +1,7 @@
/*******************************************************************************
/*
* ******************************************************************************
* sdrtrunk
* Copyright (C) 2014-2017 Dennis Sheirer
* 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
@@ -14,17 +15,18 @@
*
* 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.module.decode.event;
import com.jidesoft.swing.JideButton;
import com.jidesoft.swing.JideSplitButton;
import io.github.dsheirer.controller.channel.ChannelProcessingManager;
import io.github.dsheirer.filter.FilterEditorPanel;
import io.github.dsheirer.filter.FilterSet;
import io.github.dsheirer.message.IMessage;
import io.github.dsheirer.module.ProcessingChain;
import io.github.dsheirer.preference.UserPreferences;
import io.github.dsheirer.preference.swing.JTableColumnWidthMonitor;
import io.github.dsheirer.sample.Listener;
import net.miginfocom.swing.MigLayout;
import org.slf4j.Logger;
@@ -49,26 +51,24 @@ import java.awt.event.MouseListener;
public class MessageActivityPanel extends JPanel implements Listener<ProcessingChain>
{
private static final long serialVersionUID = 1L;
private final static Logger mLog = LoggerFactory.getLogger(MessageActivityPanel.class);
private static final String TABLE_PREFERENCE_KEY = "message.activity.panel";
private static MessageActivityModel EMPTY_MODEL = new MessageActivityModel();
private JTable mTable = new JTable(EMPTY_MODEL);
private JTableColumnWidthMonitor mTableColumnWidthMonitor;
private UserPreferences mUserPreferences;
private MessageManagementPanel mManagementPanel = new MessageManagementPanel();
private ChannelProcessingManager mChannelProcessingManager;
public MessageActivityPanel(ChannelProcessingManager channelProcessingManager)
public MessageActivityPanel(UserPreferences userPreferences)
{
mChannelProcessingManager = channelProcessingManager;
mUserPreferences = userPreferences;
setLayout(new MigLayout("insets 0 0 0 0", "[][grow,fill]", "[]0[grow,fill]"));
add(mManagementPanel, "span,growx");
add(new JScrollPane(mTable), "span,grow");
mTableColumnWidthMonitor = new JTableColumnWidthMonitor(mUserPreferences, mTable, TABLE_PREFERENCE_KEY);
}
@Override
@@ -79,6 +79,8 @@ public class MessageActivityPanel extends JPanel implements Listener<ProcessingC
@Override
public void run()
{
//Remove the existing table column width monitor and replace after swapping the table model
mTableColumnWidthMonitor.dispose();
mTable.setModel(processingChain != null ? processingChain.getMessageActivityModel() : EMPTY_MODEL);
mTable.getColumnModel().getColumn(0).setPreferredWidth(18);
@@ -93,6 +95,7 @@ public class MessageActivityPanel extends JPanel implements Listener<ProcessingC
{
mManagementPanel.disableButtons();
}
mTableColumnWidthMonitor = new JTableColumnWidthMonitor(mUserPreferences, mTable, TABLE_PREFERENCE_KEY);
}
});
}

View File

@@ -1,7 +1,7 @@
/*
* ******************************************************************************
* sdrtrunk
* Copyright (C) 2014-2018 Dennis Sheirer
* 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
@@ -33,8 +33,6 @@ import io.github.dsheirer.module.decode.p25.message.pdu.packet.sndcp.SNDCPPacket
import io.github.dsheirer.module.decode.p25.reference.DataUnitID;
import io.github.dsheirer.module.decode.p25.reference.IPHeaderCompression;
import io.github.dsheirer.module.decode.p25.reference.UDPHeaderCompression;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.List;
@@ -44,7 +42,7 @@ import java.util.List;
*/
public class PacketMessage extends P25Message
{
private final static Logger mLog = LoggerFactory.getLogger(PacketMessage.class);
// private final static Logger mLog = LoggerFactory.getLogger(PacketMessage.class);
private PDUSequence mPDUSequence;
private BinaryMessage mPayload;
@@ -88,7 +86,6 @@ public class PacketMessage extends P25Message
}
else
{
mLog.debug("***************** 2 byte SNDCP header with no data blocks header: " + getPDUSequence().getHeader().getMessage().toHexString());
mSNDCPPacketHeader = new SNDCPPacketHeader(getHeader().isOutbound());
}
}
@@ -162,7 +159,7 @@ public class PacketMessage extends P25Message
{
List<Integer> numbers = new ArrayList<>();
for(DataBlock dataBlock: getPDUSequence().getDataBlocks())
for(DataBlock dataBlock : getPDUSequence().getDataBlocks())
{
if(dataBlock instanceof ConfirmedDataBlock)
{

View File

@@ -371,8 +371,8 @@ public class P25NetworkConfigurationMonitor
}
else if(mLCSystemServiceBroadcast != null)
{
sb.append(" AVAILABLE SERVICES:").append(mTSBKSystemServiceBroadcast.getAvailableServices());
sb.append(" SUPPORTED SERVICES:").append(mTSBKSystemServiceBroadcast.getSupportedServices());
sb.append(" AVAILABLE SERVICES:").append(mLCSystemServiceBroadcast.getAvailableServices());
sb.append(" SUPPORTED SERVICES:").append(mLCSystemServiceBroadcast.getSupportedServices());
}

View File

@@ -1,21 +1,23 @@
/*
* ******************************************************************************
* 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.
* * ******************************************************************************
* * 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/>
* * *****************************************************************************
*
* 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.preference;
@@ -26,9 +28,10 @@ package io.github.dsheirer.preference;
public enum PreferenceType
{
DECODE_EVENT,
JMBE_LIBRARY,
DIRECTORY,
IDENTIFIER,
JMBE_LIBRARY,
MULTI_FREQUENCY,
RADIO_REFERENCE,
TUNER;
}

View File

@@ -1,21 +1,23 @@
/*
* ******************************************************************************
* 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.
* * ******************************************************************************
* * 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/>
* * *****************************************************************************
*
* 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.preference;
@@ -26,8 +28,10 @@ import io.github.dsheirer.preference.directory.DirectoryPreference;
import io.github.dsheirer.preference.event.DecodeEventPreference;
import io.github.dsheirer.preference.identifier.TalkgroupFormatPreference;
import io.github.dsheirer.preference.playlist.PlaylistPreference;
import io.github.dsheirer.preference.radioreference.RadioReferencePreference;
import io.github.dsheirer.preference.source.ChannelMultiFrequencyPreference;
import io.github.dsheirer.preference.source.TunerPreference;
import io.github.dsheirer.preference.swing.SwingPreference;
import io.github.dsheirer.sample.Listener;
/**
@@ -53,8 +57,10 @@ public class UserPreferences implements Listener<PreferenceType>
private DirectoryPreference mDirectoryPreference;
private ChannelMultiFrequencyPreference mChannelMultiFrequencyPreference;
private PlaylistPreference mPlaylistPreference;
private RadioReferencePreference mRadioReferencePreference;
private TalkgroupFormatPreference mTalkgroupFormatPreference;
private TunerPreference mTunerPreference;
private SwingPreference mSwingPreference = new SwingPreference();
/**
* Constructs a new user preferences instance
@@ -104,6 +110,14 @@ public class UserPreferences implements Listener<PreferenceType>
return mPlaylistPreference;
}
/**
* Radio reference web services preferences
*/
public RadioReferencePreference getRadioReferencePreference()
{
return mRadioReferencePreference;
}
/**
* Identifier preferences
*/
@@ -120,6 +134,15 @@ public class UserPreferences implements Listener<PreferenceType>
return mTunerPreference;
}
/**
* Swing window location/size user preferences
*/
public SwingPreference getSwingPreference()
{
return mSwingPreference;
}
/**
* Loads the managed preferences
*/
@@ -130,6 +153,7 @@ public class UserPreferences implements Listener<PreferenceType>
mDirectoryPreference = new DirectoryPreference(this::receive);
mChannelMultiFrequencyPreference = new ChannelMultiFrequencyPreference(this::receive);
mPlaylistPreference = new PlaylistPreference(this::receive, mDirectoryPreference);
mRadioReferencePreference = new RadioReferencePreference(this::receive);
mTalkgroupFormatPreference = new TalkgroupFormatPreference(this::receive);
mTunerPreference = new TunerPreference(this::receive);
}

View File

@@ -1,7 +1,7 @@
/*
* ******************************************************************************
* sdrtrunk
* Copyright (C) 2014-2018 Dennis Sheirer
* 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
@@ -42,6 +42,7 @@ public class APCO25TalkgroupFormatter extends IntegerFormatter
switch(format)
{
case DECIMAL:
case FORMATTED:
return toDecimal(identifier.getValue(),
(identifier.isGroup() ? GROUP_DECIMAL_WIDTH : UNIT_DECIMAL_WIDTH));
case HEXADECIMAL:

View File

@@ -38,6 +38,7 @@ public class MDC1200TalkgroupFormatter extends IntegerFormatter
switch(format)
{
case DECIMAL:
case FORMATTED:
return toDecimal(identifier.getValue(), DECIMAL_WIDTH);
case HEXADECIMAL:
return toHex(identifier.getValue(), HEXADECIMAL_WIDTH);

View File

@@ -38,6 +38,7 @@ public class PassportTalkgroupFormatter extends IntegerFormatter
switch(format)
{
case DECIMAL:
case FORMATTED:
return toDecimal(identifier.getValue(), DECIMAL_WIDTH);
case HEXADECIMAL:
return toHex(identifier.getValue(), HEXADECIMAL_WIDTH);

View File

@@ -0,0 +1,181 @@
/*
*
* * ******************************************************************************
* * 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.preference.radioreference;
import io.github.dsheirer.preference.Preference;
import io.github.dsheirer.preference.PreferenceType;
import io.github.dsheirer.sample.Listener;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.prefs.Preferences;
/**
* User preferences for the display of channel decode events
*/
public class RadioReferencePreference extends Preference
{
private final static Logger mLog = LoggerFactory.getLogger(RadioReferencePreference.class);
private Preferences mPreferences = Preferences.userNodeForPackage(RadioReferencePreference.class);
private static final String STORE_CREDENTIALS = "store.credentials";
private static final String USER_NAME = "user.name";
private static final String PASSWORD = "user.authorization";
private String mUserName;
private String mPassword;
private Boolean mStoreCredentials;
/**
* Constructs an instance.
* @param updateListener to receive notifications when preferences are updated.
*/
public RadioReferencePreference(Listener<PreferenceType> updateListener)
{
super(updateListener);
loadSettings();
}
@Override
public PreferenceType getPreferenceType()
{
return PreferenceType.RADIO_REFERENCE;
}
private void loadSettings()
{
mStoreCredentials = mPreferences.getBoolean(STORE_CREDENTIALS, true);
}
/**
* Clears user credentials and resets username and password to null
*/
public void removeStoreCredentials()
{
setStoreCredentials(false);
setUserName(null);
setPassword(null);
notifyPreferenceUpdated();
}
/**
* Indicates if there are any non-null values stored for user name or password
* @return true if there are values stored
*/
public boolean hasStoredCredentials()
{
return mPreferences.get(USER_NAME, null) != null || mPreferences.get(PASSWORD, null) != null;
}
/**
* User name
*/
public String getUserName()
{
if(mUserName == null)
{
mUserName = mPreferences.get(USER_NAME, null);
}
return mUserName;
}
/**
* Sets user name
* @param username to store or null to remove any stored value
*/
public void setUserName(String username)
{
mUserName = username;
if(mUserName == null)
{
mPreferences.remove(USER_NAME);
}
else
{
mPreferences.put(USER_NAME, mUserName);
}
notifyPreferenceUpdated();
}
/**
* Password
* @return password
*/
public String getPassword()
{
if(mPassword == null)
{
mPassword = mPreferences.get(PASSWORD, null);
}
return mPassword;
}
/**
* Sets the preferences persisted password.
* @param password to store or null to remove any stored value
*/
public void setPassword(String password)
{
mPassword = password;
if(mPassword == null)
{
mPreferences.remove(PASSWORD);
}
else
{
mPreferences.put(PASSWORD, mPassword);
}
notifyPreferenceUpdated();
}
/**
* Indicates if the credentials should be persisted in the preferences store.
*
* @return true if credentials should be stored.
*/
public boolean isStoreCredentials()
{
if(mStoreCredentials == null)
{
mStoreCredentials = mPreferences.getBoolean(STORE_CREDENTIALS, true);
}
return mStoreCredentials;
}
/**
* Set the store credentials option
* @param store true to store credentials
*/
public void setStoreCredentials(boolean store)
{
mStoreCredentials = store;
mPreferences.putBoolean(STORE_CREDENTIALS, store);
notifyPreferenceUpdated();
}
}

View File

@@ -0,0 +1,154 @@
/*
* ******************************************************************************
* 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.preference.swing;
import io.github.dsheirer.preference.UserPreferences;
import io.github.dsheirer.util.ThreadPool;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.swing.JTable;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.TableColumnModelEvent;
import javax.swing.event.TableColumnModelListener;
import javax.swing.table.TableColumnModel;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
/**
* Monitors a JTable column model and persists column width changes to the user preferences. Restores
* previous column width preferred sizes on application restart.
*/
public class JTableColumnWidthMonitor
{
private static final Logger mLog = LoggerFactory.getLogger(JTableColumnWidthMonitor.class);
private UserPreferences mUserPreferences;
private JTable mTable;
private String mKey;
private ColumnResizeListener mColumnResizeListener = new ColumnResizeListener();
private AtomicBoolean mSaveInProgress = new AtomicBoolean();
/**
* Constructs a column width monitor.
*
* @param userPreferences to store column widths
* @param table to monitor for column width changes
* @param key that uniquely identifies the table to monitor
*/
public JTableColumnWidthMonitor(UserPreferences userPreferences, JTable table, String key)
{
mUserPreferences = userPreferences;
mTable = table;
mKey = key;
restoreColumnWidths();
mTable.getColumnModel().addColumnModelListener(mColumnResizeListener);
}
/**
* Prepares this monitor for disposal by unregistering as a listener to the table column model.
*/
public void dispose()
{
if(mTable != null && mColumnResizeListener != null)
{
mTable.getColumnModel().removeColumnModelListener(mColumnResizeListener);
}
mTable = null;
mUserPreferences = null;
}
/**
* Sets the preferred column widths on the table from persisted settings
*/
private void restoreColumnWidths()
{
TableColumnModel model = mTable.getColumnModel();
for(int x = 0; x < model.getColumnCount(); x++)
{
int width = mUserPreferences.getSwingPreference().getInt(getColumnKey(x), Integer.MAX_VALUE);
if(width != Integer.MAX_VALUE)
{
model.getColumn(x).setPreferredWidth(width);
}
}
}
/**
* Stores the current column widths to the user preferences
*/
private void storeColumnWidths()
{
TableColumnModel model = mTable.getColumnModel();
for(int x = 0; x < model.getColumnCount(); x++)
{
mUserPreferences.getSwingPreference().setInt(getColumnKey(x), model.getColumn(x).getWidth());
}
}
/**
* Constructs a preference key for the column number
*/
private String getColumnKey(int column)
{
return mKey + ".column." + column;
}
/**
* Table column model listener.
*/
class ColumnResizeListener implements TableColumnModelListener
{
@Override
public void columnMarginChanged(ChangeEvent e)
{
if(mSaveInProgress.compareAndSet(false, true))
{
ThreadPool.SCHEDULED.schedule(new ColumnWidthSaveTask(), 2, TimeUnit.SECONDS);
}
}
@Override
public void columnAdded(TableColumnModelEvent e){}
@Override
public void columnRemoved(TableColumnModelEvent e){}
@Override
public void columnMoved(TableColumnModelEvent e){}
@Override
public void columnSelectionChanged(ListSelectionEvent e){}
}
public class ColumnWidthSaveTask implements Runnable
{
@Override
public void run()
{
storeColumnWidths();
mSaveInProgress.set(false);
}
}
}

View File

@@ -0,0 +1,148 @@
/*
* ******************************************************************************
* 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.preference.swing;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.awt.Dimension;
import java.awt.GraphicsDevice;
import java.awt.GraphicsEnvironment;
import java.awt.Point;
import java.util.prefs.Preferences;
/**
* User preference settings for Swing windows and components.
*/
public class SwingPreference
{
private static final Logger mLog = LoggerFactory.getLogger(SwingPreference.class);
private static final String LOCATION_X = ".x";
private static final String LOCATION_Y = ".y";
private static final String SIZE_HEIGHT = ".height";
private static final String SIZE_WIDTH = ".width";
private Preferences mPreferences = Preferences.userNodeForPackage(SwingPreference.class);
public SwingPreference()
{
}
/**
* Returns the stored swing related integer value
* @param key for accessing the value
* @param defaultValue to return if the value has not yet been persisted
* @return value or default value
*/
public int getInt(String key, int defaultValue)
{
return mPreferences.getInt(key, defaultValue);
}
/**
* Stores the swing related integer value
* @param key to reference the value
*/
public void setInt(String key, int value)
{
mPreferences.putInt(key, value);
}
/**
* Returns the persisted window location for the specified preference key.
* @param key identifying the window
* @return location or null
*/
public Point getLocation(String key)
{
int x = mPreferences.getInt(key + LOCATION_X, Integer.MAX_VALUE);
int y = mPreferences.getInt(key + LOCATION_Y, Integer.MAX_VALUE);
if(x != Integer.MAX_VALUE && y != Integer.MAX_VALUE)
{
Point location = new Point(x,y);
if(isValidWindowLocation(location))
{
return location;
}
}
return null;
}
/**
* Stores the current window location
* @param key to identify the window
* @param location to persist
*/
public void setLocation(String key, Point location)
{
mPreferences.putInt(key + LOCATION_X, location.x);
mPreferences.putInt(key + LOCATION_Y, location.y);
}
/**
* Returns the stored size/dimension of the window identified by the key argument.
* @param key to identify the window
* @return dimension or null
*/
public Dimension getDimension(String key)
{
int height = mPreferences.getInt(key + SIZE_HEIGHT, Integer.MAX_VALUE);
int width = mPreferences.getInt(key + SIZE_WIDTH, Integer.MAX_VALUE);
if(height != Integer.MAX_VALUE && width != Integer.MAX_VALUE)
{
return new Dimension(width, height);
}
return null;
}
public void setDimension(String key, Dimension dimension)
{
mPreferences.putInt(key + SIZE_HEIGHT, dimension.height);
mPreferences.putInt(key + SIZE_WIDTH, dimension.width);
}
/**
* Tests a stored window location to determine if it can be displayed on the currently available
* graphics display devices so that we don't position a window at a previously available location
* that is not currently available.
*
* @param location to test
* @return true if the location is displayable.
*/
private boolean isValidWindowLocation(Point location)
{
for(GraphicsDevice gd:GraphicsEnvironment.getLocalGraphicsEnvironment().getScreenDevices())
{
if(gd.getDefaultConfiguration().getBounds().contains(location))
{
return true;
}
}
return false;
}
}

View File

@@ -1,153 +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.radioreference;
import com.radioreference.api.soap2.AuthInfo;
import com.radioreference.api.soap2.RRWsdlLocator;
import com.radioreference.api.soap2.RRWsdlPortType;
import com.radioreference.api.soap2.UserFeedBroadcast;
import com.radioreference.api.soap2.UserInfo;
import org.apache.axis.AxisFault;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.xml.rpc.ServiceException;
import java.rmi.RemoteException;
import java.text.SimpleDateFormat;
public class RadioReferenceService
{
private final static Logger mLog = LoggerFactory.getLogger( RadioReferenceService.class );
private final SimpleDateFormat SDF = new SimpleDateFormat("MM-dd-yyyy");
public static final String SDRTRUNK_APP_KEY = "88969092";
public static final String RADIO_REFERENCE_API_VERSION = "14";
public static final String SOAP_RPC_RESPONSE_STYLE = "rpc";
private RRWsdlLocator mService = new RRWsdlLocator();
private RRWsdlPortType mClient;
private Long mAccountExpiration;
private AuthInfo mUserAuthorization;
/**
* Creates a SOAP web service client for interaction with the radioreference.com server.
*
* @param username for a premium subscriber
* @param password for a premium subscriber
* @throws ServiceException if the client can't be created
* @throws IllegalArgumentException if either the username or password is null or empty
*/
public RadioReferenceService(String username, String password) throws ServiceException
{
mClient = mService.getRRWsdlPort();
setAuthorizationInformation(username, password);
}
/**
* Sets new premium subscriber user credentials to authenticate to the server.
* @param username for a premium subscriber on radioreference.com
* @param password for a premium subscriber
*/
public void setAuthorizationInformation(String username, String password) throws IllegalArgumentException
{
if(username == null || username.isEmpty() || password == null || password.isEmpty())
{
throw new IllegalArgumentException("Username [" + username + "] and/or password [" + password +
"] cannot be null or empty");
}
mUserAuthorization = new AuthInfo(username, password, SDRTRUNK_APP_KEY, RADIO_REFERENCE_API_VERSION,
SOAP_RPC_RESPONSE_STYLE);
}
/**
* Indicates if the service has a non-null user authorization
*/
public void checkCredentials() throws IllegalArgumentException
{
if(mUserAuthorization == null)
{
throw new IllegalStateException("Authorization information (username/password) required");
}
}
/**
* Account expiration date
*
* @return expiration date in millis since epoch
* @throws AxisFault for remote server errors
* @throws IllegalStateException for null or empty login credentials
* @throws RemoteException for web service client errors
*/
public long getAccountExpirationDate() throws IllegalStateException, RemoteException
{
checkCredentials();
if(mAccountExpiration == null)
{
UserInfo userInfo = mClient.getUserData(mUserAuthorization);
if(userInfo != null)
{
String expiration = userInfo.getSubExpireDate();
if(expiration != null)
{
try
{
mAccountExpiration = SDF.parse(expiration).getTime();
}
catch(Exception e)
{
mLog.error("Couldn't parse account expiration date from web service response: " + expiration, e);
}
}
}
}
return mAccountExpiration;
}
/**
* Indicates if the radio reference user account is active (ie not expired)
*
* @throws IllegalStateException for null or empty login credentials
* @throws RemoteException for web service client errors
*/
public boolean isAccountActive() throws IllegalStateException, RemoteException
{
Long expiration = getAccountExpirationDate();
if(expiration != null)
{
return System.currentTimeMillis() < expiration;
}
return false;
}
public UserFeedBroadcast[] getUserFeedBroadcasts() throws IllegalArgumentException, RemoteException
{
checkCredentials();
return mClient.getUserFeedBroadcasts(mUserAuthorization);
}
}

View File

@@ -1,24 +1,28 @@
/*******************************************************************************
* SDR Trunk
* Copyright (C) 2014-2016 Dennis Sheirer
/*
* ******************************************************************************
* 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 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.
* 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/>
******************************************************************************/
* 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.source.tuner;
import com.jidesoft.swing.JideSplitPane;
import io.github.dsheirer.preference.UserPreferences;
import io.github.dsheirer.preference.swing.JTableColumnWidthMonitor;
import io.github.dsheirer.sample.Listener;
import io.github.dsheirer.source.tuner.TunerEvent.Event;
import net.miginfocom.swing.MigLayout;
@@ -49,17 +53,21 @@ public class TunerViewPanel extends JPanel
{
private static final long serialVersionUID = 1L;
private final static Logger mLog = LoggerFactory.getLogger(TunerViewPanel.class);
private static final String TABLE_PREFERENCE_KEY = "tuner.view.panel";
private TunerModel mTunerModel;
private JTable mTunerTable;
private JTableColumnWidthMonitor mColumnWidthMonitor;
private TableRowSorter<TunerModel> mRowSorter;
private JideSplitPane mSplitPane;
private TunerEditor mTunerEditor;
private UserPreferences mUserPreferences;
public TunerViewPanel(TunerModel tunerModel)
public TunerViewPanel(TunerModel tunerModel, UserPreferences userPreferences)
{
mTunerModel = tunerModel;
mTunerEditor = new TunerEditor(mTunerModel.getTunerConfigurationModel());
mUserPreferences = userPreferences;
init();
}
@@ -149,6 +157,7 @@ public class TunerViewPanel extends JPanel
TableCellRenderer renderer = new LinkCellRenderer();
mTunerTable.getColumnModel().getColumn(TunerModel.SPECTRAL_DISPLAY_NEW).setCellRenderer(renderer);
mColumnWidthMonitor = new JTableColumnWidthMonitor(mUserPreferences, mTunerTable, TABLE_PREFERENCE_KEY);
JScrollPane listScroller = new JScrollPane(mTunerTable);
listScroller.setPreferredSize(new Dimension(400, 20));

View File

@@ -1,22 +1,30 @@
/*******************************************************************************
* sdr-trunk
* 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.
* * ******************************************************************************
* * 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/>
* * *****************************************************************************
*
* 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.spectrum;
import io.github.dsheirer.properties.SystemProperties;
import io.github.dsheirer.source.tuner.Tuner;
import io.github.dsheirer.source.tuner.TunerEvent;
import io.github.dsheirer.source.tuner.TunerModel;
import javax.swing.JMenuItem;
import java.awt.EventQueue;
@@ -25,13 +33,13 @@ import java.awt.event.ActionListener;
public class ShowTunerMenuItem extends JMenuItem
{
private SpectralDisplayPanel mSpectralDisplayPanel;
private TunerModel mTunerModel;
private Tuner mTuner;
public ShowTunerMenuItem(SpectralDisplayPanel spectralDisplayPanel, Tuner tuner)
public ShowTunerMenuItem(TunerModel tunerModel, Tuner tuner)
{
super(tuner != null ? "Show: " + tuner.getName() : "(empty)");
mSpectralDisplayPanel = spectralDisplayPanel;
mTunerModel = tunerModel;
mTuner = tuner;
addActionListener(new ActionListener()
@@ -44,7 +52,7 @@ public class ShowTunerMenuItem extends JMenuItem
@Override
public void run()
{
mSpectralDisplayPanel.showTuner(mTuner);
mTunerModel.broadcast(new TunerEvent(mTuner, TunerEvent.Event.REQUEST_MAIN_SPECTRAL_DISPLAY));
SystemProperties properties = SystemProperties.getInstance();
properties.set(SpectralDisplayPanel.SPECTRAL_DISPLAY_ENABLED, true);
}

View File

@@ -1,21 +1,23 @@
/*
* ******************************************************************************
* 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.
* * ******************************************************************************
* * 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/>
* * *****************************************************************************
*
* 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.spectrum;
@@ -806,7 +808,7 @@ public class SpectralDisplayPanel extends JPanel implements Listener<ReusableCom
separatorAdded = true;
}
contextMenu.add(new ShowTunerMenuItem(SpectralDisplayPanel.this, tuner));
contextMenu.add(new ShowTunerMenuItem(mTunerModel, tuner));
}
}