#1723 Initial commit - audio call management feature.

-Implements Spring Boot and spring dependency injection - significant code refactoring.
-Creates database repository with Calls table
-Implements AudioManager to update database and manage audio recordings.
-Calls paging and searching
-Adds audio recording to wave file and places file in the calls database table.
-Audio spectrum view with 64 bins.
-Updates audio playback manager to process the incoming calls queue and support auto, auto-locked, replay, and mute playback modes.
-Adds channel lock/unlock and mute/unmute features.
-Adds column configuration editor to change column order & visibility and adds table state monitoring to save any user customization of the column ordering.
-Adds call table column sort ordering to the saved table state and restores the sort on application launch.
-Adds audio playback channel view options for: Complete, Standard and Minimal.  Updates user preferences Audio | Playback section to allow user to select preferred view style.
This commit is contained in:
Dennis Sheirer
2023-11-26 08:15:25 -05:00
parent 42e7a49d37
commit af2b80a066
206 changed files with 10859 additions and 3904 deletions

View File

@@ -1,6 +1,6 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2023 Dennis Sheirer
* Copyright (C) 2014-2024 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
@@ -20,8 +20,8 @@
package io.github.dsheirer.gui;
import com.google.common.eventbus.Subscribe;
import io.github.dsheirer.alias.AliasModel;
import io.github.dsheirer.controller.channel.map.ChannelMap;
import io.github.dsheirer.controller.channel.map.ChannelMapModel;
import io.github.dsheirer.controller.channel.map.ChannelRange;
import io.github.dsheirer.eventbus.MyEventBus;
import io.github.dsheirer.gui.icon.IconManager;
@@ -37,28 +37,30 @@ import io.github.dsheirer.gui.preference.ViewUserPreferenceEditorRequest;
import io.github.dsheirer.gui.preference.calibration.CalibrationDialog;
import io.github.dsheirer.gui.viewer.RecordingViewer;
import io.github.dsheirer.gui.viewer.ViewRecordingViewerRequest;
import io.github.dsheirer.icon.IconModel;
import io.github.dsheirer.jmbe.JmbeEditor;
import io.github.dsheirer.jmbe.JmbeEditorRequest;
import io.github.dsheirer.module.log.EventLogManager;
import io.github.dsheirer.monitor.ResourceMonitor;
import io.github.dsheirer.monitor.StatusBox;
import io.github.dsheirer.playlist.PlaylistManager;
import io.github.dsheirer.preference.UserPreferences;
import io.github.dsheirer.source.tuner.manager.TunerManager;
import jakarta.annotation.PostConstruct;
import jakarta.annotation.Resource;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import javafx.application.Application;
import javafx.application.Platform;
import javafx.embed.swing.JFXPanel;
import javafx.scene.Scene;
import javafx.stage.Stage;
import jiconfont.javafx.IconFontFX;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
/**
* Java FX window manager. Handles all secondary Java FX windows that are used within this primarily
* Swing application.
*/
@Component("javaFxWindowManager")
public class JavaFxWindowManager extends Application
{
private final static Logger mLog = LoggerFactory.getLogger(JavaFxWindowManager.class);
@@ -67,7 +69,6 @@ public class JavaFxWindowManager extends Application
public static final String ICON_MANAGER = "iconmanager";
public static final String PLAYLIST_EDITOR = "playlist";
public static final String USER_PREFERENCES_EDITOR = "preferences";
public static final String STAGE_MONITOR_KEY_CALIBRATION_DIALOG = "calibration.dialog";
public static final String STAGE_MONITOR_KEY_CHANNEL_MAP_EDITOR = "channel.map";
public static final String STAGE_MONITOR_KEY_RECORDING_VIEWER = "recording.viewer";
public static final String STAGE_MONITOR_KEY_ICON_MANAGER_EDITOR = "icon.manager";
@@ -75,50 +76,43 @@ public class JavaFxWindowManager extends Application
public static final String STAGE_MONITOR_KEY_PLAYLIST_EDITOR = "playlist";
public static final String STAGE_MONITOR_KEY_USER_PREFERENCES_EDITOR = "user.preferences";
private JFXPanel mJFXPanel;
@Resource
private ChannelMapEditor mChannelMapEditor;
@Resource
private ChannelMapModel mChannelMapModel;
@Resource
private IconManager mIconManager;
@Resource
private JmbeEditor mJmbeEditor;
@Resource
private PlaylistEditor mPlaylistEditor;
private PlaylistManager mPlaylistManager;
private TunerManager mTunerManager;
@Resource
private UserPreferences mUserPreferences;
@Resource
private UserPreferencesEditor mUserPreferencesEditor;
private RecordingViewer mRecordingViewer;
private JFXPanel mStatusPanel;
private RecordingViewer mRecordingViewer;
private Stage mChannelMapStage;
private Stage mIconManagerStage;
private Stage mJmbeEditorStage;
private Stage mPlaylistStage;
private Stage mUserPreferencesStage;
private Stage mRecordingViewerStage;
private JFXPanel mStatusPanel;
private List<Stage> mStages = new ArrayList<>();
/**
* Constructs an instance. Note: this constructor is used for Swing applications.
*/
public JavaFxWindowManager(UserPreferences userPreferences, TunerManager tunerManager, PlaylistManager playlistManager)
{
mUserPreferences = userPreferences;
mTunerManager = tunerManager;
mPlaylistManager = playlistManager;
setup();
}
/**
* Constructs an instance. Note: this constructor is used for standalone JavaFX application testing
*/
public JavaFxWindowManager()
{
mUserPreferences = new UserPreferences();
AliasModel aliasModel = new AliasModel();
EventLogManager eventLogManager = new EventLogManager(aliasModel, mUserPreferences);
mTunerManager = new TunerManager(mUserPreferences);
mTunerManager.start();
mPlaylistManager = new PlaylistManager(mUserPreferences, mTunerManager, aliasModel, eventLogManager, new IconModel());
mPlaylistManager.init();
setup();
}
@PostConstruct
private void postConstruct()
{
//Register this class to receive events via each method annotated with @Subscribe
MyEventBus.getGlobalEventBus().register(this);
}
/**
@@ -142,24 +136,11 @@ public class JavaFxWindowManager extends Application
return mStatusPanel;
}
private void setup()
{
//Register this class to receive events via each method annotated with @Subscribe
MyEventBus.getGlobalEventBus().register(this);
//Register JavaFX icon fonts
IconFontFX.register(jiconfont.icons.font_awesome.FontAwesome.getIconFont());
createJFXPanel();
}
/**
* Executes the runnable on the JavaFX application thread
*/
private void execute(Runnable runnable)
{
createJFXPanel();
if(Platform.isFxApplicationThread())
{
runnable.run();
@@ -170,18 +151,6 @@ public class JavaFxWindowManager extends Application
}
}
/**
* Creates a JavaFX panel for Swing application compatibility
*/
private void createJFXPanel()
{
if(mJFXPanel == null)
{
mJFXPanel = new JFXPanel();
Platform.setImplicitExit(false);
}
}
/**
* Removes monitoring for all JavaFX stages and shuts down the FX thread, killing all FX windows.
*/
@@ -189,12 +158,31 @@ public class JavaFxWindowManager extends Application
{
MyEventBus.getGlobalEventBus().unregister(this);
mUserPreferences.getJavaFxPreferences().clearStageMonitors();
Platform.exit();
Platform.runLater(() ->
{
Iterator<Stage> it = mStages.iterator();
while(it.hasNext())
{
try
{
it.next().close();
}
catch(Throwable t)
{
//Do nothing ... we're shutting down
}
it.remove();
}
Platform.exit();
});
}
public CalibrationDialog getCalibrationDialog(UserPreferences userPreferences)
{
createJFXPanel();
return new CalibrationDialog(userPreferences);
}
@@ -205,11 +193,11 @@ public class JavaFxWindowManager extends Application
{
if(mRecordingViewerStage == null)
{
createJFXPanel();
Scene scene = new Scene(getRecordingViewer(), 1100, 800);
mRecordingViewerStage = new Stage();
mRecordingViewerStage.setTitle("sdrtrunk - Message Recording Viewer (.bits)");
mRecordingViewerStage.setScene(scene);
mStages.add(mRecordingViewerStage);
mUserPreferences.getJavaFxPreferences().monitor(mRecordingViewerStage, STAGE_MONITOR_KEY_RECORDING_VIEWER);
}
@@ -230,11 +218,11 @@ public class JavaFxWindowManager extends Application
{
if(mIconManagerStage == null)
{
createJFXPanel();
Scene scene = new Scene(getIconManager(), 500, 500);
mIconManagerStage = new Stage();
mIconManagerStage.setTitle("sdrtrunk - Icon Manager");
mIconManagerStage.setScene(scene);
mStages.add(mIconManagerStage);
mUserPreferences.getJavaFxPreferences().monitor(mIconManagerStage, STAGE_MONITOR_KEY_ICON_MANAGER_EDITOR);
}
@@ -243,11 +231,6 @@ public class JavaFxWindowManager extends Application
public IconManager getIconManager()
{
if(mIconManager == null)
{
mIconManager = new IconManager(mPlaylistManager.getIconModel());
}
return mIconManager;
}
@@ -278,11 +261,11 @@ public class JavaFxWindowManager extends Application
{
if(mJmbeEditorStage == null)
{
createJFXPanel();
Scene scene = new Scene(getJmbeEditor(), 650, 650);
mJmbeEditorStage = new Stage();
mJmbeEditorStage.setTitle("sdrtrunk - JMBE Library Updater");
mJmbeEditorStage.setScene(scene);
mStages.add(mJmbeEditorStage);
mUserPreferences.getJavaFxPreferences().monitor(mJmbeEditorStage, STAGE_MONITOR_KEY_JMBE_EDITOR);
}
@@ -291,11 +274,6 @@ public class JavaFxWindowManager extends Application
public JmbeEditor getJmbeEditor()
{
if(mJmbeEditor == null)
{
mJmbeEditor = new JmbeEditor(mUserPreferences);
}
return mJmbeEditor;
}
@@ -304,11 +282,6 @@ public class JavaFxWindowManager extends Application
*/
public PlaylistEditor getPlaylistEditor()
{
if(mPlaylistEditor == null)
{
mPlaylistEditor = new PlaylistEditor(mPlaylistManager, mTunerManager, mUserPreferences);
}
return mPlaylistEditor;
}
@@ -319,11 +292,11 @@ public class JavaFxWindowManager extends Application
{
if(mPlaylistStage == null)
{
createJFXPanel();
Scene scene = new Scene(getPlaylistEditor(), 1000, 750);
mPlaylistStage = new Stage();
mPlaylistStage.setTitle("sdrtrunk - Playlist Editor");
mPlaylistStage.setScene(scene);
mStages.add(mPlaylistStage);
mUserPreferences.getJavaFxPreferences().monitor(mPlaylistStage, STAGE_MONITOR_KEY_PLAYLIST_EDITOR);
}
@@ -354,11 +327,6 @@ public class JavaFxWindowManager extends Application
*/
private UserPreferencesEditor getUserPreferencesEditor()
{
if(mUserPreferencesEditor == null)
{
mUserPreferencesEditor = new UserPreferencesEditor(mUserPreferences);
}
return mUserPreferencesEditor;
}
@@ -369,11 +337,11 @@ public class JavaFxWindowManager extends Application
{
if(mUserPreferencesStage == null)
{
createJFXPanel();
Scene scene = new Scene(getUserPreferencesEditor(), 900, 500);
mUserPreferencesStage = new Stage();
mUserPreferencesStage.setTitle("sdrtrunk - User Preferences");
mUserPreferencesStage.setScene(scene);
mStages.add(mUserPreferencesStage);
mUserPreferences.getJavaFxPreferences().monitor(mUserPreferencesStage, STAGE_MONITOR_KEY_USER_PREFERENCES_EDITOR);
}
@@ -397,11 +365,6 @@ public class JavaFxWindowManager extends Application
*/
private ChannelMapEditor getChannelMapEditor()
{
if(mChannelMapEditor == null)
{
mChannelMapEditor = new ChannelMapEditor(mPlaylistManager.getChannelMapModel());
}
return mChannelMapEditor;
}
@@ -412,11 +375,11 @@ public class JavaFxWindowManager extends Application
{
if(mChannelMapStage == null)
{
createJFXPanel();
Scene scene = new Scene(getChannelMapEditor(), 500, 500);
mChannelMapStage = new Stage();
mChannelMapStage.setTitle("sdrtrunk - Channel Map Editor");
mChannelMapStage.setScene(scene);
mStages.add(mChannelMapStage);
mUserPreferences.getJavaFxPreferences().monitor(mChannelMapStage, STAGE_MONITOR_KEY_CHANNEL_MAP_EDITOR);
}
@@ -489,14 +452,14 @@ public class JavaFxWindowManager extends Application
channelMap1.addRange(new ChannelRange(200,299,160000000, 25000));
channelMap1.addRange(new ChannelRange(300,399,170000000, 12500));
channelMap1.addRange(new ChannelRange(400,499,180000000, 25000));
mPlaylistManager.getChannelMapModel().addChannelMap(channelMap1);
mChannelMapModel.addChannelMap(channelMap1);
ChannelMap channelMap2 = new ChannelMap("Test Map 2");
channelMap2.addRange(new ChannelRange(1,199,450000000, 12500));
channelMap2.addRange(new ChannelRange(200,299,460000000, 25000));
channelMap2.addRange(new ChannelRange(300,399,470000000, 12500));
channelMap2.addRange(new ChannelRange(400,499,480000000, 25000));
mPlaylistManager.getChannelMapModel().addChannelMap(channelMap2);
mChannelMapModel.addChannelMap(channelMap2);
valid = true;
process(new ViewChannelMapEditorRequest());
break;

View File

@@ -18,864 +18,153 @@
*/
package io.github.dsheirer.gui;
import com.jidesoft.plaf.LookAndFeelFactory;
import com.jidesoft.swing.JideSplitPane;
import io.github.dsheirer.alias.AliasModel;
import io.github.dsheirer.audio.DuplicateCallDetector;
import io.github.dsheirer.audio.broadcast.AudioStreamingManager;
import io.github.dsheirer.audio.broadcast.BroadcastFormat;
import io.github.dsheirer.audio.broadcast.BroadcastStatusPanel;
import io.github.dsheirer.audio.playback.AudioPlaybackManager;
import io.github.dsheirer.controller.ControllerPanel;
import io.github.dsheirer.controller.channel.Channel;
import io.github.dsheirer.controller.channel.ChannelAutoStartFrame;
import io.github.dsheirer.controller.channel.ChannelException;
import io.github.dsheirer.controller.channel.ChannelSelectionManager;
import io.github.dsheirer.eventbus.MyEventBus;
import io.github.dsheirer.gui.icon.ViewIconManagerRequest;
import io.github.dsheirer.gui.playlist.ViewPlaylistRequest;
import io.github.dsheirer.gui.preference.CalibrateRequest;
import io.github.dsheirer.gui.preference.PreferenceEditorType;
import io.github.dsheirer.gui.preference.ViewUserPreferenceEditorRequest;
import io.github.dsheirer.gui.preference.calibration.CalibrationDialog;
import io.github.dsheirer.gui.viewer.ViewRecordingViewerRequest;
import io.github.dsheirer.icon.IconModel;
import io.github.dsheirer.controller.channel.ChannelModel;
import io.github.dsheirer.controller.channel.ChannelProcessingManager;
import io.github.dsheirer.log.ApplicationLog;
import io.github.dsheirer.map.MapService;
import io.github.dsheirer.module.log.EventLogManager;
import io.github.dsheirer.monitor.ResourceMonitor;
import io.github.dsheirer.playlist.PlaylistManager;
import io.github.dsheirer.preference.UserPreferences;
import io.github.dsheirer.properties.SystemProperties;
import io.github.dsheirer.record.AudioRecordingManager;
import io.github.dsheirer.sample.Listener;
import io.github.dsheirer.settings.SettingsManager;
import io.github.dsheirer.source.tuner.Tuner;
import io.github.dsheirer.source.tuner.TunerEvent;
import io.github.dsheirer.source.tuner.manager.DiscoveredTuner;
import io.github.dsheirer.source.tuner.manager.TunerManager;
import io.github.dsheirer.source.tuner.ui.TunerSpectralDisplayManager;
import io.github.dsheirer.spectrum.DisableSpectrumWaterfallMenuItem;
import io.github.dsheirer.spectrum.ShowTunerMenuItem;
import io.github.dsheirer.spectrum.SpectralDisplayPanel;
import io.github.dsheirer.util.ThreadPool;
import io.github.dsheirer.util.TimeStamp;
import io.github.dsheirer.vector.calibrate.CalibrationManager;
import java.awt.AWTException;
import java.awt.Desktop;
import java.awt.Dimension;
import jakarta.annotation.PostConstruct;
import jakarta.annotation.PreDestroy;
import jakarta.annotation.Resource;
import java.awt.EventQueue;
import java.awt.GraphicsEnvironment;
import java.awt.Point;
import java.awt.Robot;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.List;
import java.util.Locale;
import java.util.Optional;
import java.util.prefs.Preferences;
import javafx.application.Platform;
import javafx.embed.swing.JFXPanel;
import javafx.scene.control.ButtonType;
import jiconfont.icons.font_awesome.FontAwesome;
import jiconfont.swing.IconFontSwing;
import net.miginfocom.swing.MigLayout;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.Banner;
import org.springframework.boot.WebApplicationType;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.domain.EntityScan;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import javax.imageio.ImageIO;
import javax.swing.JCheckBoxMenuItem;
import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JSeparator;
import javax.swing.KeyStroke;
import javax.swing.UIManager;
import javax.swing.event.MenuEvent;
import javax.swing.event.MenuListener;
import javax.swing.plaf.metal.MetalLookAndFeel;
public class SDRTrunk implements Listener<TunerEvent>
@SpringBootApplication(scanBasePackages = {"io.github.dsheirer"})
@EnableJpaRepositories("io.github.dsheirer.audio.call")
@EntityScan("io.github.dsheirer.audio.call")
public class SDRTrunk
{
private final static Logger mLog = LoggerFactory.getLogger(SDRTrunk.class);
private Preferences mPreferences = Preferences.userNodeForPackage(SDRTrunk.class);
private static final String PREFERENCE_BROADCAST_STATUS_VISIBLE = "sdrtrunk.broadcast.status.visible";
private static final String PREFERENCE_NOW_PLAYING_DETAILS_VISIBLE = "sdrtrunk.now.playing.details.visible";
private static final String PREFERENCE_RESOURCE_STATUS_VISIBLE = "sdrtrunk.resource.status.visible";
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 boolean mResourceStatusVisible;
private boolean mNowPlayingDetailsVisible;
private AudioRecordingManager mAudioRecordingManager;
private AudioStreamingManager mAudioStreamingManager;
private BroadcastStatusPanel mBroadcastStatusPanel;
private ControllerPanel mControllerPanel;
private IconModel mIconModel = new IconModel();
private PlaylistManager mPlaylistManager;
private SettingsManager mSettingsManager;
private SpectralDisplayPanel mSpectralPanel;
private JFrame mMainGui;
private JideSplitPane mSplitPane;
private JavaFxWindowManager mJavaFxWindowManager;
private UserPreferences mUserPreferences = new UserPreferences();
private TunerManager mTunerManager;
@Resource
private ApplicationLog mApplicationLog;
private ResourceMonitor mResourceMonitor;
private JFXPanel mResourceStatusPanel;
private String mTitle;
@Resource
private AudioRecordingManager mAudioRecordingManager;
@Resource
private ChannelModel mChannelModel;
@Resource
private ChannelProcessingManager mChannelProcessingManager;
@Resource
private TunerManager mTunerManager;
@Resource
private UserPreferences mUserPreferences;
@Resource //SpringUiFactory does not instantiate this instance when headless=true
private SDRTrunkUI mSDRTrunkUI;
/**
* Constructs an instance of the SDRTrunk application
*/
public SDRTrunk()
{
if(!GraphicsEnvironment.isHeadless())
{
mMainGui = new JFrame();
}
}
mApplicationLog = new ApplicationLog(mUserPreferences);
mApplicationLog.start();
mResourceMonitor = new ResourceMonitor(mUserPreferences);
String operatingSystem = System.getProperty("os.name", "generic").toLowerCase(Locale.ENGLISH);
if(operatingSystem.contains("mac") || operatingSystem.contains("nux"))
{
try
{
UIManager.setLookAndFeel(MetalLookAndFeel.class.getName());
LookAndFeelFactory.installJideExtension();
}
catch(Exception e)
{
mLog.error("Error trying to set Metal look and feel for OS [" + operatingSystem + "]");
}
}
@PostConstruct
public void postConstruct()
{
ThreadPool.logSettings();
//Load properties file
loadProperties();
//Log current properties setting
SystemProperties.getInstance().logCurrentSettings();
//Register FontAwesome so we can use the fonts in Swing windows
IconFontSwing.register(FontAwesome.getIconFont());
mTunerManager = new TunerManager(mUserPreferences);
mTunerManager.start();
mSettingsManager = new SettingsManager();
AliasModel aliasModel = new AliasModel();
EventLogManager eventLogManager = new EventLogManager(aliasModel, mUserPreferences);
mPlaylistManager = new PlaylistManager(mUserPreferences, mTunerManager, aliasModel, eventLogManager, mIconModel);
if(!GraphicsEnvironment.isHeadless())
{
mJavaFxWindowManager = new JavaFxWindowManager(mUserPreferences, mTunerManager, mPlaylistManager);
}
CalibrationManager calibrationManager = CalibrationManager.getInstance(mUserPreferences);
final boolean calibrating = !calibrationManager.isCalibrated() &&
!mUserPreferences.getVectorCalibrationPreference().isHideCalibrationDialog();
new ChannelSelectionManager(mPlaylistManager.getChannelModel());
AudioPlaybackManager audioPlaybackManager = new AudioPlaybackManager(mUserPreferences);
mAudioRecordingManager = new AudioRecordingManager(mUserPreferences);
mAudioRecordingManager.start();
mAudioStreamingManager = new AudioStreamingManager(mPlaylistManager.getBroadcastModel(), BroadcastFormat.MP3,
mUserPreferences);
mAudioStreamingManager.start();
DuplicateCallDetector duplicateCallDetector = new DuplicateCallDetector(mUserPreferences);
mPlaylistManager.getChannelProcessingManager().addAudioSegmentListener(duplicateCallDetector);
mPlaylistManager.getChannelProcessingManager().addAudioSegmentListener(audioPlaybackManager);
mPlaylistManager.getChannelProcessingManager().addAudioSegmentListener(mAudioRecordingManager);
mPlaylistManager.getChannelProcessingManager().addAudioSegmentListener(mAudioStreamingManager);
MapService mapService = new MapService(mIconModel);
mPlaylistManager.getChannelProcessingManager().addDecodeEventListener(mapService);
mNowPlayingDetailsVisible = mPreferences.getBoolean(PREFERENCE_NOW_PLAYING_DETAILS_VISIBLE, true);
if(!GraphicsEnvironment.isHeadless())
{
mControllerPanel = new ControllerPanel(mPlaylistManager, audioPlaybackManager, mIconModel, mapService,
mSettingsManager, mTunerManager, mUserPreferences, mNowPlayingDetailsVisible);
}
mSpectralPanel = new SpectralDisplayPanel(mPlaylistManager, mSettingsManager, mTunerManager.getDiscoveredTunerModel());
TunerSpectralDisplayManager tunerSpectralDisplayManager = new TunerSpectralDisplayManager(mSpectralPanel,
mPlaylistManager, mSettingsManager, mTunerManager.getDiscoveredTunerModel());
mTunerManager.getDiscoveredTunerModel().addListener(tunerSpectralDisplayManager);
mTunerManager.getDiscoveredTunerModel().addListener(this);
mPlaylistManager.init();
if(GraphicsEnvironment.isHeadless())
{
mLog.info("starting main application headless");
mLog.info("starting main application in headless mode");
autoStartChannels();
}
else
{
mLog.info("starting main application gui");
//Initialize the GUI
initGUI();
}
//Start the gui
EventQueue.invokeLater(() -> {
try
mLog.info("starting main application with gui");
if(mSDRTrunkUI != null)
{
if(!GraphicsEnvironment.isHeadless())
{
mMainGui.setVisible(true);
Tuner tuner = tunerSpectralDisplayManager.showFirstTuner();
if(tuner != null)
{
updateTitle(tuner.getPreferredName());
}
}
if(calibrating && !GraphicsEnvironment.isHeadless())
{
Platform.runLater(() ->
{
CalibrationDialog calibrationDialog = mJavaFxWindowManager.getCalibrationDialog(mUserPreferences);
Optional<ButtonType> calibrate = calibrationDialog.showAndWait();
if(calibrate.isPresent() && calibrate.get().getText().equals("Calibrate"))
{
//Request focus and execute calibration
MyEventBus.getGlobalEventBus().post(new ViewUserPreferenceEditorRequest(PreferenceEditorType.VECTOR_CALIBRATION));
MyEventBus.getGlobalEventBus().post(new CalibrateRequest());
}
else
{
autoStartChannels();
}
});
}
else
{
autoStartChannels();
}
}
catch(Exception e)
{
e.printStackTrace();
}
});
}
/**
* Shows a dialog that lists the channels that have been designated for auto-start, sorted by auto-start order and
* allows the user to start now, cancel, or allow the timer to expire and then start the channels. The dialog will
* only show if there are one ore more channels designated for auto-start.
*/
private void autoStartChannels()
{
List<Channel> channels = mPlaylistManager.getChannelModel().getAutoStartChannels();
if(channels.size() > 0)
{
if(GraphicsEnvironment.isHeadless())
{
for(Channel channel: channels)
{
try
{
mLog.info("Auto-starting channel " + channel.getName());
mPlaylistManager.getChannelProcessingManager().start(channel);
}
catch(ChannelException ce)
{
mLog.error("Channel: " + channel.getName() + " auto-start failed: " + ce.getMessage());
}
}
EventQueue.invokeLater(() -> mSDRTrunkUI.setVisible(true));
}
else
{
new ChannelAutoStartFrame(mPlaylistManager.getChannelProcessingManager(), channels, mUserPreferences);
mLog.error("SDRTrunk user interface is null - can't start UI");
}
}
}
/**
* Initialize the contents of the frame.
* Starts processing any channel configurations that are designated to auto-start
*/
private void initGUI()
private void autoStartChannels()
{
mMainGui.setLayout(new MigLayout("insets 0 0 0 0 ", "[grow,fill]", "[grow,fill]0[shrink 0]"));
/**
* Setup main JFrame window
*/
mTitle = SystemProperties.getInstance().getApplicationName();
mMainGui.setTitle(mTitle);
Point location = mUserPreferences.getSwingPreference().getLocation(WINDOW_FRAME_IDENTIFIER);
if(location != null)
List<Channel> channels = mChannelModel.getAutoStartChannels();
for(Channel channel: channels)
{
mMainGui.setLocation(location);
}
else
{
mMainGui.setLocationRelativeTo(null);
}
mMainGui.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
mMainGui.addWindowListener(new ShutdownMonitor());
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);
if(mUserPreferences.getSwingPreference().getMaximized(WINDOW_FRAME_IDENTIFIER, false))
{
mMainGui.setExtendedState(JFrame.MAXIMIZED_BOTH);
}
}
else
{
mMainGui.setSize(new Dimension(1280, 800));
}
mSplitPane = new JideSplitPane(JideSplitPane.VERTICAL_SPLIT);
mSplitPane.setDividerSize(5);
mSplitPane.add(mSpectralPanel);
mSplitPane.add(mControllerPanel);
mBroadcastStatusVisible = mPreferences.getBoolean(PREFERENCE_BROADCAST_STATUS_VISIBLE, false);
//Show broadcast status panel when user requests - disabled by default
if(mBroadcastStatusVisible)
{
mSplitPane.add(getBroadcastStatusPanel());
}
mMainGui.add(mSplitPane, "cell 0 0,span,grow");
mResourceMonitor.start();
mResourceStatusVisible = mPreferences.getBoolean(PREFERENCE_RESOURCE_STATUS_VISIBLE, true);
if(mResourceStatusVisible)
{
mMainGui.add(getResourceStatusPanel(), "span,growx");
}
/**
* Menu items
*/
JMenuBar menuBar = new JMenuBar();
mMainGui.setJMenuBar(menuBar);
JMenu fileMenu = new JMenu("File");
menuBar.add(fileMenu);
JMenuItem exitMenu = new JMenuItem("Exit");
exitMenu.addActionListener(event -> {
processShutdown();
System.exit(0);
}
);
fileMenu.add(exitMenu);
JMenu viewMenu = new JMenu("View");
JMenuItem viewPlaylistItem = new JMenuItem("Playlist Editor");
viewPlaylistItem.setIcon(IconFontSwing.buildIcon(FontAwesome.PLAY_CIRCLE_O, 12));
viewPlaylistItem.addActionListener(e -> MyEventBus.getGlobalEventBus().post(new ViewPlaylistRequest()));
viewMenu.add(viewPlaylistItem);
viewMenu.add(new JSeparator());
JMenuItem viewApplicationLogsMenu = new JMenuItem("Application Log Files");
viewApplicationLogsMenu.setIcon(IconFontSwing.buildIcon(FontAwesome.FOLDER_OPEN_O, 12));
viewApplicationLogsMenu.addActionListener(arg0 -> {
File logsDirectory = mUserPreferences.getDirectoryPreference().getDirectoryApplicationLog().toFile();
try
{
Desktop.getDesktop().open(logsDirectory);
mLog.info("Auto-starting channel " + channel.getName());
mChannelProcessingManager.start(channel);
}
catch(Exception e)
catch(ChannelException ce)
{
mLog.error("Couldn't open file explorer");
JOptionPane.showMessageDialog(mMainGui,
"Can't launch file explorer - files are located at: " + logsDirectory,
"Can't launch file explorer",
JOptionPane.ERROR_MESSAGE);
mLog.error("Channel: " + channel.getName() + " auto-start failed: " + ce.getMessage());
}
});
viewMenu.add(viewApplicationLogsMenu);
JMenuItem viewRecordingsMenuItem = new JMenuItem("Audio Recordings");
viewRecordingsMenuItem.setIcon(IconFontSwing.buildIcon(FontAwesome.FOLDER_OPEN_O, 12));
viewRecordingsMenuItem.addActionListener(arg0 -> {
File recordingsDirectory = mUserPreferences.getDirectoryPreference().getDirectoryRecording().toFile();
try
{
Desktop.getDesktop().open(recordingsDirectory);
}
catch(Exception e)
{
mLog.error("Couldn't open file explorer");
JOptionPane.showMessageDialog(mMainGui,
"Can't launch file explorer - files are located at: " +
recordingsDirectory,
"Can't launch file explorer",
JOptionPane.ERROR_MESSAGE);
}
});
viewMenu.add(viewRecordingsMenuItem);
JMenuItem viewEventLogsMenu = new JMenuItem("Channel Event Log Files");
viewEventLogsMenu.setIcon(IconFontSwing.buildIcon(FontAwesome.FOLDER_OPEN_O, 12));
viewEventLogsMenu.addActionListener(arg0 -> {
File eventLogsDirectory = mUserPreferences.getDirectoryPreference().getDirectoryEventLog().toFile();
try
{
Desktop.getDesktop().open(eventLogsDirectory);
}
catch(Exception e)
{
mLog.error("Couldn't open file explorer");
JOptionPane.showMessageDialog(mMainGui,
"Can't launch file explorer - files are located at: " + eventLogsDirectory,
"Can't launch file explorer",
JOptionPane.ERROR_MESSAGE);
}
});
viewMenu.add(viewEventLogsMenu);
JMenuItem iconManagerMenu = new JMenuItem("Icon Manager");
iconManagerMenu.setIcon(IconFontSwing.buildIcon(FontAwesome.PICTURE_O, 12));
iconManagerMenu.addActionListener(arg0 -> MyEventBus.getGlobalEventBus().post(new ViewIconManagerRequest()));
viewMenu.add(iconManagerMenu);
JMenuItem recordingViewerMenu = new JMenuItem("Message Recording Viewer (.bits)");
recordingViewerMenu.setIcon(IconFontSwing.buildIcon(FontAwesome.BRAILLE, 12));
recordingViewerMenu.addActionListener(e -> MyEventBus.getGlobalEventBus().post(new ViewRecordingViewerRequest()));
viewMenu.add(recordingViewerMenu);
JMenuItem viewScreenCapturesMenu = new JMenuItem("Screen Captures");
viewScreenCapturesMenu.setIcon(IconFontSwing.buildIcon(FontAwesome.FOLDER_OPEN_O, 12));
viewScreenCapturesMenu.addActionListener(arg0 -> {
File screenCapturesDirectory = mUserPreferences.getDirectoryPreference().getDirectoryScreenCapture().toFile();
try
{
Desktop.getDesktop().open(screenCapturesDirectory);
}
catch(Exception e)
{
mLog.error("Couldn't open file explorer");
JOptionPane.showMessageDialog(mMainGui,
"Can't launch file explorer - files are located at: " + screenCapturesDirectory,
"Can't launch file explorer",
JOptionPane.ERROR_MESSAGE);
}
});
viewMenu.add(viewScreenCapturesMenu);
JMenuItem preferencesItem = new JMenuItem("User Preferences");
preferencesItem.setIcon(IconFontSwing.buildIcon(FontAwesome.COG, 12));
preferencesItem.addActionListener(e -> MyEventBus.getGlobalEventBus().post(new ViewUserPreferenceEditorRequest()));
viewMenu.add(preferencesItem);
viewMenu.add(new JSeparator());
viewMenu.add(new TunersMenu());
viewMenu.add(new JSeparator());
viewMenu.add(new DisableSpectrumWaterfallMenuItem(mSpectralPanel));
viewMenu.add(new NowPlayingChannelDetailsVisibleMenuItem());
viewMenu.add(new BroadcastStatusVisibleMenuItem());
viewMenu.add(new ResourceStatusVisibleMenuItem());
menuBar.add(viewMenu);
JMenuItem screenCaptureItem = new JMenuItem("Screen Capture");
screenCaptureItem.setIcon(IconFontSwing.buildIcon(FontAwesome.CAMERA, 12));
screenCaptureItem.setMnemonic(KeyEvent.VK_C);
screenCaptureItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_C, ActionEvent.ALT_MASK));
screenCaptureItem.setMaximumSize(screenCaptureItem.getPreferredSize());
screenCaptureItem.addActionListener(arg0 -> {
try
{
Robot robot = new Robot();
final BufferedImage image = robot.createScreenCapture(mMainGui.getBounds());
String filename = TimeStamp.getTimeStamp("_") + "_screen_capture.png";
final Path captureFile = mUserPreferences.getDirectoryPreference().getDirectoryScreenCapture().resolve(filename);
EventQueue.invokeLater(() -> {
try
{
ImageIO.write(image, "png", captureFile.toFile());
}
catch(IOException e)
{
mLog.error("Couldn't write screen capture to file [" + captureFile.toString() + "]", e);
}
});
}
catch(AWTException e)
{
mLog.error("Exception while taking screen capture", e);
}
});
menuBar.add(screenCaptureItem);
}
}
/**
* Performs shutdown operations
*/
private void processShutdown()
@PreDestroy
public void preDestroy()
{
mLog.info("Application shutdown started ...");
mUserPreferences.getSwingPreference().setLocation(WINDOW_FRAME_IDENTIFIER, mMainGui.getLocation());
mUserPreferences.getSwingPreference().setDimension(WINDOW_FRAME_IDENTIFIER, mMainGui.getSize());
mUserPreferences.getSwingPreference().setMaximized(WINDOW_FRAME_IDENTIFIER,
(mMainGui.getExtendedState() & JFrame.MAXIMIZED_BOTH) == JFrame.MAXIMIZED_BOTH);
mUserPreferences.getSwingPreference().setDimension(SPECTRAL_PANEL_IDENTIFIER, mSpectralPanel.getSize());
mUserPreferences.getSwingPreference().setDimension(CONTROLLER_PANEL_IDENTIFIER, mControllerPanel.getSize());
mJavaFxWindowManager.shutdown();
mLog.info("Stopping channels ...");
mPlaylistManager.getChannelProcessingManager().shutdown();
mChannelProcessingManager.shutdown();
mAudioRecordingManager.stop();
mResourceMonitor.stop();
mLog.info("Stopping spectral display ...");
mSpectralPanel.clearTuner();
mLog.info("Stopping tuners ...");
mTunerManager.stop();
mLog.info("Shutdown complete.");
mApplicationLog.stop();
}
/**
* Lazy constructor for broadcast status panel
* Monitors the SDRTrunkUI window for shutdown so that we can terminate the overall SDRTrunk application.
*/
private BroadcastStatusPanel getBroadcastStatusPanel()
{
if(mBroadcastStatusPanel == null)
{
mBroadcastStatusPanel = new BroadcastStatusPanel(mPlaylistManager.getBroadcastModel(), mUserPreferences,
"application.broadcast.status.panel");
mBroadcastStatusPanel.setPreferredSize(new Dimension(880, 70));
mBroadcastStatusPanel.getTable().setEnabled(false);
}
return mBroadcastStatusPanel;
}
/**
* Toggles visibility of the broadcast channels status panel at the bottom of the controller panel
*/
private void toggleBroadcastStatusPanelVisibility()
{
mBroadcastStatusVisible = !mBroadcastStatusVisible;
EventQueue.invokeLater(() -> {
if(mBroadcastStatusVisible)
{
mSplitPane.add(getBroadcastStatusPanel());
}
else
{
mSplitPane.remove(getBroadcastStatusPanel());
}
mMainGui.revalidate();
});
mPreferences.putBoolean(PREFERENCE_BROADCAST_STATUS_VISIBLE, mBroadcastStatusVisible);
}
/**
* Lazy constructor for resource status panel
*/
private JFXPanel getResourceStatusPanel()
{
if(mResourceStatusPanel == null)
{
mResourceStatusPanel = mJavaFxWindowManager.getStatusPanel(mResourceMonitor);
}
return mResourceStatusPanel;
}
/**
* Toggles visibility of the resource status panel at the bottom of the main UI window
*/
private void toggleResourceStatusPanelVisibility()
{
mResourceStatusVisible = !mResourceStatusVisible;
EventQueue.invokeLater(() -> {
if(mResourceStatusVisible)
{
mMainGui.add(getResourceStatusPanel(), "span,growx");
}
else
{
mMainGui.remove(getResourceStatusPanel());
}
mMainGui.revalidate();
});
mPreferences.putBoolean(PREFERENCE_RESOURCE_STATUS_VISIBLE, mResourceStatusVisible);
}
/**
* Toggles visibility of the Now Playing channel details panel
*/
private void toggleNowPlayingDetailsPanelVisibility()
{
mNowPlayingDetailsVisible = !mNowPlayingDetailsVisible;
mControllerPanel.getNowPlayingPanel().setDetailTabsVisible(mNowPlayingDetailsVisible);
mPreferences.putBoolean(PREFERENCE_NOW_PLAYING_DETAILS_VISIBLE, mNowPlayingDetailsVisible);
}
/**
* Loads the application properties file from the user's home directory,
* creating the properties file for the first-time, if necessary
*/
private void loadProperties()
{
Path propertiesPath = mUserPreferences.getDirectoryPreference().getDirectoryApplicationRoot().resolve("SDRTrunk.properties");
if(!Files.exists(propertiesPath))
{
try
{
mLog.info("SDRTrunk - creating application properties file [" + propertiesPath.toAbsolutePath() + "]");
Files.createFile(propertiesPath);
}
catch(IOException e)
{
mLog.error("SDRTrunk - couldn't create application properties file [" + propertiesPath.toAbsolutePath(), e);
}
}
if(Files.exists(propertiesPath))
{
SystemProperties.getInstance().load(propertiesPath);
}
else
{
mLog.error("SDRTrunk - couldn't find or recreate the SDRTrunk application properties file");
}
}
/**
* Gets (or creates) the SDRTRunk application home directory.
*
* Note: the user can change this setting to allow log files and other
* files to reside elsewhere on the file system.
*/
private Path getHomePath()
{
Path homePath = FileSystems.getDefault()
.getPath(System.getProperty("user.home"), "SDRTrunk");
if(!Files.exists(homePath))
{
try
{
Files.createDirectory(homePath);
mLog.info("SDRTrunk - created application home directory [" +
homePath.toString() + "]");
}
catch(Exception e)
{
homePath = null;
mLog.error("SDRTrunk: exception while creating SDRTrunk home " +
"directory in the user's home directory", e);
}
}
return homePath;
}
@Override
public void receive(TunerEvent event)
{
switch(event.getEvent())
{
case REQUEST_MAIN_SPECTRAL_DISPLAY:
updateTitle(event.getTuner().getPreferredName());
break;
case REQUEST_CLEAR_MAIN_SPECTRAL_DISPLAY:
updateTitle(null);
break;
case NOTIFICATION_SHUTTING_DOWN:
Tuner currentTuner = mSpectralPanel.getTuner();
if(event.hasTuner() && event.getTuner().equals(currentTuner) || currentTuner == null)
{
updateTitle(null);
}
break;
}
}
/**
* Updates the title bar with the tuner name
* @param tunerName optional
*/
private void updateTitle(String tunerName)
{
if(tunerName != null)
{
mMainGui.setTitle(mTitle + " - " + tunerName);
}
else
{
mMainGui.setTitle(mTitle);
}
}
public class ShutdownMonitor extends WindowAdapter
{
@Override
public void windowClosing(WindowEvent e)
{
processShutdown();
preDestroy();
}
}
/**
* Broadcast status panel visible toggle menu item
*/
public class BroadcastStatusVisibleMenuItem extends JCheckBoxMenuItem
{
public BroadcastStatusVisibleMenuItem()
{
super("Show Streaming Status");
setSelected(mBroadcastStatusVisible);
addActionListener(e -> {
toggleBroadcastStatusPanelVisibility();
setSelected(mBroadcastStatusVisible);
});
}
}
/**
* Resource status panel visible toggle menu item
*/
public class ResourceStatusVisibleMenuItem extends JCheckBoxMenuItem
{
public ResourceStatusVisibleMenuItem()
{
super("Show Resource Status");
setSelected(mResourceStatusVisible);
addActionListener(e -> {
toggleResourceStatusPanelVisibility();
setSelected(mResourceStatusVisible);
});
}
}
/**
* Now Playing channel details visible toggle menu item
*/
public class NowPlayingChannelDetailsVisibleMenuItem extends JCheckBoxMenuItem
{
public NowPlayingChannelDetailsVisibleMenuItem()
{
super("Show Now Playing Channel Details");
setSelected(mNowPlayingDetailsVisible);
addActionListener(e -> {
toggleNowPlayingDetailsPanelVisibility();
setSelected(mNowPlayingDetailsVisible);
});
}
}
public class TunersMenu extends JMenu
{
public TunersMenu()
{
super("Tuners");
addMenuListener(new MenuListener()
{
@Override
public void menuSelected(MenuEvent e)
{
removeAll();
for(DiscoveredTuner discoveredTuner: mTunerManager.getAvailableTuners())
{
add(new ShowTunerMenuItem(mTunerManager.getDiscoveredTunerModel(), discoveredTuner.getTuner()));
}
}
@Override
public void menuDeselected(MenuEvent e) { }
@Override
public void menuCanceled(MenuEvent e) { }
});
}
}
/**
* Launch the application.
*/
public static void main(String[] args)
{
new SDRTrunk();
boolean headless = GraphicsEnvironment.isHeadless();
//Set the user's preferred location for the database prior to starting the application
String dbpath = new UserPreferences().getDirectoryPreference().getDirectoryApplicationRoot().toString();
System.setProperty("derby.system.home", dbpath);
ConfigurableApplicationContext context = new SpringApplicationBuilder(SDRTrunk.class)
.bannerMode(Banner.Mode.OFF)
.headless(headless)
.web(WebApplicationType.NONE)
.registerShutdownHook(true)
.run(args);
}
}

View File

@@ -0,0 +1,709 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2023 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.gui;
import com.jidesoft.plaf.LookAndFeelFactory;
import com.jidesoft.swing.JideSplitPane;
import io.github.dsheirer.audio.broadcast.BroadcastStatusPanel;
import io.github.dsheirer.controller.ControllerPanel;
import io.github.dsheirer.controller.channel.Channel;
import io.github.dsheirer.controller.channel.ChannelAutoStartFrame;
import io.github.dsheirer.controller.channel.ChannelModel;
import io.github.dsheirer.controller.channel.ChannelProcessingManager;
import io.github.dsheirer.controller.channel.ChannelSelectionManager;
import io.github.dsheirer.eventbus.MyEventBus;
import io.github.dsheirer.gui.icon.ViewIconManagerRequest;
import io.github.dsheirer.gui.playlist.ViewPlaylistRequest;
import io.github.dsheirer.gui.preference.CalibrateRequest;
import io.github.dsheirer.gui.preference.PreferenceEditorType;
import io.github.dsheirer.gui.preference.ViewUserPreferenceEditorRequest;
import io.github.dsheirer.gui.preference.calibration.CalibrationDialog;
import io.github.dsheirer.gui.viewer.ViewRecordingViewerRequest;
import io.github.dsheirer.monitor.ResourceMonitor;
import io.github.dsheirer.preference.UserPreferences;
import io.github.dsheirer.properties.SystemProperties;
import io.github.dsheirer.sample.Listener;
import io.github.dsheirer.source.tuner.Tuner;
import io.github.dsheirer.source.tuner.TunerEvent;
import io.github.dsheirer.source.tuner.manager.DiscoveredTuner;
import io.github.dsheirer.source.tuner.manager.TunerManager;
import io.github.dsheirer.source.tuner.ui.DiscoveredTunerModel;
import io.github.dsheirer.source.tuner.ui.TunerSpectralDisplayManager;
import io.github.dsheirer.spectrum.DisableSpectrumWaterfallMenuItem;
import io.github.dsheirer.spectrum.ShowTunerMenuItem;
import io.github.dsheirer.spectrum.SpectralDisplayPanel;
import io.github.dsheirer.util.TimeStamp;
import io.github.dsheirer.vector.calibrate.CalibrationManager;
import jakarta.annotation.PostConstruct;
import jakarta.annotation.Resource;
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.KeyEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.nio.file.Path;
import java.util.List;
import java.util.Locale;
import java.util.Optional;
import java.util.prefs.Preferences;
import javafx.application.Platform;
import javafx.embed.swing.JFXPanel;
import javafx.scene.control.ButtonType;
import jiconfont.icons.font_awesome.FontAwesome;
import jiconfont.swing.IconFontSwing;
import net.miginfocom.swing.MigLayout;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.imageio.ImageIO;
import javax.swing.JCheckBoxMenuItem;
import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JSeparator;
import javax.swing.KeyStroke;
import javax.swing.UIManager;
import javax.swing.event.MenuEvent;
import javax.swing.event.MenuListener;
import javax.swing.plaf.metal.MetalLookAndFeel;
/**
* SDRTrunk primary user interface frame/window
*/
public class SDRTrunkUI extends JFrame implements Listener<TunerEvent>
{
private static final Logger mLog = LoggerFactory.getLogger(SDRTrunkUI.class);
private final Preferences mPreferences = Preferences.userNodeForPackage(SDRTrunkUI.class);
private static final String PREFERENCE_BROADCAST_STATUS_VISIBLE = "sdrtrunk.broadcast.status.visible";
private static final String PREFERENCE_NOW_PLAYING_DETAILS_VISIBLE = "sdrtrunk.now.playing.details.visible";
private static final String PREFERENCE_RESOURCE_STATUS_VISIBLE = "sdrtrunk.resource.status.visible";
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";
@Resource
private BroadcastStatusPanel mBroadcastStatusPanel;
@Resource
private ChannelModel mChannelModel;
@Resource
private ChannelProcessingManager mChannelProcessingManager;
@Resource
private ControllerPanel mControllerPanel;
@Resource
private DiscoveredTunerModel mDiscoveredTunerModel;
@Resource
private JavaFxWindowManager mJavaFxWindowManager;
@Resource
private ResourceMonitor mResourceMonitor;
@Resource
private SpectralDisplayPanel mSpectralPanel;
@Resource
private TunerSpectralDisplayManager mTunerSpectralDisplayManager;
@Resource
private TunerManager mTunerManager;
private JFXPanel mResourceStatusPanel;
private JideSplitPane mSplitPane;
private boolean mBroadcastStatusVisible;
private boolean mResourceStatusVisible;
private boolean mNowPlayingDetailsVisible;
@Resource
private UserPreferences mUserPreferences;
private String mTitle;
/**
* Constructs an instance of the SDRTrunk user interface
*/
public SDRTrunkUI()
{
//Register FontAwesome so we can use the fonts in Swing windows
IconFontSwing.register(FontAwesome.getIconFont());
}
/**
* Hook into this visible method so that we can show the auto-start channels dialog on window show.
*/
@Override
public void setVisible(boolean b)
{
super.setVisible(b);
CalibrationManager calibrationManager = CalibrationManager.getInstance(mUserPreferences);
final boolean calibrating = !calibrationManager.isCalibrated() &&
!mUserPreferences.getVectorCalibrationPreference().isHideCalibrationDialog();
if(calibrating)
{
Platform.runLater(() ->
{
CalibrationDialog calibrationDialog = mJavaFxWindowManager.getCalibrationDialog(mUserPreferences);
Optional<ButtonType> calibrate = calibrationDialog.showAndWait();
if(calibrate.isPresent() && calibrate.get().getText().equals("Calibrate"))
{
//Request focus and execute calibration
MyEventBus.getGlobalEventBus().post(new ViewUserPreferenceEditorRequest(PreferenceEditorType.VECTOR_CALIBRATION));
MyEventBus.getGlobalEventBus().post(new CalibrateRequest());
}
else
{
autoStartChannels();
}
});
}
else
{
autoStartChannels();
}
}
/**
* Shows the auto-start channels dialog
*/
private void autoStartChannels()
{
List<Channel> channels = mChannelModel.getAutoStartChannels();
if(channels.size() > 0)
{
new ChannelAutoStartFrame(mChannelProcessingManager, channels, mUserPreferences);
}
}
@PostConstruct
public void postConstruct()
{
String operatingSystem = System.getProperty("os.name", "generic").toLowerCase(Locale.ENGLISH);
if(operatingSystem.contains("mac") || operatingSystem.contains("nux"))
{
try
{
UIManager.setLookAndFeel(MetalLookAndFeel.class.getName());
LookAndFeelFactory.installJideExtension();
}
catch(Exception e)
{
mLog.error("Error trying to set Metal look and feel for OS [" + operatingSystem + "]");
}
}
new ChannelSelectionManager();
mNowPlayingDetailsVisible = mPreferences.getBoolean(PREFERENCE_NOW_PLAYING_DETAILS_VISIBLE, true);
mTunerSpectralDisplayManager.setSpectralDisplayPanel(mSpectralPanel);
mDiscoveredTunerModel.addListener(mTunerSpectralDisplayManager);
mDiscoveredTunerModel.addListener(this);
initGUI();
Tuner tuner = mTunerSpectralDisplayManager.showFirstTuner();
if(tuner != null)
{
updateTitle(tuner.getPreferredName());
}
}
/**
* Initialize the contents of the frame.
*/
private void initGUI()
{
setLayout(new MigLayout("insets 0 0 0 0 ", "[grow,fill]", "[grow,fill]0[shrink 0]"));
/**
* Setup main JFrame window
*/
mTitle = SystemProperties.getInstance().getApplicationName();
setTitle(mTitle);
Point location = mUserPreferences.getSwingPreference().getLocation(WINDOW_FRAME_IDENTIFIER);
if(location != null)
{
setLocation(location);
}
else
{
setLocationRelativeTo(null);
}
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
addWindowListener(new ShutdownMonitor());
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);
}
setSize(dimension);
if(mUserPreferences.getSwingPreference().getMaximized(WINDOW_FRAME_IDENTIFIER, false))
{
setExtendedState(JFrame.MAXIMIZED_BOTH);
}
}
else
{
setSize(new Dimension(1280, 800));
}
mSplitPane = new JideSplitPane(JideSplitPane.VERTICAL_SPLIT);
mSplitPane.setDividerSize(5);
mSplitPane.add(mSpectralPanel);
mSplitPane.add(mControllerPanel);
mBroadcastStatusVisible = mPreferences.getBoolean(PREFERENCE_BROADCAST_STATUS_VISIBLE, false);
//Show broadcast status panel when user requests - disabled by default
if(mBroadcastStatusVisible)
{
mSplitPane.add(getBroadcastStatusPanel());
}
add(mSplitPane, "cell 0 0,span,grow");
mResourceMonitor.start();
mResourceStatusVisible = mPreferences.getBoolean(PREFERENCE_RESOURCE_STATUS_VISIBLE, true);
if(mResourceStatusVisible)
{
add(getResourceStatusPanel(), "span,growx");
}
/**
* Menu items
*/
JMenuBar menuBar = new JMenuBar();
setJMenuBar(menuBar);
JMenu fileMenu = new JMenu("File");
menuBar.add(fileMenu);
JMenuItem exitMenu = new JMenuItem("Exit");
exitMenu.addActionListener(event -> {
shutdown();
System.exit(0);
}
);
fileMenu.add(exitMenu);
JMenu viewMenu = new JMenu("View");
JMenuItem viewPlaylistItem = new JMenuItem("Playlist Editor");
viewPlaylistItem.setIcon(IconFontSwing.buildIcon(FontAwesome.PLAY_CIRCLE_O, 12));
viewPlaylistItem.addActionListener(e -> MyEventBus.getGlobalEventBus().post(new ViewPlaylistRequest()));
viewMenu.add(viewPlaylistItem);
viewMenu.add(new JSeparator());
JMenuItem viewApplicationLogsMenu = new JMenuItem("Application Log Files");
viewApplicationLogsMenu.setIcon(IconFontSwing.buildIcon(FontAwesome.FOLDER_OPEN_O, 12));
viewApplicationLogsMenu.addActionListener(arg0 -> {
File logsDirectory = mUserPreferences.getDirectoryPreference().getDirectoryApplicationLog().toFile();
try
{
Desktop.getDesktop().open(logsDirectory);
}
catch(Exception e)
{
mLog.error("Couldn't open file explorer");
JOptionPane.showMessageDialog(this,
"Can't launch file explorer - files are located at: " + logsDirectory,
"Can't launch file explorer",
JOptionPane.ERROR_MESSAGE);
}
});
viewMenu.add(viewApplicationLogsMenu);
JMenuItem viewRecordingsMenuItem = new JMenuItem("Audio Recordings");
viewRecordingsMenuItem.setIcon(IconFontSwing.buildIcon(FontAwesome.FOLDER_OPEN_O, 12));
viewRecordingsMenuItem.addActionListener(arg0 -> {
File recordingsDirectory = mUserPreferences.getDirectoryPreference().getDirectoryRecording().toFile();
try
{
Desktop.getDesktop().open(recordingsDirectory);
}
catch(Exception e)
{
mLog.error("Couldn't open file explorer");
JOptionPane.showMessageDialog(this,
"Can't launch file explorer - files are located at: " +
recordingsDirectory,
"Can't launch file explorer",
JOptionPane.ERROR_MESSAGE);
}
});
viewMenu.add(viewRecordingsMenuItem);
JMenuItem viewEventLogsMenu = new JMenuItem("Channel Event Log Files");
viewEventLogsMenu.setIcon(IconFontSwing.buildIcon(FontAwesome.FOLDER_OPEN_O, 12));
viewEventLogsMenu.addActionListener(arg0 -> {
File eventLogsDirectory = mUserPreferences.getDirectoryPreference().getDirectoryEventLog().toFile();
try
{
Desktop.getDesktop().open(eventLogsDirectory);
}
catch(Exception e)
{
mLog.error("Couldn't open file explorer");
JOptionPane.showMessageDialog(this, "Can't launch file explorer - files are located at: " + eventLogsDirectory,
"Can't launch file explorer", JOptionPane.ERROR_MESSAGE);
}
});
viewMenu.add(viewEventLogsMenu);
JMenuItem iconManagerMenu = new JMenuItem("Icon Manager");
iconManagerMenu.setIcon(IconFontSwing.buildIcon(FontAwesome.PICTURE_O, 12));
iconManagerMenu.addActionListener(arg0 -> MyEventBus.getGlobalEventBus().post(new ViewIconManagerRequest()));
viewMenu.add(iconManagerMenu);
JMenuItem recordingViewerMenu = new JMenuItem("Message Recording Viewer (.bits)");
recordingViewerMenu.setIcon(IconFontSwing.buildIcon(FontAwesome.BRAILLE, 12));
recordingViewerMenu.addActionListener(e -> MyEventBus.getGlobalEventBus().post(new ViewRecordingViewerRequest()));
viewMenu.add(recordingViewerMenu);
JMenuItem viewScreenCapturesMenu = new JMenuItem("Screen Captures");
viewScreenCapturesMenu.setIcon(IconFontSwing.buildIcon(FontAwesome.FOLDER_OPEN_O, 12));
viewScreenCapturesMenu.addActionListener(arg0 -> {
File screenCapturesDirectory = mUserPreferences.getDirectoryPreference().getDirectoryScreenCapture().toFile();
try
{
Desktop.getDesktop().open(screenCapturesDirectory);
}
catch(Exception e)
{
mLog.error("Couldn't open file explorer");
JOptionPane.showMessageDialog(this, "Can't launch file explorer - files are located at: " + screenCapturesDirectory,
"Can't launch file explorer", JOptionPane.ERROR_MESSAGE);
}
});
viewMenu.add(viewScreenCapturesMenu);
JMenuItem preferencesItem = new JMenuItem("User Preferences");
preferencesItem.setIcon(IconFontSwing.buildIcon(FontAwesome.COG, 12));
preferencesItem.addActionListener(e -> MyEventBus.getGlobalEventBus().post(new ViewUserPreferenceEditorRequest()));
viewMenu.add(preferencesItem);
viewMenu.add(new JSeparator());
viewMenu.add(new TunersMenu());
viewMenu.add(new JSeparator());
viewMenu.add(new DisableSpectrumWaterfallMenuItem(mSpectralPanel, mUserPreferences));
viewMenu.add(new NowPlayingChannelDetailsVisibleMenuItem());
viewMenu.add(new BroadcastStatusVisibleMenuItem());
viewMenu.add(new ResourceStatusVisibleMenuItem());
menuBar.add(viewMenu);
JMenuItem screenCaptureItem = new JMenuItem("Screen Capture");
screenCaptureItem.setIcon(IconFontSwing.buildIcon(FontAwesome.CAMERA, 12));
screenCaptureItem.setMnemonic(KeyEvent.VK_C);
screenCaptureItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_C, ActionEvent.ALT_MASK));
screenCaptureItem.setMaximumSize(screenCaptureItem.getPreferredSize());
screenCaptureItem.addActionListener(arg0 -> {
try
{
Robot robot = new Robot();
final BufferedImage image = robot.createScreenCapture(getBounds());
String filename = TimeStamp.getTimeStamp("_") + "_screen_capture.png";
final Path captureFile = mUserPreferences.getDirectoryPreference().getDirectoryScreenCapture().resolve(filename);
EventQueue.invokeLater(() -> {
try
{
ImageIO.write(image, "png", captureFile.toFile());
}
catch(IOException e)
{
mLog.error("Couldn't write screen capture to file [" + captureFile.toString() + "]", e);
}
});
}
catch(AWTException e)
{
mLog.error("Exception while taking screen capture", e);
}
});
menuBar.add(screenCaptureItem);
}
/**
* Updates the title bar with the tuner name
* @param tunerName optional
*/
private void updateTitle(String tunerName)
{
if(tunerName != null)
{
setTitle(mTitle + " - " + tunerName);
}
else
{
setTitle(mTitle);
}
}
@Override
public void receive(TunerEvent event)
{
switch(event.getEvent())
{
case REQUEST_MAIN_SPECTRAL_DISPLAY:
updateTitle(event.getTuner().getPreferredName());
break;
case REQUEST_CLEAR_MAIN_SPECTRAL_DISPLAY:
updateTitle(null);
break;
case NOTIFICATION_SHUTTING_DOWN:
Tuner currentTuner = mSpectralPanel.getTuner();
if(event.hasTuner() && event.getTuner().equals(currentTuner) || currentTuner == null)
{
updateTitle(null);
}
break;
}
}
/**
* Lazy constructor for broadcast status panel
*/
private BroadcastStatusPanel getBroadcastStatusPanel()
{
return mBroadcastStatusPanel;
}
/**
* Toggles visibility of the broadcast channels status panel at the bottom of the controller panel
*/
private void toggleBroadcastStatusPanelVisibility()
{
mBroadcastStatusVisible = !mBroadcastStatusVisible;
EventQueue.invokeLater(() -> {
if(mBroadcastStatusVisible)
{
mSplitPane.add(getBroadcastStatusPanel());
}
else
{
mSplitPane.remove(getBroadcastStatusPanel());
}
revalidate();
});
mPreferences.putBoolean(PREFERENCE_BROADCAST_STATUS_VISIBLE, mBroadcastStatusVisible);
}
/**
* Lazy constructor for resource status panel
*/
private JFXPanel getResourceStatusPanel()
{
if(mResourceStatusPanel == null)
{
mResourceStatusPanel = mJavaFxWindowManager.getStatusPanel(mResourceMonitor);
}
return mResourceStatusPanel;
}
/**
* Toggles visibility of the resource status panel at the bottom of the main UI window
*/
private void toggleResourceStatusPanelVisibility()
{
mResourceStatusVisible = !mResourceStatusVisible;
EventQueue.invokeLater(() -> {
if(mResourceStatusVisible)
{
add(getResourceStatusPanel(), "span,growx");
}
else
{
remove(getResourceStatusPanel());
}
revalidate();
});
mPreferences.putBoolean(PREFERENCE_RESOURCE_STATUS_VISIBLE, mResourceStatusVisible);
}
/**
* Toggles visibility of the Now Playing channel details panel
*/
private void toggleNowPlayingDetailsPanelVisibility()
{
mNowPlayingDetailsVisible = !mNowPlayingDetailsVisible;
mControllerPanel.getNowPlayingPanel().setDetailTabsVisible(mNowPlayingDetailsVisible);
mPreferences.putBoolean(PREFERENCE_NOW_PLAYING_DETAILS_VISIBLE, mNowPlayingDetailsVisible);
}
/**
* Performs shutdown operations. This is triggered by the shutdown monitor's windowClosing() method
*/
private void shutdown()
{
mLog.info("SDRTrunk UI shutdown started ...");
mUserPreferences.getSwingPreference().setLocation(WINDOW_FRAME_IDENTIFIER, getLocation());
mUserPreferences.getSwingPreference().setDimension(WINDOW_FRAME_IDENTIFIER, getSize());
mUserPreferences.getSwingPreference().setMaximized(WINDOW_FRAME_IDENTIFIER,
(getExtendedState() & JFrame.MAXIMIZED_BOTH) == JFrame.MAXIMIZED_BOTH);
mUserPreferences.getSwingPreference().setDimension(SPECTRAL_PANEL_IDENTIFIER, mSpectralPanel.getSize());
mUserPreferences.getSwingPreference().setDimension(CONTROLLER_PANEL_IDENTIFIER, mControllerPanel.getSize());
mLog.info("Stopping JavaFX window manager ...");
mJavaFxWindowManager.shutdown();
mLog.info("Stopping resource monitor ...");
mResourceMonitor.stop();
mLog.info("Stopping spectral display ...");
mSpectralPanel.clearTuner();
}
/**
* Window adapter to receive window closing event to initiate shutdown.
*/
public class ShutdownMonitor extends WindowAdapter
{
@Override
public void windowClosing(WindowEvent e)
{
shutdown();
}
}
/**
* Broadcast status panel visible toggle menu item
*/
public class BroadcastStatusVisibleMenuItem extends JCheckBoxMenuItem
{
public BroadcastStatusVisibleMenuItem()
{
super("Show Streaming Status");
setSelected(mBroadcastStatusVisible);
addActionListener(e -> {
toggleBroadcastStatusPanelVisibility();
setSelected(mBroadcastStatusVisible);
});
}
}
/**
* Resource status panel visible toggle menu item
*/
public class ResourceStatusVisibleMenuItem extends JCheckBoxMenuItem
{
public ResourceStatusVisibleMenuItem()
{
super("Show Resource Status");
setSelected(mResourceStatusVisible);
addActionListener(e -> {
toggleResourceStatusPanelVisibility();
setSelected(mResourceStatusVisible);
});
}
}
/**
* Now Playing channel details visible toggle menu item
*/
public class NowPlayingChannelDetailsVisibleMenuItem extends JCheckBoxMenuItem
{
public NowPlayingChannelDetailsVisibleMenuItem()
{
super("Show Now Playing Channel Details");
setSelected(mNowPlayingDetailsVisible);
addActionListener(e -> {
toggleNowPlayingDetailsPanelVisibility();
setSelected(mNowPlayingDetailsVisible);
});
}
}
/**
* Menu for selecting tuners for display.
*/
public class TunersMenu extends JMenu
{
public TunersMenu()
{
super("Tuners");
addMenuListener(new MenuListener()
{
@Override
public void menuSelected(MenuEvent e)
{
removeAll();
for(DiscoveredTuner discoveredTuner: mTunerManager.getAvailableTuners())
{
add(new ShowTunerMenuItem(discoveredTuner.getTuner(), mDiscoveredTunerModel, mUserPreferences));
}
}
@Override
public void menuDeselected(MenuEvent e) { }
@Override
public void menuCanceled(MenuEvent e) { }
});
}
}
}

View File

@@ -0,0 +1,710 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2024 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.gui;
import io.github.dsheirer.audio.broadcast.openmhz.OpenMHzEditor;
import io.github.dsheirer.audio.call.CallView;
import io.github.dsheirer.audio.playbackfx.AudioPlaybackChannelsView;
import io.github.dsheirer.gui.icon.IconManager;
import io.github.dsheirer.gui.playlist.PlaylistEditor;
import io.github.dsheirer.gui.playlist.alias.AliasBulkEditor;
import io.github.dsheirer.gui.playlist.alias.AliasConfigurationEditor;
import io.github.dsheirer.gui.playlist.alias.AliasEditor;
import io.github.dsheirer.gui.playlist.alias.AliasItemEditor;
import io.github.dsheirer.gui.playlist.alias.AliasViewByIdentifierEditor;
import io.github.dsheirer.gui.playlist.alias.AliasViewByRecordingEditor;
import io.github.dsheirer.gui.playlist.channel.AMConfigurationEditor;
import io.github.dsheirer.gui.playlist.channel.ChannelEditor;
import io.github.dsheirer.gui.playlist.channel.DMRConfigurationEditor;
import io.github.dsheirer.gui.playlist.channel.LTRConfigurationEditor;
import io.github.dsheirer.gui.playlist.channel.LTRNetConfigurationEditor;
import io.github.dsheirer.gui.playlist.channel.MPT1327ConfigurationEditor;
import io.github.dsheirer.gui.playlist.channel.NBFMConfigurationEditor;
import io.github.dsheirer.gui.playlist.channel.P25P1ConfigurationEditor;
import io.github.dsheirer.gui.playlist.channel.P25P2ConfigurationEditor;
import io.github.dsheirer.gui.playlist.channel.PassportConfigurationEditor;
import io.github.dsheirer.gui.playlist.channel.UnknownConfigurationEditor;
import io.github.dsheirer.gui.playlist.channelMap.ChannelMapEditor;
import io.github.dsheirer.gui.playlist.manager.PlaylistManagerEditor;
import io.github.dsheirer.gui.playlist.radioreference.CountyAgencyEditor;
import io.github.dsheirer.gui.playlist.radioreference.CountySystemEditor;
import io.github.dsheirer.gui.playlist.radioreference.NationalAgencyEditor;
import io.github.dsheirer.gui.playlist.radioreference.RadioReferenceEditor;
import io.github.dsheirer.gui.playlist.radioreference.SiteEditor;
import io.github.dsheirer.gui.playlist.radioreference.StateAgencyEditor;
import io.github.dsheirer.gui.playlist.radioreference.StateSystemEditor;
import io.github.dsheirer.gui.playlist.radioreference.SystemSiteSelectionEditor;
import io.github.dsheirer.gui.playlist.radioreference.SystemTalkgroupSelectionEditor;
import io.github.dsheirer.gui.playlist.radioreference.TalkgroupEditor;
import io.github.dsheirer.gui.playlist.streaming.BroadcastifyCallEditor;
import io.github.dsheirer.gui.playlist.streaming.BroadcastifyStreamEditor;
import io.github.dsheirer.gui.playlist.streaming.IcecastHTTPStreamEditor;
import io.github.dsheirer.gui.playlist.streaming.IcecastTCPStreamEditor;
import io.github.dsheirer.gui.playlist.streaming.RdioScannerEditor;
import io.github.dsheirer.gui.playlist.streaming.ShoutcastV1StreamEditor;
import io.github.dsheirer.gui.playlist.streaming.ShoutcastV2StreamEditor;
import io.github.dsheirer.gui.playlist.streaming.StreamAliasSelectionEditor;
import io.github.dsheirer.gui.playlist.streaming.StreamingEditor;
import io.github.dsheirer.gui.playlist.streaming.UnknownStreamEditor;
import io.github.dsheirer.gui.preference.DecodeEventViewPreferenceEditor;
import io.github.dsheirer.gui.preference.TalkgroupFormatPreferenceEditor;
import io.github.dsheirer.gui.preference.UserPreferencesEditor;
import io.github.dsheirer.gui.preference.application.ApplicationPreferenceEditor;
import io.github.dsheirer.gui.preference.calibration.VectorCalibrationPreferenceEditor;
import io.github.dsheirer.gui.preference.call.CallManagementPreferenceEditor;
import io.github.dsheirer.gui.preference.decoder.JmbeLibraryPreferenceEditor;
import io.github.dsheirer.gui.preference.directory.DirectoryPreferenceEditor;
import io.github.dsheirer.gui.preference.mp3.MP3PreferenceEditor;
import io.github.dsheirer.gui.preference.playback.PlaybackPreferenceEditor;
import io.github.dsheirer.gui.preference.record.RecordPreferenceEditor;
import io.github.dsheirer.gui.preference.tuner.TunerPreferenceEditor;
import io.github.dsheirer.jmbe.JmbeEditor;
import java.awt.GraphicsEnvironment;
import javafx.application.Platform;
import javafx.embed.swing.JFXPanel;
import jiconfont.javafx.IconFontFX;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Lazy;
import org.springframework.context.annotation.Scope;
/**
* Spring configuration factory for JavaFX UI components that require dependency injection (@Resource)
*
* Note: all bean creator methods are also annotated as @Lazy so that Spring doesn't auto-instantiate these beans when
* we're running in a headless environment.
*/
@Configuration
public class SpringJavaFxBeanFactory
{
private JFXPanel mJFXPanel;
/**
* Constructs an instance
*/
public SpringJavaFxBeanFactory()
{
if(!GraphicsEnvironment.isHeadless())
{
//Initialize the JavafX platform by creating a JFXPanel.
createJFXPanel();
//Register JavaFX icon fonts
IconFontFX.register(jiconfont.icons.font_awesome.FontAwesome.getIconFont());
IconFontFX.register(jiconfont.icons.elusive.Elusive.getIconFont());
}
}
/**
* Initialize the JavaFX platform by creating a JavaFX panel for Swing application compatibility
*/
private void createJFXPanel()
{
if(mJFXPanel == null)
{
mJFXPanel = new JFXPanel();
Platform.setImplicitExit(false);
}
}
/**
* Call view Panel
*/
@Bean
@Lazy
public CallView getCallViewPanel()
{
return new CallView();
}
/**
* Audio playback channels view
*/
@Bean
@Lazy
public AudioPlaybackChannelsView getAudioPlaybackChannelsView()
{
return new AudioPlaybackChannelsView();
}
/**
* Alias Item Editor
*/
@Bean
@Lazy
public AliasItemEditor getAliasItemEditor()
{
return new AliasItemEditor();
}
/**
* Alias Bulk Editor
*/
@Bean
@Lazy
public AliasBulkEditor getAliasBulkEditor()
{
return new AliasBulkEditor();
}
/**
* Alias Configuration Editor
*/
@Bean
@Lazy
public AliasConfigurationEditor getAliasConfigurationEditor()
{
return new AliasConfigurationEditor();
}
/**
* Alias Editor
*/
@Bean
@Lazy
public AliasEditor getAliasEditor()
{
return new AliasEditor();
}
/**
* Alias View By Identifier Editor
*/
@Bean
@Lazy
public AliasViewByIdentifierEditor getAliasViewByIdentifierEditor()
{
return new AliasViewByIdentifierEditor();
}
/**
* Channel Editor
*/
@Bean
@Lazy
public ChannelEditor getChannelEditor()
{
return new ChannelEditor();
}
/**
* Playlist Editor
*/
@Bean
@Lazy
public PlaylistEditor getPlaylistEditor()
{
return new PlaylistEditor();
}
/**
* Playlist Manager Editor
*/
@Bean
@Lazy
public PlaylistManagerEditor getPlaylistManagerEditor()
{
return new PlaylistManagerEditor();
}
/**
* Radio Reference Editor
*/
@Bean
@Lazy
public RadioReferenceEditor getRadioReferenceEditor()
{
return new RadioReferenceEditor();
}
/**
* Stream alias selection editor
*/
@Bean
@Lazy
public StreamAliasSelectionEditor getStreamAliasSelectionEditor()
{
return new StreamAliasSelectionEditor();
}
/**
* Streaming editor
*/
@Bean
@Lazy
public StreamingEditor getStreamingEditor()
{
return new StreamingEditor();
}
/**
* AM channel configuration editor
*/
@Bean
@Lazy
public AMConfigurationEditor getAMConfigurationEditor()
{
return new AMConfigurationEditor();
}
/**
* NBFM channel configuration editor
*/
@Bean
@Lazy
public NBFMConfigurationEditor getNBFMConfigurationEditor()
{
return new NBFMConfigurationEditor();
}
/**
* DMR channel configuration editor
*/
@Bean
@Lazy
public DMRConfigurationEditor getDMRConfigurationEditor()
{
return new DMRConfigurationEditor();
}
/**
* LTR channel configuration editor
*/
@Bean
@Lazy
public LTRConfigurationEditor getLTRConfigurationEditor()
{
return new LTRConfigurationEditor();
}
/**
* LTR-Net channel configuration editor
*/
@Bean
@Lazy
public LTRNetConfigurationEditor getLTRNetConfigurationEditor()
{
return new LTRNetConfigurationEditor();
}
/**
* MPT-1327 channel configuration editor
*/
@Bean
@Lazy
public MPT1327ConfigurationEditor getMPT1327ConfigurationEditor()
{
return new MPT1327ConfigurationEditor();
}
/**
* P25 Phase 1 channel configuration editor
*/
@Bean
@Lazy
public P25P1ConfigurationEditor getP25P1ConfigurationEditor()
{
return new P25P1ConfigurationEditor();
}
/**
* P25 Phase 2 channel configuration editor
*/
@Bean
@Lazy
public P25P2ConfigurationEditor getP25P2ConfigurationEditor()
{
return new P25P2ConfigurationEditor();
}
/**
* Passport channel configuration editor
*/
@Bean
@Lazy
public PassportConfigurationEditor getPassportConfigurationEditor()
{
return new PassportConfigurationEditor();
}
/**
* Unknown channel configuration editor
*/
@Bean
@Lazy
public UnknownConfigurationEditor getUnknownConfigurationEditor()
{
return new UnknownConfigurationEditor();
}
/**
* Broadcastify stream editor
*/
@Bean
@Lazy
public BroadcastifyStreamEditor getBroadcastifyStreamEditor()
{
return new BroadcastifyStreamEditor();
}
/**
* Broadcastify calls stream editor
*/
@Bean
@Lazy
public BroadcastifyCallEditor getBroadcastifyCallEditor()
{
return new BroadcastifyCallEditor();
}
/**
* OpenMHZ stream editor
*/
@Bean
@Lazy
public OpenMHzEditor getOpenMhzEditor()
{
return new OpenMHzEditor();
}
/**
* Rdio scanner stream editor
*/
@Bean
@Lazy
public RdioScannerEditor getRdioScannerEditor()
{
return new RdioScannerEditor();
}
/**
* Icecast TCP stream editor
*/
@Bean
@Lazy
public IcecastTCPStreamEditor getIcecastTcpStreamEditor()
{
return new IcecastTCPStreamEditor();
}
/**
* Icecast HTTP stream editor
*/
@Bean
@Lazy
public IcecastHTTPStreamEditor getIcecastHTTPStreamEditor()
{
return new IcecastHTTPStreamEditor();
}
/**
* Shoutcast V1 stream editor
*/
@Bean
@Lazy
public ShoutcastV1StreamEditor getShoutcastV1StreamEditor()
{
return new ShoutcastV1StreamEditor();
}
/**
* Shoutcast V1 stream editor
*/
@Bean
@Lazy
public ShoutcastV2StreamEditor getShoutcastV2StreamEditor()
{
return new ShoutcastV2StreamEditor();
}
/**
* Unknown stream editor
*/
@Bean
@Lazy
public UnknownStreamEditor getUnknownStreamEditor()
{
return new UnknownStreamEditor();
}
/**
* County agency editor
*/
@Bean
@Lazy
public CountyAgencyEditor getCountyAgencyEditor()
{
return new CountyAgencyEditor();
}
/**
* County system editor
*/
@Bean
@Lazy
public CountySystemEditor getCountySystemEditor()
{
return new CountySystemEditor();
}
/**
* State agency editor
*/
@Bean
@Lazy
public StateAgencyEditor getStateAgencyEditor()
{
return new StateAgencyEditor();
}
/**
* State system editor
*/
@Bean
@Lazy
public StateSystemEditor getStateSystemEditor()
{
return new StateSystemEditor();
}
/**
* National agency editor
*/
@Bean
@Lazy
public NationalAgencyEditor getNationalAgencyEditor()
{
return new NationalAgencyEditor();
}
/**
* System talkgroup selection editor
*
* Note: editor is used in multiple places so we create a new instance for each (ie scope=prototype)
*/
@Bean
@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
@Lazy
public SystemTalkgroupSelectionEditor getSystemTalkgroupSelectionEditor()
{
return new SystemTalkgroupSelectionEditor();
}
/**
* Site editor
*
* Note: editor is used in multiple places so we create a new instance for each (ie scope=prototype)
*/
@Bean
@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
@Lazy
public SiteEditor getSiteEditor()
{
return new SiteEditor();
}
/**
* Talkgroup editor
*
* Note: editor is used in multiple places so we create a new instance for each (ie scope=prototype)
*/
@Bean
@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
@Lazy
public TalkgroupEditor getTalkgroupEditor()
{
return new TalkgroupEditor();
}
/**
* System site selection editor
*
* Note: editor is used in multiple places so we create a new instance for each (ie scope=prototype)
*/
@Bean
@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
@Lazy
public SystemSiteSelectionEditor getSystemSiteSelectionEditor()
{
return new SystemSiteSelectionEditor();
}
/**
* Channel map editor
*/
@Bean
@Lazy
public ChannelMapEditor getChannelMapEditor()
{
return new ChannelMapEditor();
}
/**
* Channel map editor
*/
@Bean
@Lazy
public IconManager getIconManager()
{
return new IconManager();
}
/**
* User preferences editor
*/
@Bean
@Lazy
public UserPreferencesEditor getUserPreferencesEditor()
{
return new UserPreferencesEditor();
}
/**
* Jmbe editor
*/
@Bean
@Lazy
public JmbeEditor getJmbeEditor()
{
return new JmbeEditor();
}
/**
* Alias view-by recording editor
*/
@Bean
@Lazy
public AliasViewByRecordingEditor getAliasViewByRecordingEditor()
{
return new AliasViewByRecordingEditor();
}
/**
* Application preference editor
*/
@Bean
@Lazy
public ApplicationPreferenceEditor getApplicationPreferenceEditor()
{
return new ApplicationPreferenceEditor();
}
/**
* Call management preference editor
*/
@Bean
@Lazy
public CallManagementPreferenceEditor getCallManagementPreferenceEditor()
{
return new CallManagementPreferenceEditor();
}
/**
* Application preference editor
*/
@Bean
@Lazy
public MP3PreferenceEditor getMp3PreferenceEditor()
{
return new MP3PreferenceEditor();
}
/**
* Playback preference editor
*/
@Bean
@Lazy
public PlaybackPreferenceEditor getPlaybackPreferenceEditor()
{
return new PlaybackPreferenceEditor();
}
/**
* Record preference editor
*/
@Bean
@Lazy
public RecordPreferenceEditor getRecordPreferenceEditor()
{
return new RecordPreferenceEditor();
}
/**
* Decode event view preference editor
*/
@Bean
@Lazy
public DecodeEventViewPreferenceEditor getDecodeEventViewPreferenceEditor()
{
return new DecodeEventViewPreferenceEditor();
}
/**
* Directory preference editor
*/
@Bean
@Lazy
public DirectoryPreferenceEditor getDirectoryPreferenceEditor()
{
return new DirectoryPreferenceEditor();
}
/**
* Playback preference editor
*/
@Bean
@Lazy
public JmbeLibraryPreferenceEditor getJmbeLibraryPreferenceEditor()
{
return new JmbeLibraryPreferenceEditor();
}
/**
* Tuner preference editor
*/
@Bean
@Lazy
public TunerPreferenceEditor getTunerPreferenceEditor()
{
return new TunerPreferenceEditor();
}
/**
* Talkgroup format preference editor
*/
@Bean
@Lazy
public TalkgroupFormatPreferenceEditor getTalkgroupFormatPreferenceEditor()
{
return new TalkgroupFormatPreferenceEditor();
}
/**
* Vector calibration preference editor
*/
@Bean
@Lazy
public VectorCalibrationPreferenceEditor getVectorCalibrationPreferenceEditor()
{
return new VectorCalibrationPreferenceEditor();
}
}

View File

@@ -0,0 +1,249 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2023 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.gui;
import io.github.dsheirer.audio.broadcast.BroadcastStatusPanel;
import io.github.dsheirer.audio.playback.AudioPanel;
import io.github.dsheirer.channel.details.ChannelDetailPanel;
import io.github.dsheirer.channel.metadata.ChannelMetadataPanel;
import io.github.dsheirer.channel.metadata.NowPlayingPanel;
import io.github.dsheirer.controller.ControllerPanel;
import io.github.dsheirer.gui.power.ChannelPowerPanel;
import io.github.dsheirer.module.decode.event.DecodeEventPanel;
import io.github.dsheirer.source.tuner.ui.DiscoveredTunerEditor;
import io.github.dsheirer.source.tuner.ui.TunerViewPanel;
import io.github.dsheirer.spectrum.FrequencyOverlayPanel;
import io.github.dsheirer.spectrum.OverlayPanel;
import io.github.dsheirer.spectrum.SpectralDisplayPanel;
import io.github.dsheirer.spectrum.SpectrumFrame;
import io.github.dsheirer.spectrum.SpectrumPanel;
import io.github.dsheirer.spectrum.WaterfallPanel;
import java.awt.GraphicsEnvironment;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Lazy;
import org.springframework.context.annotation.Scope;
/**
* Spring configuration factory for Swing UI components that require dependency injection (@Resource)
*
* Note: all bean creator methods are also annotated as @Lazy so that Spring doesn't auto-instantiate these beans when
* we're running in a headless environment.
*/
@Configuration
public class SpringSwingBeanFactory
{
/**
* Constructs an instance
*/
public SpringSwingBeanFactory()
{
}
/**
* Constructs the primary UI instance when running in non-headless mode.
*/
@Bean
@Lazy
public SDRTrunkUI getSDRTrunkUI()
{
if(GraphicsEnvironment.isHeadless())
{
return null;
}
return new SDRTrunkUI();
}
/**
* Constructs a new spectrum frame
*/
@Bean
@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
@Lazy
public SpectrumFrame createSpectrumFrame()
{
return new SpectrumFrame();
}
/**
* Constructs the spectral display panel
*/
@Bean
@Lazy
@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public SpectralDisplayPanel getSpectralDisplayPanel()
{
return new SpectralDisplayPanel();
}
/**
* Constructs the spectrum panel
*
* Note: editor is used in multiple places so we create a new instance for each (ie scope=prototype)
*/
@Bean
@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
@Lazy
public SpectrumPanel getSpectrumPanel()
{
return new SpectrumPanel();
}
/**
* Constructs the waterfall panel
*
* Note: editor is used in multiple places so we create a new instance for each (ie scope=prototype)
*/
@Bean
@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
@Lazy
public WaterfallPanel getWaterfallPanel()
{
return new WaterfallPanel();
}
/**
* Constructs the overlay panel
*
* Note: editor is used in multiple places so we create a new instance for each (ie scope=prototype)
*/
@Bean
@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
@Lazy
public OverlayPanel getOverlayPanel()
{
return new OverlayPanel();
}
/**
* Constructs the frequency overlay panel
*
* Note: editor is used in multiple places so we create a new instance for each (ie scope=prototype)
*/
@Bean
@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
@Lazy
public FrequencyOverlayPanel getFrequencyOverlayPanel()
{
return new FrequencyOverlayPanel();
}
/**
* Creates a broadcast (ie streaming) status panel
*/
@Bean
@Lazy
public BroadcastStatusPanel getBroadcastStatusPanel()
{
return new BroadcastStatusPanel();
}
/**
* Creates the controller panel
*/
@Bean
@Lazy
public ControllerPanel controllerPanel()
{
return new ControllerPanel();
}
/**
* Creates the now playing panel
*/
@Bean
@Lazy
public NowPlayingPanel getNowPlayingPanel()
{
return new NowPlayingPanel();
}
/**
* Creates the channel metadata panel
*/
@Bean
@Lazy
public ChannelMetadataPanel getChannelMetadataPanel()
{
return new ChannelMetadataPanel();
}
/**
* Creates the channel detail panel
*/
@Bean
@Lazy
public ChannelDetailPanel getChannelDetailPanel()
{
return new ChannelDetailPanel();
}
/**
* Creates the channel power panel
*/
@Bean
@Lazy
public ChannelPowerPanel getChannelPowerPanel()
{
return new ChannelPowerPanel();
}
/**
* Creates the decode event panel
*/
@Bean
@Lazy
public DecodeEventPanel getDecodeEventPanel()
{
return new DecodeEventPanel();
}
/**
* Creates an audio panel
*/
@Bean
@Lazy
public AudioPanel getAudioPanel()
{
return new AudioPanel();
}
/**
* Creates discovered tuner editor
*/
@Bean
@Lazy
public DiscoveredTunerEditor getDiscoveredTunerEditor()
{
return new DiscoveredTunerEditor();
}
/**
* Creates tuner view panel
*/
@Bean
@Lazy
public TunerViewPanel getTunerViewPanel()
{
return new TunerViewPanel();
}
}

View File

@@ -0,0 +1,30 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2023 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.gui.calls;
import javafx.scene.layout.VBox;
/**
* Call audio panel for managing call audio playback.
*/
public class CallAudioPanel extends VBox
{
}

View File

@@ -1,6 +1,6 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2022 Dennis Sheirer
* Copyright (C) 2014-2023 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
@@ -35,6 +35,13 @@ import io.github.dsheirer.spectrum.ComplexDftProcessor;
import io.github.dsheirer.spectrum.DFTSize;
import io.github.dsheirer.spectrum.SpectrumPanel;
import io.github.dsheirer.spectrum.converter.ComplexDecibelConverter;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import net.miginfocom.swing.MigLayout;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -47,13 +54,6 @@ import javax.swing.JToggleButton;
import javax.swing.SpinnerNumberModel;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class ChannelizerViewer extends JFrame
{
@@ -316,7 +316,7 @@ public class ChannelizerViewer extends JFrame
public PrimarySpectrumPanel(SettingsManager settingsManager, double sampleRate)
{
setLayout(new MigLayout("insets 0 0 0 0", "[grow,fill]", "[grow,fill]"));
mSpectrumPanel = new SpectrumPanel(settingsManager);
mSpectrumPanel = new SpectrumPanel();
mSpectrumPanel.setSampleSize(28);
add(mSpectrumPanel);
@@ -354,7 +354,7 @@ public class ChannelizerViewer extends JFrame
public ChannelPanel(SettingsManager settingsManager, double sampleRate, long frequency, int bandwidth, boolean enableLogging)
{
setLayout(new MigLayout("insets 0 0 0 0", "[center,grow,fill][]", "[grow,fill][]"));
mSpectrumPanel = new SpectrumPanel(settingsManager);
mSpectrumPanel = new SpectrumPanel();
mSpectrumPanel.setSampleSize(32);
add(mSpectrumPanel, "span");
@@ -435,7 +435,7 @@ public class ChannelizerViewer extends JFrame
{
mSource = source;
setLayout(new MigLayout("insets 0 0 0 0", "[center,grow,fill][]", "[grow,fill][]"));
mSpectrumPanel = new SpectrumPanel(settingsManager);
mSpectrumPanel = new SpectrumPanel();
mSpectrumPanel.setSampleSize(32);
add(mSpectrumPanel, "span");
add(new JLabel("Index:" + index));

View File

@@ -333,7 +333,7 @@ public class ChannelizerViewer2 extends JFrame
public PrimarySpectrumPanel(SettingsManager settingsManager, double sampleRate)
{
setLayout(new MigLayout("insets 0 0 0 0", "[grow,fill]", "[grow,fill]"));
mSpectrumPanel = new SpectrumPanel(settingsManager);
mSpectrumPanel = new SpectrumPanel();
mSpectrumPanel.setSampleSize(16);
add(mSpectrumPanel);
@@ -371,7 +371,7 @@ public class ChannelizerViewer2 extends JFrame
public ChannelPanel(SettingsManager settingsManager, double sampleRate, long frequency, int bandwidth)
{
setLayout(new MigLayout("insets 0 0 0 0", "[grow,fill][grow,fill][grow,fill]", "[grow,fill][]"));
mSpectrumPanel = new SpectrumPanel(settingsManager);
mSpectrumPanel = new SpectrumPanel();
mSpectrumPanel.setSampleSize(16);
add(mSpectrumPanel, "span");

View File

@@ -1,6 +1,6 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2022 Dennis Sheirer
* Copyright (C) 2014-2023 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
@@ -36,6 +36,13 @@ import io.github.dsheirer.spectrum.ComplexDftProcessor;
import io.github.dsheirer.spectrum.DFTSize;
import io.github.dsheirer.spectrum.SpectrumPanel;
import io.github.dsheirer.spectrum.converter.ComplexDecibelConverter;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import net.miginfocom.swing.MigLayout;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -48,13 +55,6 @@ import javax.swing.JToggleButton;
import javax.swing.SpinnerNumberModel;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class HeterodyneChannelizerViewer extends JFrame
{
@@ -315,7 +315,7 @@ public class HeterodyneChannelizerViewer extends JFrame
public PrimarySpectrumPanel(SettingsManager settingsManager, double sampleRate)
{
setLayout(new MigLayout("insets 0 0 0 0", "[grow,fill]", "[grow,fill]"));
mSpectrumPanel = new SpectrumPanel(settingsManager);
mSpectrumPanel = new SpectrumPanel();
mSpectrumPanel.setSampleSize(28);
add(mSpectrumPanel);
@@ -353,7 +353,7 @@ public class HeterodyneChannelizerViewer extends JFrame
public ChannelPanel(SettingsManager settingsManager, double sampleRate, long frequency, int bandwidth, boolean enableLogging)
{
setLayout(new MigLayout("insets 0 0 0 0", "[center,grow,fill][]", "[grow,fill][]"));
mSpectrumPanel = new SpectrumPanel(settingsManager);
mSpectrumPanel = new SpectrumPanel();
mSpectrumPanel.setSampleSize(32);
add(mSpectrumPanel, "span");
@@ -435,7 +435,7 @@ public class HeterodyneChannelizerViewer extends JFrame
{
mSource = source;
setLayout(new MigLayout("insets 0 0 0 0", "[center,grow,fill][]", "[grow,fill][]"));
mSpectrumPanel = new SpectrumPanel(settingsManager);
mSpectrumPanel = new SpectrumPanel();
mSpectrumPanel.setSampleSize(32);
add(mSpectrumPanel, "span");
add(new JLabel("Index:" + index));

View File

@@ -1,6 +1,6 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2022 Dennis Sheirer
* Copyright (C) 2014-2023 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
@@ -150,7 +150,7 @@ public class SynthesizerViewer extends JFrame
public PrimarySpectrumPanel(SettingsManager settingsManager)
{
setLayout(new MigLayout("insets 0 0 0 0", "[grow,fill]", "[grow,fill]"));
mSpectrumPanel = new SpectrumPanel(settingsManager);
mSpectrumPanel = new SpectrumPanel();
mSpectrumPanel.setSampleSize(16);
add(mSpectrumPanel);
@@ -181,7 +181,7 @@ public class SynthesizerViewer extends JFrame
public ChannelPanel(SettingsManager settingsManager, ChannelControlPanel channelControlPanel)
{
setLayout(new MigLayout("insets 0 0 0 0", "[grow,fill]", "[grow,fill][]"));
mSpectrumPanel = new SpectrumPanel(settingsManager);
mSpectrumPanel = new SpectrumPanel();
mSpectrumPanel.setSampleSize(16);
add(mSpectrumPanel, "wrap");
add(channelControlPanel);

View File

@@ -0,0 +1,64 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2023 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.gui.control;
import java.text.SimpleDateFormat;
import javafx.scene.control.TableCell;
import javafx.scene.control.TableColumn;
import javafx.util.Callback;
/**
* Table cell factory that formats a long timestamp column value as a formatted date/time stamp.
* @param <T> is the table row entity type
*/
public class TimestampTableCellFactory<T> implements Callback<TableColumn<T, Long>, TableCell<T, Long>>
{
private SimpleDateFormat mSDF = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
@Override
public TableCell<T, Long> call(TableColumn<T, Long> param)
{
return new TableCell<>()
{
@Override
protected void updateItem(Long item, boolean empty)
{
super.updateItem(item, empty);
if(empty)
{
setText(null);
}
else
{
if(item instanceof Long l)
{
String a = mSDF.format(l);
setText(a);
}
else
{
setText("Not a Long: " + item.getClass());
}
}
}
};
}
}

View File

@@ -1,6 +1,6 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2020 Dennis Sheirer
* Copyright (C) 2014-2023 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
@@ -22,6 +22,11 @@ package io.github.dsheirer.gui.icon;
import io.github.dsheirer.gui.playlist.Editor;
import io.github.dsheirer.icon.Icon;
import io.github.dsheirer.icon.IconModel;
import jakarta.annotation.PostConstruct;
import jakarta.annotation.Resource;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Path;
import javafx.beans.binding.Bindings;
import javafx.collections.transformation.SortedList;
import javafx.geometry.HPos;
@@ -48,17 +53,16 @@ import javafx.util.Callback;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Path;
/**
* GUI manager/editor for standard and custom icons used in sdrtrunk
*/
public class IconManager extends Editor<Icon>
{
private static final Logger mLog = LoggerFactory.getLogger(IconManager.class);
@Resource
private IconModel mIconModel;
private TableView<Icon> mIconTableView;
private Button mAddButton;
private Button mEditButton;
@@ -78,9 +82,13 @@ public class IconManager extends Editor<Icon>
/**
* Constructs an instance
*/
public IconManager(IconModel iconModel)
public IconManager()
{
}
@PostConstruct
public void postConstruct()
{
mIconModel = iconModel;
getChildren().addAll(getAccordion());
}

View File

@@ -0,0 +1,250 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2024 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.gui.javafx.table;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.geometry.Insets;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.cell.CheckBoxTableCell;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.layout.ColumnConstraints;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.Priority;
import javafx.scene.layout.VBox;
/**
* Editor for managing table column ordering and visibility.
*/
public class TableColumnEditor extends VBox
{
private TableView mTableView;
private TableViewColumnController mTableViewColumnController;
private TableView mColumnsTableView;
private Button mCloseButton;
private Button mOrderUpButton;
private Button mOrderDownButton;
private Button mResetColumnsButton;
private BooleanProperty mClosed = new SimpleBooleanProperty();
/**
* Constructs an instance
* @param tableView to be configured/managed
*/
public TableColumnEditor(TableView tableView, TableViewColumnController monitor)
{
mTableView = tableView;
mTableViewColumnController = monitor;
setPadding(new Insets(10));
GridPane gridPane = new GridPane();
gridPane.setHgap(10);
gridPane.setVgap(5);
int row = 0;
Label infoLabel = new Label("Configure Columns");
GridPane.setConstraints(infoLabel, 0, row);
gridPane.getChildren().add(infoLabel);
GridPane.setConstraints(getCloseButton(), 1, row);
gridPane.getChildren().add(getCloseButton());
row++;
GridPane.setConstraints(getColumnsTableView(), 0, row, 1, 6);
gridPane.getChildren().add(getColumnsTableView());
row += 2;
GridPane.setConstraints(getOrderUpButton(), 1, row);
gridPane.getChildren().add(getOrderUpButton());
row++;
GridPane.setConstraints(getOrderDownButton(), 1, row);
gridPane.getChildren().add(getOrderDownButton());
row++;
GridPane.setConstraints(getResetColumnsButton(), 1, row);
gridPane.getChildren().add(getResetColumnsButton());
ColumnConstraints cc1 = new ColumnConstraints();
cc1.setHgrow(Priority.ALWAYS);
ColumnConstraints cc2 = new ColumnConstraints();
cc2.setHgrow(Priority.NEVER);
cc2.setPrefWidth(60);
cc2.setMinWidth(60);
gridPane.getColumnConstraints().addAll(cc1, cc2);
VBox.setVgrow(gridPane, Priority.ALWAYS);
getChildren().add(gridPane);
}
private TableView<TableColumn> getColumnsTableView()
{
if(mColumnsTableView == null)
{
mColumnsTableView = new TableView(mTableView.getColumns());
mColumnsTableView.setEditable(true);
TableColumn textColumn = new TableColumn("Column");
textColumn.setEditable(false);
textColumn.setCellValueFactory(new PropertyValueFactory<>("text"));
mColumnsTableView.getColumns().add(textColumn);
TableColumn<TableColumn,Boolean> visibleColumn = new TableColumn("Visible");
visibleColumn.setCellValueFactory(new PropertyValueFactory<>("visible"));
visibleColumn.setCellFactory(CheckBoxTableCell.forTableColumn(visibleColumn));
visibleColumn.setEditable(true);
mColumnsTableView.getColumns().add(visibleColumn);
mColumnsTableView.getSelectionModel().selectedItemProperty().addListener(new ChangeListener()
{
@Override
public void changed(ObservableValue observable, Object oldValue, Object newValue)
{
//If monitored table has only a single column keep both buttons disabled.
if(mTableView.getColumns().size() <= 1)
{
getOrderDownButton().setDisable(true);
getOrderUpButton().setDisable(true);
return;
}
if(newValue == null)
{
getOrderDownButton().setDisable(true);
getOrderUpButton().setDisable(true);
}
else
{
if(mColumnsTableView.getSelectionModel().isSelected(0))
{
getOrderDownButton().setDisable(false);
getOrderUpButton().setDisable(true);
}
else if(mColumnsTableView.getSelectionModel().isSelected(mTableView.getColumns().size() - 1))
{
getOrderDownButton().setDisable(true);
getOrderUpButton().setDisable(false);
}
else
{
getOrderDownButton().setDisable(false);
getOrderUpButton().setDisable(false);
}
}
}
});
}
return mColumnsTableView;
}
/**
* Close button
* @return button
*/
private Button getCloseButton()
{
if(mCloseButton == null)
{
mCloseButton = new Button("Close");
mCloseButton.setMaxWidth(Double.MAX_VALUE);
mCloseButton.setOnAction(event -> closedProperty().set(true));
}
return mCloseButton;
}
private Button getOrderUpButton()
{
if(mOrderUpButton == null)
{
mOrderUpButton = new Button("Up");
mOrderUpButton.setDisable(true);
mOrderUpButton.setMaxWidth(Double.MAX_VALUE);
mOrderUpButton.setOnAction(event -> {
if(mColumnsTableView.getSelectionModel().selectedItemProperty().get() != null)
{
TableColumn selected = (TableColumn)mColumnsTableView.getSelectionModel().getSelectedItem();
int currentIndex = mTableView.getColumns().indexOf(selected);
mTableView.getColumns().remove(selected);
mTableView.getColumns().add(currentIndex - 1, selected);
//Reselect the column since it moved and lost the selection
mColumnsTableView.getSelectionModel().select(selected);
}
});
}
return mOrderUpButton;
}
private Button getOrderDownButton()
{
if(mOrderDownButton == null)
{
mOrderDownButton = new Button("Down");
mOrderDownButton.setDisable(true);
mOrderDownButton.setMaxWidth(Double.MAX_VALUE);
mOrderDownButton.setOnAction(event -> {
if(mColumnsTableView.getSelectionModel().selectedItemProperty().get() != null)
{
TableColumn selected = (TableColumn)mColumnsTableView.getSelectionModel().getSelectedItem();
int currentIndex = mTableView.getColumns().indexOf(selected);
mTableView.getColumns().remove(selected);
mTableView.getColumns().add(currentIndex + 1, selected);
//Reselect the column since it moved and lost the selection
mColumnsTableView.getSelectionModel().select(selected);
}
});
}
return mOrderDownButton;
}
/**
* Resets the columns to default state.
* @return button to reset
*/
private Button getResetColumnsButton()
{
if(mResetColumnsButton == null)
{
mResetColumnsButton = new Button("Reset");
mResetColumnsButton.setMaxWidth(Double.MAX_VALUE);
mResetColumnsButton.setOnAction(event -> mTableViewColumnController.reset());
}
return mResetColumnsButton;
}
/**
* Observable property to monitor for when the user clicks the close button.
*/
public BooleanProperty closedProperty()
{
return mClosed;
}
}

View File

@@ -0,0 +1,126 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2024 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.gui.javafx.table;
import java.util.ArrayList;
import java.util.List;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
/**
* Captures the state of table columns.
*/
public class TableState
{
private List<ColumnState> mColumns = new ArrayList<>();
private List<SortedColumn> mSort = new ArrayList<>();
/**
* Constructs an instance
*/
public TableState()
{
}
/**
* Utility method to create a table state from a list of table columns
* @param columns to capture state
* @return table state instance
*/
public static TableState create(TableView tableView)
{
TableState tableState = new TableState();
List<TableColumn> columns = tableView.getColumns();
for(TableColumn column: columns)
{
if(column.getId() == null || column.getId().isEmpty())
{
throw new IllegalArgumentException("TableColumns must have a non-null, non-empty ID value.");
}
tableState.addColumn(new ColumnState(column.getId(), column.isVisible(), (int)column.getWidth()));
}
List<TableColumn> sortedColumns = tableView.getSortOrder();
for(TableColumn sortedColumn: sortedColumns)
{
if(sortedColumn.getId() == null || sortedColumn.getId().isEmpty())
{
throw new IllegalArgumentException("TableColumns must have a non-null, non-empty ID value.");
}
tableState.addSort(new SortedColumn(sortedColumn.getId(), sortedColumn.getSortType()));
}
return tableState;
}
/**
* Column states.
* @return column states
*/
public List<ColumnState> getColumns()
{
return mColumns;
}
/**
* Adds a column state
* @param columnState to add
*/
public void addColumn(ColumnState columnState)
{
mColumns.add(columnState);
}
/**
* List of sorted columns
* @return sorted columns
*/
public List<SortedColumn> getSort()
{
return mSort;
}
/**
* Add the sorted column
* @param sortedColumn to add
*/
public void addSort(SortedColumn sortedColumn)
{
mSort.add(sortedColumn);
}
/**
* Column state. Note: arg/variable names are abbreviated for brevity in json representation.
* @param id for the column
* @param v visible status
* @param w width of the column
*/
public record ColumnState(String id, boolean v, int w) {};
/**
* Sorted column
* @param id of the column
* @param sortType ascending or descending
*/
public record SortedColumn(String id, TableColumn.SortType sortType) {};
}

View File

@@ -0,0 +1,241 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2024 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.gui.javafx.table;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.util.ArrayList;
import java.util.List;
import java.util.prefs.Preferences;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.collections.ListChangeListener;
import javafx.collections.ObservableList;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Utility to monitor a JavaFX TableView's column ordering, sizes and visibiltiy and restore those settings on startup.
*/
public class TableViewColumnController implements ListChangeListener
{
private static final Logger LOG = LoggerFactory.getLogger(TableViewColumnController.class);
private final Preferences mPreferences = Preferences.userNodeForPackage(TableViewColumnController.class);
private static final String PREFERENCE_KEY_TABLE_STATE = ".table.state";
private String mPreferencePrefix;
private TableView mTableView;
private TableState mDefaultTableState;
private ColumnVisibilityMonitor mVisibilityMonitor = new ColumnVisibilityMonitor();
private ColumnWidthMonitor mWidthMonitor = new ColumnWidthMonitor();
/**
* Constructs an instance
* @param tableView to be monitor and control the column settings
* @param preferencePrefix to use for storing the table state in preferences, must be non-null/non-empty/unique.
*/
public TableViewColumnController(TableView tableView, String preferencePrefix)
{
if(preferencePrefix == null || preferencePrefix.isEmpty())
{
throw new IllegalArgumentException("Preference prefix [" + preferencePrefix + "] must be non-null & non-empty");
}
mTableView = tableView;
mPreferencePrefix = preferencePrefix;
mDefaultTableState = TableState.create(mTableView);
TableState storedTableState = getStoredTableState();
if(storedTableState != null)
{
apply(storedTableState);
}
//Register listeners on the table and the individual columns
mTableView.getColumns().addListener(this);
mTableView.getSortOrder().addListener(this);
ObservableList<TableColumn> columns = mTableView.getColumns();
for(TableColumn tableColumn: columns)
{
tableColumn.visibleProperty().addListener(mVisibilityMonitor);
tableColumn.widthProperty().addListener(mWidthMonitor);
}
}
/**
* Resets the table columns to the default state
*/
public void reset()
{
apply(mDefaultTableState);
//Clear the preference since we're at default state
mPreferences.put(mPreferencePrefix + PREFERENCE_KEY_TABLE_STATE, null);
}
/**
* Applies the table state to the currently monitored table view.
* @param tableState to apply.
*/
private void apply(TableState tableState)
{
if(tableState != null)
{
if(tableState.getColumns().size() != mTableView.getColumns().size())
{
LOG.error("Unable to restore tableview column state - column size mismatch");
return;
}
//Remove the columns from the table and re-add them according to the stored ordering.
List<TableColumn> columns = new ArrayList<>(mTableView.getColumns());
mTableView.getColumns().clear();
for(TableState.ColumnState columnState: tableState.getColumns())
{
TableColumn match = null;
for(TableColumn tableColumn: columns)
{
if(tableColumn.getId() != null && tableColumn.getId().equals(columnState.id()))
{
match = tableColumn;
break;
}
}
if(match != null)
{
match.setVisible(columnState.v());
match.setPrefWidth(columnState.w());
mTableView.getColumns().add(match);
}
else
{
LOG.warn("Unable to find matching table column for id [" + columnState.id() + "]");
}
}
//Clear and restore the sort ordering
mTableView.getSortOrder().clear();
for(TableState.SortedColumn sortedColumn: tableState.getSort())
{
List<TableColumn> tableColumns = mTableView.getColumns();
for(TableColumn column: tableColumns)
{
if(sortedColumn.id().equals(column.getId()))
{
column.setSortType(sortedColumn.sortType());
mTableView.getSortOrder().add(column);
break;
}
}
}
}
}
/**
* Stores current table state
*/
private void storeTableState()
{
TableState tableState = TableState.create(mTableView);
ObjectMapper objectMapper = new ObjectMapper();
try
{
String json = objectMapper.writeValueAsString(tableState);
mPreferences.put(mPreferencePrefix + PREFERENCE_KEY_TABLE_STATE, json);
}
catch(Exception e)
{
LOG.error("Error creating table state json for table [" + mPreferencePrefix + "]", e);
}
}
/**
* Get the stored table state
* @return stored table state or null if this is the first usage or the state has been reset to default.
*/
private TableState getStoredTableState()
{
String json = mPreferences.get(mPreferencePrefix + PREFERENCE_KEY_TABLE_STATE, null);
if(json != null)
{
ObjectMapper objectMapper = new ObjectMapper();
try
{
return objectMapper.readValue(json.getBytes(), TableState.class);
}
catch(Exception e)
{
LOG.error("Error deserializing table state from json [" + json + "] " + e.getLocalizedMessage());
}
}
return null;
}
/**
* Implements the ListChangeListener interface to monitor the table columns for ordering changes.
* @param c an object representing the change that was done
*/
@Override
public void onChanged(Change c)
{
boolean changed = false;
while(c.next())
{
changed = true;
}
if(changed)
{
storeTableState();
}
}
/**
* Column visibility monitor
*/
private class ColumnVisibilityMonitor implements ChangeListener<Boolean>
{
@Override
public void changed(ObservableValue<? extends Boolean> observable, Boolean oldValue, Boolean newValue)
{
storeTableState();
}
}
/**
* Column width monitor
*/
private class ColumnWidthMonitor implements ChangeListener<Number>
{
@Override
public void changed(ObservableValue<? extends Number> observable, Number oldValue, Number newValue)
{
storeTableState();
}
}
}

View File

@@ -1,6 +1,6 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2022 Dennis Sheirer
* Copyright (C) 2014-2023 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
@@ -30,11 +30,14 @@ import io.github.dsheirer.gui.playlist.radioreference.RadioReferenceEditor;
import io.github.dsheirer.gui.playlist.streaming.StreamingEditor;
import io.github.dsheirer.gui.preference.PreferenceEditorType;
import io.github.dsheirer.gui.preference.ViewUserPreferenceEditorRequest;
import io.github.dsheirer.playlist.PlaylistManager;
import io.github.dsheirer.preference.UserPreferences;
import io.github.dsheirer.source.tuner.manager.TunerManager;
import io.github.dsheirer.util.ThreadPool;
import io.github.dsheirer.util.TimeStamp;
import jakarta.annotation.PostConstruct;
import jakarta.annotation.Resource;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.nio.file.Path;
import javafx.application.Platform;
import javafx.embed.swing.SwingFXUtils;
import javafx.scene.control.Menu;
@@ -54,9 +57,6 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.nio.file.Path;
/**
* JavaFX playlist, channels, aliases, streaming and radioreference.com import editor
@@ -64,9 +64,17 @@ import java.nio.file.Path;
public class PlaylistEditor extends BorderPane
{
private static final Logger mLog = LoggerFactory.getLogger(PlaylistEditor.class);
private PlaylistManager mPlaylistManager;
private TunerManager mTunerManager;
@Resource
private AliasEditor mAliasEditor;
@Resource
private ChannelEditor mChannelEditor;
@Resource
private PlaylistManagerEditor mPlaylistManagerEditor;
@Resource
private RadioReferenceEditor mRadioReferenceEditor;
@Resource
private StreamingEditor mStreamingEditor;
@Resource
private UserPreferences mUserPreferences;
private MenuBar mMenuBar;
private TabPane mTabPane;
@@ -75,21 +83,17 @@ public class PlaylistEditor extends BorderPane
private Tab mAliasesTab;
private Tab mRadioReferenceTab;
private Tab mStreamingTab;
private AliasEditor mAliasEditor;
private ChannelEditor mChannelEditor;
/**
* Constructs an instance
* @param playlistManager for alias and channel models
* @param tunerManager for tuners
* @param userPreferences for settings
*/
public PlaylistEditor(PlaylistManager playlistManager, TunerManager tunerManager, UserPreferences userPreferences)
public PlaylistEditor()
{
mPlaylistManager = playlistManager;
mTunerManager = tunerManager;
mUserPreferences = userPreferences;
}
@PostConstruct
public void postConstruct()
{
//Throw a new runnable back onto the FX thread to lazy load the editor content after the editor has been
//constructed and shown.
Platform.runLater(() -> {
@@ -221,11 +225,6 @@ public class PlaylistEditor extends BorderPane
private AliasEditor getAliasEditor()
{
if(mAliasEditor == null)
{
mAliasEditor = new AliasEditor(mPlaylistManager, mUserPreferences);
}
return mAliasEditor;
}
@@ -242,11 +241,6 @@ public class PlaylistEditor extends BorderPane
private ChannelEditor getChannelEditor()
{
if(mChannelEditor == null)
{
mChannelEditor = new ChannelEditor(mPlaylistManager, mTunerManager, mUserPreferences);
}
return mChannelEditor;
}
@@ -255,7 +249,7 @@ public class PlaylistEditor extends BorderPane
if(mPlaylistsTab == null)
{
mPlaylistsTab = new Tab("Playlists");
mPlaylistsTab.setContent(new PlaylistManagerEditor(mPlaylistManager, mUserPreferences));
mPlaylistsTab.setContent(mPlaylistManagerEditor);
}
return mPlaylistsTab;
@@ -266,7 +260,15 @@ public class PlaylistEditor extends BorderPane
if(mRadioReferenceTab == null)
{
mRadioReferenceTab = new Tab("Radio Reference");
mRadioReferenceTab.setContent(new RadioReferenceEditor(mUserPreferences, mPlaylistManager));
mRadioReferenceTab.setContent(mRadioReferenceEditor);
//Only login once/if the user selects the radio reference tab
mRadioReferenceTab.selectedProperty().addListener((observable, oldValue, selected) -> {
if(selected)
{
mRadioReferenceEditor.login();
}
});
}
return mRadioReferenceTab;
@@ -277,7 +279,7 @@ public class PlaylistEditor extends BorderPane
if(mStreamingTab == null)
{
mStreamingTab = new Tab("Streaming");
mStreamingTab.setContent(new StreamingEditor(mPlaylistManager));
mStreamingTab.setContent(mStreamingEditor);
}
return mStreamingTab;

View File

@@ -1,6 +1,6 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2022 Dennis Sheirer
* Copyright (C) 2014-2023 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
@@ -19,13 +19,12 @@
package io.github.dsheirer.gui.playlist;
import io.github.dsheirer.alias.AliasModel;
import io.github.dsheirer.gui.JavaFxWindowManager;
import io.github.dsheirer.icon.IconModel;
import io.github.dsheirer.module.log.EventLogManager;
import io.github.dsheirer.playlist.PlaylistManager;
import io.github.dsheirer.preference.UserPreferences;
import io.github.dsheirer.source.tuner.manager.TunerManager;
import jakarta.annotation.PostConstruct;
import javafx.application.Application;
import javafx.scene.Parent;
import javafx.scene.Scene;
@@ -45,14 +44,16 @@ public class PlaylistEditorApplication extends Application
public PlaylistEditorApplication()
{
AliasModel aliasModel = new AliasModel();
EventLogManager eventLogManager = new EventLogManager(aliasModel, mUserPreferences);
mTunerManager = new TunerManager(mUserPreferences);
mTunerManager.start();
mPlaylistManager = new PlaylistManager(mUserPreferences, mTunerManager, aliasModel, eventLogManager, new IconModel());
}
mPlaylistManager.init();
mJavaFxWindowManager = new JavaFxWindowManager(mUserPreferences, mTunerManager, mPlaylistManager);
@PostConstruct
public void postConstruct()
{
EventLogManager eventLogManager = new EventLogManager();
mTunerManager = new TunerManager();
mTunerManager.start();
mPlaylistManager = new PlaylistManager();
mJavaFxWindowManager = new JavaFxWindowManager();
}
@Override
@@ -69,7 +70,7 @@ public class PlaylistEditorApplication extends Application
{
if(mPlaylistEditor == null)
{
mPlaylistEditor = new PlaylistEditor(mPlaylistManager, mTunerManager, mUserPreferences);
mPlaylistEditor = new PlaylistEditor();
}
return mPlaylistEditor;

View File

@@ -23,7 +23,9 @@ import com.google.common.collect.Ordering;
import io.github.dsheirer.alias.Alias;
import io.github.dsheirer.gui.playlist.Editor;
import io.github.dsheirer.icon.Icon;
import io.github.dsheirer.playlist.PlaylistManager;
import io.github.dsheirer.icon.IconModel;
import jakarta.annotation.PostConstruct;
import jakarta.annotation.Resource;
import java.util.List;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.ReadOnlyBooleanProperty;
@@ -52,7 +54,8 @@ import org.controlsfx.control.ToggleSwitch;
*/
public class AliasBulkEditor extends Editor<List<Alias>>
{
private PlaylistManager mPlaylistManager;
@Resource
private IconModel mIconModel;
private Label mEditingLabel;
private ColorPicker mColorPicker;
private Button mApplyColorButton;
@@ -65,20 +68,19 @@ public class AliasBulkEditor extends Editor<List<Alias>>
private ToggleSwitch mRecordToggleSwitch;
private Button mApplyRecordButton;
private BooleanProperty mChangeInProgressProperty;
private BooleanProperty mChangeInProgressProperty = new SimpleBooleanProperty();
private ReadOnlyBooleanProperty mChangeInProgressROProperty;
/**
* Constructs an instance
*
* @param playlistManager for accessing icon manager
*/
public AliasBulkEditor(PlaylistManager playlistManager)
public AliasBulkEditor()
{
mPlaylistManager = playlistManager;
mChangeInProgressProperty = new SimpleBooleanProperty();
}
@PostConstruct
public void postConstruct()
{
GridPane gridPane = new GridPane();
gridPane.setPadding(new Insets(10, 10, 10, 10));
gridPane.setHgap(10);
@@ -280,7 +282,7 @@ public class AliasBulkEditor extends Editor<List<Alias>>
{
mIconNodeComboBox = new ComboBox<>();
mIconNodeComboBox.setMaxWidth(Double.MAX_VALUE);
mIconNodeComboBox.setItems(new SortedList(mPlaylistManager.getIconModel().iconsProperty(), Ordering.natural()));
mIconNodeComboBox.setItems(new SortedList(mIconModel.iconsProperty(), Ordering.natural()));
mIconNodeComboBox.setCellFactory(new IconCellFactory());
mIconNodeComboBox.getSelectionModel().selectedItemProperty()
.addListener((observable, oldValue, newValue) ->
@@ -298,25 +300,20 @@ public class AliasBulkEditor extends Editor<List<Alias>>
{
mApplyIconButton = new Button("Apply");
mApplyIconButton.setDisable(true);
mApplyIconButton.setOnAction(new EventHandler<ActionEvent>()
{
@Override
public void handle(ActionEvent event)
mApplyIconButton.setOnAction(event -> {
startChange();
Icon icon = getIconNodeComboBox().getSelectionModel().getSelectedItem();
if(icon != null)
{
startChange();
Icon icon = getIconNodeComboBox().getSelectionModel().getSelectedItem();
if(icon != null)
for(Alias alias : getItem())
{
for(Alias alias : getItem())
{
alias.setIconName(icon.getName());
}
alias.setIconName(icon.getName());
}
endChange();
}
endChange();
});
}

View File

@@ -26,8 +26,9 @@ import io.github.dsheirer.alias.AliasModel;
import io.github.dsheirer.gui.control.MaxLengthUnaryOperator;
import io.github.dsheirer.gui.playlist.Editor;
import io.github.dsheirer.icon.Icon;
import io.github.dsheirer.playlist.PlaylistManager;
import io.github.dsheirer.preference.UserPreferences;
import io.github.dsheirer.icon.IconModel;
import jakarta.annotation.PostConstruct;
import jakarta.annotation.Resource;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
@@ -80,9 +81,13 @@ public class AliasConfigurationEditor extends SplitPane
{
private static final Logger mLog = LoggerFactory.getLogger(AliasConfigurationEditor.class);
private PlaylistManager mPlaylistManager;
private UserPreferences mUserPreferences;
@Resource
private AliasModel mAliasModel;
@Resource
private IconModel mIconModel;
@Resource
private AliasItemEditor mAliasItemEditor;
@Resource
private AliasBulkEditor mAliasBulkEditor;
private Editor mCurrentEditor;
private TableView<Alias> mAliasTableView;
@@ -100,11 +105,13 @@ public class AliasConfigurationEditor extends SplitPane
private SortedList<Alias> mAliasSortedList;
private AliasPredicate mAliasPredicate;
public AliasConfigurationEditor(PlaylistManager playlistManager, UserPreferences userPreferences)
public AliasConfigurationEditor()
{
mPlaylistManager = playlistManager;
mUserPreferences = userPreferences;
}
@PostConstruct
public void postConstruct()
{
VBox leftBox = new VBox();
VBox.setVgrow(getAliasTableView(), Priority.ALWAYS);
leftBox.getChildren().addAll(getSearchAndListSelectionBox(), getAliasTableView());
@@ -209,21 +216,11 @@ public class AliasConfigurationEditor extends SplitPane
private AliasItemEditor getAliasItemEditor()
{
if(mAliasItemEditor == null)
{
mAliasItemEditor = new AliasItemEditor(mPlaylistManager, mUserPreferences);
}
return mAliasItemEditor;
}
private AliasBulkEditor getAliasBulkEditor()
{
if(mAliasBulkEditor == null)
{
mAliasBulkEditor = new AliasBulkEditor(mPlaylistManager);
}
return mAliasBulkEditor;
}
@@ -288,7 +285,7 @@ public class AliasConfigurationEditor extends SplitPane
{
if(mAliasListNameComboBox == null)
{
mAliasListNameComboBox = new ComboBox<>(mPlaylistManager.getAliasModel().aliasListNames());
mAliasListNameComboBox = new ComboBox<>(mAliasModel.aliasListNames());
mAliasListNameComboBox.getSelectionModel().selectedItemProperty()
.addListener((ObservableValue<? extends String> observable, String oldValue, String newValue) ->
{
@@ -337,7 +334,7 @@ public class AliasConfigurationEditor extends SplitPane
if(name != null && !name.isEmpty())
{
name = name.trim();
mPlaylistManager.getAliasModel().addAliasList(name);
mAliasModel.addAliasList(name);
getAliasListNameComboBox().getSelectionModel().select(name);
}
});
@@ -436,7 +433,7 @@ public class AliasConfigurationEditor extends SplitPane
{
if(mAliasFilteredList == null)
{
mAliasFilteredList = new FilteredList<>(mPlaylistManager.getAliasModel().aliasList(), getAliasPredicate());
mAliasFilteredList = new FilteredList<>(mAliasModel.aliasList(), getAliasPredicate());
}
return mAliasFilteredList;
@@ -506,7 +503,7 @@ public class AliasConfigurationEditor extends SplitPane
{
Alias alias = new Alias("New Alias");
alias.setAliasListName(getAliasListNameComboBox().getSelectionModel().getSelectedItem());
mPlaylistManager.getAliasModel().addAlias(alias);
mAliasModel.addAlias(alias);
//Queue a select alias action to allow table to update filter predicate and display the alias
Platform.runLater(() ->
@@ -544,7 +541,7 @@ public class AliasConfigurationEditor extends SplitPane
if(result.get() == ButtonType.YES)
{
List<Alias> selectedAliases = new ArrayList<>(getAliasTableView().getSelectionModel().getSelectedItems());
mPlaylistManager.getAliasModel().removeAliases(selectedAliases);
mAliasModel.removeAliases(selectedAliases);
}
});
}
@@ -563,7 +560,7 @@ public class AliasConfigurationEditor extends SplitPane
{
Alias original = getAliasTableView().getSelectionModel().getSelectedItem();
Alias copy = AliasFactory.shallowCopyOf(original);
mPlaylistManager.getAliasModel().addAlias(copy);
mAliasModel.addAlias(copy);
getAliasTableView().getSelectionModel().clearSelection();
getAliasTableView().getSelectionModel().select(copy);
getAliasTableView().scrollTo(copy);
@@ -587,7 +584,7 @@ public class AliasConfigurationEditor extends SplitPane
emptyItem.setDisable(true);
mMoveToAliasButton.getItems().addAll(emptyItem, new SeparatorMenuItem());
List<String> aliasLists = mPlaylistManager.getAliasModel().getListNames();
List<String> aliasLists = mAliasModel.getListNames();
for(String aliasList : aliasLists)
{
@@ -614,11 +611,11 @@ public class AliasConfigurationEditor extends SplitPane
List<Alias> selectedAliases = new ArrayList<>(getAliasTableView().getSelectionModel().getSelectedItems());
for(Alias selected : selectedAliases)
{
AliasList existing = mPlaylistManager.getAliasModel().getAliasList(selected.getAliasListName());
AliasList existing = mAliasModel.getAliasList(selected.getAliasListName());
existing.removeAlias(selected);
selected.setAliasListName(getText());
AliasList moveToList = mPlaylistManager.getAliasModel().getAliasList(selected.getAliasListName());
AliasList moveToList = mAliasModel.getAliasList(selected.getAliasListName());
moveToList.addAlias(selected);
}
});
@@ -830,7 +827,7 @@ public class AliasConfigurationEditor extends SplitPane
if(alias != null)
{
Icon icon = mPlaylistManager.getIconModel().getIcon(alias.getIconName());
Icon icon = mIconModel.getIcon(alias.getIconName());
if(icon != null && icon.getFxImage() != null)
{

View File

@@ -1,6 +1,6 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2020 Dennis Sheirer
* Copyright (C) 2014-2023 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
@@ -20,9 +20,11 @@
package io.github.dsheirer.gui.playlist.alias;
import io.github.dsheirer.alias.Alias;
import io.github.dsheirer.alias.AliasModel;
import io.github.dsheirer.alias.id.AliasID;
import io.github.dsheirer.playlist.PlaylistManager;
import io.github.dsheirer.preference.UserPreferences;
import jakarta.annotation.PostConstruct;
import jakarta.annotation.Resource;
import javafx.beans.InvalidationListener;
import javafx.geometry.Insets;
import javafx.scene.control.Tab;
import javafx.scene.control.TabPane;
@@ -32,29 +34,49 @@ import javafx.scene.control.TabPane;
*/
public class AliasEditor extends TabPane
{
private PlaylistManager mPlaylistManager;
private UserPreferences mUserPreferences;
@Resource
private AliasConfigurationEditor mAliasConfigurationEditor;
@Resource
private AliasModel mAliasModel;
@Resource
private AliasViewByIdentifierEditor mAliasViewByIdentifierEditor;
@Resource
private AliasViewByRecordingEditor mAliasViewByRecordingEditor;
private Tab mAliasConfigurationTab;
private Tab mAliasIdentifierTab;
private Tab mAliasRecordingTab;
private boolean mAliasListInvalidated = false;
/**
* Constructs an instance
* @param playlistManager for alias model access
* @param userPreferences for settings
*/
public AliasEditor(PlaylistManager playlistManager, UserPreferences userPreferences)
public AliasEditor()
{
mPlaylistManager = playlistManager;
mUserPreferences = userPreferences;
}
@PostConstruct
public void postConstruct()
{
setPadding(new Insets(4,0,0,0));
setTabClosingPolicy(TabPane.TabClosingPolicy.UNAVAILABLE);
Tab viewByTab = new Tab("View By:");
viewByTab.setDisable(true);
getTabs().addAll(viewByTab, getAliasConfigurationTab(), getAliasIdentifierTab(), getAliasRecordingTab());
//Note if aliases change since we're using a static snapshot
mAliasModel.aliasList().addListener((InvalidationListener)observable -> mAliasListInvalidated = true);
//Only update the alias list when this tab is selected to avoid many slow updates in the background
getAliasIdentifierTab().selectedProperty().addListener(observable ->
{
if (mAliasListInvalidated)
{
getAliasViewByIdentifierEditor().updateList();
mAliasListInvalidated = false;
}
});
}
/**
@@ -101,11 +123,6 @@ public class AliasEditor extends TabPane
private AliasConfigurationEditor getAliasConfigurationEditor()
{
if(mAliasConfigurationEditor == null)
{
mAliasConfigurationEditor = new AliasConfigurationEditor(mPlaylistManager, mUserPreferences);
}
return mAliasConfigurationEditor;
}
@@ -122,20 +139,20 @@ public class AliasEditor extends TabPane
private AliasViewByIdentifierEditor getAliasViewByIdentifierEditor()
{
if(mAliasViewByIdentifierEditor == null)
{
mAliasViewByIdentifierEditor = new AliasViewByIdentifierEditor(mPlaylistManager, getAliasIdentifierTab().selectedProperty());
}
return mAliasViewByIdentifierEditor;
}
private AliasViewByRecordingEditor getAliasViewByRecordingEditor()
{
return mAliasViewByRecordingEditor;
}
private Tab getAliasRecordingTab()
{
if(mAliasRecordingTab == null)
{
mAliasRecordingTab = new Tab("Record");
mAliasRecordingTab.setContent(new AliasViewByRecordingEditor(mPlaylistManager));
mAliasRecordingTab.setContent(getAliasViewByRecordingEditor());
}
return mAliasRecordingTab;

View File

@@ -20,12 +20,12 @@
package io.github.dsheirer.gui.playlist.alias;
import com.google.common.collect.Ordering;
import com.google.common.eventbus.Subscribe;
import impl.org.controlsfx.autocompletion.AutoCompletionTextFieldBinding;
import impl.org.controlsfx.autocompletion.SuggestionProvider;
import io.github.dsheirer.alias.Alias;
import io.github.dsheirer.alias.AliasFactory;
import io.github.dsheirer.alias.AliasList;
import io.github.dsheirer.alias.AliasModel;
import io.github.dsheirer.alias.action.AliasAction;
import io.github.dsheirer.alias.action.AliasActionType;
import io.github.dsheirer.alias.action.beep.BeepAction;
@@ -48,6 +48,7 @@ import io.github.dsheirer.alias.id.talkgroup.Talkgroup;
import io.github.dsheirer.alias.id.talkgroup.TalkgroupFormatter;
import io.github.dsheirer.alias.id.talkgroup.TalkgroupRange;
import io.github.dsheirer.alias.id.tone.TonesID;
import io.github.dsheirer.audio.broadcast.BroadcastModel;
import io.github.dsheirer.audio.broadcast.ConfiguredBroadcast;
import io.github.dsheirer.eventbus.MyEventBus;
import io.github.dsheirer.gui.playlist.Editor;
@@ -58,11 +59,15 @@ import io.github.dsheirer.gui.playlist.alias.identifier.EmptyIdentifierEditor;
import io.github.dsheirer.gui.playlist.alias.identifier.IdentifierEditor;
import io.github.dsheirer.gui.playlist.alias.identifier.IdentifierEditorFactory;
import io.github.dsheirer.icon.Icon;
import io.github.dsheirer.playlist.PlaylistManager;
import io.github.dsheirer.icon.IconModel;
import io.github.dsheirer.preference.IPreferenceUpdateListener;
import io.github.dsheirer.preference.PreferenceType;
import io.github.dsheirer.preference.UserPreferences;
import io.github.dsheirer.preference.identifier.IntegerFormat;
import io.github.dsheirer.protocol.Protocol;
import jakarta.annotation.PostConstruct;
import jakarta.annotation.PreDestroy;
import jakarta.annotation.Resource;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -114,11 +119,17 @@ import org.slf4j.LoggerFactory;
/**
* Editor for configuring individual aliases
*/
public class AliasItemEditor extends Editor<Alias>
public class AliasItemEditor extends Editor<Alias> implements IPreferenceUpdateListener
{
private static final Logger mLog = LoggerFactory.getLogger(AliasItemEditor.class);
private PlaylistManager mPlaylistManager;
@Resource
private AliasModel mAliasModel;
@Resource
private BroadcastModel mBroadcastModel;
@Resource
private IconModel mIconModel;
@Resource
private UserPreferences mUserPreferences;
private EditorModificationListener mEditorModificationListener = new EditorModificationListener();
private IdentifierEditorModificationListener mIdentifierEditorModificationListener = new IdentifierEditorModificationListener();
@@ -161,15 +172,16 @@ public class AliasItemEditor extends Editor<Alias>
private EmptyActionEditor mEmptyActionEditor = new EmptyActionEditor();
private ActionEditor mActionEditor;
public AliasItemEditor(PlaylistManager playlistManager, UserPreferences userPreferences)
public AliasItemEditor()
{
mPlaylistManager = playlistManager;
mUserPreferences = userPreferences;
}
@PostConstruct
public void postConstruct()
{
mUserPreferences.addUpdateListener(this);
//Listen for changes to the stream configurations and refresh the stream lists
mPlaylistManager.getBroadcastModel().getConfiguredBroadcasts()
.addListener((ListChangeListener<ConfiguredBroadcast>)c -> updateStreamViews());
mBroadcastModel.getConfiguredBroadcasts().addListener((ListChangeListener<ConfiguredBroadcast>)c -> updateStreamViews());
MyEventBus.getGlobalEventBus().register(this);
@@ -192,8 +204,13 @@ public class AliasItemEditor extends Editor<Alias>
getChildren().add(hbox);
}
@PreDestroy
public void dispose()
{
mUserPreferences.removeUpdateListener(this);
}
@Subscribe
@Override
public void preferenceUpdated(PreferenceType preferenceType)
{
if(preferenceType == PreferenceType.TALKGROUP_FORMAT)
@@ -251,7 +268,7 @@ public class AliasItemEditor extends Editor<Alias>
String iconName = alias.getIconName();
if(iconName != null)
{
icon = mPlaylistManager.getIconModel().getIcon(iconName);
icon = mIconModel.getIcon(iconName);
}
getIconNodeComboBox().getSelectionModel().select(icon);
@@ -399,7 +416,7 @@ public class AliasItemEditor extends Editor<Alias>
}
}
AliasList aliasList = mPlaylistManager.getAliasModel().getAliasList(alias.getAliasListName());
AliasList aliasList = mAliasModel.getAliasList(alias.getAliasListName());
aliasList.updateAlias(alias);
//Reset the alias to refresh the editor.
@@ -409,12 +426,6 @@ public class AliasItemEditor extends Editor<Alias>
}
}
@Override
public void dispose()
{
MyEventBus.getGlobalEventBus().unregister(this);
}
private VBox getTitledPanesBox()
{
if(mTitledPanesBox == null)
@@ -819,7 +830,7 @@ public class AliasItemEditor extends Editor<Alias>
if(getItem() != null)
{
List<String> availableStreams = mPlaylistManager.getBroadcastModel().getBroadcastConfigurationNames();
List<String> availableStreams = mBroadcastModel.getBroadcastConfigurationNames();
Set<BroadcastChannel> selectedChannels = getItem().getBroadcastChannels();
@@ -1088,7 +1099,7 @@ public class AliasItemEditor extends Editor<Alias>
mIconNodeComboBox = new ComboBox<>();
mIconNodeComboBox.setMaxWidth(Double.MAX_VALUE);
mIconNodeComboBox.setDisable(true);
mIconNodeComboBox.setItems(new SortedList(mPlaylistManager.getIconModel().iconsProperty(), Ordering.natural()));
mIconNodeComboBox.setItems(new SortedList(mIconModel.iconsProperty(), Ordering.natural()));
mIconNodeComboBox.setCellFactory(new IconCellFactory());
mIconNodeComboBox.getSelectionModel().selectedItemProperty()
.addListener((observable, oldValue, newValue) -> modifiedProperty().set(true));
@@ -1103,14 +1114,14 @@ public class AliasItemEditor extends Editor<Alias>
private void refreshAutoCompleteBindings()
{
getGroupSuggestionProvider().clearSuggestions();
getGroupSuggestionProvider().addPossibleSuggestions(mPlaylistManager.getAliasModel().getGroupNames());
getGroupSuggestionProvider().addPossibleSuggestions(mAliasModel.getGroupNames());
}
private SuggestionProvider<String> getGroupSuggestionProvider()
{
if(mGroupSuggestionProvider == null)
{
mGroupSuggestionProvider = SuggestionProvider.create(mPlaylistManager.getAliasModel().getGroupNames());
mGroupSuggestionProvider = SuggestionProvider.create(mAliasModel.getGroupNames());
}
return mGroupSuggestionProvider;

View File

@@ -1,6 +1,6 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2020 Dennis Sheirer
* Copyright (C) 2014-2023 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
@@ -24,10 +24,13 @@ import io.github.dsheirer.alias.AliasModel;
import io.github.dsheirer.alias.id.AliasID;
import io.github.dsheirer.alias.id.AliasIDType;
import io.github.dsheirer.eventbus.MyEventBus;
import io.github.dsheirer.playlist.PlaylistManager;
import javafx.beans.InvalidationListener;
import jakarta.annotation.PostConstruct;
import jakarta.annotation.Resource;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.function.Predicate;
import javafx.beans.Observable;
import javafx.beans.property.ReadOnlyBooleanProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.collections.FXCollections;
@@ -50,35 +53,24 @@ import javafx.util.Callback;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.function.Predicate;
public class AliasViewByIdentifierEditor extends VBox
{
private static final Logger mLog = LoggerFactory.getLogger(AliasViewByIdentifierEditor.class);
private PlaylistManager mPlaylistManager;
@Resource
private AliasModel mAliasModel;
private ComboBox<String> mAliasListNameComboBox;
private ComboBox<AliasIDType> mAliasIDTypeComboBox;
private TableView<AliasAndIdentifier> mAliasAndIdentifierTableView;
private TableColumn<AliasAndIdentifier,String> mIdentifierColumn;
private Button mViewAliasButton;
private boolean aliasListInvalidated;
public AliasViewByIdentifierEditor(PlaylistManager playlistManager, ReadOnlyBooleanProperty tabSelectedProperty)
public AliasViewByIdentifierEditor()
{
aliasListInvalidated = false;
mPlaylistManager = playlistManager;
//Note if aliases change since we're using a static snapshot
mPlaylistManager.getAliasModel().aliasList().addListener((InvalidationListener)observable -> aliasListInvalidated = true);
//Only update the list when this tab is selected to avoid many slow updates in the background
tabSelectedProperty.addListener(observable -> {
if (aliasListInvalidated)
updateList();
});
}
@PostConstruct
public void postConstruct()
{
GridPane gridPane = new GridPane();
gridPane.setPadding(new Insets(10,10,10,10));
gridPane.setVgap(10);
@@ -115,14 +107,14 @@ public class AliasViewByIdentifierEditor extends VBox
updateList();
}
private void updateList()
public void updateList()
{
getAliasAndIdentifierTableView().getItems().clear();
String aliasList = getAliasListNameComboBox().getSelectionModel().getSelectedItem();
AliasIDType type = getAliasIDTypeComboBox().getSelectionModel().getSelectedItem();
List<Alias> aliases = mPlaylistManager.getAliasModel().getAliases(aliasList, type);
List<Alias> aliases = mAliasModel.getAliases(aliasList, type);
for(Alias alias: aliases)
{
@@ -136,8 +128,6 @@ public class AliasViewByIdentifierEditor extends VBox
}
getAliasAndIdentifierTableView().getSortOrder().setAll(mIdentifierColumn);
aliasListInvalidated = false;
}
/**
@@ -230,8 +220,7 @@ public class AliasViewByIdentifierEditor extends VBox
if(mAliasListNameComboBox == null)
{
Predicate<String> filterPredicate = s -> !s.contentEquals(AliasModel.NO_ALIAS_LIST);
FilteredList<String> filteredChannelList =
new FilteredList<>(mPlaylistManager.getAliasModel().aliasListNames(), filterPredicate);
FilteredList<String> filteredChannelList = new FilteredList<>(mAliasModel.aliasListNames(), filterPredicate);
mAliasListNameComboBox = new ComboBox<>(filteredChannelList);
mAliasListNameComboBox.setPadding(new Insets(0,10,0,0));
if(mAliasListNameComboBox.getItems().size() > 0)

View File

@@ -1,6 +1,6 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2020 Dennis Sheirer
* Copyright (C) 2014-2023 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
@@ -21,7 +21,11 @@ package io.github.dsheirer.gui.playlist.alias;
import io.github.dsheirer.alias.Alias;
import io.github.dsheirer.alias.AliasModel;
import io.github.dsheirer.playlist.PlaylistManager;
import jakarta.annotation.PostConstruct;
import jakarta.annotation.Resource;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Predicate;
import javafx.collections.ListChangeListener;
import javafx.collections.transformation.FilteredList;
import javafx.collections.transformation.SortedList;
@@ -44,15 +48,11 @@ import org.controlsfx.control.textfield.TextFields;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Predicate;
public class AliasViewByRecordingEditor extends VBox
{
private final static Logger mLog = LoggerFactory.getLogger(AliasViewByRecordingEditor.class);
private PlaylistManager mPlaylistManager;
@Resource
private AliasModel mAliasModel;
private ComboBox<String> mAliasListNameComboBox;
private HBox mTopBox;
private TextField mSearchField;
@@ -67,10 +67,13 @@ public class AliasViewByRecordingEditor extends VBox
private AvailablePredicate mNoRecordPredicate;
private SelectedPredicate mRecordPredicate;
public AliasViewByRecordingEditor(PlaylistManager playlistManager)
public AliasViewByRecordingEditor()
{
mPlaylistManager = playlistManager;
}
@PostConstruct
public void postConstruct()
{
VBox buttonBox = new VBox();
buttonBox.setMaxHeight(Double.MAX_VALUE);
buttonBox.setAlignment(Pos.CENTER);
@@ -105,8 +108,7 @@ public class AliasViewByRecordingEditor extends VBox
if(mAliasListNameComboBox == null)
{
Predicate<String> filterPredicate = s -> !s.contentEquals(AliasModel.NO_ALIAS_LIST);
FilteredList<String> filteredChannelList =
new FilteredList<>(mPlaylistManager.getAliasModel().aliasListNames(), filterPredicate);
FilteredList<String> filteredChannelList = new FilteredList<>(mAliasModel.aliasListNames(), filterPredicate);
mAliasListNameComboBox = new ComboBox<>(filteredChannelList);
mAliasListNameComboBox.setPadding(new Insets(0,10,0,0));
mAliasListNameComboBox.getSelectionModel().selectedItemProperty()
@@ -126,8 +128,7 @@ public class AliasViewByRecordingEditor extends VBox
{
if(mNoRecordFilteredList == null)
{
mNoRecordFilteredList = new FilteredList<>(mPlaylistManager.getAliasModel().aliasList(),
getNoRecordPredicate());
mNoRecordFilteredList = new FilteredList<>(mAliasModel.aliasList(), getNoRecordPredicate());
}
return mNoRecordFilteredList;
@@ -137,8 +138,7 @@ public class AliasViewByRecordingEditor extends VBox
{
if(mRecordFilteredList == null)
{
mRecordFilteredList = new FilteredList<>(mPlaylistManager.getAliasModel().aliasList(),
getRecordPredicate());
mRecordFilteredList = new FilteredList<>(mAliasModel.aliasList(), getRecordPredicate());
}
return mRecordFilteredList;

View File

@@ -32,14 +32,12 @@ import io.github.dsheirer.module.decode.config.DecodeConfiguration;
import io.github.dsheirer.module.decode.nbfm.DecodeConfigNBFM;
import io.github.dsheirer.module.log.EventLogType;
import io.github.dsheirer.module.log.config.EventLogConfiguration;
import io.github.dsheirer.playlist.PlaylistManager;
import io.github.dsheirer.preference.UserPreferences;
import io.github.dsheirer.preference.identifier.IntegerFormat;
import io.github.dsheirer.protocol.Protocol;
import io.github.dsheirer.record.RecorderType;
import io.github.dsheirer.record.config.RecordConfiguration;
import io.github.dsheirer.source.config.SourceConfiguration;
import io.github.dsheirer.source.tuner.manager.TunerManager;
import jakarta.annotation.PostConstruct;
import java.util.ArrayList;
import java.util.List;
import javafx.beans.value.ChangeListener;
@@ -87,14 +85,15 @@ public class AMConfigurationEditor extends ChannelConfigurationEditor
/**
* Constructs an instance
* @param playlistManager for playlists
* @param tunerManager for tuners
* @param userPreferences for preferences
*/
public AMConfigurationEditor(PlaylistManager playlistManager, TunerManager tunerManager,
UserPreferences userPreferences, IFilterProcessor filterProcessor)
public AMConfigurationEditor()
{
super(playlistManager, tunerManager, userPreferences, filterProcessor);
}
@PostConstruct
public void postConstruct()
{
super.postConstruct();
getTitledPanesBox().getChildren().add(getSourcePane());
getTitledPanesBox().getChildren().add(getDecoderPane());
getTitledPanesBox().getChildren().add(getEventLogPane());

View File

@@ -22,6 +22,7 @@ package io.github.dsheirer.gui.playlist.channel;
import io.github.dsheirer.alias.AliasModel;
import io.github.dsheirer.controller.channel.Channel;
import io.github.dsheirer.controller.channel.ChannelException;
import io.github.dsheirer.controller.channel.ChannelProcessingManager;
import io.github.dsheirer.eventbus.MyEventBus;
import io.github.dsheirer.gui.control.MaxLengthUnaryOperator;
import io.github.dsheirer.gui.playlist.Editor;
@@ -31,13 +32,13 @@ import io.github.dsheirer.module.decode.DecoderType;
import io.github.dsheirer.module.decode.config.AuxDecodeConfiguration;
import io.github.dsheirer.module.decode.config.DecodeConfiguration;
import io.github.dsheirer.module.log.config.EventLogConfiguration;
import io.github.dsheirer.playlist.PlaylistManager;
import io.github.dsheirer.preference.UserPreferences;
import io.github.dsheirer.record.config.RecordConfiguration;
import io.github.dsheirer.source.config.SourceConfigTuner;
import io.github.dsheirer.source.config.SourceConfiguration;
import io.github.dsheirer.source.tuner.manager.TunerManager;
import io.github.dsheirer.util.ThreadPool;
import jakarta.annotation.Resource;
import java.util.Optional;
import java.util.function.Predicate;
import javafx.application.Platform;
@@ -78,8 +79,13 @@ public abstract class ChannelConfigurationEditor extends Editor<Channel>
{
private final static Logger mLog = LoggerFactory.getLogger(ChannelConfigurationEditor.class);
private PlaylistManager mPlaylistManager;
@Resource
private AliasModel mAliasModel;
@Resource
private ChannelProcessingManager mChannelProcessingManager;
@Resource
protected TunerManager mTunerManager;
@Resource
protected UserPreferences mUserPreferences;
protected EditorModificationListener mEditorModificationListener = new EditorModificationListener();
private Button mPlayButton;
@@ -103,20 +109,20 @@ public abstract class ChannelConfigurationEditor extends Editor<Channel>
/**
* Constructs an instance
* @param playlistManager for playlists
* @param tunerManager for tuners
* @param userPreferences for preferences
*/
public ChannelConfigurationEditor(PlaylistManager playlistManager, TunerManager tunerManager,
UserPreferences userPreferences, IFilterProcessor filterProcessor)
public ChannelConfigurationEditor()
{
mPlaylistManager = playlistManager;
mTunerManager = tunerManager;
mUserPreferences = userPreferences;
mFilterProcessor = filterProcessor;
}
/**
* Spring post-construct setup that is common for all sub-class implementations.
*
* Note: this method is not annotated with @PostConstruct because the subclass method would mask it. So, we
* invoke this method from the subclass.
*/
public void postConstruct()
{
setMaxWidth(Double.MAX_VALUE);
HBox hbox = new HBox();
hbox.setSpacing(10);
hbox.setPadding(new Insets(10,10,10,10));
@@ -125,16 +131,16 @@ public abstract class ChannelConfigurationEditor extends Editor<Channel>
HBox.setHgrow(getButtonBox(), Priority.NEVER);
hbox.getChildren().addAll(getTextFieldPane(), getButtonBox());
VBox.setVgrow(getTitledPanesScrollPane(), Priority.ALWAYS);
getChildren().addAll(hbox, getTitledPanesScrollPane());
}
/**
* Provides subclass access to the playlist manager and related components
* Sets the filter processor to be controlled by this instance
* @param filterProcessor to be controlled.
*/
protected PlaylistManager getPlaylistManager()
public void setFilterProcessor(IFilterProcessor filterProcessor)
{
return mPlaylistManager;
mFilterProcessor = filterProcessor;
}
@Override
@@ -181,7 +187,7 @@ public abstract class ChannelConfigurationEditor extends Editor<Channel>
{
if(!getAliasListComboBox().getItems().contains(aliasListName))
{
mPlaylistManager.getAliasModel().addAliasList(aliasListName);
mAliasModel.addAliasList(aliasListName);
}
getAliasListComboBox().getSelectionModel().select(aliasListName);
@@ -370,7 +376,7 @@ public abstract class ChannelConfigurationEditor extends Editor<Channel>
ThreadPool.CACHED.execute(() -> {
try
{
mPlaylistManager.getChannelProcessingManager().start(getItem());
mChannelProcessingManager.start(getItem());
}
catch(ChannelException ce)
{
@@ -391,7 +397,7 @@ public abstract class ChannelConfigurationEditor extends Editor<Channel>
ThreadPool.CACHED.execute(() -> {
try
{
mPlaylistManager.getChannelProcessingManager().stop(getItem());
mChannelProcessingManager.stop(getItem());
}
catch(ChannelException ce)
{
@@ -623,7 +629,7 @@ public abstract class ChannelConfigurationEditor extends Editor<Channel>
{
Predicate<String> filterPredicate = s -> !s.contentEquals(AliasModel.NO_ALIAS_LIST);
FilteredList<String> filteredChannelList =
new FilteredList<>(mPlaylistManager.getAliasModel().aliasListNames(), filterPredicate);
new FilteredList<>(mAliasModel.aliasListNames(), filterPredicate);
mAliasListComboBox = new ComboBox<>(filteredChannelList);
mAliasListComboBox.setPrefWidth(150);
mAliasListComboBox.setDisable(true);
@@ -659,7 +665,7 @@ public abstract class ChannelConfigurationEditor extends Editor<Channel>
if(name != null && !name.isEmpty())
{
name = name.trim();
mPlaylistManager.getAliasModel().addAliasList(name);
mAliasModel.addAliasList(name);
getAliasListComboBox().getSelectionModel().select(name);
}
});
@@ -713,8 +719,8 @@ public abstract class ChannelConfigurationEditor extends Editor<Channel>
{
try
{
mPlaylistManager.getChannelProcessingManager().stop(getItem());
mPlaylistManager.getChannelProcessingManager().start(getItem());
mChannelProcessingManager.stop(getItem());
mChannelProcessingManager.start(getItem());
}
catch(ChannelException se)
{

View File

@@ -1,81 +0,0 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2023 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.gui.playlist.channel;
import io.github.dsheirer.module.decode.DecoderType;
import io.github.dsheirer.playlist.PlaylistManager;
import io.github.dsheirer.preference.UserPreferences;
import io.github.dsheirer.source.tuner.manager.TunerManager;
import java.util.ArrayList;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Provides access to channel configuration editors for various decoder types.
*/
public class ChannelConfigurationEditorFactory
{
private final static Logger mLog = LoggerFactory.getLogger(ChannelConfigurationEditorFactory.class);
private static List<DecoderType> mLoggedUnrecognizedTypes = new ArrayList<>();
/**
* Constructs an editor for the specified decoder type
* @param decoderType to create
* @param playlistManager for the editor
* @param tunerManager for tuners
* @param userPreferences for preferences
* @param filterProcessor to be notified to clear and restore any applied filters.
* @return constructed editor
*/
public static ChannelConfigurationEditor getEditor(DecoderType decoderType, PlaylistManager playlistManager,
TunerManager tunerManager, UserPreferences userPreferences,
IFilterProcessor filterProcessor)
{
switch(decoderType)
{
case AM:
return new AMConfigurationEditor(playlistManager, tunerManager, userPreferences, filterProcessor);
case DMR:
return new DMRConfigurationEditor(playlistManager, tunerManager, userPreferences, filterProcessor);
case NBFM:
return new NBFMConfigurationEditor(playlistManager, tunerManager, userPreferences, filterProcessor);
case LTR_NET:
return new LTRNetConfigurationEditor(playlistManager, tunerManager, userPreferences, filterProcessor);
case LTR:
return new LTRConfigurationEditor(playlistManager, tunerManager, userPreferences, filterProcessor);
case MPT1327:
return new MPT1327ConfigurationEditor(playlistManager, tunerManager, userPreferences, filterProcessor);
case PASSPORT:
return new PassportConfigurationEditor(playlistManager, tunerManager, userPreferences, filterProcessor);
case P25_PHASE1:
return new P25P1ConfigurationEditor(playlistManager, tunerManager, userPreferences, filterProcessor);
case P25_PHASE2:
return new P25P2ConfigurationEditor(playlistManager, tunerManager, userPreferences, filterProcessor);
default:
if(decoderType != null && !mLoggedUnrecognizedTypes.contains(decoderType))
{
mLog.warn("Can't create channel configuration editor - unrecognized decoder type: " + decoderType);
mLoggedUnrecognizedTypes.add(decoderType);
}
return null;
}
}
}

View File

@@ -21,11 +21,12 @@ package io.github.dsheirer.gui.playlist.channel;
import com.google.common.base.Joiner;
import io.github.dsheirer.controller.channel.Channel;
import io.github.dsheirer.controller.channel.ChannelModel;
import io.github.dsheirer.controller.channel.ChannelProcessingManager;
import io.github.dsheirer.module.decode.DecoderFactory;
import io.github.dsheirer.module.decode.DecoderType;
import io.github.dsheirer.playlist.PlaylistManager;
import io.github.dsheirer.preference.UserPreferences;
import io.github.dsheirer.source.tuner.manager.TunerManager;
import jakarta.annotation.PostConstruct;
import jakarta.annotation.Resource;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
@@ -75,9 +76,13 @@ import org.slf4j.LoggerFactory;
public class ChannelEditor extends SplitPane implements IFilterProcessor
{
private final static Logger mLog = LoggerFactory.getLogger(ChannelEditor.class);
private PlaylistManager mPlaylistManager;
private TunerManager mTunerManager;
private UserPreferences mUserPreferences;
@Resource
private ChannelModel mChannelModel;
@Resource
private ChannelProcessingManager mChannelProcessingManager;
@Resource
private List<ChannelConfigurationEditor> mChannelConfigurationEditors;
private TableView<Channel> mChannelTableView;
private Label mPlaceholderLabel;
private MenuButton mNewButton;
@@ -91,22 +96,32 @@ public class ChannelEditor extends SplitPane implements IFilterProcessor
private ToggleButton mAutoStartToggleButton;
private ToggleButton mPlayingToggleButton;
private ChannelConfigurationEditor mChannelConfigurationEditor;
private UnknownConfigurationEditor mUnknownConfigurationEditor;
private ChannelConfigurationEditor mUnknownConfigurationEditor;
private Map<DecoderType,ChannelConfigurationEditor> mChannelConfigurationEditorMap = new HashMap();
private FilteredList<Channel> mChannelFilteredList;
private ChannelListFilter mChannelListFilter = new ChannelListFilter();
/**
* Constructs an instance
* @param playlistManager containing playlists and channel configurations
*/
public ChannelEditor(PlaylistManager playlistManager, TunerManager tunerManager, UserPreferences userPreferences)
public ChannelEditor()
{
mPlaylistManager = playlistManager;
mTunerManager = tunerManager;
mUserPreferences = userPreferences;
mUnknownConfigurationEditor = new UnknownConfigurationEditor(mPlaylistManager, mTunerManager,
userPreferences, this);
}
@PostConstruct
public void postConstruct()
{
//Spring instantiates all editors so place them into a lookup map
for(ChannelConfigurationEditor editor: mChannelConfigurationEditors)
{
mChannelConfigurationEditorMap.put(editor.getDecoderType(), editor);
editor.setFilterProcessor(this);
if(editor instanceof UnknownConfigurationEditor unknown)
{
mUnknownConfigurationEditor = unknown;
}
}
HBox channelsBox = new HBox();
channelsBox.setSpacing(10.0);
@@ -202,17 +217,12 @@ public class ChannelEditor extends SplitPane implements IFilterProcessor
if(editor == null)
{
editor = ChannelConfigurationEditorFactory.getEditor(channelDecoderType, mPlaylistManager,
mTunerManager, mUserPreferences, this);
mLog.warn("Unable to find channel configuration editor for " + channelDecoderType);
if(editor != null)
for(Map.Entry<DecoderType,ChannelConfigurationEditor> entry: mChannelConfigurationEditorMap.entrySet())
{
mChannelConfigurationEditorMap.put(channelDecoderType, editor);
System.out.println("Type: " + entry.getKey() + " Editor: " + entry.getValue().getClass());
}
}
if(editor == null)
{
editor = mUnknownConfigurationEditor;
}
@@ -228,7 +238,7 @@ public class ChannelEditor extends SplitPane implements IFilterProcessor
{
Channel channel = new Channel();
channel.setDecodeConfiguration(DecoderFactory.getDecodeConfiguration(decoderType));
mPlaylistManager.getChannelModel().addChannel(channel);
mChannelModel.addChannel(channel);
getChannelTableView().getSelectionModel().select(channel);
getChannelTableView().scrollTo(channel);
}
@@ -498,7 +508,7 @@ public class ChannelEditor extends SplitPane implements IFilterProcessor
mChannelTableView.setPlaceholder(getPlaceholderLabel());
//Sorting and filtering for the table
mChannelFilteredList = new FilteredList<>(mPlaylistManager.getChannelModel().channelList(), mChannelListFilter);
mChannelFilteredList = new FilteredList<>(mChannelModel.channelList(), mChannelListFilter);
SortedList<Channel> sortedList = new SortedList<>(mChannelFilteredList);
sortedList.comparatorProperty().bind(mChannelTableView.comparatorProperty());
mChannelTableView.setItems(sortedList);
@@ -585,7 +595,7 @@ public class ChannelEditor extends SplitPane implements IFilterProcessor
{
try
{
mPlaylistManager.getChannelProcessingManager().stop(selected);
mChannelProcessingManager.stop(selected);
}
catch(Exception e)
{
@@ -593,7 +603,7 @@ public class ChannelEditor extends SplitPane implements IFilterProcessor
}
}
mPlaylistManager.getChannelModel().removeChannel(selected);
mChannelModel.removeChannel(selected);
}
}
});
@@ -612,7 +622,7 @@ public class ChannelEditor extends SplitPane implements IFilterProcessor
mCloneButton.setOnAction(event -> {
Channel selected = getChannelTableView().getSelectionModel().getSelectedItem();
Channel copy = selected.copyOf();
mPlaylistManager.getChannelModel().addChannel(copy);
mChannelModel.addChannel(copy);
getChannelTableView().getSelectionModel().select(copy);
});
}

View File

@@ -32,12 +32,10 @@ import io.github.dsheirer.module.decode.dmr.DecodeConfigDMR;
import io.github.dsheirer.module.decode.dmr.channel.TimeslotFrequency;
import io.github.dsheirer.module.log.EventLogType;
import io.github.dsheirer.module.log.config.EventLogConfiguration;
import io.github.dsheirer.playlist.PlaylistManager;
import io.github.dsheirer.preference.UserPreferences;
import io.github.dsheirer.record.RecorderType;
import io.github.dsheirer.record.config.RecordConfiguration;
import io.github.dsheirer.source.config.SourceConfiguration;
import io.github.dsheirer.source.tuner.manager.TunerManager;
import jakarta.annotation.PostConstruct;
import java.util.ArrayList;
import java.util.List;
import javafx.beans.property.SimpleStringProperty;
@@ -94,14 +92,15 @@ public class DMRConfigurationEditor extends ChannelConfigurationEditor
/**
* Constructs an instance
* @param playlistManager for playlists
* @param tunerManager for tuners
* @param userPreferences for preferences
*/
public DMRConfigurationEditor(PlaylistManager playlistManager, TunerManager tunerManager,
UserPreferences userPreferences, IFilterProcessor filterProcessor)
public DMRConfigurationEditor()
{
super(playlistManager, tunerManager, userPreferences, filterProcessor);
}
@PostConstruct
public void postConstruct()
{
super.postConstruct();
getTitledPanesBox().getChildren().add(getSourcePane());
getTitledPanesBox().getChildren().add(getDecoderPane());
getTitledPanesBox().getChildren().add(getEventLogPane());

View File

@@ -31,12 +31,10 @@ import io.github.dsheirer.module.decode.config.DecodeConfiguration;
import io.github.dsheirer.module.decode.ltrstandard.DecodeConfigLTRStandard;
import io.github.dsheirer.module.log.EventLogType;
import io.github.dsheirer.module.log.config.EventLogConfiguration;
import io.github.dsheirer.playlist.PlaylistManager;
import io.github.dsheirer.preference.UserPreferences;
import io.github.dsheirer.record.RecorderType;
import io.github.dsheirer.record.config.RecordConfiguration;
import io.github.dsheirer.source.config.SourceConfiguration;
import io.github.dsheirer.source.tuner.manager.TunerManager;
import jakarta.annotation.PostConstruct;
import java.util.ArrayList;
import java.util.List;
import javafx.geometry.HPos;
@@ -71,14 +69,15 @@ public class LTRConfigurationEditor extends ChannelConfigurationEditor
/**
* Constructs an instance
* @param playlistManager for playlists
* @param tunerManager for tuners
* @param userPreferences for preferences
*/
public LTRConfigurationEditor(PlaylistManager playlistManager, TunerManager tunerManager,
UserPreferences userPreferences, IFilterProcessor filterProcessor)
public LTRConfigurationEditor()
{
super(playlistManager, tunerManager, userPreferences, filterProcessor);
}
@PostConstruct
public void postConstruct()
{
super.postConstruct();
getTitledPanesBox().getChildren().add(getSourcePane());
getTitledPanesBox().getChildren().add(getDecoderPane());
getTitledPanesBox().getChildren().add(getAuxDecoderPane());

View File

@@ -31,12 +31,10 @@ import io.github.dsheirer.module.decode.config.DecodeConfiguration;
import io.github.dsheirer.module.decode.ltrnet.DecodeConfigLTRNet;
import io.github.dsheirer.module.log.EventLogType;
import io.github.dsheirer.module.log.config.EventLogConfiguration;
import io.github.dsheirer.playlist.PlaylistManager;
import io.github.dsheirer.preference.UserPreferences;
import io.github.dsheirer.record.RecorderType;
import io.github.dsheirer.record.config.RecordConfiguration;
import io.github.dsheirer.source.config.SourceConfiguration;
import io.github.dsheirer.source.tuner.manager.TunerManager;
import jakarta.annotation.PostConstruct;
import java.util.ArrayList;
import java.util.List;
import javafx.geometry.HPos;
@@ -71,14 +69,15 @@ public class LTRNetConfigurationEditor extends ChannelConfigurationEditor
/**
* Constructs an instance
* @param playlistManager for playlists
* @param tunerManager for tuners
* @param userPreferences for preferences
*/
public LTRNetConfigurationEditor(PlaylistManager playlistManager, TunerManager tunerManager,
UserPreferences userPreferences, IFilterProcessor filterProcessor)
public LTRNetConfigurationEditor()
{
super(playlistManager, tunerManager, userPreferences, filterProcessor);
}
@PostConstruct
public void postConstruct()
{
super.postConstruct();
getTitledPanesBox().getChildren().add(getSourcePane());
getTitledPanesBox().getChildren().add(getDecoderPane());
getTitledPanesBox().getChildren().add(getAuxDecoderPane());

View File

@@ -20,6 +20,7 @@
package io.github.dsheirer.gui.playlist.channel;
import io.github.dsheirer.controller.channel.map.ChannelMap;
import io.github.dsheirer.controller.channel.map.ChannelMapModel;
import io.github.dsheirer.eventbus.MyEventBus;
import io.github.dsheirer.gui.playlist.channelMap.ViewChannelMapEditorRequest;
import io.github.dsheirer.gui.playlist.eventlog.EventLogConfigurationEditor;
@@ -32,12 +33,11 @@ import io.github.dsheirer.module.decode.config.DecodeConfiguration;
import io.github.dsheirer.module.decode.mpt1327.DecodeConfigMPT1327;
import io.github.dsheirer.module.log.EventLogType;
import io.github.dsheirer.module.log.config.EventLogConfiguration;
import io.github.dsheirer.playlist.PlaylistManager;
import io.github.dsheirer.preference.UserPreferences;
import io.github.dsheirer.record.RecorderType;
import io.github.dsheirer.record.config.RecordConfiguration;
import io.github.dsheirer.source.config.SourceConfiguration;
import io.github.dsheirer.source.tuner.manager.TunerManager;
import jakarta.annotation.PostConstruct;
import jakarta.annotation.Resource;
import java.util.ArrayList;
import java.util.List;
import javafx.event.ActionEvent;
@@ -67,6 +67,8 @@ import static io.github.dsheirer.module.decode.config.DecodeConfiguration.CALL_T
public class MPT1327ConfigurationEditor extends ChannelConfigurationEditor
{
private final static Logger mLog = LoggerFactory.getLogger(MPT1327ConfigurationEditor.class);
@Resource
private ChannelMapModel mChannelMapModel;
private TitledPane mDecoderPane;
private TitledPane mEventLogPane;
private TitledPane mRecordPane;
@@ -81,14 +83,15 @@ public class MPT1327ConfigurationEditor extends ChannelConfigurationEditor
/**
* Constructs an instance
* @param playlistManager for playlists
* @param tunerManager for tuners
* @param userPreferences for preferences
*/
public MPT1327ConfigurationEditor(PlaylistManager playlistManager, TunerManager tunerManager,
UserPreferences userPreferences, IFilterProcessor filterProcessor)
public MPT1327ConfigurationEditor()
{
super(playlistManager, tunerManager, userPreferences, filterProcessor);
}
@PostConstruct
public void postConstruct()
{
super.postConstruct();
getTitledPanesBox().getChildren().add(getSourcePane());
getTitledPanesBox().getChildren().add(getDecoderPane());
getTitledPanesBox().getChildren().add(getEventLogPane());
@@ -251,7 +254,7 @@ public class MPT1327ConfigurationEditor extends ChannelConfigurationEditor
mChannelMapComboBox.setMaxWidth(Double.MAX_VALUE);
mChannelMapComboBox.setDisable(true);
mChannelMapComboBox.setTooltip(new Tooltip("Select a channel map to use for this system"));
mChannelMapComboBox.setItems(getPlaylistManager().getChannelMapModel().getChannelMaps());
mChannelMapComboBox.setItems(mChannelMapModel.getChannelMaps());
mChannelMapComboBox.setDisable(true);
mChannelMapComboBox.getSelectionModel().selectedItemProperty()
.addListener((observable, oldValue, newValue) -> modifiedProperty().set(true));

View File

@@ -20,7 +20,6 @@
package io.github.dsheirer.gui.playlist.channel;
import io.github.dsheirer.gui.control.DbPowerMeter;
import io.github.dsheirer.gui.control.HexFormatter;
import io.github.dsheirer.gui.control.IntegerFormatter;
import io.github.dsheirer.gui.playlist.decoder.AuxDecoderConfigurationEditor;
import io.github.dsheirer.gui.playlist.eventlog.EventLogConfigurationEditor;
@@ -32,14 +31,12 @@ import io.github.dsheirer.module.decode.config.DecodeConfiguration;
import io.github.dsheirer.module.decode.nbfm.DecodeConfigNBFM;
import io.github.dsheirer.module.log.EventLogType;
import io.github.dsheirer.module.log.config.EventLogConfiguration;
import io.github.dsheirer.playlist.PlaylistManager;
import io.github.dsheirer.preference.UserPreferences;
import io.github.dsheirer.preference.identifier.IntegerFormat;
import io.github.dsheirer.protocol.Protocol;
import io.github.dsheirer.record.RecorderType;
import io.github.dsheirer.record.config.RecordConfiguration;
import io.github.dsheirer.source.config.SourceConfiguration;
import io.github.dsheirer.source.tuner.manager.TunerManager;
import jakarta.annotation.PostConstruct;
import java.util.ArrayList;
import java.util.List;
import javafx.beans.value.ChangeListener;
@@ -84,18 +81,18 @@ public class NBFMConfigurationEditor extends ChannelConfigurationEditor
private EventLogConfigurationEditor mEventLogConfigurationEditor;
private final TalkgroupValueChangeListener mTalkgroupValueChangeListener = new TalkgroupValueChangeListener();
private final IntegerFormatter mDecimalFormatter = new IntegerFormatter(1, 65535);
private final HexFormatter mHexFormatter = new HexFormatter(1, 65535);
/**
* Constructs an instance
* @param playlistManager for playlists
* @param tunerManager for tuners
* @param userPreferences for preferences
*/
public NBFMConfigurationEditor(PlaylistManager playlistManager, TunerManager tunerManager,
UserPreferences userPreferences, IFilterProcessor filterProcessor)
public NBFMConfigurationEditor()
{
super(playlistManager, tunerManager, userPreferences, filterProcessor);
}
@PostConstruct
public void postConstruct()
{
super.postConstruct();
getTitledPanesBox().getChildren().add(getSourcePane());
getTitledPanesBox().getChildren().add(getDecoderPane());
getTitledPanesBox().getChildren().add(getAuxDecoderPane());

View File

@@ -30,12 +30,10 @@ import io.github.dsheirer.module.decode.p25.phase1.DecodeConfigP25Phase1;
import io.github.dsheirer.module.decode.p25.phase1.P25P1Decoder;
import io.github.dsheirer.module.log.EventLogType;
import io.github.dsheirer.module.log.config.EventLogConfiguration;
import io.github.dsheirer.playlist.PlaylistManager;
import io.github.dsheirer.preference.UserPreferences;
import io.github.dsheirer.record.RecorderType;
import io.github.dsheirer.record.config.RecordConfiguration;
import io.github.dsheirer.source.config.SourceConfiguration;
import io.github.dsheirer.source.tuner.manager.TunerManager;
import jakarta.annotation.PostConstruct;
import java.util.ArrayList;
import java.util.List;
import javafx.beans.value.ChangeListener;
@@ -77,14 +75,15 @@ public class P25P1ConfigurationEditor extends ChannelConfigurationEditor
/**
* Constructs an instance
* @param playlistManager for playlists
* @param tunerManager for tuners
* @param userPreferences for preferences
*/
public P25P1ConfigurationEditor(PlaylistManager playlistManager, TunerManager tunerManager,
UserPreferences userPreferences, IFilterProcessor filterProcessor)
public P25P1ConfigurationEditor()
{
super(playlistManager, tunerManager, userPreferences, filterProcessor);
}
@PostConstruct
public void postConstruct()
{
super.postConstruct();
getTitledPanesBox().getChildren().add(getSourcePane());
getTitledPanesBox().getChildren().add(getDecoderPane());
getTitledPanesBox().getChildren().add(getEventLogPane());

View File

@@ -31,12 +31,10 @@ import io.github.dsheirer.module.decode.p25.phase2.DecodeConfigP25Phase2;
import io.github.dsheirer.module.decode.p25.phase2.enumeration.ScrambleParameters;
import io.github.dsheirer.module.log.EventLogType;
import io.github.dsheirer.module.log.config.EventLogConfiguration;
import io.github.dsheirer.playlist.PlaylistManager;
import io.github.dsheirer.preference.UserPreferences;
import io.github.dsheirer.record.RecorderType;
import io.github.dsheirer.record.config.RecordConfiguration;
import io.github.dsheirer.source.config.SourceConfiguration;
import io.github.dsheirer.source.tuner.manager.TunerManager;
import jakarta.annotation.PostConstruct;
import java.util.ArrayList;
import java.util.List;
import javafx.geometry.HPos;
@@ -67,13 +65,15 @@ public class P25P2ConfigurationEditor extends ChannelConfigurationEditor
/**
* Constructs an instance
* @param playlistManager
* @param userPreferences
*/
public P25P2ConfigurationEditor(PlaylistManager playlistManager, TunerManager tunerManager,
UserPreferences userPreferences, IFilterProcessor filterProcessor)
public P25P2ConfigurationEditor()
{
super(playlistManager, tunerManager, userPreferences, filterProcessor);
}
@PostConstruct
public void postConstruct()
{
super.postConstruct();
getTitledPanesBox().getChildren().add(getSourcePane());
getTitledPanesBox().getChildren().add(getDecoderPane());
getTitledPanesBox().getChildren().add(getEventLogPane());

View File

@@ -30,12 +30,10 @@ import io.github.dsheirer.module.decode.config.DecodeConfiguration;
import io.github.dsheirer.module.decode.passport.DecodeConfigPassport;
import io.github.dsheirer.module.log.EventLogType;
import io.github.dsheirer.module.log.config.EventLogConfiguration;
import io.github.dsheirer.playlist.PlaylistManager;
import io.github.dsheirer.preference.UserPreferences;
import io.github.dsheirer.record.RecorderType;
import io.github.dsheirer.record.config.RecordConfiguration;
import io.github.dsheirer.source.config.SourceConfiguration;
import io.github.dsheirer.source.tuner.manager.TunerManager;
import jakarta.annotation.PostConstruct;
import java.util.ArrayList;
import java.util.List;
import javafx.geometry.Insets;
@@ -63,13 +61,15 @@ public class PassportConfigurationEditor extends ChannelConfigurationEditor
/**
* Constructs an instance
* @param playlistManager
* @param userPreferences
*/
public PassportConfigurationEditor(PlaylistManager playlistManager, TunerManager tunerManager,
UserPreferences userPreferences, IFilterProcessor filterProcessor)
public PassportConfigurationEditor()
{
super(playlistManager, tunerManager, userPreferences, filterProcessor);
}
@PostConstruct
public void postConstruct()
{
super.postConstruct();
getTitledPanesBox().getChildren().add(getSourcePane());
getTitledPanesBox().getChildren().add(getDecoderPane());
getTitledPanesBox().getChildren().add(getAuxDecoderPane());

View File

@@ -23,11 +23,9 @@ import io.github.dsheirer.module.decode.DecoderType;
import io.github.dsheirer.module.decode.config.AuxDecodeConfiguration;
import io.github.dsheirer.module.decode.config.DecodeConfiguration;
import io.github.dsheirer.module.log.config.EventLogConfiguration;
import io.github.dsheirer.playlist.PlaylistManager;
import io.github.dsheirer.preference.UserPreferences;
import io.github.dsheirer.record.config.RecordConfiguration;
import io.github.dsheirer.source.config.SourceConfiguration;
import io.github.dsheirer.source.tuner.manager.TunerManager;
import jakarta.annotation.PostConstruct;
import javafx.scene.control.TitledPane;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -42,12 +40,15 @@ public class UnknownConfigurationEditor extends ChannelConfigurationEditor
/**
* Constructs an instance
* @param aliasModel
*/
public UnknownConfigurationEditor(PlaylistManager playlistManager, TunerManager tunerManager,
UserPreferences userPreferences, IFilterProcessor filterProcessor)
public UnknownConfigurationEditor()
{
super(playlistManager, tunerManager, userPreferences, filterProcessor);
}
@PostConstruct
public void postConstruct()
{
super.postConstruct();
getTitledPanesBox().getChildren().add(getDecoderPane());
}

View File

@@ -1,6 +1,6 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2020 Dennis Sheirer
* Copyright (C) 2014-2023 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
@@ -24,6 +24,9 @@ import io.github.dsheirer.controller.channel.map.ChannelMapModel;
import io.github.dsheirer.controller.channel.map.ChannelRange;
import io.github.dsheirer.gui.control.IntegerTextField;
import io.github.dsheirer.gui.playlist.Editor;
import jakarta.annotation.PostConstruct;
import jakarta.annotation.Resource;
import java.util.Optional;
import javafx.application.Platform;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
@@ -52,16 +55,14 @@ import javafx.scene.layout.VBox;
import javafx.scene.text.Text;
import javafx.util.Callback;
import java.util.Optional;
/**
* JavaFX editor for channel maps
*/
public class ChannelMapEditor extends SplitPane
{
private static final String COPY_NAME = " (copy)";
@Resource
private ChannelMapModel mChannelMapModel;
private SplitPane mSplitPane;
private MapEditor mMapEditor;
private HBox mChannelMapManagerBox;
private VBox mMapButtonsBox;
@@ -78,12 +79,14 @@ public class ChannelMapEditor extends SplitPane
/**
* Constructs an instance
*
* @param channelMapModel for accessing channel maps
*/
public ChannelMapEditor(ChannelMapModel channelMapModel)
public ChannelMapEditor()
{
}
@PostConstruct
public void postConstruct()
{
mChannelMapModel = channelMapModel;
setOrientation(Orientation.VERTICAL);
getItems().addAll(getChannelMapManagerBox(), getMapEditor(), getChannelRangePane());
}
@@ -123,15 +126,10 @@ public class ChannelMapEditor extends SplitPane
mNewMapButton = new Button("New Map");
mNewMapButton.setTooltip(new Tooltip("Create a new channel map"));
mNewMapButton.setMaxWidth(Double.MAX_VALUE);
mNewMapButton.setOnAction(new EventHandler<ActionEvent>()
{
@Override
public void handle(ActionEvent event)
{
ChannelMap channelMap = new ChannelMap("New Channel Map");
mChannelMapModel.addChannelMap(channelMap);
getChannelMapListView().getSelectionModel().select(channelMap);
}
mNewMapButton.setOnAction(event -> {
ChannelMap channelMap = new ChannelMap("New Channel Map");
mChannelMapModel.addChannelMap(channelMap);
getChannelMapListView().getSelectionModel().select(channelMap);
});
}
@@ -146,32 +144,27 @@ public class ChannelMapEditor extends SplitPane
mCloneMapButton.setTooltip(new Tooltip("Create a copy of the selected channel map"));
mCloneMapButton.setDisable(true);
mCloneMapButton.setMaxWidth(Double.MAX_VALUE);
mCloneMapButton.setOnAction(new EventHandler<ActionEvent>()
{
@Override
public void handle(ActionEvent event)
mCloneMapButton.setOnAction(event -> {
ChannelMap selected = getChannelMapListView().getSelectionModel().getSelectedItem();
if(selected != null)
{
ChannelMap selected = getChannelMapListView().getSelectionModel().getSelectedItem();
ChannelMap copy = selected.copyOf();
if(selected != null)
String name = selected.getName();
if(name == null)
{
ChannelMap copy = selected.copyOf();
String name = selected.getName();
if(name == null)
{
name = COPY_NAME;
}
else if(!name.toLowerCase().endsWith(COPY_NAME))
{
name = name + COPY_NAME;
}
copy.setName(name);
mChannelMapModel.addChannelMap(copy);
getChannelMapListView().getSelectionModel().select(copy);
name = COPY_NAME;
}
else if(!name.toLowerCase().endsWith(COPY_NAME))
{
name = name + COPY_NAME;
}
copy.setName(name);
mChannelMapModel.addChannelMap(copy);
getChannelMapListView().getSelectionModel().select(copy);
}
});
}

View File

@@ -1,35 +1,42 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2023 Dennis Sheirer
*
* * ******************************************************************************
* * Copyright (C) 2014-2020 Dennis Sheirer
* *
* * This program is free software: you can redistribute it and/or modify
* * it under the terms of the GNU General Public License as published by
* * the Free Software Foundation, either version 3 of the License, or
* * (at your option) any later version.
* *
* * This program is distributed in the hope that it will be useful,
* * but WITHOUT ANY WARRANTY; without even the implied warranty of
* * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* * GNU General Public License for more details.
* *
* * You should have received a copy of the GNU General Public License
* * along with this program. If not, see <http://www.gnu.org/licenses/>
* * *****************************************************************************
* 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.gui.playlist.manager;
import com.google.common.eventbus.Subscribe;
import com.google.common.io.Files;
import io.github.dsheirer.controller.channel.Channel;
import io.github.dsheirer.controller.channel.ChannelException;
import io.github.dsheirer.eventbus.MyEventBus;
import io.github.dsheirer.controller.channel.ChannelModel;
import io.github.dsheirer.controller.channel.ChannelProcessingManager;
import io.github.dsheirer.playlist.PlaylistManager;
import io.github.dsheirer.preference.IPreferenceUpdateListener;
import io.github.dsheirer.preference.PreferenceType;
import io.github.dsheirer.preference.UserPreferences;
import jakarta.annotation.PostConstruct;
import jakarta.annotation.PreDestroy;
import jakarta.annotation.Resource;
import java.io.File;
import java.io.IOException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.List;
import java.util.Optional;
import javafx.application.Platform;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
@@ -53,17 +60,10 @@ import jiconfont.javafx.IconNode;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.File;
import java.io.IOException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.List;
import java.util.Optional;
/**
* Editor for managing playlists via the playlist manager
*/
public class PlaylistManagerEditor extends HBox
public class PlaylistManagerEditor extends HBox implements IPreferenceUpdateListener
{
private static final Logger mLog = LoggerFactory.getLogger(PlaylistManagerEditor.class);
@@ -72,7 +72,13 @@ public class PlaylistManagerEditor extends HBox
private static final FileChooser.ExtensionFilter ALL_FILES_FILE_FILTER =
new FileChooser.ExtensionFilter("All Files (*.*)", "*.*");
@Resource
private ChannelModel mChannelModel;
@Resource
private ChannelProcessingManager mChannelProcessingManager;
@Resource
private PlaylistManager mPlaylistManager;
@Resource
private UserPreferences mUserPreferences;
private TableView<Path> mPlaylistTableView;
private VBox mButtonBox;
@@ -86,25 +92,25 @@ public class PlaylistManagerEditor extends HBox
/**
* Constructs an instance
* @param playlistManager for managing playlist
* @param userPreferences for accessing preferences
*/
public PlaylistManagerEditor(PlaylistManager playlistManager, UserPreferences userPreferences)
public PlaylistManagerEditor()
{
mPlaylistManager = playlistManager;
mUserPreferences = userPreferences;
//Register to receive preferences updates
MyEventBus.getGlobalEventBus().register(this);
}
@PostConstruct
public void postConstruct()
{
mUserPreferences.addUpdateListener(this);
setPadding(new Insets(5, 5, 5, 5));
HBox.setHgrow(getPlaylistTableView(), Priority.ALWAYS);
getChildren().addAll(getPlaylistTableView(), getButtonBox());
updateButtons();
}
@PreDestroy
public void dispose()
{
MyEventBus.getGlobalEventBus().unregister(this);
mUserPreferences.removeUpdateListener(this);
}
/**
@@ -272,7 +278,7 @@ public class PlaylistManagerEditor extends HBox
}
}
final List<Channel> autoStartChannels = mPlaylistManager.getChannelModel().getAutoStartChannels();
final List<Channel> autoStartChannels = mChannelModel.getAutoStartChannels();
if(autoStartChannels.size() > 0)
{
@@ -290,7 +296,7 @@ public class PlaylistManagerEditor extends HBox
{
try
{
mPlaylistManager.getChannelProcessingManager().start(channel);
mChannelProcessingManager.start(channel);
}
catch(ChannelException ce)
{
@@ -526,7 +532,7 @@ public class PlaylistManagerEditor extends HBox
* to the playlist list view so that we can reflect changes to the current playlist.
* @param preferenceType that was updated
*/
@Subscribe
@Override
public void preferenceUpdated(PreferenceType preferenceType)
{
if(preferenceType == PreferenceType.PLAYLIST)

View File

@@ -1,6 +1,6 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2022 Dennis Sheirer
* Copyright (C) 2014-2023 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
@@ -19,13 +19,17 @@
package io.github.dsheirer.gui.playlist.radioreference;
import io.github.dsheirer.playlist.PlaylistManager;
import io.github.dsheirer.controller.channel.ChannelModel;
import io.github.dsheirer.preference.UserPreferences;
import io.github.dsheirer.rrapi.type.Agency;
import io.github.dsheirer.rrapi.type.AgencyInfo;
import io.github.dsheirer.rrapi.type.CountyInfo;
import io.github.dsheirer.service.radioreference.RadioReference;
import io.github.dsheirer.util.ThreadPool;
import jakarta.annotation.Resource;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import javafx.application.Platform;
import javafx.beans.binding.Bindings;
import javafx.beans.property.IntegerProperty;
@@ -39,19 +43,18 @@ import javafx.scene.layout.VBox;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
/**
* Editor for displaying a list of agencies and viewing frequency lists for each agency
*/
public class AgencyEditor extends VBox
public abstract class AgencyEditor extends VBox
{
private static final Logger mLog = LoggerFactory.getLogger(AgencyEditor.class);
@Resource
private UserPreferences mUserPreferences;
@Resource
private ChannelModel mChannelModel;
@Resource
private RadioReference mRadioReference;
private PlaylistManager mPlaylistManager;
private Level mLevel;
private ListView<Agency> mAgencyListView;
private AgencyFrequencyEditor mAgencyFrequencyEditor;
@@ -59,19 +62,19 @@ public class AgencyEditor extends VBox
/**
* Constructs an instance
* @param userPreferences
* @param radioReference
* @param level
*/
public AgencyEditor(UserPreferences userPreferences, RadioReference radioReference,
PlaylistManager playlistManager, Level level)
public AgencyEditor(Level level)
{
mUserPreferences = userPreferences;
mRadioReference = radioReference;
mPlaylistManager = playlistManager;
mLevel = level;
mAgencyCountProperty.bind(Bindings.size(getAgencyListView().getItems()));
}
/**
* Not annotated. Sub-class must implement the annotation and invoke this method.
*/
public void postConstruct()
{
mAgencyCountProperty.bind(Bindings.size(getAgencyListView().getItems()));
setPadding(new Insets(10,10,10,10));
setSpacing(10);
VBox.setVgrow(getAgencyFrequencyEditor(), Priority.ALWAYS);
@@ -127,7 +130,7 @@ public class AgencyEditor extends VBox
{
if(mAgencyFrequencyEditor == null)
{
mAgencyFrequencyEditor = new AgencyFrequencyEditor(mUserPreferences, mRadioReference, mPlaylistManager, mLevel);
mAgencyFrequencyEditor = new AgencyFrequencyEditor(mUserPreferences, mRadioReference, mChannelModel, mLevel);
}
return mAgencyFrequencyEditor;

View File

@@ -1,6 +1,6 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2022 Dennis Sheirer
* Copyright (C) 2014-2023 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
@@ -19,7 +19,7 @@
package io.github.dsheirer.gui.playlist.radioreference;
import io.github.dsheirer.playlist.PlaylistManager;
import io.github.dsheirer.controller.channel.ChannelModel;
import io.github.dsheirer.preference.UserPreferences;
import io.github.dsheirer.rrapi.type.Category;
import io.github.dsheirer.rrapi.type.Frequency;
@@ -27,6 +27,11 @@ import io.github.dsheirer.rrapi.type.Mode;
import io.github.dsheirer.rrapi.type.SubCategory;
import io.github.dsheirer.service.radioreference.RadioReference;
import io.github.dsheirer.util.ThreadPool;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import javafx.application.Platform;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.value.ObservableValue;
@@ -46,12 +51,6 @@ import javafx.util.StringConverter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
/**
* Grid pane with components for visualizing and selecting categories of frequencies for an agency
*/
@@ -62,8 +61,8 @@ public class AgencyFrequencyEditor extends GridPane
private static final SubCategory ALL_SUB_CATEGORIES = new AllSubCategories();
private UserPreferences mUserPreferences;
private ChannelModel mChannelModel;
private RadioReference mRadioReference;
private PlaylistManager mPlaylistManager;
private Level mLevel;
private ComboBox<Category> mCategoryComboBox;
private ComboBox<SubCategory> mSubCategoryComboBox;
@@ -72,12 +71,17 @@ public class AgencyFrequencyEditor extends GridPane
private ProgressIndicator mProgressIndicator;
private FrequencyEditor mFrequencyEditor;
/**
* Constructs an instance
* @param radioReference to use for lookups
* @param level for this editor.
*/
public AgencyFrequencyEditor(UserPreferences userPreferences, RadioReference radioReference,
PlaylistManager playlistManager, Level level)
ChannelModel channelModel, Level level)
{
mUserPreferences = userPreferences;
mChannelModel = channelModel;
mRadioReference = radioReference;
mPlaylistManager = playlistManager;
mLevel = level;
setPadding(new Insets(5, 5, 5,5));
@@ -175,7 +179,7 @@ public class AgencyFrequencyEditor extends GridPane
{
if(mFrequencyEditor == null)
{
mFrequencyEditor = new FrequencyEditor(mUserPreferences, mRadioReference, mPlaylistManager, mLevel);
mFrequencyEditor = new FrequencyEditor(mUserPreferences, mRadioReference, mChannelModel, mLevel);
}
return mFrequencyEditor;

View File

@@ -0,0 +1,39 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2023 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.gui.playlist.radioreference;
import jakarta.annotation.PostConstruct;
public class CountyAgencyEditor extends AgencyEditor
{
/**
* Constructs an instance
*/
public CountyAgencyEditor()
{
super(Level.COUNTY);
}
@PostConstruct
public void postConstruct()
{
super.postConstruct();
}
}

View File

@@ -0,0 +1,39 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2023 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.gui.playlist.radioreference;
import jakarta.annotation.PostConstruct;
public class CountySystemEditor extends SystemEditor
{
/**
* Constructs an instance
*/
public CountySystemEditor()
{
super(Level.COUNTY);
}
@PostConstruct
public void postConstruct()
{
super.postConstruct();
}
}

View File

@@ -1,6 +1,6 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2022 Dennis Sheirer
* Copyright (C) 2014-2023 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
@@ -20,6 +20,7 @@
package io.github.dsheirer.gui.playlist.radioreference;
import io.github.dsheirer.controller.channel.Channel;
import io.github.dsheirer.controller.channel.ChannelModel;
import io.github.dsheirer.eventbus.MyEventBus;
import io.github.dsheirer.gui.playlist.channel.ViewChannelRequest;
import io.github.dsheirer.module.decode.DecoderFactory;
@@ -27,7 +28,6 @@ import io.github.dsheirer.module.decode.config.DecodeConfiguration;
import io.github.dsheirer.module.decode.nbfm.DecodeConfigNBFM;
import io.github.dsheirer.module.decode.p25.phase1.DecodeConfigP25Phase1;
import io.github.dsheirer.module.decode.p25.phase1.P25P1Decoder;
import io.github.dsheirer.playlist.PlaylistManager;
import io.github.dsheirer.preference.UserPreferences;
import io.github.dsheirer.rrapi.type.Category;
import io.github.dsheirer.rrapi.type.Frequency;
@@ -36,6 +36,7 @@ import io.github.dsheirer.rrapi.type.SubCategory;
import io.github.dsheirer.service.radioreference.RadioReference;
import io.github.dsheirer.source.config.SourceConfigTuner;
import io.github.dsheirer.util.ThreadPool;
import java.text.DecimalFormat;
import javafx.animation.FadeTransition;
import javafx.application.Platform;
import javafx.geometry.HPos;
@@ -53,15 +54,13 @@ import javafx.util.Duration;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.text.DecimalFormat;
public class FrequencyEditor extends VBox
{
private static final Logger mLog = LoggerFactory.getLogger(FrequencyEditor.class);
private final DecimalFormat FREQUENCY_FORMATTER = new DecimalFormat("0.00000");
private UserPreferences mUserPreferences;
private ChannelModel mChannelModel;
private RadioReference mRadioReference;
private PlaylistManager mPlaylistManager;
private UserPreferences mUserPreferences;
private Level mLevel;
private TextField mAlphaTagTextField;
private TextField mFrequencyTextField;
@@ -77,12 +76,18 @@ public class FrequencyEditor extends VBox
private ModeDecoderType mModeDecoderType;
private long mFrequency;
public FrequencyEditor(UserPreferences userPreferences, RadioReference radioReference,
PlaylistManager playlistManager, Level level)
/**
* Constructs an instance
* @param userPreferences for preferences lookups
* @param radioReference for data lookups
* @param channelModel for channels
* @param level for this editor.
*/
public FrequencyEditor(UserPreferences userPreferences, RadioReference radioReference, ChannelModel channelModel, Level level)
{
mUserPreferences = userPreferences;
mRadioReference = radioReference;
mPlaylistManager = playlistManager;
mChannelModel = channelModel;
mLevel = level;
GridPane gridPane = new GridPane();
@@ -372,7 +377,7 @@ public class FrequencyEditor extends VBox
if(channel != null)
{
getCreateButton().setDisable(true);
mPlaylistManager.getChannelModel().addChannel(channel);
mChannelModel.addChannel(channel);
if(getShowCreatedChannelCheckBox().selectedProperty().get())
{

View File

@@ -1,6 +1,6 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2020 Dennis Sheirer
* Copyright (C) 2014-2023 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
@@ -24,6 +24,7 @@ import io.github.dsheirer.rrapi.RadioReferenceException;
import io.github.dsheirer.rrapi.response.Fault;
import io.github.dsheirer.rrapi.type.AuthorizationInformation;
import io.github.dsheirer.service.radioreference.RadioReference;
import java.net.ConnectException;
import javafx.application.Platform;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
@@ -48,11 +49,6 @@ import jiconfont.javafx.IconNode;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.net.ConnectException;
import java.text.ParseException;
import java.util.Calendar;
import java.util.Date;
/**
* Login dialog for radioreference.com with support for testing connection to the service.
*/

View File

@@ -0,0 +1,39 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2023 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.gui.playlist.radioreference;
import jakarta.annotation.PostConstruct;
public class NationalAgencyEditor extends AgencyEditor
{
/**
* Constructs an instance
*/
public NationalAgencyEditor()
{
super(Level.NATIONAL);
}
@PostConstruct
public void postConstruct()
{
super.postConstruct();
}
}

View File

@@ -1,6 +1,6 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2022 Dennis Sheirer
* Copyright (C) 2014-2023 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
@@ -19,7 +19,6 @@
package io.github.dsheirer.gui.playlist.radioreference;
import io.github.dsheirer.playlist.PlaylistManager;
import io.github.dsheirer.preference.UserPreferences;
import io.github.dsheirer.rrapi.RadioReferenceException;
import io.github.dsheirer.rrapi.type.Agency;
@@ -32,6 +31,12 @@ import io.github.dsheirer.rrapi.type.State;
import io.github.dsheirer.rrapi.type.StateInfo;
import io.github.dsheirer.service.radioreference.RadioReference;
import io.github.dsheirer.util.ThreadPool;
import jakarta.annotation.PostConstruct;
import jakarta.annotation.PreDestroy;
import jakarta.annotation.Resource;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;
import javafx.application.Platform;
import javafx.beans.binding.Bindings;
import javafx.geometry.Insets;
@@ -56,16 +61,23 @@ import jiconfont.javafx.IconNode;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;
public class RadioReferenceEditor extends BorderPane implements Consumer<AuthorizationInformation>
{
private static final Logger mLog = LoggerFactory.getLogger(RadioReferenceEditor.class);
private UserPreferences mUserPreferences;
@Resource
private CountyAgencyEditor mCountyAgencyEditor;
@Resource
private CountySystemEditor mCountySystemEditor;
@Resource
private NationalAgencyEditor mNationalAgencyEditor;
@Resource
private RadioReference mRadioReference;
private PlaylistManager mPlaylistManager;
@Resource
private StateAgencyEditor mStateAgencyEditor;
@Resource
private StateSystemEditor mStateSystemEditor;
@Resource
private UserPreferences mUserPreferences;
private VBox mTopBox;
private HBox mCredentialsBox;
private Label mRRStatusText;
@@ -79,11 +91,6 @@ public class RadioReferenceEditor extends BorderPane implements Consumer<Authori
private ComboBox<Country> mCountryComboBox;
private ComboBox<State> mStateComboBox;
private ComboBox<County> mCountyComboBox;
private AgencyEditor mNationalAgencyEditor;
private AgencyEditor mStateAgencyEditor;
private AgencyEditor mCountyAgencyEditor;
private SystemEditor mStateSystemEditor;
private SystemEditor mCountySystemEditor;
private TabPane mTabPane;
private Tab mNationalAgencyTab;
private Tab mStateAgencyTab;
@@ -91,34 +98,42 @@ public class RadioReferenceEditor extends BorderPane implements Consumer<Authori
private Tab mStateSystemTab;
private Tab mCountySystemTab;
public RadioReferenceEditor(UserPreferences userPreferences, PlaylistManager playlistManager)
/**
* Constructs an instance
*/
public RadioReferenceEditor()
{
mUserPreferences = userPreferences;
mRadioReference = playlistManager.getRadioReference();
mPlaylistManager = playlistManager;
}
@PostConstruct
public void postConstruct()
{
setTop(getTopBox());
setCenter(getTabPane());
login();
if(mRadioReference.premiumAccountProperty().get())
{
refreshCountries();
}
//Refresh the countries combo box once we're logged on, if not already
mRadioReference.premiumAccountProperty().addListener((observable, oldValue, newValue) -> {
if(newValue)
//Only refresh the countries if we're now logged in and we weren't previously logged in.
if(newValue && oldValue ^ newValue)
{
refreshCountries();
}
});
}
private void login()
@PreDestroy
public void preDestroy()
{
if(mUserPreferences.getRadioReferencePreference().hasStoredCredentials())
}
/**
* Performs radio reference login and sets the state of the premiumAccountProperty and if the login is good, it
* triggers a refresh on the countries list box.
*/
public void login()
{
if(mUserPreferences.getRadioReferencePreference().hasStoredCredentials() &&
!mRadioReference.premiumAccountProperty().get())
{
AuthorizationInformation credentials = mUserPreferences.getRadioReferencePreference().getAuthorizationInformation();
@@ -139,6 +154,7 @@ public class RadioReferenceEditor extends BorderPane implements Consumer<Authori
final int preferredCountryId = mUserPreferences.getRadioReferencePreference().getPreferredCountryId();
//Retrieve the countries off of the JavaFX event thread and load the UI on the JavaFX platform thread
ThreadPool.CACHED.execute(() -> {
try
{
@@ -297,56 +313,31 @@ public class RadioReferenceEditor extends BorderPane implements Consumer<Authori
return mCountySystemTab;
}
private AgencyEditor getNationalAgencyEditor()
private AgencyEditor getCountyAgencyEditor()
{
if(mNationalAgencyEditor == null)
{
mNationalAgencyEditor = new AgencyEditor(mUserPreferences, mRadioReference, mPlaylistManager, Level.NATIONAL);
}
return mNationalAgencyEditor;
return mCountyAgencyEditor;
}
private AgencyEditor getStateAgencyEditor()
{
if(mStateAgencyEditor == null)
{
mStateAgencyEditor = new AgencyEditor(mUserPreferences, mRadioReference, mPlaylistManager, Level.STATE);
}
return mStateAgencyEditor;
}
private AgencyEditor getCountyAgencyEditor()
private AgencyEditor getNationalAgencyEditor()
{
if(mCountyAgencyEditor == null)
{
mCountyAgencyEditor = new AgencyEditor(mUserPreferences, mRadioReference, mPlaylistManager, Level.COUNTY);
}
return mCountyAgencyEditor;
}
private SystemEditor getStateSystemEditor()
{
if(mStateSystemEditor == null)
{
mStateSystemEditor = new SystemEditor(mUserPreferences, mRadioReference, mPlaylistManager, Level.STATE);
}
return mStateSystemEditor;
return mNationalAgencyEditor;
}
private SystemEditor getCountySystemEditor()
{
if(mCountySystemEditor == null)
{
mCountySystemEditor = new SystemEditor(mUserPreferences, mRadioReference, mPlaylistManager, Level.COUNTY);
}
return mCountySystemEditor;
}
private SystemEditor getStateSystemEditor()
{
return mStateSystemEditor;
}
private VBox getTopBox()
{
if(mTopBox == null)

View File

@@ -1,6 +1,6 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2020 Dennis Sheirer
* Copyright (C) 2014-2023 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
@@ -21,6 +21,7 @@ package io.github.dsheirer.gui.playlist.radioreference;
import io.github.dsheirer.alias.AliasModel;
import io.github.dsheirer.controller.channel.Channel;
import io.github.dsheirer.controller.channel.ChannelModel;
import io.github.dsheirer.eventbus.MyEventBus;
import io.github.dsheirer.gui.control.MaxLengthUnaryOperator;
import io.github.dsheirer.gui.playlist.channel.ViewChannelRequest;
@@ -33,7 +34,6 @@ import io.github.dsheirer.module.decode.p25.phase1.DecodeConfigP25Phase1;
import io.github.dsheirer.module.decode.p25.phase1.P25P1Decoder;
import io.github.dsheirer.module.decode.p25.phase2.DecodeConfigP25Phase2;
import io.github.dsheirer.module.decode.p25.phase2.enumeration.ScrambleParameters;
import io.github.dsheirer.playlist.PlaylistManager;
import io.github.dsheirer.preference.UserPreferences;
import io.github.dsheirer.rrapi.type.RadioNetwork;
import io.github.dsheirer.rrapi.type.Site;
@@ -42,6 +42,15 @@ import io.github.dsheirer.rrapi.type.System;
import io.github.dsheirer.rrapi.type.SystemInformation;
import io.github.dsheirer.source.config.SourceConfigTuner;
import io.github.dsheirer.source.config.SourceConfigTunerMultipleFrequency;
import jakarta.annotation.PostConstruct;
import jakarta.annotation.Resource;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import java.util.Optional;
import java.util.function.Predicate;
import javafx.animation.RotateTransition;
import javafx.beans.property.ReadOnlyObjectWrapper;
import javafx.beans.property.SimpleStringProperty;
@@ -74,14 +83,6 @@ import org.controlsfx.control.SegmentedButton;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import java.util.Optional;
import java.util.function.Predicate;
public class SiteEditor extends GridPane
{
private static final Logger mLog = LoggerFactory.getLogger(SiteEditor.class);
@@ -90,9 +91,12 @@ public class SiteEditor extends GridPane
private static final String PRIMARY_CONTROL_CHANNEL = "d";
private static final String TOGGLE_BUTTON_CONTROL = "Control";
private static final String TOGGLE_BUTTON_P25_VOICE = "All P25 Voice";
@Resource
private AliasModel mAliasModel;
@Resource
private ChannelModel mChannelModel;
@Resource
private UserPreferences mUserPreferences;
private PlaylistManager mPlaylistManager;
private TableView<SiteFrequency> mSiteFrequencyTableView;
private TableColumn<SiteFrequency,String> mTypeColumn;
private SegmentedButton mFrequenciesSegmentedButton;
@@ -116,11 +120,13 @@ public class SiteEditor extends GridPane
private ComboBox mAliasListNameComboBox;
private Button mNewAliasListButton;
public SiteEditor(UserPreferences userPreferences, PlaylistManager playlistManager)
public SiteEditor()
{
mUserPreferences = userPreferences;
mPlaylistManager = playlistManager;
}
@PostConstruct
public void postConstruct()
{
setMaxHeight(Double.MAX_VALUE);
setHgap(5.0);
setVgap(5.0);
@@ -476,7 +482,7 @@ public class SiteEditor extends GridPane
gotoChannel = channel;
}
mPlaylistManager.getChannelModel().addChannel(channel);
mChannelModel.addChannel(channel);
}
}
@@ -540,7 +546,7 @@ public class SiteEditor extends GridPane
channel.setSourceConfiguration(sourceConfig);
}
mPlaylistManager.getChannelModel().addChannel(channel);
mChannelModel.addChannel(channel);
if(getGoToChannelEditorCheckBox().isSelected())
{
@@ -614,7 +620,7 @@ public class SiteEditor extends GridPane
channel.setSourceConfiguration(sourceConfig);
}
mPlaylistManager.getChannelModel().addChannel(channel);
mChannelModel.addChannel(channel);
if(getGoToChannelEditorCheckBox().isSelected())
{
@@ -641,7 +647,7 @@ public class SiteEditor extends GridPane
gotoChannel = channel;
}
mPlaylistManager.getChannelModel().addChannel(channel);
mChannelModel.addChannel(channel);
}
if(getGoToChannelEditorCheckBox().isSelected() && gotoChannel != null)
@@ -706,7 +712,7 @@ public class SiteEditor extends GridPane
channel.setSourceConfiguration(sourceConfig);
}
mPlaylistManager.getChannelModel().addChannel(channel);
mChannelModel.addChannel(channel);
if(getGoToChannelEditorCheckBox().isSelected())
{
@@ -734,7 +740,7 @@ public class SiteEditor extends GridPane
gotoChannel = channel;
}
mPlaylistManager.getChannelModel().addChannel(channel);
mChannelModel.addChannel(channel);
}
if(getGoToChannelEditorCheckBox().isSelected() && gotoChannel != null)
@@ -768,7 +774,7 @@ public class SiteEditor extends GridPane
{
Predicate<String> filterPredicate = s -> !s.contentEquals(AliasModel.NO_ALIAS_LIST);
FilteredList<String> filteredChannelList =
new FilteredList<>(mPlaylistManager.getAliasModel().aliasListNames(), filterPredicate);
new FilteredList<>(mAliasModel.aliasListNames(), filterPredicate);
mAliasListNameComboBox = new ComboBox<>(filteredChannelList);
mAliasListNameComboBox.setDisable(true);
mAliasListNameComboBox.setMaxWidth(Double.MAX_VALUE);
@@ -806,7 +812,7 @@ public class SiteEditor extends GridPane
if(name != null && !name.isEmpty())
{
name = name.trim();
mPlaylistManager.getAliasModel().addAliasList(name);
mAliasModel.addAliasList(name);
getAliasListNameComboBox().getSelectionModel().select(name);
}
});

View File

@@ -0,0 +1,39 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2023 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.gui.playlist.radioreference;
import jakarta.annotation.PostConstruct;
public class StateAgencyEditor extends AgencyEditor
{
/**
* Constructs an instance
*/
public StateAgencyEditor()
{
super(Level.STATE);
}
@PostConstruct
public void postConstruct()
{
super.postConstruct();
}
}

View File

@@ -0,0 +1,39 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2023 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.gui.playlist.radioreference;
import jakarta.annotation.PostConstruct;
public class StateSystemEditor extends SystemEditor
{
/**
* Constructs an instance
*/
public StateSystemEditor()
{
super(Level.STATE);
}
@PostConstruct
public void postConstruct()
{
super.postConstruct();
}
}

View File

@@ -1,6 +1,6 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2022 Dennis Sheirer
* Copyright (C) 2014-2023 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
@@ -19,7 +19,6 @@
package io.github.dsheirer.gui.playlist.radioreference;
import io.github.dsheirer.playlist.PlaylistManager;
import io.github.dsheirer.preference.UserPreferences;
import io.github.dsheirer.rrapi.RadioReferenceException;
import io.github.dsheirer.rrapi.type.CountyInfo;
@@ -34,6 +33,7 @@ import io.github.dsheirer.rrapi.type.Type;
import io.github.dsheirer.rrapi.type.Voice;
import io.github.dsheirer.service.radioreference.RadioReference;
import io.github.dsheirer.util.ThreadPool;
import jakarta.annotation.Resource;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
@@ -63,37 +63,40 @@ import org.slf4j.LoggerFactory;
/**
* Radio Reference editor for trunked radio systems
*/
public class SystemEditor extends VBox
public abstract class SystemEditor extends VBox
{
private static final Logger mLog = LoggerFactory.getLogger(SystemEditor.class);
private UserPreferences mUserPreferences;
@Resource
private RadioReference mRadioReference;
private PlaylistManager mPlaylistManager;
@Resource
private SystemTalkgroupSelectionEditor mSystemTalkgroupSelectionEditor;
@Resource
private UserPreferences mUserPreferences;
private Level mLevel;
private ComboBox<System> mSystemComboBox;
private IntegerProperty mSystemCountProperty = new SimpleIntegerProperty();
private TabPane mTabPane;
private Tab mSystemTab;
private Tab mTalkgroupTab;
@Resource
private SystemSiteSelectionEditor mSystemSiteSelectionEditor;
private SystemTalkgroupSelectionEditor mSystemTalkgroupSelectionEditor;
private RadioReferenceDecoder mRadioReferenceDecoder;
/**
* Constructs an instance
* @param userPreferences for preferences
* @param radioReference to access radio reference
* @param playlistManager
* @param level STATE or COUNTY
*/
public SystemEditor(UserPreferences userPreferences, RadioReference radioReference,
PlaylistManager playlistManager, Level level)
public SystemEditor(Level level)
{
mUserPreferences = userPreferences;
mRadioReference = radioReference;
mPlaylistManager = playlistManager;
mLevel = level;
}
/**
* Not annotated. Sub-class implementation must be annotated.
*/
public void postConstruct()
{
mSystemCountProperty.bind(Bindings.size(getSystemComboBox().getItems()));
setPadding(new Insets(20,10,10,10));
@@ -168,11 +171,6 @@ public class SystemEditor extends VBox
private SystemSiteSelectionEditor getSystemSiteSelectionEditor()
{
if(mSystemSiteSelectionEditor == null)
{
mSystemSiteSelectionEditor = new SystemSiteSelectionEditor(mUserPreferences, mPlaylistManager);
}
return mSystemSiteSelectionEditor;
}
@@ -189,11 +187,6 @@ public class SystemEditor extends VBox
private SystemTalkgroupSelectionEditor getSystemTalkgroupSelectionEditor()
{
if(mSystemTalkgroupSelectionEditor == null)
{
mSystemTalkgroupSelectionEditor = new SystemTalkgroupSelectionEditor(mUserPreferences, mPlaylistManager);
}
return mSystemTalkgroupSelectionEditor;
}

View File

@@ -1,34 +1,32 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2023 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
* * 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 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.gui.playlist.radioreference;
import io.github.dsheirer.playlist.PlaylistManager;
import io.github.dsheirer.preference.UserPreferences;
import io.github.dsheirer.rrapi.type.Flavor;
import io.github.dsheirer.rrapi.type.System;
import io.github.dsheirer.rrapi.type.SystemInformation;
import io.github.dsheirer.rrapi.type.Type;
import io.github.dsheirer.rrapi.type.Voice;
import jakarta.annotation.PostConstruct;
import jakarta.annotation.Resource;
import java.util.List;
import javafx.geometry.HPos;
import javafx.geometry.Insets;
import javafx.scene.control.Label;
@@ -42,8 +40,6 @@ import javafx.scene.layout.Priority;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.List;
/**
* View for a radio reference trunked mobile radio system
@@ -51,30 +47,28 @@ import java.util.List;
public class SystemSiteSelectionEditor extends GridPane
{
private static final Logger mLog = LoggerFactory.getLogger(SystemSiteSelectionEditor.class);
private UserPreferences mUserPreferences;
private PlaylistManager mPlaylistManager;
@Resource
private SiteEditor mSiteEditor;
private ProgressIndicator mProgressIndicator;
private Label mPlaceholderLabel;
private Label mProtocolLabel;
private Label mFlavorLabel;
private Label mVoiceLabel;
private TableView<EnrichedSite> mSiteTableView;
private SiteEditor mSiteEditor;
private RadioReferenceDecoder mRadioReferenceDecoder;
private System mCurrentSystem;
private SystemInformation mCurrentSystemInformation;
/**
* Constructs an instance
* @param userPreferences for settings
* @param playlistManager to access radio reference
*/
public SystemSiteSelectionEditor(UserPreferences userPreferences, PlaylistManager playlistManager)
public SystemSiteSelectionEditor()
{
mUserPreferences = userPreferences;
mPlaylistManager = playlistManager;
}
@PostConstruct
public void postConstruct()
{
setPadding(new Insets(10,0,0,0));
setHgap(10);
setVgap(10);
@@ -146,11 +140,6 @@ public class SystemSiteSelectionEditor extends GridPane
private SiteEditor getSiteEditor()
{
if(mSiteEditor == null)
{
mSiteEditor = new SiteEditor(mUserPreferences, mPlaylistManager);
}
return mSiteEditor;
}

View File

@@ -28,12 +28,13 @@ import io.github.dsheirer.alias.id.talkgroup.TalkgroupRange;
import io.github.dsheirer.eventbus.MyEventBus;
import io.github.dsheirer.gui.control.MaxLengthUnaryOperator;
import io.github.dsheirer.identifier.talkgroup.TalkgroupIdentifier;
import io.github.dsheirer.playlist.PlaylistManager;
import io.github.dsheirer.preference.UserPreferences;
import io.github.dsheirer.protocol.Protocol;
import io.github.dsheirer.rrapi.type.System;
import io.github.dsheirer.rrapi.type.Talkgroup;
import io.github.dsheirer.rrapi.type.TalkgroupCategory;
import jakarta.annotation.PostConstruct;
import jakarta.annotation.Resource;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
@@ -82,12 +83,15 @@ public class SystemTalkgroupSelectionEditor extends GridPane
private static final Logger mLog = LoggerFactory.getLogger(SystemTalkgroupSelectionEditor.class);
private final TalkgroupCategory ALL_TALKGROUPS = new TalkgroupCategory();
@Resource
private AliasModel mAliasModel;
@Resource
private TalkgroupEditor mTalkgroupEditor;
@Resource
private UserPreferences mUserPreferences;
private PlaylistManager mPlaylistManager;
private TableView<AliasedTalkgroup> mTalkgroupTableView;
private ComboBox<TalkgroupCategory> mTalkgroupCategoryComboBox;
private TextField mSearchField;
private TalkgroupEditor mTalkgroupEditor;
private ComboBox<String> mAliasListNameComboBox;
private Button mNewAliasListButton;
private TalkgroupFilter mTalkgroupFilter = new TalkgroupFilter();
@@ -102,14 +106,16 @@ public class SystemTalkgroupSelectionEditor extends GridPane
private ProgressIndicator mProgressIndicator;
private CheckBox mEncryptedAsDoNotMonitorCheckBox;
public SystemTalkgroupSelectionEditor(UserPreferences userPreferences, PlaylistManager playlistManager)
public SystemTalkgroupSelectionEditor()
{
}
@PostConstruct
public void postConstruct()
{
//Register to receive flash alias box requests
MyEventBus.getGlobalEventBus().register(this);
mUserPreferences = userPreferences;
mPlaylistManager = playlistManager;
ALL_TALKGROUPS.setName("(All Talkgroups)");
setPadding(new Insets(10,0,0,0));
@@ -369,7 +375,7 @@ public class SystemTalkgroupSelectionEditor extends GridPane
createdAliases.add(alias);
}
mPlaylistManager.getAliasModel().addAliases(createdAliases);
mAliasModel.addAliases(createdAliases);
}
/**
@@ -433,11 +439,6 @@ public class SystemTalkgroupSelectionEditor extends GridPane
private TalkgroupEditor getTalkgroupEditor()
{
if(mTalkgroupEditor == null)
{
mTalkgroupEditor = new TalkgroupEditor(mUserPreferences, mPlaylistManager);
}
return mTalkgroupEditor;
}
@@ -458,7 +459,7 @@ public class SystemTalkgroupSelectionEditor extends GridPane
{
Predicate<String> filterPredicate = s -> !s.contentEquals(AliasModel.NO_ALIAS_LIST);
FilteredList<String> filteredChannelList =
new FilteredList<>(mPlaylistManager.getAliasModel().aliasListNames(), filterPredicate);
new FilteredList<>(mAliasModel.aliasListNames(), filterPredicate);
mAliasListNameComboBox = new ComboBox<>(filteredChannelList);
mAliasListNameComboBox.setPrefWidth(150);
mAliasListNameComboBox.setOnAction(event -> updateAliasList(getAliasListNameComboBox()
@@ -487,7 +488,7 @@ public class SystemTalkgroupSelectionEditor extends GridPane
mAliasList.aliases().removeListener(mAliasListChangeListener);
}
mAliasList = mPlaylistManager.getAliasModel().getAliasList(aliasListName);
mAliasList = mAliasModel.getAliasList(aliasListName);
if(mAliasList != null)
{
@@ -583,7 +584,7 @@ public class SystemTalkgroupSelectionEditor extends GridPane
if(name != null && !name.isEmpty())
{
name = name.trim();
mPlaylistManager.getAliasModel().addAliasList(name);
mAliasModel.addAliasList(name);
getAliasListNameComboBox().getSelectionModel().select(name);
}
});

View File

@@ -1,6 +1,6 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2020 Dennis Sheirer
* Copyright (C) 2014-2023 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
@@ -20,12 +20,13 @@
package io.github.dsheirer.gui.playlist.radioreference;
import io.github.dsheirer.alias.Alias;
import io.github.dsheirer.alias.AliasModel;
import io.github.dsheirer.eventbus.MyEventBus;
import io.github.dsheirer.gui.playlist.alias.ViewAliasRequest;
import io.github.dsheirer.playlist.PlaylistManager;
import io.github.dsheirer.preference.UserPreferences;
import io.github.dsheirer.rrapi.type.System;
import io.github.dsheirer.rrapi.type.Talkgroup;
import jakarta.annotation.PostConstruct;
import jakarta.annotation.Resource;
import javafx.geometry.HPos;
import javafx.geometry.Orientation;
import javafx.scene.control.Alert;
@@ -42,8 +43,8 @@ import org.slf4j.LoggerFactory;
public class TalkgroupEditor extends GridPane
{
private static final Logger mLog = LoggerFactory.getLogger(TalkgroupEditor.class);
private UserPreferences mUserPreferences;
private PlaylistManager mPlaylistManager;
@Resource
private AliasModel mAliasModel;
private TextField mAlphaTagTextField;
private TextField mDescriptionTextField;
private TextField mTalkgroupTextField;
@@ -64,11 +65,13 @@ public class TalkgroupEditor extends GridPane
private Alias mAlias;
private boolean mSetEncryptedDoNotMonitor;
public TalkgroupEditor(UserPreferences userPreferences, PlaylistManager playlistManager)
public TalkgroupEditor()
{
mUserPreferences = userPreferences;
mPlaylistManager = playlistManager;
}
@PostConstruct
public void postConstruct()
{
setHgap(5);
setVgap(5);
@@ -150,7 +153,6 @@ public class TalkgroupEditor extends GridPane
getChildren().add(getCreateAliasButton());
}
public void setTalkgroup(Talkgroup talkgroup, System system, RadioReferenceDecoder decoder, Alias alias,
String aliasListName, String group, boolean setEncryptedDoNotMonitor)
{
@@ -292,7 +294,7 @@ public class TalkgroupEditor extends GridPane
alias.addAliasID(new io.github.dsheirer.alias.id.priority.Priority(priority));
}
mPlaylistManager.getAliasModel().addAlias(alias);
mAliasModel.addAlias(alias);
}
});
}

View File

@@ -1,6 +1,6 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2022 Dennis Sheirer
* Copyright (C) 2014-2023 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
@@ -19,11 +19,14 @@
package io.github.dsheirer.gui.playlist.streaming;
import io.github.dsheirer.alias.AliasModel;
import io.github.dsheirer.audio.broadcast.BroadcastConfiguration;
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.gui.playlist.Editor;
import io.github.dsheirer.playlist.PlaylistManager;
import jakarta.annotation.PostConstruct;
import jakarta.annotation.Resource;
import javafx.application.Platform;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
@@ -45,7 +48,10 @@ import org.controlsfx.control.ToggleSwitch;
*/
public abstract class AbstractBroadcastEditor<T extends BroadcastConfiguration> extends Editor<T>
{
private PlaylistManager mPlaylistManager;
@Resource
private AliasModel mAliasModel;
@Resource
private BroadcastModel mBroadcastModel;
private Button mSaveButton;
private Button mResetButton;
private TextField mFormatField;
@@ -55,11 +61,14 @@ public abstract class AbstractBroadcastEditor<T extends BroadcastConfiguration>
/**
* Constructs an instance
* @param playlistManager for access the stream manager
*/
public AbstractBroadcastEditor(PlaylistManager playlistManager)
public AbstractBroadcastEditor()
{
}
@PostConstruct
public void postConstruct()
{
mPlaylistManager = playlistManager;
getFormatField().setText(getBroadcastServerType().toString());
VBox buttonBox = new VBox();
@@ -73,11 +82,6 @@ public abstract class AbstractBroadcastEditor<T extends BroadcastConfiguration>
getChildren().addAll(editorBox);
}
protected PlaylistManager getPlaylistManager()
{
return mPlaylistManager;
}
protected abstract GridPane getEditorPane();
public abstract BroadcastServerType getBroadcastServerType();
@@ -117,7 +121,7 @@ public abstract class AbstractBroadcastEditor<T extends BroadcastConfiguration>
if(previousName != null && !previousName.isEmpty() && !updatedName.contentEquals(previousName))
{
if(getPlaylistManager().getAliasModel().hasAliasesWithBroadcastChannel(previousName))
if(mAliasModel.hasAliasesWithBroadcastChannel(previousName))
{
Alert alert = new Alert(Alert.AlertType.CONFIRMATION);
alert.getButtonTypes().clear();
@@ -136,15 +140,14 @@ public abstract class AbstractBroadcastEditor<T extends BroadcastConfiguration>
alert.showAndWait().ifPresent(buttonType -> {
if(buttonType == ButtonType.YES)
{
getPlaylistManager().getAliasModel().updateBroadcastChannel(previousName, updatedName);
mAliasModel.updateBroadcastChannel(previousName, updatedName);
}
});
}
}
//TODO: remove this after we get rid of Swing tables so that we don't have to announce these changes.
mPlaylistManager.getBroadcastModel().process(new BroadcastEvent(configuration,
BroadcastEvent.Event.CONFIGURATION_CHANGE));
mBroadcastModel.process(new BroadcastEvent(configuration, BroadcastEvent.Event.CONFIGURATION_CHANGE));
}
modifiedProperty().set(false);

View File

@@ -1,30 +1,26 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2023 Dennis Sheirer
*
* * ******************************************************************************
* * Copyright (C) 2014-2020 Dennis Sheirer
* *
* * This program is free software: you can redistribute it and/or modify
* * it under the terms of the GNU General Public License as published by
* * the Free Software Foundation, either version 3 of the License, or
* * (at your option) any later version.
* *
* * This program is distributed in the hope that it will be useful,
* * but WITHOUT ANY WARRANTY; without even the implied warranty of
* * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* * GNU General Public License for more details.
* *
* * You should have received a copy of the GNU General Public License
* * along with this program. If not, see <http://www.gnu.org/licenses/>
* * *****************************************************************************
* 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.gui.playlist.streaming;
import io.github.dsheirer.audio.broadcast.BroadcastConfiguration;
import io.github.dsheirer.gui.control.IntegerTextField;
import io.github.dsheirer.playlist.PlaylistManager;
import javafx.scene.control.CheckBox;
import javafx.scene.control.PasswordField;
import javafx.scene.control.TextField;
@@ -46,9 +42,8 @@ public abstract class AbstractStreamEditor<T extends BroadcastConfiguration> ext
/**
* Constructs an instance
*/
public AbstractStreamEditor(PlaylistManager playlistManager)
public AbstractStreamEditor()
{
super(playlistManager);
}
@Override

View File

@@ -1,6 +1,6 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2020 Dennis Sheirer
* Copyright (C) 2014-2023 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 @@ import io.github.dsheirer.audio.broadcast.BroadcastServerType;
import io.github.dsheirer.audio.broadcast.broadcastify.BroadcastifyCallBroadcaster;
import io.github.dsheirer.audio.broadcast.broadcastify.BroadcastifyCallConfiguration;
import io.github.dsheirer.gui.control.IntegerTextField;
import io.github.dsheirer.playlist.PlaylistManager;
import javafx.geometry.HPos;
import javafx.geometry.Insets;
import javafx.scene.control.Alert;
@@ -32,9 +31,9 @@ import javafx.scene.control.ButtonType;
import javafx.scene.control.Label;
import javafx.scene.control.TextField;
import javafx.scene.layout.GridPane;
import org.controlsfx.control.ToggleSwitch;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.controlsfx.control.ToggleSwitch;
/**
* Broadcastify calls API configuration editor
@@ -53,11 +52,9 @@ public class BroadcastifyCallEditor extends AbstractBroadcastEditor<Broadcastify
/**
* Constructs an instance
* @param playlistManager for accessing the broadcast model
*/
public BroadcastifyCallEditor(PlaylistManager playlistManager)
public BroadcastifyCallEditor()
{
super(playlistManager);
}
@Override

View File

@@ -1,23 +1,20 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2023 Dennis Sheirer
*
* * ******************************************************************************
* * Copyright (C) 2014-2020 Dennis Sheirer
* *
* * This program is free software: you can redistribute it and/or modify
* * it under the terms of the GNU General Public License as published by
* * the Free Software Foundation, either version 3 of the License, or
* * (at your option) any later version.
* *
* * This program is distributed in the hope that it will be useful,
* * but WITHOUT ANY WARRANTY; without even the implied warranty of
* * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* * GNU General Public License for more details.
* *
* * You should have received a copy of the GNU General Public License
* * along with this program. If not, see <http://www.gnu.org/licenses/>
* * *****************************************************************************
* 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.gui.playlist.streaming;
@@ -26,7 +23,6 @@ package io.github.dsheirer.gui.playlist.streaming;
import io.github.dsheirer.audio.broadcast.BroadcastServerType;
import io.github.dsheirer.audio.broadcast.broadcastify.BroadcastifyFeedConfiguration;
import io.github.dsheirer.gui.control.IntegerTextField;
import io.github.dsheirer.playlist.PlaylistManager;
import javafx.geometry.HPos;
import javafx.geometry.Insets;
import javafx.scene.control.Label;
@@ -42,9 +38,8 @@ public class BroadcastifyStreamEditor extends AbstractStreamEditor<BroadcastifyF
private TextField mMountPointTextField;
private IntegerTextField mFeedIdTextField;
public BroadcastifyStreamEditor(PlaylistManager playlistManager)
public BroadcastifyStreamEditor()
{
super(playlistManager);
}
@Override

View File

@@ -1,40 +1,31 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2023 Dennis Sheirer
*
* * ******************************************************************************
* * Copyright (C) 2014-2020 Dennis Sheirer
* *
* * This program is free software: you can redistribute it and/or modify
* * it under the terms of the GNU General Public License as published by
* * the Free Software Foundation, either version 3 of the License, or
* * (at your option) any later version.
* *
* * This program is distributed in the hope that it will be useful,
* * but WITHOUT ANY WARRANTY; without even the implied warranty of
* * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* * GNU General Public License for more details.
* *
* * You should have received a copy of the GNU General Public License
* * along with this program. If not, see <http://www.gnu.org/licenses/>
* * *****************************************************************************
* 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.gui.playlist.streaming;
import io.github.dsheirer.audio.broadcast.BroadcastServerType;
import io.github.dsheirer.playlist.PlaylistManager;
/**
* Stream configuration editor for Icecast 2.4+ HTTP broadcast
*/
public class IcecastHTTPStreamEditor extends IcecastStreamEditor
{
public IcecastHTTPStreamEditor(PlaylistManager playlistManager)
{
super(playlistManager);
}
@Override
public BroadcastServerType getBroadcastServerType()
{

View File

@@ -1,30 +1,26 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2023 Dennis Sheirer
*
* * ******************************************************************************
* * Copyright (C) 2014-2020 Dennis Sheirer
* *
* * This program is free software: you can redistribute it and/or modify
* * it under the terms of the GNU General Public License as published by
* * the Free Software Foundation, either version 3 of the License, or
* * (at your option) any later version.
* *
* * This program is distributed in the hope that it will be useful,
* * but WITHOUT ANY WARRANTY; without even the implied warranty of
* * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* * GNU General Public License for more details.
* *
* * You should have received a copy of the GNU General Public License
* * along with this program. If not, see <http://www.gnu.org/licenses/>
* * *****************************************************************************
* 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.gui.playlist.streaming;
import io.github.dsheirer.audio.broadcast.icecast.IcecastConfiguration;
import io.github.dsheirer.playlist.PlaylistManager;
import javafx.geometry.HPos;
import javafx.geometry.Insets;
import javafx.scene.control.Label;
@@ -45,9 +41,8 @@ public abstract class IcecastStreamEditor extends AbstractStreamEditor<IcecastCo
private TextField mGenreTextField;
private TextField mURLTextField;
public IcecastStreamEditor(PlaylistManager playlistManager)
public IcecastStreamEditor()
{
super(playlistManager);
}
@Override

View File

@@ -1,40 +1,31 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2023 Dennis Sheirer
*
* * ******************************************************************************
* * Copyright (C) 2014-2020 Dennis Sheirer
* *
* * This program is free software: you can redistribute it and/or modify
* * it under the terms of the GNU General Public License as published by
* * the Free Software Foundation, either version 3 of the License, or
* * (at your option) any later version.
* *
* * This program is distributed in the hope that it will be useful,
* * but WITHOUT ANY WARRANTY; without even the implied warranty of
* * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* * GNU General Public License for more details.
* *
* * You should have received a copy of the GNU General Public License
* * along with this program. If not, see <http://www.gnu.org/licenses/>
* * *****************************************************************************
* 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.gui.playlist.streaming;
import io.github.dsheirer.audio.broadcast.BroadcastServerType;
import io.github.dsheirer.playlist.PlaylistManager;
/**
* Stream configuration editor for Icecast 2.3 TCP broadcast
*/
public class IcecastTCPStreamEditor extends IcecastStreamEditor
{
public IcecastTCPStreamEditor(PlaylistManager playlistManager)
{
super(playlistManager);
}
@Override
public BroadcastServerType getBroadcastServerType()
{

View File

@@ -22,7 +22,6 @@ package io.github.dsheirer.gui.playlist.streaming;
import io.github.dsheirer.audio.broadcast.BroadcastServerType;
import io.github.dsheirer.audio.broadcast.rdioscanner.RdioScannerConfiguration;
import io.github.dsheirer.gui.control.IntegerTextField;
import io.github.dsheirer.playlist.PlaylistManager;
import javafx.geometry.HPos;
import javafx.geometry.Insets;
import javafx.scene.control.Label;
@@ -46,11 +45,9 @@ public class RdioScannerEditor extends AbstractBroadcastEditor<RdioScannerConfig
/**
* Constructs an instance
* @param playlistManager for accessing the broadcast model
*/
public RdioScannerEditor(PlaylistManager playlistManager)
public RdioScannerEditor()
{
super(playlistManager);
}
@Override

View File

@@ -1,23 +1,20 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2023 Dennis Sheirer
*
* * ******************************************************************************
* * Copyright (C) 2014-2020 Dennis Sheirer
* *
* * This program is free software: you can redistribute it and/or modify
* * it under the terms of the GNU General Public License as published by
* * the Free Software Foundation, either version 3 of the License, or
* * (at your option) any later version.
* *
* * This program is distributed in the hope that it will be useful,
* * but WITHOUT ANY WARRANTY; without even the implied warranty of
* * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* * GNU General Public License for more details.
* *
* * You should have received a copy of the GNU General Public License
* * along with this program. If not, see <http://www.gnu.org/licenses/>
* * *****************************************************************************
* 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.gui.playlist.streaming;
@@ -25,7 +22,6 @@ package io.github.dsheirer.gui.playlist.streaming;
import io.github.dsheirer.audio.broadcast.BroadcastServerType;
import io.github.dsheirer.audio.broadcast.shoutcast.v1.ShoutcastV1Configuration;
import io.github.dsheirer.playlist.PlaylistManager;
import javafx.geometry.HPos;
import javafx.geometry.Insets;
import javafx.scene.control.Label;
@@ -43,11 +39,6 @@ public class ShoutcastV1StreamEditor extends AbstractStreamEditor<ShoutcastV1Con
private TextField mGenreTextField;
private ToggleSwitch mPublicToggleSwitch;
public ShoutcastV1StreamEditor(PlaylistManager playlistManager)
{
super(playlistManager);
}
@Override
public BroadcastServerType getBroadcastServerType()
{

View File

@@ -1,23 +1,20 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2023 Dennis Sheirer
*
* * ******************************************************************************
* * Copyright (C) 2014-2020 Dennis Sheirer
* *
* * This program is free software: you can redistribute it and/or modify
* * it under the terms of the GNU General Public License as published by
* * the Free Software Foundation, either version 3 of the License, or
* * (at your option) any later version.
* *
* * This program is distributed in the hope that it will be useful,
* * but WITHOUT ANY WARRANTY; without even the implied warranty of
* * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* * GNU General Public License for more details.
* *
* * You should have received a copy of the GNU General Public License
* * along with this program. If not, see <http://www.gnu.org/licenses/>
* * *****************************************************************************
* 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.gui.playlist.streaming;
@@ -26,7 +23,6 @@ package io.github.dsheirer.gui.playlist.streaming;
import io.github.dsheirer.audio.broadcast.BroadcastServerType;
import io.github.dsheirer.audio.broadcast.shoutcast.v2.ShoutcastV2Configuration;
import io.github.dsheirer.gui.control.IntegerTextField;
import io.github.dsheirer.playlist.PlaylistManager;
import javafx.geometry.HPos;
import javafx.geometry.Insets;
import javafx.scene.control.Label;
@@ -46,11 +42,6 @@ public class ShoutcastV2StreamEditor extends AbstractStreamEditor<ShoutcastV2Con
private TextField mGenreTextField;
private ToggleSwitch mPublicToggleSwitch;
public ShoutcastV2StreamEditor(PlaylistManager playlistManager)
{
super(playlistManager);
}
@Override
public BroadcastServerType getBroadcastServerType()
{

View File

@@ -1,31 +1,33 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2023 Dennis Sheirer
*
* * ******************************************************************************
* * Copyright (C) 2014-2020 Dennis Sheirer
* *
* * This program is free software: you can redistribute it and/or modify
* * it under the terms of the GNU General Public License as published by
* * the Free Software Foundation, either version 3 of the License, or
* * (at your option) any later version.
* *
* * This program is distributed in the hope that it will be useful,
* * but WITHOUT ANY WARRANTY; without even the implied warranty of
* * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* * GNU General Public License for more details.
* *
* * You should have received a copy of the GNU General Public License
* * along with this program. If not, see <http://www.gnu.org/licenses/>
* * *****************************************************************************
* 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.gui.playlist.streaming;
import io.github.dsheirer.alias.Alias;
import io.github.dsheirer.alias.AliasModel;
import io.github.dsheirer.alias.id.broadcast.BroadcastChannel;
import io.github.dsheirer.audio.broadcast.BroadcastConfiguration;
import io.github.dsheirer.playlist.PlaylistManager;
import jakarta.annotation.PostConstruct;
import jakarta.annotation.Resource;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Predicate;
import javafx.collections.ListChangeListener;
import javafx.collections.transformation.FilteredList;
import javafx.collections.transformation.SortedList;
@@ -49,17 +51,14 @@ import org.controlsfx.control.textfield.TextFields;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Predicate;
/**
* Editor for managing which aliases are configured for a specific broadcast configuration audio stream
*/
public class StreamAliasSelectionEditor extends VBox
{
private final static Logger mLog = LoggerFactory.getLogger(StreamAliasSelectionEditor.class);
private PlaylistManager mPlaylistManager;
@Resource
private AliasModel mAliasModel;
private BroadcastConfiguration mSelectedBroadcastConfiguration;
private HBox mSearchBox;
private TextField mSearchField;
@@ -76,12 +75,14 @@ public class StreamAliasSelectionEditor extends VBox
/**
* Constructs an instance
* @param playlistManager for access to alias model
*/
public StreamAliasSelectionEditor(PlaylistManager playlistManager)
public StreamAliasSelectionEditor()
{
mPlaylistManager = playlistManager;
}
@PostConstruct
public void postConstruct()
{
VBox buttonBox = new VBox();
buttonBox.setMaxHeight(Double.MAX_VALUE);
buttonBox.setAlignment(Pos.CENTER);
@@ -125,8 +126,7 @@ public class StreamAliasSelectionEditor extends VBox
{
if(mAvailableFilteredList == null)
{
mAvailableFilteredList = new FilteredList<>(mPlaylistManager.getAliasModel().aliasList(),
mAvailableAliasPredicate);
mAvailableFilteredList = new FilteredList<>(mAliasModel.aliasList(), mAvailableAliasPredicate);
}
return mAvailableFilteredList;
@@ -136,8 +136,7 @@ public class StreamAliasSelectionEditor extends VBox
{
if(mSelectedFilteredList == null)
{
mSelectedFilteredList = new FilteredList<>(mPlaylistManager.getAliasModel().aliasList(),
mSelectedAliasPredicate);
mSelectedFilteredList = new FilteredList<>(mAliasModel.aliasList(), mSelectedAliasPredicate);
}
return mSelectedFilteredList;

View File

@@ -1,59 +0,0 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2022 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.gui.playlist.streaming;
import io.github.dsheirer.audio.broadcast.BroadcastServerType;
import io.github.dsheirer.playlist.PlaylistManager;
/**
* Factory for creating broadcast configuration editors
*/
public class StreamEditorFactory
{
/**
* Creates a new editor for the specified broadcast server type
* @param broadcastServerType to edit
* @return editor or the default unknown editor
*/
public static AbstractBroadcastEditor getEditor(BroadcastServerType broadcastServerType, PlaylistManager playlistManager)
{
switch(broadcastServerType)
{
case BROADCASTIFY:
return new BroadcastifyStreamEditor(playlistManager);
case RDIOSCANNER_CALL:
return new RdioScannerEditor(playlistManager);
case OPENMHZ:
return new OpenMHzEditor(playlistManager);
case BROADCASTIFY_CALL:
return new BroadcastifyCallEditor(playlistManager);
case ICECAST_HTTP:
return new IcecastHTTPStreamEditor(playlistManager);
case ICECAST_TCP:
return new IcecastTCPStreamEditor(playlistManager);
case SHOUTCAST_V1:
return new ShoutcastV1StreamEditor(playlistManager);
case SHOUTCAST_V2:
return new ShoutcastV2StreamEditor(playlistManager);
default:
return new UnknownStreamEditor(playlistManager);
}
}
}

View File

@@ -22,12 +22,15 @@ package io.github.dsheirer.gui.playlist.streaming;
import io.github.dsheirer.audio.broadcast.BroadcastConfiguration;
import io.github.dsheirer.audio.broadcast.BroadcastFactory;
import io.github.dsheirer.audio.broadcast.BroadcastFormat;
import io.github.dsheirer.audio.broadcast.BroadcastModel;
import io.github.dsheirer.audio.broadcast.BroadcastServerType;
import io.github.dsheirer.audio.broadcast.ConfiguredBroadcast;
import io.github.dsheirer.audio.broadcast.broadcastify.BroadcastifyFeedConfiguration;
import io.github.dsheirer.playlist.PlaylistManager;
import io.github.dsheirer.rrapi.type.UserFeedBroadcast;
import io.github.dsheirer.service.radioreference.RadioReference;
import io.github.dsheirer.util.ThreadPool;
import jakarta.annotation.PostConstruct;
import jakarta.annotation.Resource;
import java.util.ArrayList;
import java.util.EnumMap;
import java.util.List;
@@ -71,7 +74,15 @@ public class StreamingEditor extends SplitPane
{
private final static Logger mLog = LoggerFactory.getLogger(StreamingEditor.class);
private final PlaylistManager mPlaylistManager;
@Resource
private BroadcastModel mBroadcastModel;
@Resource
private RadioReference mRadioReference;
@Resource
private StreamAliasSelectionEditor mStreamAliasSelectionEditor;
@Resource
private List<AbstractBroadcastEditor> mBroadcastEditors;
private TableView<ConfiguredBroadcast> mConfiguredBroadcastTableView;
private MenuButton mNewButton;
private Button mDeleteButton;
@@ -81,24 +92,35 @@ public class StreamingEditor extends SplitPane
private Tab mAliasTab;
private Label mRadioReferenceLoginLabel;
private AbstractBroadcastEditor<?> mCurrentEditor;
private final UnknownStreamEditor mUnknownEditor;
private UnknownStreamEditor mUnknownEditor;
private Map<BroadcastServerType, AbstractBroadcastEditor<?>> mEditorMap = new EnumMap<>(BroadcastServerType.class);
private final List<UserFeedBroadcast> mBroadcastifyFeeds = new ArrayList<>();
private ScrollPane mEditorScrollPane;
private StreamAliasSelectionEditor mStreamAliasSelectionEditor;
private final StreamConfigurationEditorModificationListener mStreamConfigurationEditorModificationListener =
new StreamConfigurationEditorModificationListener();
/**
* Constructs an instance
* @param playlistManager for accessing streaming model
*/
public StreamingEditor(PlaylistManager playlistManager)
public StreamingEditor()
{
mPlaylistManager = playlistManager;
mUnknownEditor = new UnknownStreamEditor(mPlaylistManager);
mPlaylistManager.getRadioReference().availableProperty()
.addListener((observable, oldValue, newValue) -> refreshBroadcastifyStreams());
}
@PostConstruct
public void postConstruct()
{
//Spring instantiates stream editors and we load them into a lookup map
for(AbstractBroadcastEditor editor: mBroadcastEditors)
{
mEditorMap.put(editor.getBroadcastServerType(), editor);
if(editor instanceof UnknownStreamEditor unknown)
{
mUnknownEditor = unknown;
}
}
mRadioReference.availableProperty().addListener((observable, oldValue, newValue) -> refreshBroadcastifyStreams());
refreshBroadcastifyStreams();
VBox buttonsBox = new VBox();
@@ -200,16 +222,7 @@ public class StreamingEditor extends SplitPane
if(editor == null)
{
editor = StreamEditorFactory.getEditor(configType, mPlaylistManager);
if(editor != null)
{
mEditorMap.put(configType, editor);
}
}
if(editor == null)
{
mLog.warn("Unable to find streaming editor for server type: " + editorType);
editor = mUnknownEditor;
}
@@ -230,12 +243,12 @@ public class StreamingEditor extends SplitPane
*/
private void refreshBroadcastifyStreams()
{
if(mPlaylistManager.getRadioReference().availableProperty().get())
if(mRadioReference.availableProperty().get())
{
ThreadPool.CACHED.submit(() -> {
try
{
List<UserFeedBroadcast> feeds = mPlaylistManager.getRadioReference().getService().getUserFeeds();
List<UserFeedBroadcast> feeds = mRadioReference.getService().getUserFeeds();
mBroadcastifyFeeds.clear();
mBroadcastifyFeeds.addAll(feeds);
}
@@ -249,11 +262,6 @@ public class StreamingEditor extends SplitPane
private StreamAliasSelectionEditor getStreamAliasSelectionEditor()
{
if(mStreamAliasSelectionEditor == null)
{
mStreamAliasSelectionEditor = new StreamAliasSelectionEditor(mPlaylistManager);
}
return mStreamAliasSelectionEditor;
}
@@ -262,7 +270,7 @@ public class StreamingEditor extends SplitPane
if(mRadioReferenceLoginLabel == null)
{
mRadioReferenceLoginLabel = new Label("Note: use Radio Reference tab to login and access Broadcastify stream configuration(s)");
mRadioReferenceLoginLabel.visibleProperty().bind(mPlaylistManager.getRadioReference().availableProperty().not());
mRadioReferenceLoginLabel.visibleProperty().bind(mRadioReference.availableProperty().not());
}
return mRadioReferenceLoginLabel;
@@ -325,7 +333,7 @@ public class StreamingEditor extends SplitPane
for(UserFeedBroadcast feed: mBroadcastifyFeeds)
{
//Only show a menu item for the feed if it's not already defined
if(mPlaylistManager.getBroadcastModel().getBroadcastConfiguration(feed.getDescription()) == null)
if(mBroadcastModel.getBroadcastConfiguration(feed.getDescription()) == null)
{
mNewButton.getItems().add(new CreateBroadcastifyMenuItem(feed));
}
@@ -382,7 +390,7 @@ public class StreamingEditor extends SplitPane
if(result.get() == ButtonType.YES)
{
mPlaylistManager.getBroadcastModel().removeBroadcastConfiguration(config);
mBroadcastModel.removeBroadcastConfiguration(config);
}
}
});
@@ -398,7 +406,7 @@ public class StreamingEditor extends SplitPane
mConfiguredBroadcastTableView = new TableView<>();
mConfiguredBroadcastTableView.setPlaceholder(new Label("Click the New button to create a new " +
"audio streaming configuration"));
mConfiguredBroadcastTableView.setItems(mPlaylistManager.getBroadcastModel().getConfiguredBroadcasts());
mConfiguredBroadcastTableView.setItems(mBroadcastModel.getConfiguredBroadcasts());
TableColumn<ConfiguredBroadcast,Boolean> enabledColumn = new TableColumn("Enabled");
enabledColumn.setCellValueFactory(new PropertyValueFactory<>("enabled"));
@@ -465,8 +473,7 @@ public class StreamingEditor extends SplitPane
if(configuration != null)
{
ConfiguredBroadcast configuredBroadcast = mPlaylistManager.getBroadcastModel()
.addBroadcastConfiguration(configuration);
ConfiguredBroadcast configuredBroadcast = mBroadcastModel.addBroadcastConfiguration(configuration);
getConfiguredBroadcastTableView().getSelectionModel().select(configuredBroadcast);
}
});
@@ -487,8 +494,7 @@ public class StreamingEditor extends SplitPane
setOnAction(event -> {
BroadcastConfiguration config = BroadcastFactory.getConfiguration(mBroadcastServerType, BroadcastFormat.MP3);
ConfiguredBroadcast configuredBroadcast = mPlaylistManager.getBroadcastModel()
.addBroadcastConfiguration(config);
ConfiguredBroadcast configuredBroadcast = mBroadcastModel.addBroadcastConfiguration(config);
getConfiguredBroadcastTableView().getSelectionModel().select(configuredBroadcast);
});
}

View File

@@ -1,23 +1,20 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2023 Dennis Sheirer
*
* * ******************************************************************************
* * Copyright (C) 2014-2020 Dennis Sheirer
* *
* * This program is free software: you can redistribute it and/or modify
* * it under the terms of the GNU General Public License as published by
* * the Free Software Foundation, either version 3 of the License, or
* * (at your option) any later version.
* *
* * This program is distributed in the hope that it will be useful,
* * but WITHOUT ANY WARRANTY; without even the implied warranty of
* * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* * GNU General Public License for more details.
* *
* * You should have received a copy of the GNU General Public License
* * along with this program. If not, see <http://www.gnu.org/licenses/>
* * *****************************************************************************
* 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.gui.playlist.streaming;
@@ -25,7 +22,6 @@ package io.github.dsheirer.gui.playlist.streaming;
import io.github.dsheirer.audio.broadcast.BroadcastConfiguration;
import io.github.dsheirer.audio.broadcast.BroadcastServerType;
import io.github.dsheirer.playlist.PlaylistManager;
import javafx.geometry.HPos;
import javafx.geometry.Insets;
import javafx.scene.control.Label;
@@ -38,11 +34,6 @@ public class UnknownStreamEditor extends AbstractStreamEditor<BroadcastConfigura
{
private GridPane mEditorPane;
public UnknownStreamEditor(PlaylistManager playlistManager)
{
super(playlistManager);
}
public BroadcastServerType getBroadcastServerType()
{
return BroadcastServerType.UNKNOWN;

View File

@@ -20,6 +20,7 @@
package io.github.dsheirer.gui.power;
import io.github.dsheirer.controller.channel.Channel;
import io.github.dsheirer.controller.channel.ChannelProcessingManager;
import io.github.dsheirer.dsp.filter.channelizer.PolyphaseChannelSource;
import io.github.dsheirer.dsp.squelch.ISquelchConfiguration;
import io.github.dsheirer.gui.control.DbPowerMeter;
@@ -27,7 +28,6 @@ import io.github.dsheirer.module.ProcessingChain;
import io.github.dsheirer.playlist.PlaylistManager;
import io.github.dsheirer.sample.Listener;
import io.github.dsheirer.sample.complex.ComplexSamplesToNativeBufferModule;
import io.github.dsheirer.settings.SettingsManager;
import io.github.dsheirer.source.Source;
import io.github.dsheirer.source.SourceEvent;
import io.github.dsheirer.source.tuner.channel.HalfBandTunerChannelSource;
@@ -38,6 +38,8 @@ import io.github.dsheirer.spectrum.FrequencyOverlayPanel;
import io.github.dsheirer.spectrum.SpectrumPanel;
import io.github.dsheirer.spectrum.converter.ComplexDecibelConverter;
import io.github.dsheirer.spectrum.converter.DFTResultsConverter;
import jakarta.annotation.PostConstruct;
import jakarta.annotation.Resource;
import java.awt.Component;
import java.awt.EventQueue;
import java.awt.event.ComponentEvent;
@@ -73,14 +75,19 @@ public class ChannelPowerPanel extends JPanel implements Listener<ProcessingChai
private static final DecimalFormat DECIMAL_FORMAT = new DecimalFormat("0.0");
private static final DecimalFormat FREQUENCY_FORMAT = new DecimalFormat("0.00000");
private static final String NOT_AVAILABLE = "Not Available";
@Resource
private ChannelProcessingManager mChannelProcessingManager;
@Resource
private FrequencyOverlayPanel mFrequencyOverlayPanel;
@Resource
private PlaylistManager mPlaylistManager;
@Resource
private SpectrumPanel mSpectrumPanel;
private ProcessingChain mProcessingChain;
private ComplexSamplesToNativeBufferModule mSampleStreamTapModule = new ComplexSamplesToNativeBufferModule();
private ComplexDftProcessor mComplexDftProcessor = new ComplexDftProcessor();
private DFTResultsConverter mDFTResultsConverter = new ComplexDecibelConverter();
private JLayeredPane mLayeredPanel;
private SpectrumPanel mSpectrumPanel;
private FrequencyOverlayPanel mFrequencyOverlayPanel;
private JLayeredPane mLayeredPanel = new JLayeredPane();
private DbPowerMeter mPowerMeter = new DbPowerMeter();
private PeakMonitor mPeakMonitor = new PeakMonitor(DbPowerMeter.DEFAULT_MINIMUM_POWER);
private SourceEventProcessor mSourceEventProcessor = new SourceEventProcessor();
@@ -103,10 +110,13 @@ public class ChannelPowerPanel extends JPanel implements Listener<ProcessingChai
/**
* Constructs an instance.
*/
public ChannelPowerPanel(PlaylistManager playlistManager, SettingsManager settingsManager)
public ChannelPowerPanel()
{
mPlaylistManager = playlistManager;
}
@PostConstruct
public void postConstruct()
{
setLayout(new MigLayout("", "[][][][grow,fill]", "[grow,fill]"));
mPowerMeter.setPeakVisible(true);
mPowerMeter.setSquelchThresholdVisible(true);
@@ -227,16 +237,12 @@ public class ChannelPowerPanel extends JPanel implements Listener<ProcessingChai
fftPanel.add(labelPanel, "wrap");
mFrequencyOverlayPanel = new FrequencyOverlayPanel(settingsManager);
mSpectrumPanel = new SpectrumPanel(settingsManager);
mSpectrumPanel.setSampleSize(18.0);
/**
* The layered pane holds the overlapping spectrum and channel panels
* and manages the sizing of each panel with the resize listener
*/
mLayeredPanel = new JLayeredPane();
mLayeredPanel.addComponentListener(new ResizeListener());
/**
@@ -346,7 +352,7 @@ public class ChannelPowerPanel extends JPanel implements Listener<ProcessingChai
{
if(mProcessingChain != null)
{
Channel channel = mPlaylistManager.getChannelProcessingManager().getChannel(mProcessingChain);
Channel channel = mChannelProcessingManager.getChannel(mProcessingChain);
if(channel != null && channel.getDecodeConfiguration() instanceof ISquelchConfiguration)
{
@@ -363,7 +369,7 @@ public class ChannelPowerPanel extends JPanel implements Listener<ProcessingChai
*/
private void setConfigSquelchAutoTrack(boolean autoTrack)
{
Channel channel = mPlaylistManager.getChannelProcessingManager().getChannel(mProcessingChain);
Channel channel = mChannelProcessingManager.getChannel(mProcessingChain);
if(channel != null && channel.getDecodeConfiguration() instanceof ISquelchConfiguration)
{
@@ -440,7 +446,7 @@ public class ChannelPowerPanel extends JPanel implements Listener<ProcessingChai
mFrequencyOverlayPanel.process(SourceEvent.sampleRateChange(tcs.getSampleRate()));
}
Channel channel = mPlaylistManager.getChannelProcessingManager().getChannel(mProcessingChain);
Channel channel = mChannelProcessingManager.getChannel(mProcessingChain);
if(channel != null)
{

View File

@@ -1,50 +1,62 @@
/*******************************************************************************
* sdr-trunk
* Copyright (C) 2014-2018 Dennis Sheirer
/*
* *****************************************************************************
* Copyright (C) 2014-2023 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.gui.preference;
import io.github.dsheirer.preference.TimestampFormat;
import io.github.dsheirer.preference.UserPreferences;
import io.github.dsheirer.preference.event.DecodeEventPreference;
import jakarta.annotation.PostConstruct;
import java.util.Date;
import javafx.geometry.HPos;
import javafx.geometry.Insets;
import javafx.scene.control.ChoiceBox;
import javafx.scene.control.Label;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.HBox;
import java.util.Date;
/**
* Preference settings for channel event view
*/
public class DecodeEventViewPreferenceEditor extends HBox
public class DecodeEventViewPreferenceEditor extends PreferenceEditor
{
private DecodeEventPreference mDecodeEventPreference;
private GridPane mEditorPane;
private ChoiceBox<DisplayableTimestamp> mTimestampFormatChoiceBox;
private Label mTimestampFormatLabel;
public DecodeEventViewPreferenceEditor(UserPreferences userPreferences)
public DecodeEventViewPreferenceEditor()
{
mDecodeEventPreference = userPreferences.getDecodeEventPreference();
}
@PostConstruct
public void postConstruct()
{
mDecodeEventPreference = getUserPreferences().getDecodeEventPreference();
getChildren().add(getEditorPane());
}
@Override
public PreferenceEditorType getPreferenceEditorType()
{
return PreferenceEditorType.CHANNEL_EVENT;
}
private GridPane getEditorPane()
{
if(mEditorPane == null)

View File

@@ -0,0 +1,40 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2023 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.gui.preference;
import io.github.dsheirer.preference.UserPreferences;
import jakarta.annotation.Resource;
import javafx.scene.layout.VBox;
/**
* Preference editor base class.
*/
public abstract class PreferenceEditor extends VBox
{
@Resource
private UserPreferences mUserPreferences;
protected UserPreferences getUserPreferences()
{
return mUserPreferences;
}
public abstract PreferenceEditorType getPreferenceEditorType();
}

View File

@@ -1,69 +0,0 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2023 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.gui.preference;
import io.github.dsheirer.gui.preference.application.ApplicationPreferenceEditor;
import io.github.dsheirer.gui.preference.calibration.VectorCalibrationPreferenceEditor;
import io.github.dsheirer.gui.preference.call.CallManagementPreferenceEditor;
import io.github.dsheirer.gui.preference.decoder.JmbeLibraryPreferenceEditor;
import io.github.dsheirer.gui.preference.directory.DirectoryPreferenceEditor;
import io.github.dsheirer.gui.preference.mp3.MP3PreferenceEditor;
import io.github.dsheirer.gui.preference.playback.PlaybackPreferenceEditor;
import io.github.dsheirer.gui.preference.record.RecordPreferenceEditor;
import io.github.dsheirer.gui.preference.tuner.TunerPreferenceEditor;
import io.github.dsheirer.preference.UserPreferences;
import javafx.scene.Node;
/**
* Creates an editor for the specified preference editor type
*/
public class PreferenceEditorFactory
{
public static Node getEditor(PreferenceEditorType preferenceEditorType, UserPreferences userPreferences)
{
switch(preferenceEditorType)
{
case APPLICATION:
return new ApplicationPreferenceEditor(userPreferences);
case AUDIO_CALL_MANAGEMENT:
return new CallManagementPreferenceEditor(userPreferences);
case AUDIO_MP3:
return new MP3PreferenceEditor(userPreferences);
case AUDIO_OUTPUT:
return new PlaybackPreferenceEditor(userPreferences);
case AUDIO_RECORD:
return new RecordPreferenceEditor(userPreferences);
case CHANNEL_EVENT:
return new DecodeEventViewPreferenceEditor(userPreferences);
case DIRECTORY:
return new DirectoryPreferenceEditor(userPreferences);
case JMBE_LIBRARY:
return new JmbeLibraryPreferenceEditor(userPreferences);
case SOURCE_TUNERS:
return new TunerPreferenceEditor(userPreferences);
case TALKGROUP_FORMAT:
return new TalkgroupFormatPreferenceEditor(userPreferences);
case VECTOR_CALIBRATION:
return new VectorCalibrationPreferenceEditor(userPreferences);
}
return null;
}
}

View File

@@ -1,6 +1,6 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2023 Dennis Sheirer
* Copyright (C) 2014-2024 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
@@ -30,9 +30,9 @@ public enum PreferenceEditorType
JMBE_LIBRARY("JMBE Audio Library"),
AUDIO_MP3("MP3"),
AUDIO_RECORD("Record"),
AUDIO_OUTPUT("Output/Tones"),
AUDIO_PLAYBACK("Playback/Tones"),
AUDIO_CALL_MANAGEMENT("Call Management"),
SOURCE_TUNERS("Tuners"),
TUNERS("Tuners"),
TALKGROUP_FORMAT("Talkgroup & Radio ID"),
VECTOR_CALIBRATION("Vector Calibration"),
DEFAULT("Default");

View File

@@ -1,7 +1,6 @@
/*
* ******************************************************************************
* sdrtrunk
* Copyright (C) 2014-2018 Dennis Sheirer
* *****************************************************************************
* Copyright (C) 2014-2023 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
@@ -15,15 +14,16 @@
*
* 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.preference;
import io.github.dsheirer.preference.UserPreferences;
import io.github.dsheirer.preference.identifier.IntegerFormat;
import io.github.dsheirer.preference.identifier.TalkgroupFormatPreference;
import io.github.dsheirer.protocol.Protocol;
import jakarta.annotation.PostConstruct;
import java.util.Set;
import javafx.geometry.HPos;
import javafx.geometry.Insets;
import javafx.geometry.Orientation;
@@ -32,25 +32,33 @@ import javafx.scene.control.ChoiceBox;
import javafx.scene.control.Label;
import javafx.scene.control.Separator;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.HBox;
import java.util.Set;
/**
* Preference settings for channel event view
*/
public class TalkgroupFormatPreferenceEditor extends HBox
public class TalkgroupFormatPreferenceEditor extends PreferenceEditor
{
private TalkgroupFormatPreference mTalkgroupFormatPreference;
private GridPane mEditorPane;
public TalkgroupFormatPreferenceEditor(UserPreferences userPreferences)
public TalkgroupFormatPreferenceEditor()
{
mTalkgroupFormatPreference = userPreferences.getTalkgroupFormatPreference();
}
@PostConstruct
public void postConstruct()
{
mTalkgroupFormatPreference = getUserPreferences().getTalkgroupFormatPreference();
getChildren().add(getEditorPane());
}
@Override
public PreferenceEditorType getPreferenceEditorType()
{
return PreferenceEditorType.TALKGROUP_FORMAT;
}
private GridPane getEditorPane()
{
if(mEditorPane == null)

View File

@@ -1,6 +1,6 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2023 Dennis Sheirer
* Copyright (C) 2014-2024 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
@@ -21,8 +21,10 @@ package io.github.dsheirer.gui.preference;
import io.github.dsheirer.eventbus.MyEventBus;
import io.github.dsheirer.gui.playlist.ViewPlaylistRequest;
import io.github.dsheirer.preference.UserPreferences;
import jakarta.annotation.PostConstruct;
import jakarta.annotation.Resource;
import java.util.EnumMap;
import java.util.List;
import java.util.Map;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
@@ -53,8 +55,9 @@ public class UserPreferencesEditor extends BorderPane
{
private final static Logger mLog = LoggerFactory.getLogger(UserPreferencesEditor.class);
private Map<PreferenceEditorType,Node> mEditors = new EnumMap<>(PreferenceEditorType.class);
private UserPreferences mUserPreferences;
@Resource
private List<PreferenceEditor> mPreferenceEditors;
private Map<PreferenceEditorType,Node> mEditorMap = new EnumMap<>(PreferenceEditorType.class);
private MenuBar mMenuBar;
private TreeView mEditorSelectionTreeView;
private VBox mEditorAndButtonsBox;
@@ -63,31 +66,29 @@ public class UserPreferencesEditor extends BorderPane
/**
* Constructs an instance
*
* @param userPreferences to edit
*/
public UserPreferencesEditor(UserPreferences userPreferences)
public UserPreferencesEditor()
{
mUserPreferences = userPreferences;
}
@PostConstruct
public void postConstruct()
{
//Spring auto-constructs all of our editors for us with the @Resource tagged member variable.
for(PreferenceEditor editor: mPreferenceEditors)
{
mEditorMap.put(editor.getPreferenceEditorType(), editor);
}
mEditorMap.put(PreferenceEditorType.DEFAULT, getDefaultEditor());
setTop(getMenuBar());
HBox contentBox = new HBox();
HBox.setHgrow(getEditorAndButtonsBox(), Priority.ALWAYS);
contentBox.getChildren().addAll(getEditorSelectionTreeView(), getEditorAndButtonsBox());
setCenter(contentBox);
}
private UserPreferences getUserPreferences()
{
if(mUserPreferences == null)
{
mUserPreferences = new UserPreferences();
}
return mUserPreferences;
}
/**
* Shows the editor specified in the request by scrolling the editor view tree to the selected item.
*/
@@ -141,7 +142,7 @@ public class UserPreferencesEditor extends BorderPane
private Node getDefaultEditor()
{
Node editor = mEditors.get(PreferenceEditorType.DEFAULT);
Node editor = mEditorMap.get(PreferenceEditorType.DEFAULT);
if(editor == null)
{
@@ -149,7 +150,7 @@ public class UserPreferencesEditor extends BorderPane
defaultEditor.setPadding(new Insets(10, 10, 10, 10));
Label label = new Label("Please select a preference ...");
defaultEditor.getChildren().add(label);
mEditors.put(PreferenceEditorType.DEFAULT, defaultEditor);
mEditorMap.put(PreferenceEditorType.DEFAULT, defaultEditor);
editor = defaultEditor;
}
@@ -173,7 +174,7 @@ public class UserPreferencesEditor extends BorderPane
TreeItem<String> audioItem = new TreeItem<>("Audio");
audioItem.getChildren().add(new TreeItem(PreferenceEditorType.AUDIO_CALL_MANAGEMENT));
audioItem.getChildren().add(new TreeItem(PreferenceEditorType.AUDIO_MP3));
audioItem.getChildren().add(new TreeItem(PreferenceEditorType.AUDIO_OUTPUT));
audioItem.getChildren().add(new TreeItem(PreferenceEditorType.AUDIO_PLAYBACK));
audioItem.getChildren().add(new TreeItem(PreferenceEditorType.AUDIO_RECORD));
treeRoot.getChildren().add(audioItem);
audioItem.setExpanded(true);
@@ -200,7 +201,7 @@ public class UserPreferencesEditor extends BorderPane
storageItem.setExpanded(true);
TreeItem<String> sourceItem = new TreeItem<>("Source");
sourceItem.getChildren().add(new TreeItem(PreferenceEditorType.SOURCE_TUNERS));
sourceItem.getChildren().add(new TreeItem(PreferenceEditorType.TUNERS));
treeRoot.getChildren().add(sourceItem);
sourceItem.setExpanded(true);
@@ -266,19 +267,11 @@ public class UserPreferencesEditor extends BorderPane
private void setEditor(PreferenceEditorType type)
{
Node editor = mEditors.get(type);
Node editor = mEditorMap.get(type);
if(editor == null)
{
if(type == PreferenceEditorType.DEFAULT)
{
editor = getDefaultEditor();
}
else
{
editor = PreferenceEditorFactory.getEditor(type, getUserPreferences());
mEditors.put(type, editor);
}
editor = mEditorMap.get(PreferenceEditorType.DEFAULT);
}
getEditorAndButtonsBox().getChildren().remove(mEditor);

View File

@@ -19,20 +19,21 @@
package io.github.dsheirer.gui.preference.application;
import io.github.dsheirer.preference.UserPreferences;
import io.github.dsheirer.gui.preference.PreferenceEditor;
import io.github.dsheirer.gui.preference.PreferenceEditorType;
import io.github.dsheirer.preference.application.ApplicationPreference;
import jakarta.annotation.PostConstruct;
import javafx.geometry.HPos;
import javafx.geometry.Insets;
import javafx.scene.control.Label;
import javafx.scene.control.Spinner;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.HBox;
/**
* Preference settings for application
*/
public class ApplicationPreferenceEditor extends HBox
public class ApplicationPreferenceEditor extends PreferenceEditor
{
private ApplicationPreference mApplicationPreference;
private GridPane mEditorPane;
@@ -41,14 +42,24 @@ public class ApplicationPreferenceEditor extends HBox
/**
* Constructs an instance
* @param userPreferences for obtaining reference to preference.
*/
public ApplicationPreferenceEditor(UserPreferences userPreferences)
public ApplicationPreferenceEditor()
{
mApplicationPreference = userPreferences.getApplicationPreference();
}
@PostConstruct
public void postConstruct()
{
mApplicationPreference = getUserPreferences().getApplicationPreference();
getChildren().add(getEditorPane());
}
@Override
public PreferenceEditorType getPreferenceEditorType()
{
return PreferenceEditorType.APPLICATION;
}
private GridPane getEditorPane()
{
if(mEditorPane == null)

View File

@@ -1,6 +1,6 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2022 Dennis Sheirer
* Copyright (C) 2014-2023 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
@@ -24,13 +24,16 @@ import ch.qos.logback.classic.LoggerContext;
import com.google.common.eventbus.Subscribe;
import io.github.dsheirer.eventbus.MyEventBus;
import io.github.dsheirer.gui.preference.CalibrateRequest;
import io.github.dsheirer.gui.preference.PreferenceEditor;
import io.github.dsheirer.gui.preference.PreferenceEditorType;
import io.github.dsheirer.log.TextAreaLogAppender;
import io.github.dsheirer.preference.UserPreferences;
import io.github.dsheirer.preference.calibration.VectorCalibrationPreference;
import io.github.dsheirer.util.ThreadPool;
import io.github.dsheirer.vector.calibrate.Calibration;
import io.github.dsheirer.vector.calibrate.CalibrationException;
import io.github.dsheirer.vector.calibrate.CalibrationManager;
import jakarta.annotation.PostConstruct;
import java.util.List;
import javafx.application.Platform;
import javafx.geometry.HPos;
import javafx.geometry.Insets;
@@ -48,12 +51,10 @@ import org.controlsfx.control.ToggleSwitch;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.List;
/**
* Preference settings for duplicate call audio handling
*/
public class VectorCalibrationPreferenceEditor extends HBox
public class VectorCalibrationPreferenceEditor extends PreferenceEditor
{
private final static Logger mLog = LoggerFactory.getLogger(VectorCalibrationPreferenceEditor.class);
private VectorCalibrationPreference mPreference;
@@ -71,10 +72,15 @@ public class VectorCalibrationPreferenceEditor extends HBox
/**
* Constructs an instance
*/
public VectorCalibrationPreferenceEditor(UserPreferences userPreferences)
public VectorCalibrationPreferenceEditor()
{
}
@PostConstruct
public void postConstruct()
{
MyEventBus.getGlobalEventBus().register(this);
mPreference = userPreferences.getVectorCalibrationPreference();
mPreference = getUserPreferences().getVectorCalibrationPreference();
setMaxHeight(Double.MAX_VALUE);
setMaxWidth(Double.MAX_VALUE);
@@ -95,6 +101,12 @@ public class VectorCalibrationPreferenceEditor extends HBox
getChildren().add(vbox);
}
@Override
public PreferenceEditorType getPreferenceEditorType()
{
return PreferenceEditorType.VECTOR_CALIBRATION;
}
@Subscribe
public void process(CalibrateRequest request)
{

View File

@@ -20,8 +20,10 @@
package io.github.dsheirer.gui.preference.call;
import io.github.dsheirer.audio.broadcast.PatchGroupStreamingOption;
import io.github.dsheirer.preference.UserPreferences;
import io.github.dsheirer.gui.preference.PreferenceEditor;
import io.github.dsheirer.gui.preference.PreferenceEditorType;
import io.github.dsheirer.preference.duplicate.CallManagementPreference;
import jakarta.annotation.PostConstruct;
import javafx.beans.binding.Bindings;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
@@ -39,7 +41,7 @@ import org.slf4j.LoggerFactory;
/**
* Preference settings for audio call management for duplicate calls and patch group streaming.
*/
public class CallManagementPreferenceEditor extends HBox
public class CallManagementPreferenceEditor extends PreferenceEditor
{
private final static Logger mLog = LoggerFactory.getLogger(CallManagementPreferenceEditor.class);
private CallManagementPreference mPreference;
@@ -54,14 +56,24 @@ public class CallManagementPreferenceEditor extends HBox
/**
* Constructs an instance
*/
public CallManagementPreferenceEditor(UserPreferences userPreferences)
public CallManagementPreferenceEditor()
{
mPreference = userPreferences.getDuplicateCallDetectionPreference();
}
@PostConstruct
public void postConstruct()
{
mPreference = getUserPreferences().getCallManagementPreference();
HBox.setHgrow(getEditorPane(), Priority.ALWAYS);
getChildren().add(getEditorPane());
}
@Override
public PreferenceEditorType getPreferenceEditorType()
{
return PreferenceEditorType.AUDIO_CALL_MANAGEMENT;
}
private GridPane getEditorPane()
{
if(mEditorPane == null)

View File

@@ -1,6 +1,6 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2022 Dennis Sheirer
* Copyright (C) 2014-2023 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
@@ -21,14 +21,19 @@ package io.github.dsheirer.gui.preference.decoder;
import com.google.common.eventbus.Subscribe;
import io.github.dsheirer.eventbus.MyEventBus;
import io.github.dsheirer.gui.preference.PreferenceEditor;
import io.github.dsheirer.gui.preference.PreferenceEditorType;
import io.github.dsheirer.jmbe.JmbeCreator;
import io.github.dsheirer.jmbe.JmbeEditorRequest;
import io.github.dsheirer.jmbe.github.GitHub;
import io.github.dsheirer.jmbe.github.Release;
import io.github.dsheirer.jmbe.github.Version;
import io.github.dsheirer.preference.PreferenceType;
import io.github.dsheirer.preference.UserPreferences;
import io.github.dsheirer.util.ThreadPool;
import jakarta.annotation.PostConstruct;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Path;
import javafx.application.Platform;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
@@ -42,21 +47,16 @@ import javafx.scene.control.Label;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Region;
import javafx.scene.layout.VBox;
import javafx.stage.FileChooser;
import javafx.stage.Stage;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Path;
/**
* Preference settings for decoders
*/
public class JmbeLibraryPreferenceEditor extends VBox
public class JmbeLibraryPreferenceEditor extends PreferenceEditor
{
private final static Logger mLog = LoggerFactory.getLogger(JmbeLibraryPreferenceEditor.class);
@@ -64,8 +64,6 @@ public class JmbeLibraryPreferenceEditor extends VBox
private static final String CREATE_LIBRARY = "Create Library";
private static final String CHECK_FOR_UPDATE = "Check For Library Update";
private static final String LIBRARY_NOT_SETUP = "None. Click Select or Create Library button to setup.";
private UserPreferences mUserPreferences;
private GridPane mEditorPane;
private Label mJmbeLibraryLabel;
private Label mJmbeVersionLabel;
@@ -76,18 +74,26 @@ public class JmbeLibraryPreferenceEditor extends VBox
private HBox mButtonsBox;
private CheckBox mAlertUserWhenMissingCheckBox;
public JmbeLibraryPreferenceEditor(UserPreferences userPreferences)
public JmbeLibraryPreferenceEditor()
{
mUserPreferences = userPreferences;
}
@PostConstruct
public void postConstruct()
{
//Register to receive directory preference update notifications so we can update the path labels
MyEventBus.getGlobalEventBus().register(this);
setPadding(new Insets(10,10,10,10));
setSpacing(10);
getChildren().addAll(getEditorPane(), getButtonsBox(), getAlertUserWhenMissingCheckBox());
}
@Override
public PreferenceEditorType getPreferenceEditorType()
{
return PreferenceEditorType.JMBE_LIBRARY;
}
public void dispose()
{
MyEventBus.getGlobalEventBus().unregister(this);
@@ -98,11 +104,11 @@ public class JmbeLibraryPreferenceEditor extends VBox
if(mAlertUserWhenMissingCheckBox == null)
{
mAlertUserWhenMissingCheckBox = new CheckBox("Alert when decoder requires missing JMBE library");
mAlertUserWhenMissingCheckBox.setSelected(mUserPreferences.getJmbeLibraryPreference()
mAlertUserWhenMissingCheckBox.setSelected(getUserPreferences().getJmbeLibraryPreference()
.getAlertIfMissingLibraryRequired());
mAlertUserWhenMissingCheckBox.setOnAction(event -> {
boolean alert = mAlertUserWhenMissingCheckBox.isSelected();
mUserPreferences.getJmbeLibraryPreference().setAlertIfMissingLibraryRequired(alert);
getUserPreferences().getJmbeLibraryPreference().setAlertIfMissingLibraryRequired(alert);
});
}
@@ -154,7 +160,7 @@ public class JmbeLibraryPreferenceEditor extends VBox
if(mCreateButton == null)
{
mCreateButton = new Button();
mCreateButton.setText(mUserPreferences.getJmbeLibraryPreference()
mCreateButton.setText(getUserPreferences().getJmbeLibraryPreference()
.getPathJmbeLibrary() != null ? CHECK_FOR_UPDATE : CREATE_LIBRARY);
mCreateButton.setOnAction(event -> checkForUpdatedLibrary());
}
@@ -170,7 +176,7 @@ public class JmbeLibraryPreferenceEditor extends VBox
ThreadPool.CACHED.execute(() -> {
try
{
Version current = mUserPreferences.getJmbeLibraryPreference().getCurrentVersion();
Version current = getUserPreferences().getJmbeLibraryPreference().getCurrentVersion();
final Release release = GitHub.getLatestRelease(JmbeCreator.GITHUB_JMBE_RELEASES_URL);
mLog.info("Checking for JMBE Library Updates ...");
@@ -268,7 +274,7 @@ public class JmbeLibraryPreferenceEditor extends VBox
if(mJmbeVersionLabel == null)
{
mJmbeVersionLabel = new Label();
Version version = mUserPreferences.getJmbeLibraryPreference().getCurrentVersion();
Version version = getUserPreferences().getJmbeLibraryPreference().getCurrentVersion();
if(version != null)
{
@@ -296,7 +302,7 @@ public class JmbeLibraryPreferenceEditor extends VBox
FileChooser fileChooser = new FileChooser();
fileChooser.setTitle("Select JMBE Audio Library Location");
Path path = mUserPreferences.getDirectoryPreference().getDefaultJmbeDirectory();
Path path = getUserPreferences().getDirectoryPreference().getDefaultJmbeDirectory();
if(Files.exists(path))
{
@@ -308,7 +314,7 @@ public class JmbeLibraryPreferenceEditor extends VBox
if(selected != null)
{
mUserPreferences.getJmbeLibraryPreference().setPathJmbeLibrary(selected.toPath());
getUserPreferences().getJmbeLibraryPreference().setPathJmbeLibrary(selected.toPath());
}
}
});
@@ -322,7 +328,7 @@ public class JmbeLibraryPreferenceEditor extends VBox
if(mResetButton == null)
{
mResetButton = new Button("Reset");
mResetButton.setOnAction(event -> mUserPreferences.getJmbeLibraryPreference().resetPathJmbeLibrary());
mResetButton.setOnAction(event -> getUserPreferences().getJmbeLibraryPreference().resetPathJmbeLibrary());
}
return mResetButton;
@@ -332,7 +338,7 @@ public class JmbeLibraryPreferenceEditor extends VBox
{
if(mPathToJmbeLibraryLabel == null)
{
Path path = mUserPreferences.getJmbeLibraryPreference().getPathJmbeLibrary();
Path path = getUserPreferences().getJmbeLibraryPreference().getPathJmbeLibrary();
mPathToJmbeLibraryLabel = new Label(path != null ? path.toString() : PATH_NOT_SET);
}
@@ -344,13 +350,13 @@ public class JmbeLibraryPreferenceEditor extends VBox
{
if(preferenceType != null && preferenceType == PreferenceType.JMBE_LIBRARY)
{
Path path = mUserPreferences.getJmbeLibraryPreference().getPathJmbeLibrary();
Path path = getUserPreferences().getJmbeLibraryPreference().getPathJmbeLibrary();
getPathToJmbeLibraryLabel().setText(path != null ? path.toString() : PATH_NOT_SET);
Version version = mUserPreferences.getJmbeLibraryPreference().getCurrentVersion();
Version version = getUserPreferences().getJmbeLibraryPreference().getCurrentVersion();
getJmbeVersionLabel().setText(version != null ? version.toString() : LIBRARY_NOT_SETUP);
getCreateButton().setText(mUserPreferences.getJmbeLibraryPreference()
getCreateButton().setText(getUserPreferences().getJmbeLibraryPreference()
.getPathJmbeLibrary() != null ? CHECK_FOR_UPDATE : CREATE_LIBRARY);
getAlertUserWhenMissingCheckBox().setSelected(mUserPreferences.getJmbeLibraryPreference()
getAlertUserWhenMissingCheckBox().setSelected(getUserPreferences().getJmbeLibraryPreference()
.getAlertIfMissingLibraryRequired());
}
}

View File

@@ -19,11 +19,13 @@
package io.github.dsheirer.gui.preference.directory;
import com.google.common.eventbus.Subscribe;
import io.github.dsheirer.eventbus.MyEventBus;
import io.github.dsheirer.gui.preference.PreferenceEditor;
import io.github.dsheirer.gui.preference.PreferenceEditorType;
import io.github.dsheirer.preference.IPreferenceUpdateListener;
import io.github.dsheirer.preference.PreferenceType;
import io.github.dsheirer.preference.UserPreferences;
import io.github.dsheirer.preference.directory.DirectoryPreference;
import jakarta.annotation.PostConstruct;
import jakarta.annotation.PreDestroy;
import java.io.File;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
@@ -45,7 +47,7 @@ import org.slf4j.LoggerFactory;
/**
* Preference settings for channel event view
*/
public class DirectoryPreferenceEditor extends HBox
public class DirectoryPreferenceEditor extends PreferenceEditor implements IPreferenceUpdateListener
{
private final static Logger mLog = LoggerFactory.getLogger(DirectoryPreferenceEditor.class);
@@ -100,20 +102,29 @@ public class DirectoryPreferenceEditor extends HBox
private Spinner<Integer> mRecordingSpinner;
private Spinner<Integer> mEventLogSpinner;
public DirectoryPreferenceEditor(UserPreferences userPreferences)
public DirectoryPreferenceEditor()
{
mDirectoryPreference = userPreferences.getDirectoryPreference();
//Register to receive directory preference update notifications so we can update the path labels
MyEventBus.getGlobalEventBus().register(this);
}
@PostConstruct
public void postConstruct()
{
getUserPreferences().addUpdateListener(this);
mDirectoryPreference = getUserPreferences().getDirectoryPreference();
HBox.setHgrow(getEditorPane(), Priority.ALWAYS);
getChildren().add(getEditorPane());
}
@PreDestroy
public void dispose()
{
MyEventBus.getGlobalEventBus().unregister(this);
getUserPreferences().removeUpdateListener(this);
}
@Override
public PreferenceEditorType getPreferenceEditorType()
{
return PreferenceEditorType.DIRECTORY;
}
private GridPane getEditorPane()
@@ -886,7 +897,7 @@ public class DirectoryPreferenceEditor extends HBox
return mStreamingPathLabel;
}
@Subscribe
@Override
public void preferenceUpdated(PreferenceType preferenceType)
{
if(preferenceType != null && preferenceType == PreferenceType.DIRECTORY)

View File

@@ -1,6 +1,6 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2022 Dennis Sheirer
* Copyright (C) 2014-2023 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
@@ -21,8 +21,10 @@ package io.github.dsheirer.gui.preference.mp3;
import io.github.dsheirer.audio.convert.InputAudioFormat;
import io.github.dsheirer.audio.convert.MP3Setting;
import io.github.dsheirer.preference.UserPreferences;
import io.github.dsheirer.gui.preference.PreferenceEditor;
import io.github.dsheirer.gui.preference.PreferenceEditorType;
import io.github.dsheirer.preference.mp3.MP3Preference;
import jakarta.annotation.PostConstruct;
import javafx.geometry.HPos;
import javafx.geometry.Insets;
import javafx.scene.control.Alert;
@@ -39,7 +41,7 @@ import org.slf4j.LoggerFactory;
/**
* Preference settings for MP3
*/
public class MP3PreferenceEditor extends HBox
public class MP3PreferenceEditor extends PreferenceEditor
{
private final static Logger mLog = LoggerFactory.getLogger(MP3PreferenceEditor.class);
private MP3Preference mMP3Preference;
@@ -50,15 +52,25 @@ public class MP3PreferenceEditor extends HBox
/**
* Constructs an instance
* @param userPreferences
*/
public MP3PreferenceEditor(UserPreferences userPreferences)
public MP3PreferenceEditor()
{
mMP3Preference = userPreferences.getMP3Preference();
}
@PostConstruct
public void postConstruct()
{
mMP3Preference = getUserPreferences().getMP3Preference();
HBox.setHgrow(getEditorPane(), Priority.ALWAYS);
getChildren().add(getEditorPane());
}
@Override
public PreferenceEditorType getPreferenceEditorType()
{
return PreferenceEditorType.AUDIO_MP3;
}
private GridPane getEditorPane()
{
if(mEditorPane == null)

View File

@@ -1,6 +1,6 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2023 Dennis Sheirer
* Copyright (C) 2014-2024 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
@@ -20,10 +20,13 @@
package io.github.dsheirer.gui.preference.playback;
import io.github.dsheirer.audio.AudioFormats;
import io.github.dsheirer.preference.UserPreferences;
import io.github.dsheirer.audio.playbackfx.PlaybackView;
import io.github.dsheirer.gui.preference.PreferenceEditor;
import io.github.dsheirer.gui.preference.PreferenceEditorType;
import io.github.dsheirer.preference.playback.PlaybackPreference;
import io.github.dsheirer.source.mixer.MixerChannelConfiguration;
import io.github.dsheirer.source.mixer.MixerManager;
import jakarta.annotation.PostConstruct;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.ShortBuffer;
@@ -35,6 +38,7 @@ import javafx.scene.control.Button;
import javafx.scene.control.ComboBox;
import javafx.scene.control.Label;
import javafx.scene.control.Separator;
import javafx.scene.control.Tooltip;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Priority;
@@ -53,10 +57,10 @@ import javax.sound.sampled.DataLine;
/**
* Preference settings for audio playback
*/
public class PlaybackPreferenceEditor extends HBox
public class PlaybackPreferenceEditor extends PreferenceEditor
{
private final static Logger mLog = LoggerFactory.getLogger(PlaybackPreferenceEditor.class);
private final PlaybackPreference mPlaybackPreference;
private PlaybackPreference mPlaybackPreference;
private GridPane mEditorPane;
private ComboBox<MixerChannelConfiguration> mMixerComboBox;
private Button mMixerTestButton;
@@ -68,27 +72,48 @@ public class PlaybackPreferenceEditor extends HBox
private ComboBox<ToneVolume> mStartToneVolumeComboBox;
private ComboBox<ToneFrequency> mDropToneFrequencyComboBox;
private ComboBox<ToneVolume> mDropToneVolumeComboBox;
private ComboBox<PlaybackView> mPlaybackViewComboBox;
public PlaybackPreferenceEditor(UserPreferences userPreferences)
public PlaybackPreferenceEditor()
{
mPlaybackPreference = userPreferences.getPlaybackPreference();
}
@PostConstruct
public void postConstruct()
{
mPlaybackPreference = getUserPreferences().getPlaybackPreference();
HBox.setHgrow(getEditorPane(), Priority.ALWAYS);
getChildren().add(getEditorPane());
}
@Override
public PreferenceEditorType getPreferenceEditorType()
{
return PreferenceEditorType.AUDIO_PLAYBACK;
}
private GridPane getEditorPane()
{
if(mEditorPane == null)
{
int row = 0;
mEditorPane = new GridPane();
mEditorPane.setPadding(new Insets(10, 10, 10, 10));
mEditorPane.setHgap(10);
mEditorPane.setVgap(10);
Label outputLabel = new Label("Audio Output Device");
int row = 0;
Label playbackLabel = new Label("Audio Playback (Left/Right) Channel View");
mEditorPane.add(playbackLabel, 0, row, 3, 1);
Label styleLabel = new Label("View Style:");
GridPane.setHalignment(styleLabel, HPos.RIGHT);
mEditorPane.add(styleLabel, 0, ++row, 2, 1);
mEditorPane.add(getPlaybackViewComboBox(), 2, row, 3, 1);
mEditorPane.add(new Separator(Orientation.HORIZONTAL), 0, ++row, 6, 1);
Label outputLabel = new Label("Audio Output Device:");
GridPane.setHalignment(outputLabel, HPos.RIGHT);
mEditorPane.add(outputLabel, 0, row, 2, 1);
mEditorPane.add(outputLabel, 0, ++row, 2, 1);
mEditorPane.add(getMixerComboBox(), 2, row, 3, 1);
mEditorPane.add(getMixerTestButton(), 5, row);
mEditorPane.add(new Separator(Orientation.HORIZONTAL), 0, ++row, 6, 1);
@@ -269,6 +294,24 @@ public class PlaybackPreferenceEditor extends HBox
return mStartToneVolumeComboBox;
}
/**
* Playback view style selection box.
*/
public ComboBox<PlaybackView> getPlaybackViewComboBox()
{
if(mPlaybackViewComboBox == null)
{
mPlaybackViewComboBox = new ComboBox<>();
mPlaybackViewComboBox.setTooltip(new Tooltip("Determines the styling of the audio playback left/right channel views"));
mPlaybackViewComboBox.getItems().addAll(PlaybackView.values());
mPlaybackViewComboBox.getSelectionModel().select(mPlaybackPreference.getPlaybackView());
mPlaybackViewComboBox.getSelectionModel().selectedItemProperty()
.addListener((observable, oldValue, newValue) -> mPlaybackPreference.setPlaybackView(newValue));
}
return mPlaybackViewComboBox;
}
/**
* Plays the audio buffer over the default mono playback device
* @param audioSamples with 8 kHz mono PCM samples

View File

@@ -1,6 +1,6 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2020 Dennis Sheirer
* Copyright (C) 2014-2023 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
@@ -19,9 +19,11 @@
package io.github.dsheirer.gui.preference.record;
import io.github.dsheirer.preference.UserPreferences;
import io.github.dsheirer.gui.preference.PreferenceEditor;
import io.github.dsheirer.gui.preference.PreferenceEditorType;
import io.github.dsheirer.preference.record.RecordPreference;
import io.github.dsheirer.record.RecordFormat;
import jakarta.annotation.PostConstruct;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.geometry.Insets;
@@ -37,20 +39,31 @@ import org.slf4j.LoggerFactory;
/**
* Preference settings for recording
*/
public class RecordPreferenceEditor extends HBox
public class RecordPreferenceEditor extends PreferenceEditor
{
private final static Logger mLog = LoggerFactory.getLogger(RecordPreferenceEditor.class);
private RecordPreference mRecordPreference;
private GridPane mEditorPane;
private ComboBox<RecordFormat> mRecordFormatComboBox;
public RecordPreferenceEditor(UserPreferences userPreferences)
public RecordPreferenceEditor()
{
mRecordPreference = userPreferences.getRecordPreference();
}
@PostConstruct
public void postConstruct()
{
mRecordPreference = getUserPreferences().getRecordPreference();
HBox.setHgrow(getEditorPane(), Priority.ALWAYS);
getChildren().add(getEditorPane());
}
@Override
public PreferenceEditorType getPreferenceEditorType()
{
return PreferenceEditorType.AUDIO_RECORD;
}
private GridPane getEditorPane()
{
if(mEditorPane == null)

View File

@@ -1,6 +1,6 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2022 Dennis Sheirer
* Copyright (C) 2014-2023 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
@@ -19,25 +19,25 @@
package io.github.dsheirer.gui.preference.tuner;
import io.github.dsheirer.preference.UserPreferences;
import io.github.dsheirer.gui.preference.PreferenceEditor;
import io.github.dsheirer.gui.preference.PreferenceEditorType;
import io.github.dsheirer.preference.source.ChannelizerType;
import io.github.dsheirer.preference.source.TunerPreference;
import jakarta.annotation.PostConstruct;
import javafx.geometry.HPos;
import javafx.geometry.Insets;
import javafx.geometry.Orientation;
import javafx.scene.Node;
import javafx.scene.control.Alert;
import javafx.scene.control.ChoiceBox;
import javafx.scene.control.Label;
import javafx.scene.control.Separator;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.HBox;
/**
* Preference settings for channel event view
*/
public class TunerPreferenceEditor extends HBox
public class TunerPreferenceEditor extends PreferenceEditor
{
private static final String HELP_TEXT_POLYPHASE = "Processes all channels from tuner. This " +
"channelizer is more efficient when decoding 3 or more channels.";
@@ -55,12 +55,26 @@ public class TunerPreferenceEditor extends HBox
private ChoiceBox<RspDuoSelectionMode> mRspDuoTunerModeChoiceBox;
private Label mRspDuoModeLabel;
public TunerPreferenceEditor(UserPreferences userPreferences)
/**
* Constructs an instance
*/
public TunerPreferenceEditor()
{
mTunerPreference = userPreferences.getTunerPreference();
}
@PostConstruct
public void postConstruct()
{
mTunerPreference = getUserPreferences().getTunerPreference();
getChildren().add(getEditorPane());
}
@Override
public PreferenceEditorType getPreferenceEditorType()
{
return PreferenceEditorType.TUNERS;
}
private GridPane getEditorPane()
{
if(mEditorPane == null)
@@ -116,7 +130,7 @@ public class TunerPreferenceEditor extends HBox
label.setWrapText(true);
Alert alert = new Alert(Alert.AlertType.WARNING);
alert.getDialogPane().setContent(label);
alert.initOwner(((Node)getChannelizerTypeChoiceBox()).getScene().getWindow());
alert.initOwner((getChannelizerTypeChoiceBox()).getScene().getWindow());
alert.show();
});
}
@@ -184,7 +198,7 @@ public class TunerPreferenceEditor extends HBox
label.setWrapText(true);
Alert alert = new Alert(Alert.AlertType.WARNING);
alert.getDialogPane().setContent(label);
alert.initOwner(((Node)getRspDuoTunerModeChoiceBox()).getScene().getWindow());
alert.initOwner((getRspDuoTunerModeChoiceBox()).getScene().getWindow());
alert.show();
});
}

View File

@@ -19,6 +19,8 @@
package io.github.dsheirer.gui.viewer;
import io.github.dsheirer.audio.call.Call;
import io.github.dsheirer.gui.control.TimestampTableCellFactory;
import io.github.dsheirer.message.IMessage;
import io.github.dsheirer.module.decode.dmr.DMRMessageFramer;
import io.github.dsheirer.module.decode.dmr.DMRMessageProcessor;
@@ -339,6 +341,7 @@ public class DmrViewer extends VBox
timestampColumn.setPrefWidth(110);
timestampColumn.setText("Time");
timestampColumn.setCellValueFactory(new PropertyValueFactory<>("timestamp"));
timestampColumn.setCellFactory(new TimestampTableCellFactory<Call>());
TableColumn timeslotColumn = new TableColumn();
timeslotColumn.setPrefWidth(35);

View File

@@ -19,6 +19,8 @@
package io.github.dsheirer.gui.viewer;
import io.github.dsheirer.audio.call.Call;
import io.github.dsheirer.gui.control.TimestampTableCellFactory;
import io.github.dsheirer.message.IMessage;
import io.github.dsheirer.message.StuffBitsMessage;
import io.github.dsheirer.module.decode.p25.phase1.P25P1MessageFramer;
@@ -307,6 +309,7 @@ public class P25P1Viewer extends VBox
timestampColumn.setPrefWidth(110);
timestampColumn.setText("Time");
timestampColumn.setCellValueFactory(new PropertyValueFactory<>("timestamp"));
timestampColumn.setCellFactory(new TimestampTableCellFactory<Call>());
TableColumn validColumn = new TableColumn();
validColumn.setPrefWidth(50);