mirror of
https://github.com/DSheirer/sdrtrunk.git
synced 2026-11-12 08:37:25 -05:00
Closes #7 NXDN Decoder * Implements: NXDN4800, NXDN9600, Type-C and Type-D decoding and trunk tracking. * Redesigns tuner and channel frequency error management.
537 lines
21 KiB
Java
537 lines
21 KiB
Java
/*
|
|
* *****************************************************************************
|
|
* Copyright (C) 2014-2026 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.channel;
|
|
|
|
import io.github.dsheirer.controller.channel.Channel;
|
|
import io.github.dsheirer.dsp.filter.channelizer.PolyphaseChannelSource;
|
|
import io.github.dsheirer.gui.power.SignalPowerView;
|
|
import io.github.dsheirer.gui.squelch.NoiseSquelchView;
|
|
import io.github.dsheirer.gui.symbol.SymbolView;
|
|
import io.github.dsheirer.module.ProcessingChain;
|
|
import io.github.dsheirer.module.decode.FeedbackDecoder;
|
|
import io.github.dsheirer.module.decode.PrimaryDecoder;
|
|
import io.github.dsheirer.module.decode.am.AMDecoder;
|
|
import io.github.dsheirer.module.decode.nbfm.NBFMDecoder;
|
|
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.ChannelFrequencyCorrectionStatusNotification;
|
|
import io.github.dsheirer.source.Source;
|
|
import io.github.dsheirer.source.SourceEvent;
|
|
import io.github.dsheirer.source.tuner.channel.HalfBandTunerChannelSource;
|
|
import io.github.dsheirer.source.tuner.channel.TunerChannel;
|
|
import io.github.dsheirer.source.tuner.channel.TunerChannelSource;
|
|
import io.github.dsheirer.spectrum.ComplexDftProcessor;
|
|
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 java.awt.Component;
|
|
import java.awt.EventQueue;
|
|
import java.awt.event.ComponentEvent;
|
|
import java.awt.event.ComponentListener;
|
|
import java.awt.event.MouseEvent;
|
|
import java.net.URL;
|
|
import java.text.DecimalFormat;
|
|
import java.util.List;
|
|
import javafx.application.Platform;
|
|
import javafx.embed.swing.JFXPanel;
|
|
import javafx.scene.Scene;
|
|
import net.miginfocom.swing.MigLayout;
|
|
import org.apache.commons.lang3.StringUtils;
|
|
import org.slf4j.Logger;
|
|
import org.slf4j.LoggerFactory;
|
|
|
|
import javax.swing.JButton;
|
|
import javax.swing.JLabel;
|
|
import javax.swing.JLayeredPane;
|
|
import javax.swing.JPanel;
|
|
import javax.swing.JSpinner;
|
|
import javax.swing.JSplitPane;
|
|
import javax.swing.SpinnerNumberModel;
|
|
import javax.swing.event.MouseInputAdapter;
|
|
|
|
/**
|
|
* Display for channel FFT and squelch details
|
|
*/
|
|
public class ChannelSpectrumPanel extends JPanel implements Listener<ProcessingChain>
|
|
{
|
|
private static final Logger LOGGER = LoggerFactory.getLogger(ChannelSpectrumPanel.class);
|
|
private static final DecimalFormat FREQUENCY_FORMAT = new DecimalFormat("0.00000");
|
|
private static final DecimalFormat PPM_FORMAT = new DecimalFormat("0.00000");
|
|
private static final String LABEL_PREFIX_PPM = "Tuner PPM: ";
|
|
private static final String LABEL_PREFIX_TUNER = "Tuner:";
|
|
private static final String LABEL_PREFIX_CHANNEL = "Channel:";
|
|
private static final String LABEL_PREFIX_DECODER = "Decoder:";
|
|
private final PlaylistManager mPlaylistManager;
|
|
private ProcessingChain mProcessingChain;
|
|
private final ComplexSamplesToNativeBufferModule mSampleStreamTapModule = new ComplexSamplesToNativeBufferModule();
|
|
private final ComplexDftProcessor mComplexDftProcessor = new ComplexDftProcessor();
|
|
private SpectrumPanel mSpectrumPanel;
|
|
private final FrequencyOverlayPanel mFrequencyOverlayPanel;
|
|
private final SourceEventProcessor mSourceEventProcessor = new SourceEventProcessor();
|
|
private final SpinnerNumberModel mNoiseFloorSpinnerModel;
|
|
private final JLabel mTunerPPMLabel;
|
|
private final JLabel mTunerCorrectionLabel;
|
|
private final JLabel mChannelCorrectionLabel;
|
|
private final JLabel mDecoderCorrectionLabel;
|
|
private final JLabel mDummyLabel = new JLabel(" ");
|
|
private boolean mPanelVisible = false;
|
|
private boolean mDftProcessing = false;
|
|
private final NoiseSquelchView mNoiseSquelchView;
|
|
private final SignalPowerView mSignalPowerView;
|
|
private final SymbolView mSymbolView = new SymbolView();
|
|
private final JFXPanel mNoiseSquelchPanel;
|
|
private final JFXPanel mSymbolPanel;
|
|
private JSplitPane mSplitPane;
|
|
private FeedbackDecoder mFeedbackDecoder;
|
|
|
|
/**
|
|
* Constructs an instance.
|
|
*/
|
|
public ChannelSpectrumPanel(PlaylistManager playlistManager, SettingsManager settingsManager)
|
|
{
|
|
mPlaylistManager = playlistManager;
|
|
mNoiseSquelchView = new NoiseSquelchView(mPlaylistManager);
|
|
mSignalPowerView = new SignalPowerView(mPlaylistManager);
|
|
setLayout(new MigLayout("insets 0", "[grow,fill]", "[grow,fill]"));
|
|
|
|
JPanel fftPanel = new JPanel();
|
|
fftPanel.setLayout(new MigLayout("insets 0", "[grow,fill]", "[][grow,fill]"));
|
|
|
|
JPanel labelPanel = new JPanel();
|
|
labelPanel.setLayout(new MigLayout("insets 2", "[grow,fill][][][][][grow,fill][][]", ""));
|
|
|
|
mTunerPPMLabel = new JLabel(LABEL_PREFIX_PPM);
|
|
mTunerPPMLabel.setEnabled(false);
|
|
labelPanel.add(mTunerPPMLabel);
|
|
|
|
labelPanel.add(new JLabel("Corrections (Hz) "));
|
|
|
|
mTunerCorrectionLabel = new JLabel(LABEL_PREFIX_TUNER);
|
|
mTunerCorrectionLabel.setEnabled(false);
|
|
labelPanel.add(mTunerCorrectionLabel);
|
|
|
|
mChannelCorrectionLabel = new JLabel(LABEL_PREFIX_CHANNEL);
|
|
mChannelCorrectionLabel.setEnabled(false);
|
|
labelPanel.add(mChannelCorrectionLabel);
|
|
|
|
mDecoderCorrectionLabel = new JLabel(LABEL_PREFIX_DECODER);
|
|
mDecoderCorrectionLabel.setEnabled(false);
|
|
labelPanel.add(mDecoderCorrectionLabel);
|
|
|
|
labelPanel.add(mDummyLabel);
|
|
|
|
mNoiseFloorSpinnerModel = new SpinnerNumberModel(18, 8, 36, 1);
|
|
mNoiseFloorSpinnerModel.addChangeListener(e -> {
|
|
Number number = mNoiseFloorSpinnerModel.getNumber();
|
|
mSpectrumPanel.setSampleSize(number.doubleValue());
|
|
});
|
|
JSpinner noiseFloorSpinner = new JSpinner(mNoiseFloorSpinnerModel);
|
|
labelPanel.add(new JLabel("Noise Floor:"));
|
|
labelPanel.add(noiseFloorSpinner);
|
|
|
|
JButton logIndexesButton = new JButton("Log Settings");
|
|
logIndexesButton.addActionListener(e -> {
|
|
if(mProcessingChain != null)
|
|
{
|
|
Source source = mProcessingChain.getSource();
|
|
|
|
if(source instanceof PolyphaseChannelSource pcs)
|
|
{
|
|
List<Integer> indexes = pcs.getOutputProcessorIndexes();
|
|
double sampleRate = pcs.getSampleRate();
|
|
long indexCenterFrequency = pcs.getIndexCenterFrequency();
|
|
long appliedFrequencyOffset = pcs.getFrequencyOffset();
|
|
long requestedCenterFrequency = pcs.getFrequency();
|
|
|
|
StringBuilder sb = new StringBuilder();
|
|
sb.append("Polyphase Channel - BW: ").append(FREQUENCY_FORMAT.format(sampleRate / 1E6d));
|
|
sb.append(" Center/Requested/Mixer: ").append(FREQUENCY_FORMAT.format(indexCenterFrequency / 1E6d));
|
|
sb.append("/").append(FREQUENCY_FORMAT.format(requestedCenterFrequency / 1E6d));
|
|
sb.append("/").append(FREQUENCY_FORMAT.format(appliedFrequencyOffset / 1E6d));
|
|
sb.append(" Polyphase Indexes: ").append(indexes);
|
|
sb.append(" Tuner SR:").append(FREQUENCY_FORMAT.format(pcs.getTunerSampleRate() / 1E6d));
|
|
sb.append(" CF:").append(FREQUENCY_FORMAT.format(pcs.getTunerCenterFrequency() / 1E6d));
|
|
LOGGER.info(sb.toString());
|
|
LOGGER.info("Output Processor: " + pcs.getStateDescription());
|
|
}
|
|
else if(source instanceof HalfBandTunerChannelSource<?> hbtcs)
|
|
{
|
|
StringBuilder sb = new StringBuilder();
|
|
sb.append("Heterodyne Channel - CF:").append(FREQUENCY_FORMAT.format(hbtcs.getFrequency() / 1E6d));
|
|
sb.append(" SR:").append(FREQUENCY_FORMAT.format(hbtcs.getSampleRate() / 1E6d));
|
|
sb.append(" Mixer:").append(FREQUENCY_FORMAT.format(hbtcs.getMixerFrequency() / 1E6d));
|
|
LOGGER.info(sb.toString());
|
|
}
|
|
else
|
|
{
|
|
LOGGER.info("Unsupported channel type: " + (source != null ? source.getClass() : " null"));
|
|
}
|
|
}
|
|
});
|
|
//This is a debug button to log the current settings to the app log.
|
|
// labelPanel.add(mLogIndexesButton);
|
|
|
|
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
|
|
*/
|
|
JLayeredPane layeredPanel = new JLayeredPane();
|
|
layeredPanel.addComponentListener(new ResizeListener());
|
|
|
|
/**
|
|
* Create a mouse adapter to handle mouse events over the spectrum
|
|
* and waterfall panels
|
|
*/
|
|
MouseEventProcessor mouser = new MouseEventProcessor();
|
|
|
|
mFrequencyOverlayPanel.addMouseListener(mouser);
|
|
mFrequencyOverlayPanel.addMouseMotionListener(mouser);
|
|
mFrequencyOverlayPanel.addMouseWheelListener(mouser);
|
|
|
|
//Add the spectrum and channel panels to the layered panel
|
|
layeredPanel.add(mSpectrumPanel, 0, 0);
|
|
layeredPanel.add(mFrequencyOverlayPanel, 1, 0);
|
|
|
|
fftPanel.add(layeredPanel);
|
|
|
|
mNoiseSquelchPanel = new JFXPanel();
|
|
mSymbolPanel = new JFXPanel();
|
|
|
|
//Spin noise squelch panel construction off onto the JavafX UI thread.
|
|
Platform.runLater(() -> {
|
|
Scene scene = new Scene(mNoiseSquelchView);
|
|
mNoiseSquelchPanel.setScene(scene);
|
|
Scene scene2 = new Scene(mSymbolView);
|
|
URL resource = getClass().getResource("/sdrtrunk_style.css");
|
|
|
|
if(resource != null)
|
|
{
|
|
scene2.getStylesheets().add(resource.toExternalForm());
|
|
}
|
|
else
|
|
{
|
|
LOGGER.warn("Can't find stylesheet resource for sdrtrunk");
|
|
}
|
|
|
|
mSymbolPanel.setScene(scene2);
|
|
});
|
|
|
|
mSplitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT);
|
|
mSplitPane.add(fftPanel, JSplitPane.LEFT);
|
|
mSplitPane.add(mNoiseSquelchPanel, JSplitPane.RIGHT);
|
|
add(mSplitPane);
|
|
// mSplitPane.setDividerLocation(0.5);
|
|
|
|
mSampleStreamTapModule.setListener(mComplexDftProcessor);
|
|
DFTResultsConverter DFTResultsConverter = new ComplexDecibelConverter();
|
|
mComplexDftProcessor.addConverter(DFTResultsConverter);
|
|
DFTResultsConverter.addListener(mSpectrumPanel);
|
|
mSpectrumPanel.clearSpectrum();
|
|
}
|
|
|
|
/**
|
|
* Pads the value to a fixed width and returns the previx and padded value.
|
|
* @param prefix of the label
|
|
* @param value to pad with spaces
|
|
* @return padded, prefixed label
|
|
*/
|
|
private String getPaddedLabel(String prefix, Number value)
|
|
{
|
|
return prefix + StringUtils.leftPad(String.valueOf(value), 7);
|
|
}
|
|
|
|
/**
|
|
* Signals this panel to indicate if this panel is visible to turn on the FFT processor when the panel is visible
|
|
* and turn off the FFT processor when it's not.
|
|
*
|
|
* Note: this method is intended to be called by the Swing event thread to ensure that only a single thread is
|
|
* invoking either this method, or the receive() method, since there is no thread synchronization between these
|
|
* two methods and they each depend on stable access to the mPanelVisible variable.
|
|
*
|
|
* @param visible true to indicate that this panel is showing/visible.
|
|
*/
|
|
public void setPanelVisible(boolean visible)
|
|
{
|
|
mPanelVisible = visible;
|
|
updateFFTProcessing();
|
|
mNoiseSquelchView.setShowing(visible);
|
|
mSymbolView.setShowing(visible);
|
|
}
|
|
|
|
/**
|
|
* Updates processing state for the DFT processor. Turns on DFT processing when we have a processing chain and
|
|
* when the user has this tab selected and visible. Otherwise, turns off DFT processing.
|
|
*/
|
|
private void updateFFTProcessing()
|
|
{
|
|
if(mPanelVisible && mProcessingChain != null)
|
|
{
|
|
startDftProcessing();
|
|
}
|
|
else
|
|
{
|
|
stopDftProcessing();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Starts DFT processing
|
|
*/
|
|
private void startDftProcessing()
|
|
{
|
|
if(!mDftProcessing)
|
|
{
|
|
mDftProcessing = true;
|
|
mSampleStreamTapModule.setListener(mComplexDftProcessor);
|
|
mComplexDftProcessor.start();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Stops DFT processing
|
|
*/
|
|
private void stopDftProcessing()
|
|
{
|
|
if(mDftProcessing)
|
|
{
|
|
mSampleStreamTapModule.removeListener();
|
|
mComplexDftProcessor.stop();
|
|
mSpectrumPanel.clearSpectrum();
|
|
mDftProcessing = false;
|
|
}
|
|
}
|
|
|
|
private void broadcast(SourceEvent sourceEvent)
|
|
{
|
|
if(mProcessingChain != null)
|
|
{
|
|
mProcessingChain.broadcast(sourceEvent);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Resets controls when changing processing chain source. Note: this must be called on the Swing
|
|
* dispatch thread because it directly invokes swing components.
|
|
*/
|
|
private void reset()
|
|
{
|
|
mTunerPPMLabel.setText(LABEL_PREFIX_PPM);
|
|
mTunerPPMLabel.setEnabled(false);
|
|
mTunerCorrectionLabel.setText(LABEL_PREFIX_TUNER);
|
|
mTunerCorrectionLabel.setEnabled(false);
|
|
mChannelCorrectionLabel.setText(LABEL_PREFIX_CHANNEL);
|
|
mChannelCorrectionLabel.setEnabled(false);
|
|
mDecoderCorrectionLabel.setText(LABEL_PREFIX_DECODER);
|
|
mDecoderCorrectionLabel.setEnabled(false);
|
|
mFrequencyOverlayPanel.process(SourceEvent.frequencyChange(null, 0));
|
|
mFrequencyOverlayPanel.process(SourceEvent.sampleRateChange(0));
|
|
mFrequencyOverlayPanel.setEstimatedCarrierOffsetFrequency(0);
|
|
mFrequencyOverlayPanel.setChannelBandwidth(0);
|
|
}
|
|
|
|
/**
|
|
* Receive notifications of request to provide display of processing chain details.
|
|
*/
|
|
@Override
|
|
public void receive(ProcessingChain processingChain)
|
|
{
|
|
//Disconnect the previous processing chain.
|
|
if(mProcessingChain != null)
|
|
{
|
|
mNoiseSquelchView.setController(null);
|
|
mSignalPowerView.setProcessingChain(null);
|
|
mSymbolView.removeSymbolProvider();
|
|
mSymbolView.setProtocol("");
|
|
mProcessingChain.removeSourceEventListener(mSourceEventProcessor);
|
|
mProcessingChain.removeModule(mSampleStreamTapModule);
|
|
mFeedbackDecoder = null;
|
|
}
|
|
|
|
//Invoking reset - we're on the Swing dispatch thread here
|
|
reset();
|
|
|
|
mProcessingChain = processingChain;
|
|
|
|
if(mProcessingChain != null)
|
|
{
|
|
mProcessingChain.addSourceEventListener(mSourceEventProcessor);
|
|
|
|
PrimaryDecoder primaryDecoder = mProcessingChain.getPrimaryDecoder();
|
|
if(primaryDecoder instanceof NBFMDecoder nbfmDecoder)
|
|
{
|
|
setRightComponent(mNoiseSquelchPanel);
|
|
mNoiseSquelchView.setController(nbfmDecoder);
|
|
}
|
|
else if(primaryDecoder instanceof AMDecoder)
|
|
{
|
|
setRightComponent(mSignalPowerView);
|
|
mSignalPowerView.setProcessingChain(mProcessingChain);
|
|
}
|
|
else if(primaryDecoder instanceof FeedbackDecoder feedbackDecoder)
|
|
{
|
|
setRightComponent(mSymbolPanel);
|
|
mSymbolView.setSymbolProvider(feedbackDecoder);
|
|
mSymbolView.setProtocol(feedbackDecoder.getProtocolDescription());
|
|
mFeedbackDecoder = feedbackDecoder;
|
|
}
|
|
|
|
mProcessingChain.addModule(mSampleStreamTapModule);
|
|
Source source = mProcessingChain.getSource();
|
|
|
|
if(source instanceof TunerChannelSource tcs)
|
|
{
|
|
mFrequencyOverlayPanel.process(SourceEvent.frequencyChange(null, tcs.getFrequency()));
|
|
mFrequencyOverlayPanel.process(SourceEvent.sampleRateChange(tcs.getSampleRate()));
|
|
}
|
|
|
|
Channel channel = mPlaylistManager.getChannelProcessingManager().getChannel(mProcessingChain);
|
|
|
|
if(channel != null)
|
|
{
|
|
List<TunerChannel> tunerChannels = channel.getTunerChannels();
|
|
|
|
if(!tunerChannels.isEmpty())
|
|
{
|
|
mFrequencyOverlayPanel.setChannelBandwidth(tunerChannels.getFirst().getBandwidth());
|
|
}
|
|
}
|
|
}
|
|
|
|
updateFFTProcessing();
|
|
}
|
|
|
|
/**
|
|
* Shows the component on the right side of the split pane.
|
|
* @param component to show.
|
|
*/
|
|
private void setRightComponent(Component component)
|
|
{
|
|
Component rightComponent = mSplitPane.getRightComponent();
|
|
|
|
if(rightComponent != component)
|
|
{
|
|
mSplitPane.remove(rightComponent);
|
|
mSplitPane.setRightComponent(component);
|
|
mSplitPane.setDividerLocation(0.4);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Processor for source event stream to capture power level and squelch related source events.
|
|
*/
|
|
private class SourceEventProcessor implements Listener<SourceEvent>
|
|
{
|
|
@Override
|
|
public void receive(SourceEvent sourceEvent)
|
|
{
|
|
if(sourceEvent.getEvent() == SourceEvent.Event.NOTIFICATION_CHANNEL_FREQUENCY_CORRECTION_STATUS)
|
|
{
|
|
if(sourceEvent instanceof ChannelFrequencyCorrectionStatusNotification status)
|
|
{
|
|
//Note: we flip the sign on the error measurement because the value represents the amount of offset the PLL
|
|
//has to apply to move the signal to center/baseband
|
|
EventQueue.invokeLater(() -> {
|
|
mTunerPPMLabel.setText(LABEL_PREFIX_PPM + PPM_FORMAT.format(status.getTunerPPM()));
|
|
mTunerPPMLabel.setEnabled(true);
|
|
|
|
if(status.isAutoPPM())
|
|
{
|
|
mTunerCorrectionLabel.setText(getPaddedLabel(LABEL_PREFIX_TUNER, status.getTunerCorrection()));
|
|
}
|
|
else
|
|
{
|
|
mTunerCorrectionLabel.setText(LABEL_PREFIX_TUNER + " disabled");
|
|
}
|
|
mTunerCorrectionLabel.setEnabled(true);
|
|
mChannelCorrectionLabel.setText(getPaddedLabel(LABEL_PREFIX_CHANNEL, status.getChannelCorrection()));
|
|
mChannelCorrectionLabel.setEnabled(true);
|
|
mDecoderCorrectionLabel.setText(getPaddedLabel(LABEL_PREFIX_DECODER, status.getDecoderCorrection()));
|
|
mDecoderCorrectionLabel.setEnabled(true);
|
|
|
|
//We se the offset cursor line to negative so that it aligns with where the signal is located
|
|
//instead of the correction that's needed to bring the signal to zero.
|
|
mFrequencyOverlayPanel.setEstimatedCarrierOffsetFrequency(-status.getDecoderCorrection());
|
|
});
|
|
}
|
|
}
|
|
|
|
//TODO: is this still needed?
|
|
mSignalPowerView.receive(sourceEvent);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Monitors the sizing of the layered pane and resizes the spectrum and
|
|
* channel panels whenever the layered pane is resized
|
|
*/
|
|
public class ResizeListener implements ComponentListener
|
|
{
|
|
@Override public void componentResized(ComponentEvent e)
|
|
{
|
|
Component c = e.getComponent();
|
|
|
|
mSpectrumPanel.setBounds(0, 0, c.getWidth(), c.getHeight());
|
|
mFrequencyOverlayPanel.setBounds(0, 0, c.getWidth(), c.getHeight());
|
|
}
|
|
|
|
@Override public void componentHidden(ComponentEvent arg0) {}
|
|
@Override public void componentMoved(ComponentEvent arg0) {}
|
|
@Override public void componentShown(ComponentEvent arg0) {}
|
|
}
|
|
|
|
/**
|
|
* Mouse event handler for the spectral display panel.
|
|
*/
|
|
public class MouseEventProcessor extends MouseInputAdapter
|
|
{
|
|
@Override public void mouseMoved(MouseEvent event)
|
|
{
|
|
update(event);
|
|
}
|
|
|
|
/**
|
|
* Updates the cursor display while the mouse is performing actions
|
|
*/
|
|
private void update(MouseEvent event)
|
|
{
|
|
mFrequencyOverlayPanel.setCursorLocation(event.getPoint());
|
|
}
|
|
|
|
@Override public void mouseEntered(MouseEvent e)
|
|
{
|
|
mFrequencyOverlayPanel.setCursorVisible(true);
|
|
}
|
|
|
|
@Override public void mouseExited(MouseEvent e)
|
|
{
|
|
mFrequencyOverlayPanel.setCursorVisible(false);
|
|
}
|
|
}
|
|
}
|