Merge pull request #329 from DSheirer/328-auto-start-ordering

328 Channel Auto-Start capability
This commit is contained in:
Denny Sheirer
2018-02-10 08:15:10 -05:00
committed by GitHub
14 changed files with 886 additions and 272 deletions

View File

@@ -137,7 +137,7 @@ public class TrafficChannelManager extends Module implements ICallEventProvider,
{ {
for(Channel configuredChannel : mTrafficChannelPool) for(Channel configuredChannel : mTrafficChannelPool)
{ {
if(!configuredChannel.getEnabled()) if(!configuredChannel.isProcessing())
{ {
channel = configuredChannel; channel = configuredChannel;
break; break;
@@ -207,7 +207,7 @@ public class TrafficChannelManager extends Module implements ICallEventProvider,
//Request to enable the channel //Request to enable the channel
mChannelModel.broadcast(trafficChannelEvent); mChannelModel.broadcast(trafficChannelEvent);
if(channel.getEnabled()) if(channel.isProcessing())
{ {
mTrafficChannelsInUse.put(callEvent.getChannel(), channel); mTrafficChannelsInUse.put(callEvent.getChannel(), channel);
} }

View File

@@ -0,0 +1,105 @@
/*******************************************************************************
* 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.controller.channel;
import javax.swing.table.AbstractTableModel;
import java.util.List;
/**
* Auto-Start Channel Model
*/
public class AutoStartChannelModel extends AbstractTableModel
{
private static final long serialVersionUID = 1L;
public static final int COLUMN_ORDER = 0;
public static final int COLUMN_SYSTEM = 1;
public static final int COLUMN_SITE = 2;
public static final int COLUMN_NAME = 3;
public static final int COLUMN_DECODER = 4;
private static final String[] COLUMN_NAMES = new String[] {"Order", "System", "Site", "Name", "Decoder"};
private List<Channel> mChannels;
public AutoStartChannelModel(List<Channel> autoStartChannels)
{
mChannels = autoStartChannels;
}
@Override
public int getRowCount()
{
return mChannels.size();
}
@Override
public int getColumnCount()
{
return COLUMN_NAMES.length;
}
@Override
public String getColumnName(int columnIndex)
{
if(columnIndex < COLUMN_NAMES.length)
{
return COLUMN_NAMES[columnIndex];
}
return null;
}
@Override
public Class<?> getColumnClass(int columnIndex)
{
return String.class;
}
@Override
public boolean isCellEditable(int rowIndex, int columnIndex)
{
return false;
}
@Override
public Object getValueAt(int rowIndex, int columnIndex)
{
Channel channel = mChannels.get(rowIndex);
switch(columnIndex)
{
case COLUMN_ORDER:
return channel.getAutoStartOrder();
case COLUMN_SYSTEM:
return channel.getSystem();
case COLUMN_SITE:
return channel.getSite();
case COLUMN_NAME:
return channel.getName();
case COLUMN_DECODER:
return channel.getDecodeConfiguration().getDecoderType().getShortDisplayString();
}
return null;
}
@Override
public void setValueAt(Object aValue, int rowIndex, int columnIndex)
{
throw new IllegalArgumentException("Not yet implemented");
}
}

View File

@@ -1,6 +1,6 @@
/******************************************************************************* /*******************************************************************************
* sdrtrunk * sdrtrunk
* Copyright (C) 2014-2017 Dennis Sheirer * Copyright (C) 2014-2018 Dennis Sheirer
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
@@ -45,18 +45,18 @@ import io.github.dsheirer.source.tuner.frequency.FrequencyChangeEvent.Event;
public class Channel extends Configuration implements Listener<FrequencyChangeEvent> public class Channel extends Configuration implements Listener<FrequencyChangeEvent>
{ {
// Standard channels are persisted and traffic channels are temporary // Standard channels are persisted and traffic channels are temporary
public enum ChannelType {STANDARD, TRAFFIC} public enum ChannelType
{
STANDARD, TRAFFIC
}
// Static unique channel identifier tracking // Static unique channel identifier tracking
private static int UNIQUE_ID = 0; private static int UNIQUE_ID = 0;
private DecodeConfiguration mDecodeConfiguration = DecoderFactory.getDefaultDecodeConfiguration(); private DecodeConfiguration mDecodeConfiguration = DecoderFactory.getDefaultDecodeConfiguration();
private AuxDecodeConfiguration mAuxDecodeConfiguration = private AuxDecodeConfiguration mAuxDecodeConfiguration = new AuxDecodeConfiguration();
new AuxDecodeConfiguration(); private SourceConfiguration mSourceConfiguration = SourceConfigFactory.getDefaultSourceConfiguration();
private SourceConfiguration mSourceConfiguration = private EventLogConfiguration mEventLogConfiguration = new EventLogConfiguration();
SourceConfigFactory.getDefaultSourceConfiguration();
private EventLogConfiguration mEventLogConfiguration =
new EventLogConfiguration();
private RecordConfiguration mRecordConfiguration = new RecordConfiguration(); private RecordConfiguration mRecordConfiguration = new RecordConfiguration();
private String mAliasListName; private String mAliasListName;
@@ -64,7 +64,9 @@ public class Channel extends Configuration implements Listener<FrequencyChangeEv
private String mSite = "Site"; private String mSite = "Site";
private String mName = "Channel"; private String mName = "Channel";
private boolean mEnabled; private boolean mProcessing;
private boolean mAutoStart;
private Integer mAutoStartOrder;
private boolean mSelected; private boolean mSelected;
private TunerChannel mTunerChannel = null; private TunerChannel mTunerChannel = null;
@@ -113,6 +115,8 @@ public class Channel extends Configuration implements Listener<FrequencyChangeEv
channel.setSystem(mSystem); channel.setSystem(mSystem);
channel.setSite(mSite); channel.setSite(mSite);
channel.setAliasListName(mAliasListName); channel.setAliasListName(mAliasListName);
channel.setAutoStart(mAutoStart);
channel.setAutoStartOrder(mAutoStartOrder);
AuxDecodeConfiguration aux = new AuxDecodeConfiguration(); AuxDecodeConfiguration aux = new AuxDecodeConfiguration();
@@ -258,27 +262,90 @@ public class Channel extends Configuration implements Listener<FrequencyChangeEv
} }
/** /**
* Indicates if this channel is enabled for processing. * Indicates if this channel is currently started and processing.
*/ */
@JacksonXmlProperty(isAttribute = true, localName = "enabled") @JsonIgnore
public boolean getEnabled() public boolean isProcessing()
{ {
return mEnabled; return mProcessing;
} }
/** /**
* Indicates if this channel will automatically start processing on * Indicates if this channel is currently started and processing.
* application startup or if this channel is currently processing after
* application startup.
* *
* Note: this method is package private and is intended to be managed * Note: this method is package private and is intended to be managed by a channel processing manager instance
* by a co-package central processing manager
* *
* @see ChannelProcessingManager * @see ChannelProcessingManager
*/ */
void setEnabled(boolean enabled) void setProcessing(boolean processing)
{ {
mEnabled = enabled; mProcessing = processing;
}
/**
* Indicates if this channel has auto-start enabled.
*
* Note: the Json (de)serialization tag for auto-start is 'enabled'. We've re-purposed the now
* transient enabled property for the auto-start persisted value to maintain backward compatibility.
*
* @return true if this channel should be auto-started on application startup
*/
@JacksonXmlProperty(isAttribute = true, localName = "enabled")
public boolean getAutoStart()
{
return mAutoStart;
}
/**
* Indicates if this channel is set for auto-start
*/
@JsonIgnore
public boolean isAutoStart()
{
return mAutoStart;
}
/**
* Sets the auto-start status for this channel.
*
* @param autoStart
*/
public void setAutoStart(boolean autoStart)
{
mAutoStart = autoStart;
}
/**
* Auto-start order. Specifies an optional ordering for channels so that when the application starts up,
* the channels can be started in a user-specified order.
*
* @return auto-start order or null
*/
@JacksonXmlProperty(isAttribute = true, localName = "order")
public Integer getAutoStartOrder()
{
return mAutoStartOrder;
}
/**
* Sets the auto-start order for this channel
*
* @param order
*/
public void setAutoStartOrder(Integer order)
{
mAutoStartOrder = order;
}
/**
* Indicates if this channel has an auto-start order specified (ie non-null).
*
* @return true if this channel has an auto-start order
*/
@JsonIgnore
public boolean hasAutoStartOrder()
{
return mAutoStartOrder != null;
} }
/** /**

View File

@@ -0,0 +1,246 @@
/*******************************************************************************
* 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.controller.channel;
import io.github.dsheirer.util.ThreadPool;
import net.miginfocom.swing.MigLayout;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.util.List;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
public class ChannelAutoStartFrame extends JFrame
{
private final static Logger mLog = LoggerFactory.getLogger(ChannelAutoStartFrame.class);
private static final int COUNTDOWN_SECONDS = 10;
private ChannelEventListener mChannelEventListener;
private List<Channel> mChannels;
private JLabel mCountdownLabel;
private JButton mStartButton;
private JButton mCancelButton;
private JTable mChannelTable;
private AtomicBoolean mChannelsStarted = new AtomicBoolean();
private ScheduledFuture<?> mTimerFuture;
/**
* Creates and displays a channel auto-start gui for presenting the user with a list of channels that
* will be automatically started once the countdown timer reaches zero, or the user chooses to start
* now or cancel.
*
* @param listener to receive channel start/enable request(s)
* @param channels to auto-start
*/
public ChannelAutoStartFrame(ChannelEventListener listener, List<Channel> channels)
{
mChannelEventListener = listener;
mChannels = channels;
init();
EventQueue.invokeLater(new Runnable()
{
@Override
public void run()
{
setVisible(true);
startTimer();
}
});
}
private void init()
{
setTitle("Auto-Start Channels");
setSize(new Dimension(400,300));
setLocationRelativeTo(null);
addWindowListener(new WindowAdapter()
{
@Override
public void windowClosing(WindowEvent event)
{
mLog.info("Channel auto-start canceled by user - window closed");
stopTimer();
super.windowClosing(event);
}
});
JPanel panel = new JPanel();
panel.setLayout(new MigLayout("","[grow,fill][grow,fill]",
"[][][grow,fill][]"));
panel.add(new JLabel("The following channels will be automatically"), "span");
mCountdownLabel = new JLabel(getCountdownText(COUNTDOWN_SECONDS));
panel.add(mCountdownLabel, "span");
panel.add(new JScrollPane(getChannelTable()), "span");
mStartButton = new JButton("Start Now");
mStartButton.addActionListener(new ActionListener()
{
@Override
public void actionPerformed(ActionEvent e)
{
mLog.info("Starting [" + mChannels.size() + "] channels now - user invoked");
startChannels();
stopTimer();
}
});
panel.add(mStartButton);
mCancelButton = new JButton("Cancel");
mCancelButton.addActionListener(new ActionListener()
{
@Override
public void actionPerformed(ActionEvent e)
{
mLog.info("Channel auto-start canceled by user");
stopTimer();
}
});
panel.add(mCancelButton);
setContentPane(panel);
}
private String getCountdownText(int value)
{
return "started in: " + value + " seconds.";
}
/**
* Updates the countdown text label on the swing event thread
* @param value for the countdown
*/
private void updateCountdownText(final int value)
{
EventQueue.invokeLater(new Runnable()
{
@Override
public void run()
{
mCountdownLabel.setText(getCountdownText(value));
}
});
}
/**
* Creates (once) a table of auto-start channels
*/
private JTable getChannelTable()
{
if(mChannelTable == null)
{
AutoStartChannelModel model = new AutoStartChannelModel(mChannels);
mChannelTable = new JTable(model);
}
return mChannelTable;
}
/**
* Sends an enable (ie start) request for each of the auto start channels.
*
* This method is thread-safe and will only be executed once.
*
* Although the redundant channel-start request would be ignored, this once-only will prevent the chance
* that both the timer and the user would attempt to start the channels at the same time.
*/
private void startChannels()
{
if(mChannelsStarted.compareAndSet(false, true))
{
if(mChannelEventListener != null)
{
for(Channel channel: mChannels)
{
mChannelEventListener.channelChanged(new ChannelEvent(channel, ChannelEvent.Event.REQUEST_ENABLE));
}
}
}
}
/**
* Starts the countdown timer to fire once a second.
*/
private void startTimer()
{
if(mTimerFuture == null)
{
mTimerFuture = ThreadPool.SCHEDULED.scheduleAtFixedRate(new CountdownTimer(), 0, 1, TimeUnit.SECONDS);
}
}
/**
* Stops the countdown timer and disposes this frame
*/
private void stopTimer()
{
if(mTimerFuture != null)
{
mTimerFuture.cancel(true);
mTimerFuture = null;
}
EventQueue.invokeLater(new Runnable()
{
@Override
public void run()
{
dispose();
}
});
}
/**
* Updates the countdown text and auto-starts the channels once the timer falls below zero.
*/
public class CountdownTimer implements Runnable
{
private int mCount = COUNTDOWN_SECONDS;
@Override
public void run()
{
if(mCount >= 0)
{
updateCountdownText(mCount);
mCount--;
}
else
{
mLog.info("Starting [" + mChannels.size() + "] now - timer invoked");
startChannels();
stopTimer();
}
}
}
}

View File

@@ -8,11 +8,19 @@ import net.coderazzi.filters.gui.AutoChoices;
import net.coderazzi.filters.gui.TableFilterHeader; import net.coderazzi.filters.gui.TableFilterHeader;
import net.miginfocom.swing.MigLayout; import net.miginfocom.swing.MigLayout;
import javax.swing.*; import javax.swing.JButton;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.ListSelectionModel;
import javax.swing.event.ListSelectionEvent; import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener; import javax.swing.event.ListSelectionListener;
import javax.swing.table.DefaultTableCellRenderer; import javax.swing.table.DefaultTableCellRenderer;
import java.awt.*; import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Rectangle;
import java.awt.event.ActionEvent; import java.awt.event.ActionEvent;
import java.awt.event.ActionListener; import java.awt.event.ActionListener;
@@ -236,7 +244,7 @@ public class ChannelController extends JPanel
Channel channel = model.getChannelAtIndex( index ); Channel channel = model.getChannelAtIndex( index );
boolean enabled = channel.getEnabled(); boolean enabled = channel.isProcessing();
if( isSelected ) if( isSelected )
{ {

View File

@@ -30,221 +30,227 @@ import io.github.dsheirer.source.SourceConfigurationEditor;
import io.github.dsheirer.source.SourceManager; import io.github.dsheirer.source.SourceManager;
import net.miginfocom.swing.MigLayout; import net.miginfocom.swing.MigLayout;
import javax.swing.*; import javax.swing.JButton;
import java.awt.*; import javax.swing.JLabel;
import javax.swing.JOptionPane;
import java.awt.Color;
import java.awt.event.ActionEvent; import java.awt.event.ActionEvent;
import java.awt.event.ActionListener; import java.awt.event.ActionListener;
public class ChannelEditor extends Editor<Channel> implements ActionListener, ChannelEventListener public class ChannelEditor extends Editor<Channel> implements ActionListener, ChannelEventListener
{ {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
private static final String ACTION_START = "Start";
private static final String ACTION_STOP = "Stop";
private static final String ACTION_SAVE = "Save";
private static final String ACTION_RESET = "Reset";
private NameConfigurationEditor mNameConfigurationEditor; private NameConfigurationEditor mNameConfigurationEditor;
private SourceConfigurationEditor mSourceConfigurationEditor; private SourceConfigurationEditor mSourceConfigurationEditor;
private DecodeConfigurationEditor mDecodeConfigurationEditor; private DecodeConfigurationEditor mDecodeConfigurationEditor;
private AuxDecodeConfigurationEditor mAuxDecodeConfigurationEditor; private AuxDecodeConfigurationEditor mAuxDecodeConfigurationEditor;
private EventLogConfigurationEditor mEventLogConfigurationEditor; private EventLogConfigurationEditor mEventLogConfigurationEditor;
private RecordConfigurationEditor mRecordConfigurationEditor; private RecordConfigurationEditor mRecordConfigurationEditor;
private JButton mEnableButton = new JButton( "Enable" ); private JButton mProcessingStateButton = new JButton(ACTION_START);
private JLabel mChannelName = new JLabel( "Channel:" ); private JLabel mChannelName = new JLabel("Channel:");
private ChannelModel mChannelModel; private ChannelModel mChannelModel;
private ChannelMapModel mChannelMapModel; private ChannelMapModel mChannelMapModel;
private SourceManager mSourceManager; private SourceManager mSourceManager;
private boolean mChannelEnableRequested = false; private boolean mChannelStartRequested = false;
public ChannelEditor( ChannelModel channelModel, public ChannelEditor(ChannelModel channelModel,
ChannelMapModel channelMapModel, ChannelMapModel channelMapModel,
SourceManager sourceManager, SourceManager sourceManager,
AliasModel aliasModel ) AliasModel aliasModel)
{
mChannelModel = channelModel;
mChannelMapModel = channelMapModel;
mSourceManager = sourceManager;
mNameConfigurationEditor = new NameConfigurationEditor( aliasModel, mChannelModel );
init();
}
private void init()
{
setLayout( new MigLayout( "fill,wrap 3", "[grow,fill][grow,fill][grow,fill]", "[grow,fill][]" ) );
JideTabbedPane tabs = new JideTabbedPane();
tabs.setFont( this.getFont() );
tabs.setForeground( Color.BLACK );
tabs.setTabTrailingComponent( mChannelName );
mNameConfigurationEditor.setSaveRequestListener( this );
tabs.addTab( "Name/Alias", mNameConfigurationEditor );
mSourceConfigurationEditor = new SourceConfigurationEditor( mSourceManager );
mSourceConfigurationEditor.setSaveRequestListener( this );
tabs.addTab( "Source", mSourceConfigurationEditor );
mDecodeConfigurationEditor = new DecodeConfigurationEditor( mChannelMapModel );
mDecodeConfigurationEditor.setSaveRequestListener( this );
tabs.addTab( "Decoder", mDecodeConfigurationEditor );
mAuxDecodeConfigurationEditor = new AuxDecodeConfigurationEditor();
mAuxDecodeConfigurationEditor.setSaveRequestListener( this );
tabs.addTab( "Aux Decoders", mAuxDecodeConfigurationEditor );
mEventLogConfigurationEditor = new EventLogConfigurationEditor();
mEventLogConfigurationEditor.setSaveRequestListener( this );
tabs.addTab( "Logging", mEventLogConfigurationEditor );
mRecordConfigurationEditor = new RecordConfigurationEditor();
mRecordConfigurationEditor.setSaveRequestListener( this );
tabs.addTab( "Recording", mRecordConfigurationEditor );
add( tabs, "span" );
mEnableButton.addActionListener( this );
mEnableButton.setEnabled( false );
mEnableButton.setToolTipText( "Start the currently selected channel running/decoding" );
add( mEnableButton );
JButton btnSave = new JButton( "Save" );
btnSave.setToolTipText( "Save changes to the currently selected channel" );
btnSave.addActionListener( ChannelEditor.this );
add( btnSave );
JButton btnReset = new JButton( "Reset" );
btnReset.setToolTipText( "Reload the currently selected channel" );
btnReset.addActionListener( ChannelEditor.this );
add( btnReset );
}
@Override
public void channelChanged( ChannelEvent event )
{
if( hasItem() && getItem() == event.getChannel() )
{
switch( event.getEvent() )
{
case NOTIFICATION_CONFIGURATION_CHANGE:
case NOTIFICATION_PROCESSING_START:
case NOTIFICATION_PROCESSING_STOP:
setItem( getItem() );
mChannelEnableRequested = false;
break;
case NOTIFICATION_DELETE:
setItem( null );
break;
case NOTIFICATION_ENABLE_REJECTED:
if( mChannelEnableRequested )
{
JOptionPane.showMessageDialog( ChannelEditor.this, "Channel could not be "
+ "enabled. This is likely because there are no tuner channels "
+ "available", "Couldn't enable channel", JOptionPane.INFORMATION_MESSAGE );
mChannelEnableRequested = false;
}
break;
default:
break;
}
}
}
@Override
public void actionPerformed( ActionEvent e )
{ {
String command = e.getActionCommand(); mChannelModel = channelModel;
mChannelMapModel = channelMapModel;
mSourceManager = sourceManager;
mNameConfigurationEditor = new NameConfigurationEditor(aliasModel, mChannelModel);
if( command.contentEquals( "Enable" ) ) init();
{
if( hasItem() )
{
save();
mChannelEnableRequested = true;
mChannelModel.broadcast( new ChannelEvent( getItem(), ChannelEvent.Event.REQUEST_ENABLE ) );
}
}
else if( command.contentEquals( "Disable" ) )
{
if( hasItem() )
{
mChannelModel.broadcast( new ChannelEvent( getItem(), ChannelEvent.Event.REQUEST_DISABLE ) );
}
}
else if( command.contentEquals( "Save" ) )
{
save();
}
else if( command.contentEquals( "Reset" ) )
{
setItem( getItem() );
}
} }
/** private void init()
* Sets the channel configuration in each of the channel editor components {
* Note: this method must be invoked on the swing event dispatch thread setLayout(new MigLayout("fill,wrap 3", "[grow,fill][grow,fill][grow,fill]", "[grow,fill][]"));
*/
public void setItem( final Channel channel )
{
super.setItem( channel );
mNameConfigurationEditor.setItem( channel ); JideTabbedPane tabs = new JideTabbedPane();
mSourceConfigurationEditor.setItem( channel ); tabs.setFont(this.getFont());
mDecodeConfigurationEditor.setItem( channel ); tabs.setForeground(Color.BLACK);
mAuxDecodeConfigurationEditor.setItem( channel );
mEventLogConfigurationEditor.setItem( channel );
mRecordConfigurationEditor.setItem( channel );
if( channel != null ) tabs.setTabTrailingComponent(mChannelName);
{
mChannelName.setText( "Channel: " + channel.getName() ); mNameConfigurationEditor.setSaveRequestListener(this);
mEnableButton.setText( channel.getEnabled() ? "Disable" : "Enable" ); tabs.addTab("Name/Alias", mNameConfigurationEditor);
mEnableButton.setEnabled( true );
mEnableButton.setBackground( channel.getEnabled() ? Color.GREEN : getBackground() ); mSourceConfigurationEditor = new SourceConfigurationEditor(mSourceManager);
} mSourceConfigurationEditor.setSaveRequestListener(this);
else tabs.addTab("Source", mSourceConfigurationEditor);
{
mChannelName.setText( "Channel: " ); mDecodeConfigurationEditor = new DecodeConfigurationEditor(mChannelMapModel);
mEnableButton.setText( "Enable" ); mDecodeConfigurationEditor.setSaveRequestListener(this);
mEnableButton.setEnabled( false ); tabs.addTab("Decoder", mDecodeConfigurationEditor);
mEnableButton.setBackground( getBackground() );
} mAuxDecodeConfigurationEditor = new AuxDecodeConfigurationEditor();
} mAuxDecodeConfigurationEditor.setSaveRequestListener(this);
tabs.addTab("Aux Decoders", mAuxDecodeConfigurationEditor);
mEventLogConfigurationEditor = new EventLogConfigurationEditor();
mEventLogConfigurationEditor.setSaveRequestListener(this);
tabs.addTab("Logging", mEventLogConfigurationEditor);
mRecordConfigurationEditor = new RecordConfigurationEditor();
mRecordConfigurationEditor.setSaveRequestListener(this);
tabs.addTab("Recording", mRecordConfigurationEditor);
add(tabs, "span");
mProcessingStateButton.addActionListener(this);
mProcessingStateButton.setEnabled(false);
mProcessingStateButton.setToolTipText("Start/Stop the currently selected channel");
add(mProcessingStateButton);
JButton btnSave = new JButton(ACTION_SAVE);
btnSave.setToolTipText("Save changes to the currently selected channel");
btnSave.addActionListener(ChannelEditor.this);
add(btnSave);
JButton btnReset = new JButton(ACTION_RESET);
btnReset.setToolTipText("Reload the currently selected channel");
btnReset.addActionListener(ChannelEditor.this);
add(btnReset);
}
@Override
public void channelChanged(ChannelEvent event)
{
if(hasItem() && getItem() == event.getChannel())
{
switch(event.getEvent())
{
case NOTIFICATION_CONFIGURATION_CHANGE:
case NOTIFICATION_PROCESSING_START:
case NOTIFICATION_PROCESSING_STOP:
setItem(getItem());
mChannelStartRequested = false;
break;
case NOTIFICATION_DELETE:
setItem(null);
break;
case NOTIFICATION_START_PROCESSING_REJECTED:
if(mChannelStartRequested)
{
JOptionPane.showMessageDialog(ChannelEditor.this, "Channel could not be "
+ "started. This is likely because there are no tuner channels "
+ "available", "Couldn't start channel", JOptionPane.INFORMATION_MESSAGE);
mChannelStartRequested = false;
}
break;
default:
break;
}
}
}
@Override
public void actionPerformed(ActionEvent e)
{
String command = e.getActionCommand();
if(command.contentEquals(ACTION_START))
{
if(hasItem())
{
save();
mChannelStartRequested = true;
mChannelModel.broadcast(new ChannelEvent(getItem(), ChannelEvent.Event.REQUEST_ENABLE));
}
}
else if(command.contentEquals(ACTION_STOP))
{
if(hasItem())
{
mChannelModel.broadcast(new ChannelEvent(getItem(), ChannelEvent.Event.REQUEST_DISABLE));
}
}
else if(command.contentEquals(ACTION_SAVE))
{
save();
}
else if(command.contentEquals(ACTION_RESET))
{
setItem(getItem());
}
}
/**
* Sets the channel configuration in each of the channel editor components
* Note: this method must be invoked on the swing event dispatch thread
*/
public void setItem(final Channel channel)
{
super.setItem(channel);
mNameConfigurationEditor.setItem(channel);
mSourceConfigurationEditor.setItem(channel);
mDecodeConfigurationEditor.setItem(channel);
mAuxDecodeConfigurationEditor.setItem(channel);
mEventLogConfigurationEditor.setItem(channel);
mRecordConfigurationEditor.setItem(channel);
if(channel != null)
{
mChannelName.setText("Channel: " + channel.getName());
mProcessingStateButton.setText(channel.isProcessing() ? ACTION_STOP : ACTION_START);
mProcessingStateButton.setEnabled(true);
mProcessingStateButton.setBackground(channel.isProcessing() ? Color.GREEN : getBackground());
}
else
{
mChannelName.setText("Channel: ");
mProcessingStateButton.setText(ACTION_START);
mProcessingStateButton.setEnabled(false);
mProcessingStateButton.setBackground(getBackground());
}
}
public void save() public void save()
{ {
if( hasItem() ) if(hasItem())
{ {
mNameConfigurationEditor.save(); mNameConfigurationEditor.save();
mSourceConfigurationEditor.save(); mSourceConfigurationEditor.save();
mDecodeConfigurationEditor.save(); mDecodeConfigurationEditor.save();
mAuxDecodeConfigurationEditor.save(); mAuxDecodeConfigurationEditor.save();
mEventLogConfigurationEditor.save(); mEventLogConfigurationEditor.save();
mRecordConfigurationEditor.save(); mRecordConfigurationEditor.save();
try try
{ {
mDecodeConfigurationEditor.validate( mSourceConfigurationEditor ); mDecodeConfigurationEditor.validate(mSourceConfigurationEditor);
mDecodeConfigurationEditor.validate( mAuxDecodeConfigurationEditor ); mDecodeConfigurationEditor.validate(mAuxDecodeConfigurationEditor);
} }
catch( EditorValidationException e ) catch(EditorValidationException e)
{ {
e.getEditor().requestFocus(); e.getEditor().requestFocus();
JOptionPane.showMessageDialog( e.getEditor(), e.getReason(), JOptionPane.showMessageDialog(e.getEditor(), e.getReason(),
"Configuration Error", JOptionPane.ERROR_MESSAGE ); "Configuration Error", JOptionPane.ERROR_MESSAGE);
return; return;
} }
mChannelModel.broadcast( new ChannelEvent( getItem(), mChannelModel.broadcast(new ChannelEvent(getItem(),
ChannelEvent.Event.NOTIFICATION_CONFIGURATION_CHANGE ) ); ChannelEvent.Event.NOTIFICATION_CONFIGURATION_CHANGE));
} }
setModified( false ); setModified(false);
} }
} }

View File

@@ -1,6 +1,6 @@
/******************************************************************************* /*******************************************************************************
* SDR Trunk * SDR Trunk
* Copyright (C) 2014-2016 Dennis Sheirer * Copyright (C) 2014-2018 Dennis Sheirer
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
@@ -43,6 +43,11 @@ public class ChannelEvent
return mEvent; return mEvent;
} }
public String toString()
{
return getEvent().name() + " Channel:" + getChannel().getName();
}
/** /**
* Channel events to describe the specific event * Channel events to describe the specific event
*/ */
@@ -55,7 +60,7 @@ public class ChannelEvent
//Channel is deleted/removed //Channel is deleted/removed
NOTIFICATION_DELETE, NOTIFICATION_DELETE,
//Channel enable request was rejected //Channel enable request was rejected
NOTIFICATION_ENABLE_REJECTED, NOTIFICATION_START_PROCESSING_REJECTED,
//Channel has started processing/decoding //Channel has started processing/decoding
NOTIFICATION_PROCESSING_START, NOTIFICATION_PROCESSING_START,
//Channel has stopped processing/decoding //Channel has stopped processing/decoding

View File

@@ -25,6 +25,7 @@ import io.github.dsheirer.module.decode.DecoderType;
import javax.swing.table.AbstractTableModel; import javax.swing.table.AbstractTableModel;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.Comparator;
import java.util.List; import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.CopyOnWriteArrayList;
@@ -42,9 +43,11 @@ public class ChannelModel extends AbstractTableModel implements IChannelEventBro
public static final int COLUMN_ALIAS_LIST = 4; public static final int COLUMN_ALIAS_LIST = 4;
public static final int COLUMN_SOURCE = 5; public static final int COLUMN_SOURCE = 5;
public static final int COLUMN_DECODER = 6; public static final int COLUMN_DECODER = 6;
public static final int COLUMN_AUTO_START = 7;
private static final String[] COLUMN_NAMES = new String[] {"Enabled", "System", "Site", "Name", "Alias List", private static final String[] COLUMN_NAMES = new String[] {"Playing", "System", "Site", "Name", "Alias List",
"Source", "Decoder"}; "Source", "Decoder", "Auto-Start"};
private static final String VALUE_YES = "Yes";
private List<Channel> mChannels = new CopyOnWriteArrayList<>(); private List<Channel> mChannels = new CopyOnWriteArrayList<>();
private List<Channel> mTrafficChannels = new CopyOnWriteArrayList<>(); private List<Channel> mTrafficChannels = new CopyOnWriteArrayList<>();
@@ -209,11 +212,6 @@ public class ChannelModel extends AbstractTableModel implements IChannelEventBro
broadcast(new ChannelEvent(channel, Event.NOTIFICATION_ADD)); broadcast(new ChannelEvent(channel, Event.NOTIFICATION_ADD));
if(channel.getEnabled())
{
broadcast(new ChannelEvent(channel, Event.REQUEST_ENABLE));
}
return index; return index;
} }
@@ -247,6 +245,48 @@ public class ChannelModel extends AbstractTableModel implements IChannelEventBro
} }
} }
/**
* List of channels that have the auto-start flag set.
*
* @return auto-start channels sorted by the channel order value.
*/
public List<Channel> getAutoStartChannels()
{
List<Channel> autoStartChannels = new ArrayList<>();
for(Channel channel: getChannels())
{
if(channel.isAutoStart())
{
autoStartChannels.add(channel);
}
}
Collections.sort(autoStartChannels, new Comparator<Channel>()
{
@Override
public int compare(Channel channel1, Channel channel2)
{
if(channel1.hasAutoStartOrder() && channel2.hasAutoStartOrder())
{
return Integer.compare(channel1.getAutoStartOrder(), channel2.getAutoStartOrder());
}
else if(channel1.hasAutoStartOrder())
{
return -1;
}
else if(channel2.hasAutoStartOrder())
{
return 1;
}
return 0;
}
});
return autoStartChannels;
}
/** /**
* Returns a list of channels that fall within the frequency range * Returns a list of channels that fall within the frequency range
* *
@@ -331,7 +371,7 @@ public class ChannelModel extends AbstractTableModel implements IChannelEventBro
switch(columnIndex) switch(columnIndex)
{ {
case COLUMN_ENABLED: case COLUMN_ENABLED:
return channel.getEnabled() ? "Enabled" : null; return channel.isProcessing() ? VALUE_YES : null;
case COLUMN_SYSTEM: case COLUMN_SYSTEM:
return channel.getSystem(); return channel.getSystem();
case COLUMN_SITE: case COLUMN_SITE:
@@ -344,6 +384,19 @@ public class ChannelModel extends AbstractTableModel implements IChannelEventBro
return channel.getSourceConfiguration().getDescription(); return channel.getSourceConfiguration().getDescription();
case COLUMN_DECODER: case COLUMN_DECODER:
return channel.getDecodeConfiguration().getDecoderType().getShortDisplayString(); return channel.getDecodeConfiguration().getDecoderType().getShortDisplayString();
case COLUMN_AUTO_START:
if(channel.isAutoStart())
{
if(channel.hasAutoStartOrder())
{
return channel.getAutoStartOrder();
}
else
{
return VALUE_YES;
}
}
break;
} }
return null; return null;

View File

@@ -159,7 +159,7 @@ public class ChannelProcessingManager implements ChannelEventListener
} }
break; break;
case REQUEST_DISABLE: case REQUEST_DISABLE:
if(channel.getEnabled()) if(channel.isProcessing())
{ {
switch(channel.getChannelType()) switch(channel.getChannelType())
{ {
@@ -176,7 +176,7 @@ public class ChannelProcessingManager implements ChannelEventListener
} }
} }
case NOTIFICATION_DELETE: case NOTIFICATION_DELETE:
if(channel.getEnabled()) if(channel.isProcessing())
{ {
stopProcessing(channel, true); stopProcessing(channel, true);
} }
@@ -220,9 +220,9 @@ public class ChannelProcessingManager implements ChannelEventListener
if(source == null) if(source == null)
{ {
channel.setEnabled(false); channel.setProcessing(false);
mChannelModel.broadcast(new ChannelEvent(channel, ChannelEvent.Event.NOTIFICATION_ENABLE_REJECTED)); mChannelModel.broadcast(new ChannelEvent(channel, ChannelEvent.Event.NOTIFICATION_START_PROCESSING_REJECTED));
return; return;
} }
@@ -325,7 +325,7 @@ public class ChannelProcessingManager implements ChannelEventListener
getChannelMetadataModel().add(processingChain.getChannelState().getMutableMetadata(), channel); getChannelMetadataModel().add(processingChain.getChannelState().getMutableMetadata(), channel);
channel.setEnabled(true); channel.setProcessing(true);
mProcessingChains.put(channel.getChannelID(), processingChain); mProcessingChains.put(channel.getChannelID(), processingChain);
@@ -334,7 +334,7 @@ public class ChannelProcessingManager implements ChannelEventListener
private void stopProcessing(Channel channel, boolean remove) private void stopProcessing(Channel channel, boolean remove)
{ {
channel.setEnabled(false); channel.setProcessing(false);
if(mProcessingChains.containsKey(channel.getChannelID())) if(mProcessingChains.containsKey(channel.getChannelID()))
{ {

View File

@@ -4,8 +4,11 @@ import io.github.dsheirer.channel.state.DecoderState;
import io.github.dsheirer.module.ProcessingChain; import io.github.dsheirer.module.ProcessingChain;
import io.github.dsheirer.module.decode.event.ActivitySummaryFrame; import io.github.dsheirer.module.decode.event.ActivitySummaryFrame;
import javax.swing.*; import javax.swing.JMenu;
import java.awt.*; import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JSeparator;
import java.awt.Component;
import java.awt.event.ActionEvent; import java.awt.event.ActionEvent;
import java.awt.event.ActionListener; import java.awt.event.ActionListener;
@@ -23,7 +26,7 @@ public class ChannelUtils
{ {
JMenu menu = new JMenu( "Channel: " + channel.getName() ); JMenu menu = new JMenu( "Channel: " + channel.getName() );
if( channel.getEnabled() ) if( channel.isProcessing() )
{ {
JMenuItem disable = new JMenuItem( "Disable" ); JMenuItem disable = new JMenuItem( "Disable" );
disable.addActionListener( new ActionListener() disable.addActionListener( new ActionListener()

View File

@@ -23,9 +23,21 @@ import net.miginfocom.swing.MigLayout;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import javax.swing.*; import javax.swing.ComboBoxModel;
import javax.swing.DefaultComboBoxModel;
import javax.swing.JCheckBox;
import javax.swing.JComboBox;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JSpinner;
import javax.swing.JTextField;
import javax.swing.SpinnerModel;
import javax.swing.SpinnerNumberModel;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.event.DocumentEvent; import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener; import javax.swing.event.DocumentListener;
import java.awt.Dimension;
import java.awt.event.ActionEvent; import java.awt.event.ActionEvent;
import java.awt.event.ActionListener; import java.awt.event.ActionListener;
import java.util.Collections; import java.util.Collections;
@@ -48,6 +60,8 @@ public class NameConfigurationEditor extends Editor<Channel> implements Document
private JComboBox<String> mSystemNameCombo; private JComboBox<String> mSystemNameCombo;
private JComboBox<String> mSiteNameCombo; private JComboBox<String> mSiteNameCombo;
private JComboBox<String> mAliasListCombo; private JComboBox<String> mAliasListCombo;
private JCheckBox mAutoStartCheckBox;
private JSpinner mAutoStartOrder;
public NameConfigurationEditor(AliasModel aliasModel, ChannelModel model) public NameConfigurationEditor(AliasModel aliasModel, ChannelModel model)
{ {
@@ -139,6 +153,40 @@ public class NameConfigurationEditor extends Editor<Channel> implements Document
add(new JLabel("Site:")); add(new JLabel("Site:"));
add(mSiteNameCombo); add(mSiteNameCombo);
JPanel autoStartPanel = new JPanel();
mAutoStartCheckBox = new JCheckBox("Auto-Start");
mAutoStartCheckBox.setToolTipText("Automatically start this channel each time the application runs.");
mAutoStartCheckBox.addActionListener(new ActionListener()
{
@Override
public void actionPerformed(ActionEvent e)
{
mAutoStartOrder.setEnabled(mAutoStartCheckBox.isSelected());
setModified(true);
}
});
autoStartPanel.add(mAutoStartCheckBox);
SpinnerModel model = new SpinnerNumberModel(0,0,500,1);
mAutoStartOrder = new JSpinner(model);
mAutoStartOrder.setEnabled(false);
mAutoStartOrder.setPreferredSize(new Dimension(60, mAutoStartOrder.getPreferredSize().height));
mAutoStartOrder.setToolTipText("Auto-start order: 1 (high) <> 500 (low). 0 = no ordering");
mAutoStartOrder.addChangeListener(new ChangeListener()
{
@Override
public void stateChanged(ChangeEvent e)
{
setModified(true);
}
});
autoStartPanel.add(new JLabel("Order:"));
autoStartPanel.add(mAutoStartOrder);
add(new JLabel()); //Empty space holder
add(autoStartPanel);
} }
@Override @Override
@@ -156,6 +204,11 @@ public class NameConfigurationEditor extends Editor<Channel> implements Document
Object aliasList = mAliasListCombo.getSelectedItem(); Object aliasList = mAliasListCombo.getSelectedItem();
getItem().setAliasListName(aliasList == null ? null : (String)aliasList); getItem().setAliasListName(aliasList == null ? null : (String)aliasList);
getItem().setAutoStart(mAutoStartCheckBox.isSelected());
int order = ((Number)mAutoStartOrder.getValue()).intValue();
getItem().setAutoStartOrder((order > 0 ? order : null));
} }
setModified(false); setModified(false);
@@ -182,6 +235,16 @@ public class NameConfigurationEditor extends Editor<Channel> implements Document
{ {
mSiteNameCombo.setEnabled(enabled); mSiteNameCombo.setEnabled(enabled);
} }
if(mAutoStartCheckBox.isEnabled() != enabled)
{
mAutoStartCheckBox.setEnabled(enabled);
}
if(mAutoStartOrder.isEnabled() != enabled)
{
mAutoStartOrder.setEnabled(enabled);
}
} }
@Override @Override
@@ -244,6 +307,19 @@ public class NameConfigurationEditor extends Editor<Channel> implements Document
mChannelName.setText(null); mChannelName.setText(null);
} }
if(channel.isAutoStart())
{
mAutoStartCheckBox.setSelected(channel.isAutoStart());
mAutoStartOrder.setEnabled(true);
}
else
{
mAutoStartCheckBox.setSelected(false);
mAutoStartOrder.setEnabled(false);
}
mAutoStartOrder.setValue(channel.hasAutoStartOrder() ? channel.getAutoStartOrder() : 0);
setModified(false); setModified(false);
} }

View File

@@ -25,6 +25,8 @@ import io.github.dsheirer.audio.AudioManager;
import io.github.dsheirer.audio.broadcast.BroadcastModel; import io.github.dsheirer.audio.broadcast.BroadcastModel;
import io.github.dsheirer.audio.broadcast.BroadcastStatusPanel; import io.github.dsheirer.audio.broadcast.BroadcastStatusPanel;
import io.github.dsheirer.controller.ControllerPanel; 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.ChannelModel;
import io.github.dsheirer.controller.channel.ChannelProcessingManager; import io.github.dsheirer.controller.channel.ChannelProcessingManager;
import io.github.dsheirer.controller.channel.ChannelSelectionManager; import io.github.dsheirer.controller.channel.ChannelSelectionManager;
@@ -51,8 +53,19 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import javax.imageio.ImageIO; import javax.imageio.ImageIO;
import javax.swing.*; import javax.swing.JCheckBoxMenuItem;
import java.awt.*; 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 java.awt.AWTException;
import java.awt.Desktop;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Robot;
import java.awt.event.ActionEvent; import java.awt.event.ActionEvent;
import java.awt.event.ActionListener; import java.awt.event.ActionListener;
import java.awt.event.KeyEvent; import java.awt.event.KeyEvent;
@@ -61,6 +74,7 @@ import java.io.IOException;
import java.nio.file.FileSystems; import java.nio.file.FileSystems;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
import java.util.List;
public class SDRTrunk implements Listener<TunerEvent> public class SDRTrunk implements Listener<TunerEvent>
{ {
@@ -72,6 +86,8 @@ public class SDRTrunk implements Listener<TunerEvent>
private BroadcastStatusPanel mBroadcastStatusPanel; private BroadcastStatusPanel mBroadcastStatusPanel;
private BroadcastModel mBroadcastModel; private BroadcastModel mBroadcastModel;
private ControllerPanel mControllerPanel; private ControllerPanel mControllerPanel;
private ChannelModel mChannelModel;
private ChannelProcessingManager mChannelProcessingManager;
private SettingsManager mSettingsManager; private SettingsManager mSettingsManager;
private SpectralDisplayPanel mSpectralPanel; private SpectralDisplayPanel mSpectralPanel;
private JFrame mMainGui = new JFrame(); private JFrame mMainGui = new JFrame();
@@ -117,7 +133,7 @@ public class SDRTrunk implements Listener<TunerEvent>
AliasModel aliasModel = new AliasModel(); AliasModel aliasModel = new AliasModel();
ChannelModel channelModel = new ChannelModel(); mChannelModel = new ChannelModel();
ChannelMapModel channelMapModel = new ChannelMapModel(); ChannelMapModel channelMapModel = new ChannelMapModel();
@@ -127,43 +143,42 @@ public class SDRTrunk implements Listener<TunerEvent>
SourceManager sourceManager = new SourceManager(tunerModel, mSettingsManager); SourceManager sourceManager = new SourceManager(tunerModel, mSettingsManager);
ChannelProcessingManager channelProcessingManager = new ChannelProcessingManager( mChannelProcessingManager = new ChannelProcessingManager(mChannelModel, channelMapModel, aliasModel,
channelModel, channelMapModel, aliasModel, eventLogManager, recorderManager, sourceManager); eventLogManager, recorderManager, sourceManager);
channelProcessingManager.addAudioPacketListener(recorderManager); mChannelProcessingManager.addAudioPacketListener(recorderManager);
channelModel.addListener(channelProcessingManager); mChannelModel.addListener(mChannelProcessingManager);
ChannelSelectionManager channelSelectionManager = ChannelSelectionManager channelSelectionManager =
new ChannelSelectionManager(channelModel); new ChannelSelectionManager(mChannelModel);
channelModel.addListener(channelSelectionManager); mChannelModel.addListener(channelSelectionManager);
AliasActionManager aliasActionManager = new AliasActionManager(); AliasActionManager aliasActionManager = new AliasActionManager();
channelProcessingManager.addMessageListener(aliasActionManager); mChannelProcessingManager.addMessageListener(aliasActionManager);
AudioManager audioManager = new AudioManager(sourceManager.getMixerManager()); AudioManager audioManager = new AudioManager(sourceManager.getMixerManager());
channelProcessingManager.addAudioPacketListener(audioManager); mChannelProcessingManager.addAudioPacketListener(audioManager);
mBroadcastModel = new BroadcastModel(mIconManager); mBroadcastModel = new BroadcastModel(mIconManager);
channelProcessingManager.addAudioPacketListener(mBroadcastModel); mChannelProcessingManager.addAudioPacketListener(mBroadcastModel);
MapService mapService = new MapService(mIconManager); MapService mapService = new MapService(mIconManager);
channelProcessingManager.addMessageListener(mapService); mChannelProcessingManager.addMessageListener(mapService);
mControllerPanel = new ControllerPanel(audioManager, aliasModel, mBroadcastModel, mControllerPanel = new ControllerPanel(audioManager, aliasModel, mBroadcastModel,
channelModel, channelMapModel, channelProcessingManager, mIconManager, mChannelModel, channelMapModel, mChannelProcessingManager, mIconManager,
mapService, mSettingsManager, sourceManager, tunerModel); mapService, mSettingsManager, sourceManager, tunerModel);
mSpectralPanel = new SpectralDisplayPanel(channelModel, mSpectralPanel = new SpectralDisplayPanel(mChannelModel,
channelProcessingManager, mSettingsManager); mChannelProcessingManager, mSettingsManager);
TunerSpectralDisplayManager tunerSpectralDisplayManager = TunerSpectralDisplayManager tunerSpectralDisplayManager = new TunerSpectralDisplayManager(mSpectralPanel,
new TunerSpectralDisplayManager(mSpectralPanel, mChannelModel, mChannelProcessingManager, mSettingsManager);
channelModel, channelProcessingManager, mSettingsManager);
tunerModel.addListener(tunerSpectralDisplayManager); tunerModel.addListener(tunerSpectralDisplayManager);
tunerModel.addListener(this); tunerModel.addListener(this);
PlaylistManager playlistManager = new PlaylistManager(aliasModel, mBroadcastModel, channelModel, PlaylistManager playlistManager = new PlaylistManager(aliasModel, mBroadcastModel, mChannelModel,
channelMapModel); channelMapModel);
playlistManager.init(); playlistManager.init();
@@ -183,6 +198,7 @@ public class SDRTrunk implements Listener<TunerEvent>
try try
{ {
mMainGui.setVisible(true); mMainGui.setVisible(true);
autoStartChannels();
} }
catch(Exception e) catch(Exception e)
{ {
@@ -192,6 +208,21 @@ public class SDRTrunk implements Listener<TunerEvent>
}); });
} }
/**
* 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 = mChannelModel.getAutoStartChannels();
if(channels.size() > 0)
{
ChannelAutoStartFrame autoStartFrame = new ChannelAutoStartFrame(mChannelProcessingManager, channels);
}
}
/** /**
* Launch the application. * Launch the application.
*/ */
@@ -212,7 +243,8 @@ public class SDRTrunk implements Listener<TunerEvent>
*/ */
mTitle = SystemProperties.getInstance().getApplicationName(); mTitle = SystemProperties.getInstance().getApplicationName();
mMainGui.setTitle(mTitle); mMainGui.setTitle(mTitle);
mMainGui.setBounds(100, 100, 1280, 800); mMainGui.setSize(new Dimension(1280,800));
mMainGui.setLocationRelativeTo(null);
mMainGui.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); mMainGui.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
//Set preferred sizes to influence the split //Set preferred sizes to influence the split

View File

@@ -30,8 +30,13 @@ import io.github.dsheirer.spectrum.menu.SmoothingItem;
import io.github.dsheirer.spectrum.menu.SmoothingTypeItem; import io.github.dsheirer.spectrum.menu.SmoothingTypeItem;
import net.miginfocom.swing.MigLayout; import net.miginfocom.swing.MigLayout;
import javax.swing.*; import javax.swing.JLayeredPane;
import java.awt.*; import javax.swing.JMenu;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.JSeparator;
import javax.swing.SwingUtilities;
import java.awt.Component;
import java.awt.event.ComponentEvent; import java.awt.event.ComponentEvent;
import java.awt.event.ComponentListener; import java.awt.event.ComponentListener;
import java.awt.event.MouseEvent; import java.awt.event.MouseEvent;
@@ -193,7 +198,7 @@ public class ChannelSpectrumPanel extends JPanel
private void start() private void start()
{ {
if( mEnabled.get() && mCurrentChannel != null && mCurrentChannel.getEnabled() ) if( mEnabled.get() && mCurrentChannel != null && mCurrentChannel.isProcessing() )
{ {
ProcessingChain processingChain = mChannelProcessingManager ProcessingChain processingChain = mChannelProcessingManager
.getProcessingChain( mCurrentChannel ); .getProcessingChain( mCurrentChannel );
@@ -209,7 +214,7 @@ public class ChannelSpectrumPanel extends JPanel
private void stop() private void stop()
{ {
if( mCurrentChannel != null && mCurrentChannel.getEnabled() ) if( mCurrentChannel != null && mCurrentChannel.isProcessing() )
{ {
ProcessingChain processingChain = mChannelProcessingManager ProcessingChain processingChain = mChannelProcessingManager
.getProcessingChain( mCurrentChannel ); .getProcessingChain( mCurrentChannel );

View File

@@ -34,8 +34,16 @@ import io.github.dsheirer.source.tuner.frequency.IFrequencyChangeProcessor;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import javax.swing.*; import javax.swing.JPanel;
import java.awt.*; import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.RenderingHints;
import java.awt.Stroke;
import java.awt.event.ComponentEvent; import java.awt.event.ComponentEvent;
import java.awt.event.ComponentListener; import java.awt.event.ComponentListener;
import java.awt.geom.Line2D; import java.awt.geom.Line2D;
@@ -517,14 +525,14 @@ public class OverlayPanel extends JPanel implements ChannelEventListener, IFrequ
for(Channel channel : mVisibleChannels) for(Channel channel : mVisibleChannels)
{ {
if(mChannelDisplay == ChannelDisplay.ALL || if(mChannelDisplay == ChannelDisplay.ALL ||
(mChannelDisplay == ChannelDisplay.ENABLED && channel.getEnabled())) (mChannelDisplay == ChannelDisplay.ENABLED && channel.isProcessing()))
{ {
//Choose the correct background color to use //Choose the correct background color to use
if(channel.isSelected()) if(channel.isSelected())
{ {
graphics.setColor(mColorChannelConfigSelected); graphics.setColor(mColorChannelConfigSelected);
} }
else if(channel.getEnabled()) else if(channel.isProcessing())
{ {
graphics.setColor(mColorChannelConfigProcessing); graphics.setColor(mColorChannelConfigProcessing);
} }