#5 Playlist Editor With Radio Reference Support (#760)

* WIP - Adds support for aliasing P25 Phase 2 tones.Playlist editor - adds editor support for alias actions

* Playlist editor implementation and integration into application is complete. Adds JavaFX window monitoring to persist window size/location across application runs.

Co-authored-by: Denny <denny@denny-desktop>
This commit is contained in:
Denny Sheirer
2020-04-26 11:53:43 +00:00
committed by GitHub
parent 39f6d6c831
commit cc52ae5bd7
290 changed files with 31251 additions and 15306 deletions

View File

@@ -1,145 +0,0 @@
/*
* ******************************************************************************
* sdrtrunk
* Copyright (C) 2014-2019 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
* *****************************************************************************
*/
package io.github.dsheirer.source;
import io.github.dsheirer.controller.channel.Channel;
import io.github.dsheirer.gui.editor.Editor;
import io.github.dsheirer.gui.editor.EmptyEditor;
import io.github.dsheirer.source.config.SourceConfiguration;
import io.github.dsheirer.source.mixer.MixerSourceEditor;
import io.github.dsheirer.source.tuner.TunerSourceEditor;
import io.github.dsheirer.source.tuner.TunerSourceMultipleFrequencyEditor;
import net.miginfocom.swing.MigLayout;
import javax.swing.DefaultComboBoxModel;
import javax.swing.JComboBox;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
public class SourceConfigurationEditor extends Editor<Channel>
{
private static final long serialVersionUID = 1L;
private JComboBox<SourceType> mComboSources;
private MixerSourceEditor mMixerSourceEditor;
private TunerSourceEditor mTunerSourceEditor;
private TunerSourceMultipleFrequencyEditor mTunerSourceMultipleFrequencyEditor;
private Editor<Channel> mCurrentEditor;
public SourceConfigurationEditor(SourceManager sourceManager)
{
mMixerSourceEditor = new MixerSourceEditor(sourceManager);
mMixerSourceEditor.setSaveRequestListener(this);
mTunerSourceEditor = new TunerSourceEditor(sourceManager.getTunerModel());
mTunerSourceEditor.setSaveRequestListener(this);
mTunerSourceMultipleFrequencyEditor = new TunerSourceMultipleFrequencyEditor(sourceManager.getTunerModel());
mTunerSourceMultipleFrequencyEditor.setSaveRequestListener(this);
init();
}
public SourceConfiguration getSourceConfiguration()
{
return hasItem() ? getItem().getSourceConfiguration() : null;
}
private void init()
{
setLayout(new MigLayout("wrap 2", "[][grow,fill]", "[align top][grow,fill]"));
mComboSources = new JComboBox<SourceType>();
mComboSources.setModel(new DefaultComboBoxModel<SourceType>(SourceType.getTypes()));
mComboSources.addActionListener(new ActionListener()
{
@Override
public void actionPerformed(ActionEvent e)
{
SourceType selected = (SourceType) mComboSources.getSelectedItem();
switch(selected)
{
case MIXER:
setEditor(mMixerSourceEditor);
break;
case TUNER:
setEditor(mTunerSourceEditor);
break;
case TUNER_MULTIPLE_FREQUENCIES:
setEditor(mTunerSourceMultipleFrequencyEditor);
break;
default:
setEditor(new EmptyEditor<Channel>());
break;
}
}
});
add(mComboSources);
mCurrentEditor = mTunerSourceEditor;
add(mCurrentEditor, "span 1 2");
}
private void setEditor(Editor<Channel> editor)
{
if(mCurrentEditor != editor)
{
//Set channel to null to force a save prompt as required
mCurrentEditor.setItem(null);
remove(mCurrentEditor);
mCurrentEditor = editor;
mCurrentEditor.setSaveRequestListener(this);
add(mCurrentEditor, "span 1 2");
mCurrentEditor.setItem(getItem());
revalidate();
}
}
@Override
public void save()
{
if(hasItem())
{
mCurrentEditor.save();
}
}
/**
* Sets the channel configuration. Note: this method must be invoked from
* the swing event dispatch thread.
*/
@Override
public void setItem(Channel channel)
{
super.setItem(channel);
if(hasItem())
{
mComboSources.setSelectedItem(getItem().getSourceConfiguration().getSourceType());
repaint();
mCurrentEditor.setItem(channel);
}
}
}

View File

@@ -1,175 +0,0 @@
/*******************************************************************************
* SDR Trunk
* Copyright (C) 2014-2016 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
******************************************************************************/
package io.github.dsheirer.source.mixer;
import io.github.dsheirer.controller.channel.Channel;
import io.github.dsheirer.gui.editor.Editor;
import io.github.dsheirer.source.SourceManager;
import io.github.dsheirer.source.config.SourceConfigMixer;
import io.github.dsheirer.source.config.SourceConfiguration;
import net.miginfocom.swing.MigLayout;
import javax.swing.DefaultComboBoxModel;
import javax.swing.JComboBox;
import javax.swing.JLabel;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.EnumSet;
public class MixerSourceEditor extends Editor<Channel>
{
private static final long serialVersionUID = 1L;
private JComboBox<MixerManager.InputMixerConfiguration> mComboMixers;
private JComboBox<MixerChannel> mComboChannels;
private SourceManager mSourceManager;
public MixerSourceEditor(SourceManager sourceManager)
{
mSourceManager = sourceManager;
init();
}
private void init()
{
setLayout(new MigLayout("insets 0 0 0 0,wrap 4", "[right][grow,fill][right][grow,fill]", ""));
mComboMixers = new JComboBox<MixerManager.InputMixerConfiguration>();
DefaultComboBoxModel<MixerManager.InputMixerConfiguration> model = new DefaultComboBoxModel<>();
model.addAll(MixerManager.getInputMixers());
mComboMixers.setModel(model);
mComboMixers.setEnabled(false);
mComboMixers.addActionListener(new ActionListener()
{
@Override
public void actionPerformed(ActionEvent e)
{
MixerManager.InputMixerConfiguration selected = (MixerManager.InputMixerConfiguration)mComboMixers.getSelectedItem();
EnumSet<MixerChannel> channels = selected.getChannels();
mComboChannels.setModel(new DefaultComboBoxModel<MixerChannel>(
channels.toArray(new MixerChannel[0])));
repaint();
setModified(true);
}
});
add(new JLabel("Mixer:"));
add(mComboMixers);
mComboChannels = new JComboBox<MixerChannel>();
MixerManager.InputMixerConfiguration selected = (MixerManager.InputMixerConfiguration)mComboMixers.getSelectedItem();
if(selected != null)
{
EnumSet<MixerChannel> channels = selected.getChannels();
mComboChannels.setModel(new DefaultComboBoxModel<MixerChannel>(
channels.toArray(new MixerChannel[0])));
}
mComboChannels.setEnabled(false);
mComboChannels.addActionListener(new ActionListener()
{
@Override
public void actionPerformed(ActionEvent e)
{
setModified(true);
}
});
add(new JLabel("Channel:"));
add(mComboChannels);
}
@Override
public void setItem(Channel item)
{
super.setItem(item);
if(hasItem())
{
mComboMixers.setEnabled(true);
mComboChannels.setEnabled(true);
SourceConfiguration config = item.getSourceConfiguration();
if(config instanceof SourceConfigMixer)
{
SourceConfigMixer mixer = (SourceConfigMixer)config;
for(MixerManager.InputMixerConfiguration inputMixer : MixerManager.getInputMixers())
{
if(inputMixer.getMixerName().equalsIgnoreCase(mixer.getMixer()))
{
mComboMixers.setSelectedItem(inputMixer);
for(MixerChannel channel : inputMixer.getChannels())
{
if(channel.getLabel() != null && mixer.getChannel() != null)
{
if(channel.getLabel().equalsIgnoreCase(mixer.getChannel().getLabel()))
{
mComboChannels.setSelectedItem(channel);
}
}
}
continue;
}
}
setModified(false);
}
else
{
setModified(true);
}
}
else
{
mComboMixers.setEnabled(false);
mComboChannels.setEnabled(false);
setModified(false);
}
}
public void save()
{
if(hasItem() && isModified())
{
SourceConfigMixer config = new SourceConfigMixer();
MixerManager.InputMixerConfiguration selected =
(MixerManager.InputMixerConfiguration)mComboMixers.getSelectedItem();
config.setMixer(selected.getMixerName());
MixerChannel channel = (MixerChannel)mComboChannels.getSelectedItem();
config.setChannel(channel);
getItem().setSourceConfiguration(config);
}
setModified(false);
}
}

View File

@@ -40,6 +40,7 @@ import org.slf4j.LoggerFactory;
import javax.swing.table.AbstractTableModel;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.TimeUnit;
@@ -89,6 +90,23 @@ public class TunerModel extends AbstractTableModel implements Listener<TunerEven
return mTuners;
}
/**
* Sorted list of tuner names
*/
public List<String> getTunerNames()
{
List<String> names = new ArrayList<>();
for(Tuner tuner: mTuners)
{
names.add(tuner.getName());
}
Collections.sort(names);
return names;
}
public Tuner getTuner(int index)
{
if(index < mTuners.size())

View File

@@ -1,190 +0,0 @@
/*******************************************************************************
* SDR Trunk
* Copyright (C) 2014-2018 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* 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.source.tuner;
import io.github.dsheirer.controller.channel.Channel;
import io.github.dsheirer.gui.control.JFrequencyControl;
import io.github.dsheirer.gui.editor.Editor;
import io.github.dsheirer.source.ISourceEventProcessor;
import io.github.dsheirer.source.SourceEvent;
import io.github.dsheirer.source.SourceException;
import io.github.dsheirer.source.config.SourceConfigTuner;
import io.github.dsheirer.source.config.SourceConfiguration;
import net.miginfocom.swing.MigLayout;
import javax.swing.DefaultComboBoxModel;
import javax.swing.JComboBox;
import javax.swing.JLabel;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class TunerSourceEditor extends Editor<Channel>
{
private static final long serialVersionUID = 1L;
private static final String NO_PREFERRED_TUNER = "(none)";
private static final String UNAVAILABLE_TUNER = " (unavailable)";
private JFrequencyControl mFrequencyControl;
private JComboBox<String> mTunerNameComboBox;
private TunerModel mTunerModel;
private List<String> mCurrentTunerNames;
public TunerSourceEditor(TunerModel tunerModel)
{
mTunerModel = tunerModel;
init();
}
private void init()
{
loadTunerNames();
setLayout(new MigLayout("insets 0 0 0 0", "[left]", ""));
mFrequencyControl = new JFrequencyControl();
mFrequencyControl.setEnabled(false);
mFrequencyControl.addListener(new ISourceEventProcessor()
{
@Override
public void process(SourceEvent event) throws SourceException
{
setModified(true);
}
});
add(mFrequencyControl);
mTunerNameComboBox = new JComboBox<>(mCurrentTunerNames.toArray(new String[0]));
mTunerNameComboBox.addActionListener(new ActionListener()
{
@Override
public void actionPerformed(ActionEvent e)
{
setModified(true);
}
});
add(new JLabel("Preferred Tuner:"));
add(mTunerNameComboBox);
}
private void loadTunerNames()
{
mCurrentTunerNames = new ArrayList<>();
for(Tuner tuner: mTunerModel.getTuners())
{
mCurrentTunerNames.add(tuner.getName());
}
Collections.sort(mCurrentTunerNames);
mCurrentTunerNames.add(0, NO_PREFERRED_TUNER);
}
private void updateTunerNameCombo(String preferredTuner)
{
if(preferredTuner != null && !mCurrentTunerNames.contains(preferredTuner))
{
List<String> updatedTunerNameList = new ArrayList<>(mCurrentTunerNames);
String unavailableTuner = preferredTuner + UNAVAILABLE_TUNER;
updatedTunerNameList.add(unavailableTuner);
String[] tunerNameArray = updatedTunerNameList.toArray(new String[0]);
mTunerNameComboBox.setModel(new DefaultComboBoxModel<>(tunerNameArray));
mTunerNameComboBox.setSelectedItem(unavailableTuner);
}
else
{
String[] tunerNameArray = mCurrentTunerNames.toArray(new String[0]);
mTunerNameComboBox.setModel(new DefaultComboBoxModel<>(tunerNameArray));
if(preferredTuner == null)
{
mTunerNameComboBox.setSelectedItem(NO_PREFERRED_TUNER);
}
else
{
mTunerNameComboBox.setSelectedItem(preferredTuner);
}
}
}
public void save()
{
if(hasItem() && isModified())
{
SourceConfigTuner config = new SourceConfigTuner();
config.setFrequency(mFrequencyControl.getFrequency());
String preferredTuner = (String)mTunerNameComboBox.getSelectedItem();
if(preferredTuner != null)
{
if(preferredTuner.equalsIgnoreCase(NO_PREFERRED_TUNER))
{
config.setPreferredTuner(null);
}
else if(preferredTuner.contains(UNAVAILABLE_TUNER))
{
//don't do anything - the config already contains this unavailable tuner name
}
else
{
config.setPreferredTuner(preferredTuner);
}
}
getItem().setSourceConfiguration(config);
}
setModified(false);
}
@Override
public void setItem(Channel item)
{
super.setItem(item);
if(hasItem())
{
SourceConfiguration config = getItem().getSourceConfiguration();
mFrequencyControl.setEnabled(true);
if(config instanceof SourceConfigTuner)
{
SourceConfigTuner tunerConfig = (SourceConfigTuner)config;
mFrequencyControl.setFrequency(tunerConfig.getFrequency(), false);
updateTunerNameCombo(tunerConfig.getPreferredTuner());
setModified(false);
}
else
{
mFrequencyControl.setFrequency(101000000, false);
setModified(true);
}
}
else
{
mFrequencyControl.setEnabled(false);
setModified(false);
}
}
}

View File

@@ -1,184 +0,0 @@
/*
* ******************************************************************************
* sdrtrunk
* Copyright (C) 2014-2019 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
* *****************************************************************************
*/
package io.github.dsheirer.source.tuner;
import io.github.dsheirer.controller.channel.Channel;
import io.github.dsheirer.gui.control.MultipleFrequencyEditor;
import io.github.dsheirer.gui.editor.Editor;
import io.github.dsheirer.source.config.SourceConfigTunerMultipleFrequency;
import io.github.dsheirer.source.config.SourceConfiguration;
import net.miginfocom.swing.MigLayout;
import javax.swing.DefaultComboBoxModel;
import javax.swing.JComboBox;
import javax.swing.JLabel;
import javax.swing.event.TableModelEvent;
import javax.swing.event.TableModelListener;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class TunerSourceMultipleFrequencyEditor extends Editor<Channel>
{
private static final long serialVersionUID = 1L;
private static final String NO_PREFERRED_TUNER = "(none)";
private static final String UNAVAILABLE_TUNER = " (unavailable)";
private MultipleFrequencyEditor mFrequencyEditor = new MultipleFrequencyEditor(0.0, 6000.0);
private JComboBox<String> mTunerNameComboBox;
private TunerModel mTunerModel;
private List<String> mCurrentTunerNames;
public TunerSourceMultipleFrequencyEditor(TunerModel tunerModel)
{
mTunerModel = tunerModel;
init();
}
private void init()
{
loadTunerNames();
setLayout(new MigLayout("insets 0 0 0 0",
"[grow,fill][][right][grow,fill,left]", "[][grow,fill]"));
mFrequencyEditor.getFrequencyModel().addTableModelListener(new TableModelListener()
{
@Override
public void tableChanged(TableModelEvent e)
{
setModified(true);
}
});
// mFrequencyEditor.setSize(new Dimension(100, 30));
add(mFrequencyEditor, "span 2 2");
mTunerNameComboBox = new JComboBox<>(mCurrentTunerNames.toArray(new String[0]));
mTunerNameComboBox.addActionListener(new ActionListener()
{
@Override
public void actionPerformed(ActionEvent e)
{
setModified(true);
}
});
add(new JLabel("Preferred Tuner:"));
add(mTunerNameComboBox);
}
private void loadTunerNames()
{
mCurrentTunerNames = new ArrayList<>();
for(Tuner tuner: mTunerModel.getTuners())
{
mCurrentTunerNames.add(tuner.getName());
}
Collections.sort(mCurrentTunerNames);
mCurrentTunerNames.add(0, NO_PREFERRED_TUNER);
}
private void updateTunerNameCombo(String preferredTuner)
{
if(preferredTuner != null && !mCurrentTunerNames.contains(preferredTuner))
{
List<String> updatedTunerNameList = new ArrayList<>(mCurrentTunerNames);
String unavailableTuner = preferredTuner + UNAVAILABLE_TUNER;
updatedTunerNameList.add(unavailableTuner);
String[] tunerNameArray = updatedTunerNameList.toArray(new String[0]);
mTunerNameComboBox.setModel(new DefaultComboBoxModel<>(tunerNameArray));
mTunerNameComboBox.setSelectedItem(unavailableTuner);
}
else
{
String[] tunerNameArray = mCurrentTunerNames.toArray(new String[0]);
mTunerNameComboBox.setModel(new DefaultComboBoxModel<>(tunerNameArray));
if(preferredTuner == null)
{
mTunerNameComboBox.setSelectedItem(NO_PREFERRED_TUNER);
}
else
{
mTunerNameComboBox.setSelectedItem(preferredTuner);
}
}
}
public void save()
{
if(hasItem() && isModified())
{
SourceConfigTunerMultipleFrequency config = new SourceConfigTunerMultipleFrequency();
String preferredTuner = (String)mTunerNameComboBox.getSelectedItem();
if(preferredTuner != null)
{
if(preferredTuner.equalsIgnoreCase(NO_PREFERRED_TUNER))
{
config.setPreferredTuner(null);
}
else if(preferredTuner.contains(UNAVAILABLE_TUNER))
{
//don't do anything - the config already contains this unavailable tuner name
}
else
{
config.setPreferredTuner(preferredTuner);
}
}
config.setFrequencies(mFrequencyEditor.getFrequencyModel().getFrequencies());
getItem().setSourceConfiguration(config);
}
setModified(false);
}
@Override
public void setItem(Channel item)
{
super.setItem(item);
mFrequencyEditor.getFrequencyModel().clear();
if(hasItem())
{
SourceConfiguration config = getItem().getSourceConfiguration();
if(config instanceof SourceConfigTunerMultipleFrequency)
{
SourceConfigTunerMultipleFrequency tunerConfig = (SourceConfigTunerMultipleFrequency) config;
updateTunerNameCombo(tunerConfig.getPreferredTuner());
mFrequencyEditor.getFrequencyModel().setFrequencies(tunerConfig.getFrequencies());
setModified(false);
}
}
else
{
setModified(false);
}
}
}

View File

@@ -15,8 +15,7 @@
******************************************************************************/
package io.github.dsheirer.source.tuner;
import io.github.dsheirer.controller.channel.ChannelModel;
import io.github.dsheirer.controller.channel.ChannelProcessingManager;
import io.github.dsheirer.playlist.PlaylistManager;
import io.github.dsheirer.sample.Listener;
import io.github.dsheirer.settings.SettingsManager;
import io.github.dsheirer.spectrum.SpectralDisplayPanel;
@@ -27,20 +26,15 @@ import java.awt.EventQueue;
public class TunerSpectralDisplayManager implements Listener<TunerEvent>
{
private SpectralDisplayPanel mSpectralDisplayPanel;
private ChannelModel mChannelModel;
private ChannelProcessingManager mChannelProcessingManager;
private PlaylistManager mPlaylistManager;
private SettingsManager mSettingsManager;
private TunerModel mTunerModel;
public TunerSpectralDisplayManager(SpectralDisplayPanel panel,
ChannelModel channelModel,
ChannelProcessingManager channelProcessingManager,
SettingsManager settingsManager,
TunerModel tunerModel)
public TunerSpectralDisplayManager(SpectralDisplayPanel panel, PlaylistManager playlistManager,
SettingsManager settingsManager, TunerModel tunerModel)
{
mSpectralDisplayPanel = panel;
mChannelModel = channelModel;
mChannelProcessingManager = channelProcessingManager;
mPlaylistManager = playlistManager;
mSettingsManager = settingsManager;
mTunerModel = tunerModel;
}
@@ -57,17 +51,8 @@ public class TunerSpectralDisplayManager implements Listener<TunerEvent>
mSpectralDisplayPanel.showTuner(event.getTuner());
break;
case REQUEST_NEW_SPECTRAL_DISPLAY:
final SpectrumFrame frame = new SpectrumFrame(mChannelModel,
mChannelProcessingManager, mSettingsManager, mTunerModel, event.getTuner());
EventQueue.invokeLater(new Runnable()
{
@Override
public void run()
{
frame.setVisible(true);
}
});
final SpectrumFrame frame = new SpectrumFrame(mPlaylistManager, mSettingsManager, mTunerModel, event.getTuner());
EventQueue.invokeLater(() -> frame.setVisible(true));
break;
default:
break;