();
-
- public ControllerTreeCellRenderer()
- {
- }
-
- @Override
- public Component getTreeCellRendererComponent( JTree tree,
- Object value, boolean selected, boolean expanded, boolean isLeaf,
- int row, boolean focused )
- {
- Component c = super.getTreeCellRendererComponent( tree, value, selected,
- expanded, isLeaf, row, focused );
-
- if( value instanceof BaseNode )
- {
- BaseNode node = (BaseNode)value;
-
- ImageIcon icon = getIcon( node );
-
- setIcon( icon );
-
- Color background = node.getBackgroundColor();
-
- if( background != null )
- {
- setBackground( background );
- setOpaque( true );
-
- if( selected )
- {
- if( background == Color.CYAN )
- {
- setForeground( Color.ORANGE );
- }
- else if( background == Color.GREEN )
- {
- setForeground( Color.MAGENTA );
- }
- else if( background == Color.RED )
- {
- setForeground( Color.YELLOW );
- }
- }
- }
- else
- {
- setOpaque( false );
- }
- }
-
- return c;
- }
-
- private ImageIcon getIcon( BaseNode node )
- {
- ImageIcon icon = null;
-
- String filepath = node.getIconPath();
-
- if( filepath != null )
- {
- if( mIcons.containsKey( filepath ) )
- {
- icon = mIcons.get( filepath );
- }
- else
- {
- icon = new ImageIcon( filepath );
-
- if( icon.getIconHeight() > MAX_ICON_HEIGHT )
- {
- Image image = icon.getImage();
-
- double scale = (double)icon.getIconHeight() / (double)MAX_ICON_HEIGHT;
-
- int scaledWidth = (int)( (double)icon.getIconWidth() / scale );
-
- Image scaledImage = image.getScaledInstance( scaledWidth,
- MAX_ICON_HEIGHT, java.awt.Image.SCALE_SMOOTH );
-
- mIcons.put( filepath, new ImageIcon( scaledImage ) );
- }
- else
- {
- mIcons.put( filepath, icon );
- }
-
- }
- }
-
- return icon;
- }
-}
diff --git a/src/controller/Editor.java b/src/controller/Editor.java
deleted file mode 100644
index 56411ce1..00000000
--- a/src/controller/Editor.java
+++ /dev/null
@@ -1,51 +0,0 @@
-/*******************************************************************************
- * SDR Trunk
- * Copyright (C) 2014 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
- ******************************************************************************/
-package controller;
-
-import javax.swing.JPanel;
-
-import net.miginfocom.swing.MigLayout;
-import controller.channel.ChannelValidationException;
-
-public abstract class Editor extends JPanel
-{
- private static final long serialVersionUID = 1L;
-
- public Editor()
- {
- setLayout( new MigLayout() );
- }
-
- public abstract void save();
-
- public abstract void reset();
-
- /**
- * Override this method to allow the editor to inspect the argument for
- * valid configuration
- *
- * @param editor - any editor
- *
- * @throws ChannelValidationException if the configuration is not compatible
- * with this editor's configuration. Supplies a validation text value for
- * display to the user
- */
- public void validate( Editor editor ) throws ChannelValidationException
- {
- }
-}
diff --git a/src/controller/EmptyEditor.java b/src/controller/EmptyEditor.java
deleted file mode 100644
index 83f21de3..00000000
--- a/src/controller/EmptyEditor.java
+++ /dev/null
@@ -1,59 +0,0 @@
-/*******************************************************************************
- * SDR Trunk
- * Copyright (C) 2014 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
- ******************************************************************************/
-package controller;
-
-import java.awt.BorderLayout;
-import java.awt.Dimension;
-
-import javax.swing.JLabel;
-import javax.swing.JPanel;
-
-public class EmptyEditor extends JPanel
-{
- private static final long serialVersionUID = 1L;
-
- public EmptyEditor()
- {
- setLayout( new BorderLayout() );
- setPreferredSize( new Dimension( 200, (int)getPreferredSize().getHeight() ) );
-
- String text = ""
- + "SDR Trunk
"
- + "
"
- + "Use the tree structure to the left to configure SDRTrunk"
- + " to use multiple SDR tuners and sound card sources to"
- + " decode multiple trunked and digital radio channels.
"
- + "
"
- + "Each tree branch can be accessed by clicking on the branch"
- + " and a configuration editor will appear here.
"
- + "
"
- + "Many of the branches contain context sensitive menus that "
- + " you can access by right-clicking on the node.
"
- + "
"
- + "To begin, right-click on the 'Systems' branch under 'Playlist'"
- + "and create a new 'System'. Right-click the 'System' node to create "
- + "a new 'Site'. Right-click on the 'Site' node to create a new Channel.
"
- + "
"
- + "In the new channel, select a channel source and decoder "
- + "and select event logging and recording options. Check 'Enabled'"
- + " and hit the 'Save' button to begin decoding.
"
- + "";
-
- add( new JLabel( text ), BorderLayout.NORTH );
- }
-}
diff --git a/src/controller/ResourceManager.java b/src/controller/ResourceManager.java
deleted file mode 100644
index 91256fda..00000000
--- a/src/controller/ResourceManager.java
+++ /dev/null
@@ -1,183 +0,0 @@
-/*******************************************************************************
- * SDR Trunk
- * Copyright (C) 2014 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
- ******************************************************************************/
-package controller;
-
-import map.MapService;
-import module.log.EventLogManager;
-import playlist.PlaylistManager;
-import record.RecorderManager;
-import settings.SettingsManager;
-import source.SourceManager;
-import source.recording.RecordingSourceManager;
-import source.tuner.TunerManager;
-import audio.AudioManager;
-import controller.channel.ChannelManager;
-
-/**
- * Manager for system wide resources
- */
-public class ResourceManager
-{
- private AudioManager mAudioManager;
- private ChannelManager mChannelManager;
- private EventLogManager mEventLogManager;
- private PlaylistManager mPlaylistManager;
- private RecorderManager mRecorderManager;
- private RecordingSourceManager mRecordingSourceManager;
- private SettingsManager mSettingsManager;
- private SourceManager mSourceManager;
- private TunerManager mTunerManager;
- private ConfigurationControllerModel mController;
- private MapService mMapService;
- private ThreadPoolManager mThreadPoolManager;
-
- public ResourceManager()
- {
- this( new SettingsManager(),
- new PlaylistManager(),
- new EventLogManager() );
- }
-
- public ResourceManager( SettingsManager settingsManager,
- PlaylistManager playlistManager,
- EventLogManager eventLogManager )
- {
- mSettingsManager = settingsManager;
- mPlaylistManager = playlistManager;
- mEventLogManager = eventLogManager;
-
- /**
- * ChannelManager requires a reference to the ResourceManager so that
- * channel processing chains can access system wide resources
- */
- mThreadPoolManager = new ThreadPoolManager();
- mAudioManager = new AudioManager( mThreadPoolManager );
- mRecorderManager = new RecorderManager( mThreadPoolManager );
- mChannelManager = new ChannelManager( this );
- mSourceManager = new SourceManager( this );
- mRecordingSourceManager = new RecordingSourceManager( this );
- mTunerManager = new TunerManager( this );
- mController = new ConfigurationControllerModel( this );
- mMapService = new MapService( this );
- }
-
- public AudioManager getAudioManager()
- {
- return mAudioManager;
- }
-
- public void setAudioManager( AudioManager audioManager )
- {
- mAudioManager = audioManager;
- }
-
- public ConfigurationControllerModel getController()
- {
- return mController;
- }
-
- public ThreadPoolManager getThreadPoolManager()
- {
- return mThreadPoolManager;
- }
-
- public void setController( ConfigurationControllerModel controller )
- {
- mController = controller;
- }
-
- public TunerManager getTunerManager()
- {
- return mTunerManager;
- }
-
- public MapService getMapService()
- {
- return mMapService;
- }
-
- public SettingsManager getSettingsManager()
- {
- return mSettingsManager;
- }
-
- public void setSettingsManager( SettingsManager manager )
- {
- mSettingsManager = manager;
- }
-
- public PlaylistManager getPlaylistManager()
- {
- return mPlaylistManager;
- }
-
- public void setPlaylistManager( PlaylistManager aliasManager )
- {
- mPlaylistManager = aliasManager;
- }
-
- public EventLogManager getEventLogManager()
- {
- return mEventLogManager;
- }
-
- public void setEventLogManager( EventLogManager eventLogManager )
- {
- mEventLogManager = eventLogManager;
- }
-
- public RecorderManager getRecorderManager()
- {
- return mRecorderManager;
- }
-
- public void setRecorderManager( RecorderManager recorderManager )
- {
- mRecorderManager = recorderManager;
- }
-
- public SourceManager getSourceManager()
- {
- return mSourceManager;
- }
-
- public void setSourceManager( SourceManager sourceManager )
- {
- mSourceManager = sourceManager;
- }
-
- public RecordingSourceManager getRecordingSourceManager()
- {
- return mRecordingSourceManager;
- }
-
- public void setRecordingSourceManager( RecordingSourceManager manager )
- {
- mRecordingSourceManager = manager;
- }
-
- public ChannelManager getChannelManager()
- {
- return mChannelManager;
- }
-
- public void setChannelManager( ChannelManager channelManager )
- {
- mChannelManager = channelManager;
- }
-}
diff --git a/src/controller/ThreadPoolManager.java b/src/controller/ThreadPoolManager.java
index 0ca9a00b..ca993295 100644
--- a/src/controller/ThreadPoolManager.java
+++ b/src/controller/ThreadPoolManager.java
@@ -36,20 +36,29 @@ public class ThreadPoolManager
private HashMap,ThreadType> mTasks =
new HashMap,ThreadType>();
- private ScheduledExecutorService mExecutor =
- Executors.newScheduledThreadPool( THREAD_POOL_SIZE,
- new NamingThreadFactory( "sdrtrunk" ) );
+ private ScheduledExecutorService mExecutor;
public ThreadPoolManager()
{
}
+ public ScheduledExecutorService getScheduledExecutorService()
+ {
+ return mExecutor;
+ }
+
public ScheduledFuture> scheduleFixedRate( ThreadType type,
Runnable command,
long period,
TimeUnit unit )
throws RejectedExecutionException
{
+ if( mExecutor == null )
+ {
+ mExecutor = Executors.newScheduledThreadPool( THREAD_POOL_SIZE,
+ new NamingThreadFactory( "sdrtrunk" ) );
+ }
+
ScheduledFuture> task =
mExecutor.scheduleAtFixedRate( command, 0, period, unit );
@@ -61,6 +70,12 @@ public class ThreadPoolManager
public void scheduleOnce( Runnable command, long delay, TimeUnit unit )
throws RejectedExecutionException
{
+ if( mExecutor == null )
+ {
+ mExecutor = Executors.newScheduledThreadPool( THREAD_POOL_SIZE,
+ new NamingThreadFactory( "sdrtrunk" ) );
+ }
+
mExecutor.schedule( command, delay, unit );
}
diff --git a/src/controller/channel/AbstractChannelEditor.java b/src/controller/channel/AbstractChannelEditor.java
deleted file mode 100644
index 41c3b4aa..00000000
--- a/src/controller/channel/AbstractChannelEditor.java
+++ /dev/null
@@ -1,42 +0,0 @@
-/*******************************************************************************
- * SDR Trunk
- * Copyright (C) 2014 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
- ******************************************************************************/
-package controller.channel;
-
-import controller.Editor;
-
-public abstract class AbstractChannelEditor extends Editor
-{
- private static final long serialVersionUID = 1L;
-
- protected ChannelNode mChannelNode;
-
- public AbstractChannelEditor( ChannelNode channel )
- {
- super();
- mChannelNode = channel;
- }
-
- public ChannelNode getChannelNode()
- {
- return mChannelNode;
- }
-
- public abstract void save();
-
- public abstract void reset();
-}
diff --git a/src/controller/channel/Channel.java b/src/controller/channel/Channel.java
index 2b150c4f..81ced518 100644
--- a/src/controller/channel/Channel.java
+++ b/src/controller/channel/Channel.java
@@ -1,6 +1,6 @@
/*******************************************************************************
* SDR Trunk
- * Copyright (C) 2014 Dennis Sheirer
+ * 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
@@ -17,41 +17,20 @@
******************************************************************************/
package controller.channel;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.concurrent.CopyOnWriteArrayList;
-
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlSeeAlso;
import javax.xml.bind.annotation.XmlTransient;
-import message.Message;
-import module.Module;
-import module.ProcessingChain;
import module.decode.DecoderFactory;
+import module.decode.DecoderType;
import module.decode.config.AuxDecodeConfiguration;
-import module.decode.config.DecodeConfigFactory;
import module.decode.config.DecodeConfiguration;
-import module.decode.event.CallEventModel;
-import module.decode.event.MessageActivityModel;
-import module.decode.state.ChannelState;
-import module.log.MessageEventLogger;
+import module.log.EventLogType;
import module.log.config.EventLogConfiguration;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import record.RecorderManager;
import record.RecorderType;
import record.config.RecordConfiguration;
-import sample.Listener;
-import source.Source;
-import source.SourceException;
import source.SourceType;
import source.config.SourceConfigFactory;
import source.config.SourceConfigRecording;
@@ -59,43 +38,23 @@ import source.config.SourceConfigTuner;
import source.config.SourceConfiguration;
import source.tuner.TunerChannel;
import source.tuner.TunerChannel.Type;
-import alias.AliasList;
-import audio.metadata.Metadata;
-import audio.metadata.MetadataType;
-import controller.ResourceManager;
-import controller.channel.ChannelEvent.Event;
+import source.tuner.frequency.FrequencyChangeEvent;
+import source.tuner.frequency.FrequencyChangeEvent.Event;
+import source.tuner.frequency.IFrequencyChangeProcessor;
import controller.config.Configuration;
-import controller.site.Site;
-import controller.system.System;
-import filter.FilterSet;
@XmlSeeAlso( { Configuration.class } )
@XmlRootElement( name = "channel" )
-public class Channel extends Configuration
+public class Channel extends Configuration implements IFrequencyChangeProcessor
{
- private final static Logger mLog = LoggerFactory.getLogger( Channel.class );
+ // Standard channels are persisted and traffic channels are temporary
+ public enum ChannelType { STANDARD, TRAFFIC };
+
- private static final boolean ENABLED = true;
- private static final boolean DISABLED = false;
- private static final boolean BROADCAST_CHANGE = true;
+ // Static unique channel identifier tracking
+ private static int UNIQUE_ID = 0;
- public enum ChannelType
- {
- STANDARD,
- TRAFFIC
- };
-
-
- private CopyOnWriteArrayList mChannelListeners =
- new CopyOnWriteArrayList();
-
- private CopyOnWriteArrayList> mMessageListeners =
- new CopyOnWriteArrayList>();
-
- private Map mTrafficChannels = new HashMap<>();
-
- private DecodeConfiguration mDecodeConfiguration =
- DecodeConfigFactory.getDefaultDecodeConfiguration();
+ private DecodeConfiguration mDecodeConfiguration = DecoderFactory.getDefaultDecodeConfiguration();
private AuxDecodeConfiguration mAuxDecodeConfiguration =
new AuxDecodeConfiguration();
private SourceConfiguration mSourceConfiguration =
@@ -104,134 +63,107 @@ public class Channel extends Configuration
new EventLogConfiguration();
private RecordConfiguration mRecordConfiguration = new RecordConfiguration();
- private ResourceManager mResourceManager;
private String mAliasListName;
- private System mSystem;
- private Site mSite;
- private String mName;
+ private String mSystem = "System";
+ private String mSite = "Site";
+ private String mName = "Channel";
+
private boolean mEnabled;
private boolean mSelected;
- private ChannelType mChannelType;
-
- private CallEventModel mCallEventModel;
- private MessageActivityModel mMessageActivityModel;
- private MessageEventLogger mEventLogger;
- private ProcessingChain mProcessingChain;
+ private TunerChannel mTunerChannel = null;
- private ChannelState mChannelState;
- private long mTrafficChannelTimeout =
- DecodeConfiguration.DEFAULT_CALL_TIMEOUT_SECONDS;
+ private ChannelType mChannelType = ChannelType.STANDARD;
- /**
- * Constructs a new standard channel with a default name of "New Channel"
- */
- public Channel()
- {
- this( "New Channel", ChannelType.STANDARD );
- }
+ private int mChannelID;
+ private int mChannelFrequencyCorrection = 0;
/**
- * Constructs a new standard channel with the specified channel name
- */
- public Channel( String channelName )
- {
- this( channelName, ChannelType.STANDARD );
- }
-
- /**
- * Constructs a new channel with the specified channel name and type.
+ * Channel represents a complete set of configurations needed to setup and
+ * manage a processing chain and/or manage as a set of business objects that
+ * can be displayed in graphical components.
*
* @param channelName
* @param channelType
*/
public Channel( String channelName, ChannelType channelType )
{
- mName = channelName;
+ this( channelName );
mChannelType = channelType;
}
-
+
/**
- * Indicates if the channel has Automatic or Direct Frequency Control
+ * Constructs a new standard channel with the specified channel name
*/
- public boolean hasFrequencyControl()
+ public Channel( String channelName )
{
- return mProcessingChain != null &&
- mProcessingChain.hasFrequencyCorrectionControl();
+ this();
+ mName = channelName;
}
-
- public long getFrequencyCorrection()
+
+ /**
+ * Constructs a new standard channel with a default name of "Channel"
+ */
+ public Channel()
{
- if( hasFrequencyControl() )
+ mChannelID = UNIQUE_ID++;
+ }
+
+ /**
+ * Creates a (new) deep copy of this channel
+ */
+ public Channel copyOf()
+ {
+ Channel channel = new Channel( mName );
+ channel.setSystem( mSystem );
+ channel.setSite( mSite );
+ channel.setAliasListName( mAliasListName );
+
+ AuxDecodeConfiguration aux = new AuxDecodeConfiguration();
+
+ for( DecoderType auxType: aux.getAuxDecoders() )
{
- return mProcessingChain.getFrequencyCorrectionControl().getErrorCorrection();
+ aux.addAuxDecoder( auxType );
}
- return 0;
- }
-
- /**
- * Sets the channel state traffic channel call end timer to the specified
- * value.
- */
- public void setTrafficChannelTimeout( long milliseconds )
- {
- mTrafficChannelTimeout = milliseconds;
+ channel.setAuxDecodeConfiguration( aux );
- if( mChannelState != null )
- {
- mChannelState.setTrafficChannelTimeout( milliseconds );
- }
- }
-
- /**
- * Sets the resource manager responsible for this channel and registers
- * the channel manager as a listener to this channel. Fires channel add
- * channel event to the newly added channel manager channel listener.
- *
- * Note: resource manager instance is applied after object construction
- * since we construct the channel objects from the xml playlist
- */
- public void setResourceManager( ResourceManager resourceManager )
- {
- mResourceManager = resourceManager;
-
- /* Add system-wide channel listeners onto this channel */
- addListeners( mResourceManager.getChannelManager().getChannelListeners() );
+ channel.setDecodeConfiguration( DecoderFactory.copy( mDecodeConfiguration ) );
- /* If we're enabled, fire enable changed event to get processing started */
- if( mEnabled )
+ EventLogConfiguration log = new EventLogConfiguration();
+
+ for( EventLogType logType: mEventLogConfiguration.getLoggers() )
{
- enableChanged();
+ log.addLogger( logType );
}
+
+ channel.setEventLogConfiguration( log );
+
+ RecordConfiguration record = new RecordConfiguration();
+
+ for( RecorderType recordType: mRecordConfiguration.getRecorders() )
+ {
+ record.addRecorder( recordType );
+ }
+
+ channel.setRecordConfiguration( record );
+
+ channel.setSourceConfiguration( SourceConfigFactory.copy( mSourceConfiguration ) );
+
+ return channel;
}
- @XmlTransient
- public ResourceManager getResourceManager()
- {
- return mResourceManager;
- }
-
/**
- * Returns the processing chain for this channel. If the processing chain
- * is not currently constructed and running, this method will return null.
+ * Unique identifier for this channel. Value is transient (not persisted).
*/
@XmlTransient
- public ProcessingChain getProcessingChain()
+ public int getChannelID()
{
- return mProcessingChain;
+ return mChannelID;
}
/**
- * Indicates if this channel has a processing chain constructed and it
- * is processing.
- */
- public boolean isProcessing()
- {
- return mProcessingChain != null && mProcessingChain.processing();
- }
-
- /**
- * Indicates if this channel has been selected for audio output
+ * Indicates if this channel has been selected for prioritized audio output
+ * and display of processing chain products
*/
@XmlTransient
public boolean isSelected()
@@ -239,46 +171,18 @@ public class Channel extends Configuration
return mSelected;
}
- public void setSelected( boolean selected )
+ /**
+ * Sets selection status of the channel.
+ *
+ * NOTE: this method is package private so that only the ChannelSelectionManager
+ * can toggle the selection state. Use the channel model to broadcast a
+ * request select/deselect channel event.
+ *
+ * @param selected
+ */
+ void setSelected( boolean selected )
{
mSelected = selected;
-
- fireChannelEvent( Event.CHANGE_SELECTED );
- }
-
- @XmlTransient
- public CallEventModel getCallEventModel()
- {
- return mCallEventModel;
- }
-
- /**
- * Returns the MessageEventLogger for this processing chain
- */
- @XmlTransient
- public MessageEventLogger getEventLogger()
- {
- return mEventLogger;
- }
-
- /**
- * Sets the MessageEventLogger for this processing chain
- */
- public void setEventLogger( MessageEventLogger eventLogger )
- {
- mEventLogger = eventLogger;
- }
-
- @XmlTransient
- public MessageActivityModel getMessageActivityModel()
- {
- return mMessageActivityModel;
- }
-
- @XmlTransient
- public ChannelState getChannelState()
- {
- return mChannelState;
}
@XmlTransient
@@ -286,193 +190,104 @@ public class Channel extends Configuration
{
return mChannelType;
}
-
- /**
- * Orderly shutdown method when this channel is going to be deleted.
- */
- public void dispose()
- {
- setEnabled( DISABLED, BROADCAST_CHANGE );
-
- /* Broadcast channel deleted event */
- fireChannelEvent( Event.CHANNEL_DELETED );
-
- mProcessingChain.dispose();
- mProcessingChain = null;
- }
-
- /**
- * Returns a full channel name containing the system, site and channel name.
- */
- public String getChannelDisplayName()
- {
- StringBuilder sb = new StringBuilder();
-
- if( mSystem != null )
- {
- sb.append( mSystem.getName() );
- }
- else
- {
- sb.append( "-" );
- }
-
- sb.append( "/" );
-
- if( mSite != null )
- {
- sb.append( mSite.getName() );
- }
- else
- {
- sb.append( "-" );
- }
-
- sb.append( "/" );
-
- sb.append( mName );
-
- return sb.toString();
- }
-
- /**
- * Returns the tuner channel requirement for this channel when the source
- * configuration specifies a tuner and frequency as the source for this
- * channel.
- *
- * If the source configuration is anything else, this method returns null.
- */
- public TunerChannel getTunerChannel()
- {
- TunerChannel retVal = null;
-
- if( mSourceConfiguration.getSourceType() == SourceType.TUNER )
- {
- SourceConfigTuner config = (SourceConfigTuner)mSourceConfiguration;
-
- retVal = new TunerChannel( Type.LOCKED, config.getFrequency(),
- mDecodeConfiguration.getDecoderType().getChannelBandwidth() );
- }
- else if( mSourceConfiguration.getSourceType() == SourceType.RECORDING )
- {
- SourceConfigRecording config =
- (SourceConfigRecording)mSourceConfiguration;
-
- retVal = new TunerChannel( Type.LOCKED, config.getFrequency(),
- mDecodeConfiguration.getDecoderType().getChannelBandwidth() );
- }
-
- return retVal;
- }
-
/**
* Returns the owning system for this channel.
*/
- public System getSystem()
+ @XmlAttribute( name = "system" )
+ public String getSystem()
{
return mSystem;
}
+
+ public void setSystem( String system )
+ {
+ mSystem = system;
+ }
public boolean hasSystem()
{
return mSystem != null;
}
- /**
- * Sets the owning system for this channel and optionally broadcasts a
- * system change channel event.
- */
- public void setSystem( System system, boolean fireChangeEvent )
- {
- mSystem = system;
-
- if( fireChangeEvent )
- {
- fireChannelEvent( Event.CHANGE_SYSTEM );
- }
- }
-
/**
* Returns the owning site for this channel.
*/
- public Site getSite()
+ @XmlAttribute( name = "site" )
+ public String getSite()
{
- return mSite;
+ return mSite;
}
-
+
+ public void setSite( String site )
+ {
+ mSite = site;
+ }
+
public boolean hasSite()
{
return mSite != null;
}
/**
- * Sets the owning site for this channel and optionally broadcasts a site
- * change channel event.
+ * Returns the name of this channel.
*/
- public void setSite( Site site, boolean fireChangeEvent )
- {
- mSite = site;
-
- if( fireChangeEvent )
- {
- fireChannelEvent( Event.CHANGE_SITE );
- }
- }
+ @XmlAttribute( name = "name" )
+ public String getName()
+ {
+ return mName;
+ }
+
+ /**
+ * Sets the name of this channel.
+ */
+ public void setName( String name )
+ {
+ mName = name;
+ }
/**
- * Default display string for this channel -- the channel name.
+ * Default display string for this channel: SYSTEM_SITE_NAME
*/
public String toString()
{
- return mName;
+ StringBuilder sb = new StringBuilder();
+ sb.append( hasSystem() ? getSystem() : "SYSTEM" );
+ sb.append( "_" );
+ sb.append( hasSite() ? getSite() : "SITE" );
+ sb.append( "_" );
+ sb.append( getName() );
+
+ return sb.toString();
}
/**
* Indicates if this channel is enabled for processing.
*/
- @XmlAttribute
+ @XmlAttribute( name = "enabled" )
public boolean getEnabled()
{
return mEnabled;
}
/**
- * Enables/disables this channel for processing. Automatically starts or
- * stops channel processing in response to this change.
+ * Indicates if this channel will automatically start processing on
+ * application startup or if this channel is currently processing after
+ * application startup.
+ *
+ * Note: this method is package private and is intended to be managed
+ * by a co-package central processing manager
+ *
+ * @see controller.ChannelProcessingManager
*/
- public void setEnabled( boolean enabled )
+ void setEnabled( boolean enabled )
{
mEnabled = enabled;
-
- enableChanged();
- }
-
- /**
- * Enables/disables this channel for processing. Automatically starts or
- * stops channel processing in response to this change. Optionally
- * broadcasts a enabled change channel event.
- */
- public void setEnabled( boolean enabled, boolean fireChannelEvent )
- {
- setEnabled( enabled );
-
- if( fireChannelEvent )
- {
- if( enabled )
- {
- fireChannelEvent( Event.CHANNEL_ENABLED );
- }
- else
- {
- fireChannelEvent( Event.CHANNEL_DISABLED );
- }
- }
}
/**
* Returns the alias list that is used by this channel for looking up alias
- * values for the various identifiers produced by the decoder.
+ * values for the various identifiers produced by the decoder(s).
*/
@XmlElement( name = "alias_list_name" )
public String getAliasListName()
@@ -488,25 +303,9 @@ public class Channel extends Configuration
{
mAliasListName = name;
}
-
- /**
- * Sets the alias list to be used for looking up the alias values for the
- * various identifiers produced by the decoder. This method is the same
- * as setAliasName(name) and optionally broadcasts an alias list change
- * channel event.
- */
- public void setAliasListName( String name, boolean fireChangeEvent )
- {
- mAliasListName = name;
-
- if( fireChangeEvent )
- {
- fireChannelEvent( Event.CHANGE_ALIAS_LIST );
- }
- }
/**
- * Gets the decoder configuration used by this channel
+ * Gets the primary decoder configuration used by this channel
*/
@XmlElement( name = "decode_configuration" )
public DecodeConfiguration getDecodeConfiguration()
@@ -515,7 +314,7 @@ public class Channel extends Configuration
}
/**
- * Sets the decoder configuration used by this channel
+ * Sets the primary decoder configuration used by this channel
*/
public void setDecodeConfiguration( DecodeConfiguration config )
{
@@ -523,7 +322,8 @@ public class Channel extends Configuration
}
/**
- * Gets the aux decoder configuration used by this channel
+ * Gets the aux decoder configuration used by this channel. Aux decoders
+ * operate on demodulated audio
*/
@XmlElement( name = "aux_decode_configuration" )
public AuxDecodeConfiguration getAuxDecodeConfiguration()
@@ -540,22 +340,8 @@ public class Channel extends Configuration
}
/**
- * Sets the decoder configuration used by this channel and optionally
- * broadcasts a decoder change channel event.
- */
- public void setDecodeConfiguration( DecodeConfiguration config,
- boolean fireChangeEvent )
- {
- setDecodeConfiguration( config );
-
- if( fireChangeEvent )
- {
- fireChannelEvent( Event.CHANGE_DECODER );
- }
- }
-
- /**
- * Returns the source configuration for this channel.
+ * Returns the source configuration for this channel. A channel source
+ * identifies where the processing chain will get source sample data from.
*/
@XmlElement( name = "source_configuration" )
public SourceConfiguration getSourceConfiguration()
@@ -569,23 +355,12 @@ public class Channel extends Configuration
public void setSourceConfiguration( SourceConfiguration config )
{
mSourceConfiguration = config;
+
+ //Clear the tune channel object so that it can be recreated if the
+ //source configuration changes
+ mTunerChannel = null;
}
- /**
- * Sets the source configuration for this channel and optionally broadcasts
- * a source change channel event.
- */
- public void setSourceConfiguration( SourceConfiguration config,
- boolean fireChangeEvent )
- {
- setSourceConfiguration( config );
-
- if( fireChangeEvent )
- {
- fireChannelEvent( Event.CHANGE_SOURCE );
- }
- }
-
/**
* Returns the event logger configuration for this channel.
*/
@@ -603,21 +378,6 @@ public class Channel extends Configuration
mEventLogConfiguration = config;
}
- /**
- * Sets the event logger configuration for this channel and optionally
- * broadcasts an event logger change channel event.
- */
- public void setEventLogConfiguration( EventLogConfiguration config,
- boolean fireChangeEvent )
- {
- setEventLogConfiguration( config );
-
- if( fireChangeEvent )
- {
- fireChannelEvent( Event.CHANGE_EVENT_LOGGER );
- }
- }
-
/**
* Returns the recorder configuration for this channel.
*/
@@ -636,370 +396,41 @@ public class Channel extends Configuration
}
/**
- * Sets the recorder configuration for this channel and optionally broadcasts
- * a recorder configuration change channel event.
- */
- public void setRecordConfiguration( RecordConfiguration config,
- boolean fireChangeEvent )
- {
- setRecordConfiguration( config );
-
- if( fireChangeEvent )
- {
- fireChannelEvent( Event.CHANGE_RECORDER );
- }
- }
-
- /**
- * Returns the name of this channel.
- */
- @XmlAttribute
- public String getName()
- {
- return mName;
- }
-
- /**
- * Sets the name of this channel.
- */
- public void setName( String name )
- {
- mName = name;
- }
-
- /**
- * Sets the name of this channel and optionally broadcasts a name change
- * channel event.
- */
- public void setName( String name, boolean fireChangeEvent )
- {
- mName = name;
-
- if( fireChangeEvent )
- {
- fireChannelEvent( Event.CHANGE_NAME );
- }
- }
-
- /**
- * Responds to changes in the enabled setting by starting the processing
- * change if the channel is now enabled, or stopping the procesing chain
- * if it was already started.
- */
- private void enableChanged()
- {
- if( mEnabled )
- {
- start();
- }
- else
- {
- stop();
- }
- }
-
- /**
- * Starts the channel processing chain.
- */
- private void start()
- {
- if( mEnabled && mResourceManager != null )
- {
- setupChannelState();
-
- try
- {
- setup();
-
- mProcessingChain.start();
-
- fireChannelEvent( Event.CHANNEL_PROCESSING_STARTED );
- }
- catch( SourceException se )
- {
- //Do nothing, we couldn't get a source for the channel
- }
- }
- }
-
- private void setupChannelState()
- {
- if( mChannelState == null )
- {
- mChannelState = new ChannelState( mResourceManager
- .getThreadPoolManager(), mChannelType );
-
- mChannelState.setTrafficChannelTimeout( mTrafficChannelTimeout );
- }
- }
-
- /**
- * Sets up the channel and prepares to start processing. Sets up the
- * processing chain and requests a source.
+ * Convenience method to construct a tuner channel object representing a
+ * tuner or recording source frequency and bandwidth that can be used by
+ * application components for graphically representing this channel.
*
- * @throws SourceException - if no source is available or if there is an
- * error obtaining a source
+ * If the source configuration is not a tuner or recording, this method
+ * returns null.
*/
- public void setup() throws SourceException
- {
- setupProcessingChain();
- setupSource();
- }
+ @XmlTransient
+ public TunerChannel getTunerChannel()
+ {
+ if( mTunerChannel == null )
+ {
+ if( mSourceConfiguration.getSourceType() == SourceType.TUNER )
+ {
+ SourceConfigTuner config = (SourceConfigTuner)mSourceConfiguration;
+
+ mTunerChannel = new TunerChannel( Type.LOCKED, config.getFrequency(),
+ mDecodeConfiguration.getDecoderType().getChannelBandwidth() );
+ }
+ else if( mSourceConfiguration.getSourceType() == SourceType.RECORDING )
+ {
+ SourceConfigRecording config =
+ (SourceConfigRecording)mSourceConfiguration;
+
+ mTunerChannel = new TunerChannel( Type.LOCKED, config.getFrequency(),
+ mDecodeConfiguration.getDecoderType().getChannelBandwidth() );
+ }
+ }
+
+ return mTunerChannel;
+ }
/**
- * Setup recording options. Adds RecorderManager as a audio packet listener
- * to receive and record decoded audio.
- */
- private void setupRecording()
- {
- List recorders = mRecordConfiguration.getRecorders();
-
- if( !recorders.isEmpty() )
- {
- if( recorders.contains( RecorderType.AUDIO ) )
- {
- mProcessingChain.addAudioPacketListener(
- mResourceManager.getRecorderManager() );
- }
-
- /* Add baseband recorder */
- if( ( recorders.contains( RecorderType.BASEBAND ) &&
- mChannelType == ChannelType.STANDARD ) )
- {
- mProcessingChain.addModule( RecorderManager.getBasebandRecorder(
- mResourceManager.getThreadPoolManager(), getChannelName() ) );
- }
-
- /* Add traffic channel baseband recorder */
- if( recorders.contains( RecorderType.TRAFFIC_BASEBAND ) &&
- mChannelType == ChannelType.TRAFFIC )
- {
- mProcessingChain.addModule( RecorderManager.getBasebandRecorder(
- mResourceManager.getThreadPoolManager(), getChannelName() ) );
- }
- }
- }
-
- private String getChannelName()
- {
- StringBuilder sb = new StringBuilder();
- sb.append( mSystem );
- sb.append( "_" );
- sb.append( mSite );
- sb.append( "_" );
- sb.append( mName );
-
- return sb.toString();
- }
-
- private void setupLogging()
- {
- if( mProcessingChain != null )
- {
- String channelName = getChannelName();
-
- List loggers = mResourceManager.getEventLogManager()
- .getLoggers( mEventLogConfiguration, channelName );
-
- if( !loggers.isEmpty() )
- {
- mProcessingChain.addModules( loggers );
- }
- }
- }
-
- private void setupProcessingChain()
- {
- if( mProcessingChain == null )
- {
- mProcessingChain = new ProcessingChain( getName() );
-
- /* Get the optional alias list for the decode modules to use */
- AliasList aliasList = mResourceManager.getPlaylistManager()
- .getPlayist().getAliasDirectory().getAliasList( mAliasListName );
-
- /* Add the channel state as a module */
- mProcessingChain.addModule( mChannelState );
-
- /* Processing Modules */
- List modules = DecoderFactory.getModules( mChannelType,
- mResourceManager, mDecodeConfiguration, mRecordConfiguration,
- mAuxDecodeConfiguration, aliasList, mSystem,
- mSite, mName );
-
- mProcessingChain.addModules( modules );
-
- /* Get message filters for the set of processing modules */
- FilterSet messageFilter = DecoderFactory.getMessageFilters( modules );
-
- /* Setup the message activity model and add message listeners */
- mMessageActivityModel = new MessageActivityModel( messageFilter );
- mProcessingChain.addMessageListener( mMessageActivityModel );
-
- /* Add audio manager as listener for audio packets */
- mProcessingChain.addAudioPacketListener(
- mResourceManager.getAudioManager() );
-
- /* Add system-wide message listeners */
- mProcessingChain.addMessageListeners(
- mResourceManager.getChannelManager().getMessageListeners() );
-
- /* Add individual message listeners */
- mProcessingChain.addMessageListeners( mMessageListeners );
-
- /* Create the call event model to receive call events */
- mCallEventModel = new CallEventModel();
- mProcessingChain.addCallEventListener( mCallEventModel );
-
- setupLogging();
-
- setupRecording();
-
- /* Inject channel metadata that will be inserted into audio packets
- * for the recorder manager and streaming */
- mProcessingChain.broadcast(
- new Metadata( MetadataType.SYSTEM, mSystem.getName() ) );
- mProcessingChain.broadcast(
- new Metadata( MetadataType.SITE_ID, mSite.getName() ) );
- mProcessingChain.broadcast(
- new Metadata( MetadataType.CHANNEL_NAME, mName ) );
- }
- }
-
- /**
- * Attempts to obtain a source and apply it to the processing chain.
- *
- * @throws SourceException - if the processing chain is not setup or if
- * there is an error produced while obtaining the source
- */
- private void setupSource() throws SourceException
- {
- if( mProcessingChain != null )
- {
- Source source = mResourceManager.getSourceManager()
- .getSource( getSourceConfiguration(), getDecodeConfiguration()
- .getDecoderType().getChannelBandwidth() );
-
- if( source != null )
- {
- mProcessingChain.setSource( source );
- }
- else
- {
- throw new SourceException( "Couldn't obtain tuner channel source" );
- }
- }
- else
- {
- throw new SourceException( "Cannot obtain source for a null "
- + "processing chain - setup the processing chain first" );
- }
- }
-
- /**
- * Stops the channel processing chain
- */
- private void stop()
- {
- if( !mEnabled && mProcessingChain != null )
- {
- fireChannelEvent( Event.CHANNEL_PROCESSING_STOPPED );
-
- mProcessingChain.stop();
-
- if( mChannelType == ChannelType.STANDARD )
- {
- mProcessingChain.dispose();
- mProcessingChain = null;
- }
-
- mSelected = false;
- }
- }
-
- /**
- * Broadcasts a channel change event to all registered listeners
- */
- public void fireChannelEvent( Event event )
- {
- ChannelEvent channelEvent = new ChannelEvent( this, event );
-
- for( ChannelEventListener listener: mChannelListeners )
- {
- listener.channelChanged( channelEvent );
- }
-
- /* Send event to processing chain so modules can respond to channel
- * selection events */
- if( mProcessingChain != null )
- {
- mProcessingChain.getChannelEventListener().receive( channelEvent );
- }
- }
-
- /**
- * Adds a channel listener to receive all channel events from this channel
- * and automatically sends the listener a channel add event.
- */
- public void addListener( ChannelEventListener listener )
- {
- if( !mChannelListeners.contains( listener ) )
- {
- mChannelListeners.add( listener );
-
- listener.channelChanged(
- new ChannelEvent( this, Event.CHANNEL_ADDED ) );
- }
- else
- {
- mLog.error( "Channel - attempt to add already existing channel "
- + "listener [" + listener.getClass() + "]" );
- }
- }
-
- /**
- * Adds a list of channel listeners to receive all channel events from this
- * channel and automatically sends each one a channel add event.
- */
- public void addListeners( List listeners )
- {
- for( ChannelEventListener listener: listeners )
- {
- addListener( listener );
- }
- }
-
- /**
- * Removes a channel listener from receiving channel events from this channel
- */
- public void removeListener( ChannelEventListener listener )
- {
- mChannelListeners.remove( listener );
- }
-
- public void addListener( Listener listener )
- {
- mMessageListeners.add( listener );
-
- if( isProcessing() )
- {
- mProcessingChain.addMessageListener( listener );
- }
- }
-
- public void removeListener( Listener listener )
- {
- mMessageListeners.remove( listener );
-
- if( mProcessingChain != null )
- {
- mProcessingChain.removeMessageListener( listener );
- }
- }
-
- /**
- * Indicates if any part of this channel is contained within the
- * minimum and maximum frequency values.
+ * Convenience method to indicate if any part of this channel is contained
+ * within the minimum and maximum frequency values.
*/
public boolean isWithin( long minimum, long maximum )
{
@@ -1009,34 +440,24 @@ public class Channel extends Configuration
tunerChannel.overlaps( minimum, maximum );
}
- public void addTrafficChannel( String channelID, Channel channel )
+ /**
+ * Convenience method to make the current channel frequency correction value
+ * available for use outside of a constructed processing chain while maintaining
+ * linkage to the source channel. This hack allows the correction value to
+ * be used to visually show in the spectral display.
+ */
+ @Override
+ public void frequencyChanged( FrequencyChangeEvent event )
{
- mTrafficChannels.put( channelID, channel );
- }
-
- public boolean hasTrafficChannel( Channel channel )
- {
- return mTrafficChannels.containsValue( channel );
- }
-
- public void removeTrafficChannel( Channel channel )
- {
- String name = channel.getChannelDisplayName();
-
- if( mTrafficChannels.containsKey( name ) )
+ if( event.getEvent() == Event.NOTIFICATION_CHANNEL_FREQUENCY_CORRECTION_CHANGE )
{
- mTrafficChannels.remove( name );
+ mChannelFrequencyCorrection = event.getValue().intValue();
}
}
-
- public Collection getTrafficChannels()
+
+ @XmlTransient
+ public int getChannelFrequencyCorrection()
{
- return Collections.unmodifiableCollection( mTrafficChannels.values() );
+ return mChannelFrequencyCorrection;
}
-
- public boolean hasTrafficChannel( String name )
- {
- return mTrafficChannels.containsKey( name );
- }
-
}
diff --git a/src/controller/channel/ChannelController.java b/src/controller/channel/ChannelController.java
new file mode 100644
index 00000000..60d0623f
--- /dev/null
+++ b/src/controller/channel/ChannelController.java
@@ -0,0 +1,279 @@
+package controller.channel;
+
+import java.awt.Color;
+import java.awt.Component;
+import java.awt.Dimension;
+import java.awt.Rectangle;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+
+import 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.ListSelectionListener;
+import javax.swing.table.DefaultTableCellRenderer;
+
+import net.coderazzi.filters.gui.AutoChoices;
+import net.coderazzi.filters.gui.TableFilterHeader;
+import net.miginfocom.swing.MigLayout;
+import source.SourceManager;
+import alias.AliasModel;
+
+import com.jidesoft.swing.JideSplitPane;
+
+import controller.channel.map.ChannelMapModel;
+
+public class ChannelController extends JPanel
+ implements ActionListener, ListSelectionListener
+{
+ private static final long serialVersionUID = 1L;
+
+ private ChannelModel mChannelModel;
+ private JTable mChannelTable;
+ private TableFilterHeader mTableFilterHeader;
+ private ChannelEditor mEditor;
+
+ private static final String NEW_CHANNEL = "New";
+ private static final String COPY_CHANNEL = "Copy";
+ private static final String DELETE_CHANNEL = "Delete";
+
+ private JButton mNewChannelButton = new JButton( NEW_CHANNEL );
+ private JButton mCopyChannelButton = new JButton( COPY_CHANNEL );
+ private JButton mDeleteChannelButton = new JButton( DELETE_CHANNEL );
+
+ public ChannelController( ChannelModel channelModel,
+ ChannelMapModel channelMapModel,
+ SourceManager sourceManager,
+ AliasModel aliasModel )
+ {
+ mChannelModel = channelModel;
+
+ mEditor = new ChannelEditor( channelModel, channelMapModel, sourceManager, aliasModel );
+ mChannelModel.addListener( mEditor );
+
+ init();
+ }
+
+ private void init()
+ {
+ setLayout( new MigLayout( "", "[grow,fill]", "[grow,fill]") );
+
+ //System Configuration View and Editor
+ mChannelTable = new JTable( mChannelModel );
+ mChannelTable.setDefaultRenderer( String.class, new ChannelTableCellRenderer() );
+ mChannelTable.setSelectionMode( ListSelectionModel.SINGLE_SELECTION );
+ mChannelTable.getSelectionModel().addListSelectionListener( this );
+ mChannelTable.setAutoCreateRowSorter( true );
+
+ mTableFilterHeader = new TableFilterHeader( mChannelTable, AutoChoices.ENABLED );
+ mTableFilterHeader.setFilterOnUpdates( true );
+
+ JScrollPane channelScroller = new JScrollPane( mChannelTable );
+
+ JPanel buttonsPanel = new JPanel();
+
+ buttonsPanel.setLayout(
+ new MigLayout( "insets 0 0 0 0", "[grow,fill][grow,fill][grow,fill]", "[]") );
+
+ mNewChannelButton.addActionListener( this );
+ mNewChannelButton.setToolTipText( "Create a new channel configuration" );
+ buttonsPanel.add( mNewChannelButton );
+
+ mCopyChannelButton.addActionListener( this );
+ mCopyChannelButton.setEnabled( false );
+ mCopyChannelButton.setToolTipText( "Create a copy of the currently selected channel configuration" );
+ buttonsPanel.add( mCopyChannelButton );
+
+ mDeleteChannelButton.addActionListener( this );
+ mDeleteChannelButton.setEnabled( false );
+ mDeleteChannelButton.setToolTipText( "Delete the currently selected channel configuration" );
+ buttonsPanel.add( mDeleteChannelButton );
+
+ JPanel listAndButtonsPanel = new JPanel();
+
+ listAndButtonsPanel.setLayout(
+ new MigLayout( "insets 0 0 0 0", "[grow,fill]", "[grow,fill][]") );
+
+ listAndButtonsPanel.add( channelScroller, "wrap" );
+ listAndButtonsPanel.add( buttonsPanel );
+
+ JideSplitPane splitPane = new JideSplitPane( JideSplitPane.VERTICAL_SPLIT );
+ splitPane.setDividerSize( 5 );
+ splitPane.setShowGripper( true );
+
+ //Shrink this guy so that he'll eventually be expanded to the scroller preferred size
+ listAndButtonsPanel.setPreferredSize( new Dimension( 10, 10 ) );
+
+ //Attempt to get a 60:40 vertical sizing preference
+ JScrollPane listScroller = new JScrollPane( listAndButtonsPanel );
+ listScroller.setPreferredSize( new Dimension( 800, 58 ) );
+
+ JScrollPane editorScroller = new JScrollPane( mEditor );
+ editorScroller.setPreferredSize( new Dimension( 800, 42 ) );
+
+ splitPane.add( listScroller );
+ splitPane.add( editorScroller );
+
+ add( splitPane );
+ }
+
+ @Override
+ public void valueChanged( ListSelectionEvent event )
+ {
+ //This limits event firing to only when selection is complete
+ if( !event.getValueIsAdjusting() )
+ {
+ int selectedRow = mChannelTable.getSelectedRow();
+
+ if( selectedRow != -1 )
+ {
+ int index = mChannelTable.convertRowIndexToModel( mChannelTable.getSelectedRow() );
+
+ Channel channel = mChannelModel.getChannelAtIndex( index );
+
+ if( channel != null )
+ {
+ mEditor.setItem( channel );
+ mCopyChannelButton.setEnabled( true );
+ mDeleteChannelButton.setEnabled( true );
+ }
+ }
+ else
+ {
+ mEditor.setItem( null );
+ mCopyChannelButton.setEnabled( false );
+ mDeleteChannelButton.setEnabled( false );
+ }
+ }
+ }
+
+ /**
+ * Adds the channel to the channel model/table and scrolls to view it
+ */
+ private void addChannel( Channel channel )
+ {
+ //HACK: when inserting a row to the model, the JTable gets
+ //notified and attempts to tell the coderazzi table filter
+ //adaptive choices filter to refresh before the table filter is
+ //notified of the row additions, causing an index out of bounds
+ //exception. We turn off adaptive choices temporarily, add the
+ //channel, and turn on adaptive choices again.
+ mTableFilterHeader.setAdaptiveChoices( false );
+
+ int index = mChannelModel.addChannel( channel );
+
+ mTableFilterHeader.setAdaptiveChoices( true );
+
+ if( index >= 0 )
+ {
+ int translatedIndex = mChannelTable.convertRowIndexToView( index );
+ mChannelTable.setRowSelectionInterval( translatedIndex, translatedIndex );
+ mChannelTable.scrollRectToVisible(
+ new Rectangle( mChannelTable.getCellRect( translatedIndex, 0, true ) ) );
+ }
+ }
+
+ private Channel getSelectedChannel()
+ {
+ int index = mChannelTable.getSelectedRow();
+
+ if( index >= 0 )
+ {
+ return mChannelModel.getChannelAtIndex(
+ mChannelTable.convertRowIndexToModel( index ) );
+ }
+
+ return null;
+ }
+
+ /**
+ * Responds to New, Copy and Delete Channel button invocations
+ */
+ @Override
+ public void actionPerformed( ActionEvent event )
+ {
+ switch( event.getActionCommand() )
+ {
+ case NEW_CHANNEL:
+ addChannel( new Channel( "New Channel" ) );
+ break;
+ case COPY_CHANNEL:
+ Channel selected = getSelectedChannel();
+
+ if( selected != null )
+ {
+ addChannel( selected.copyOf() );
+ }
+ break;
+ case DELETE_CHANNEL:
+ Channel toDelete = getSelectedChannel();
+
+ if( toDelete != null )
+ {
+ int choice = JOptionPane.showConfirmDialog( ChannelController.this,
+ "Do you want to delete this channel?", "Delete Channel?",
+ JOptionPane.YES_NO_OPTION, JOptionPane.WARNING_MESSAGE );
+
+ if( choice == JOptionPane.YES_OPTION )
+ {
+ mChannelModel.removeChannel( toDelete );
+ }
+ }
+ break;
+ default:
+ break;
+ }
+ }
+
+ public class ChannelTableCellRenderer extends DefaultTableCellRenderer
+ {
+ private static final long serialVersionUID = 1L;
+
+ @Override
+ public Component getTableCellRendererComponent( JTable table,
+ Object value, boolean isSelected, boolean hasFocus, int row,
+ int column )
+ {
+ Component component = super.getTableCellRendererComponent(
+ table, value, isSelected, hasFocus, row, column );
+
+ ChannelModel model = (ChannelModel)table.getModel();
+
+ int index = table.convertRowIndexToModel( row );
+
+ Channel channel = model.getChannelAtIndex( index );
+
+ boolean enabled = channel.getEnabled();
+
+ if( isSelected )
+ {
+ if( enabled )
+ {
+ component.setBackground( Color.YELLOW );
+ }
+ else
+ {
+ component.setBackground( table.getSelectionBackground() );
+ }
+ }
+ else
+ {
+ if( enabled )
+ {
+ component.setBackground( Color.GREEN );
+ }
+ else
+ {
+ component.setBackground( table.getBackground() );
+ }
+ }
+
+ return component;
+ }
+
+ }
+}
diff --git a/src/controller/channel/ChannelEditor.java b/src/controller/channel/ChannelEditor.java
index 21306577..46edcd77 100644
--- a/src/controller/channel/ChannelEditor.java
+++ b/src/controller/channel/ChannelEditor.java
@@ -1,6 +1,6 @@
/*******************************************************************************
* SDR Trunk
- * Copyright (C) 2014 Dennis Sheirer
+ * 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
@@ -17,147 +17,145 @@
******************************************************************************/
package controller.channel;
+import gui.editor.Editor;
+import gui.editor.EditorValidationException;
import java.awt.Color;
-import java.awt.ComponentOrientation;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
-import java.util.Collections;
-import java.util.List;
-import javax.swing.DefaultComboBoxModel;
import javax.swing.JButton;
-import javax.swing.JCheckBox;
-import javax.swing.JComboBox;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
-import javax.swing.JTextField;
-import javax.swing.tree.DefaultTreeModel;
-import javax.swing.tree.TreePath;
-import module.decode.AuxDecodeComponentEditor;
-import module.decode.DecodeComponentEditor;
-import module.log.EventLogComponentEditor;
+import module.decode.AuxDecodeConfigurationEditor;
+import module.decode.DecodeConfigurationEditor;
+import module.log.EventLogConfigurationEditor;
import net.miginfocom.swing.MigLayout;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import playlist.PlaylistManager;
-import record.RecordComponentEditor;
-import source.SourceComponentEditor;
-import alias.AliasList;
+import record.RecordConfigurationEditor;
+import source.SourceConfigurationEditor;
+import source.SourceManager;
+import alias.AliasModel;
import com.jidesoft.swing.JideTabbedPane;
-import controller.Editor;
+import controller.channel.ChannelEvent.Event;
+import controller.channel.map.ChannelMapModel;
-public class ChannelEditor extends Editor implements ActionListener
+public class ChannelEditor extends Editor implements ActionListener, ChannelEventListener
{
- private final static Logger mLog = LoggerFactory.getLogger( ChannelEditor.class );
-
private static final long serialVersionUID = 1L;
- private DefaultTreeModel mTreeModel;
- private ChannelNode mChannelNode;
+
+ private NameConfigurationEditor mNameConfigurationEditor;
+ private SourceConfigurationEditor mSourceConfigurationEditor;
+ private DecodeConfigurationEditor mDecodeConfigurationEditor;
+ private AuxDecodeConfigurationEditor mAuxDecodeConfigurationEditor;
+ private EventLogConfigurationEditor mEventLogConfigurationEditor;
+ private RecordConfigurationEditor mRecordConfigurationEditor;
- private DecodeComponentEditor mDecodeEditor;
- private AuxDecodeComponentEditor mAuxDecodeEditor;
- private EventLogComponentEditor mEventLogEditor;
- private RecordComponentEditor mRecordEditor;
- private SourceComponentEditor mSourceEditor;
+ private JButton mEnableButton = new JButton( "Enable" );
+ private JLabel mChannelName = new JLabel( "Channel:" );
- private JTextField mChannelName;
- private JCheckBox mChannelEnabled = new JCheckBox();
- private JComboBox mComboAliasLists;
+ private ChannelModel mChannelModel;
+ private ChannelMapModel mChannelMapModel;
+ private SourceManager mSourceManager;
- private PlaylistManager mPlaylistManager;
+ private boolean mChannelEnableRequested = false;
- public ChannelEditor( ChannelNode channel, PlaylistManager playlistManager )
+ public ChannelEditor( ChannelModel channelModel,
+ ChannelMapModel channelMapModel,
+ SourceManager sourceManager,
+ AliasModel aliasModel )
{
- super();
+ mChannelModel = channelModel;
+ mChannelMapModel = channelMapModel;
+ mSourceManager = sourceManager;
+ mNameConfigurationEditor = new NameConfigurationEditor( aliasModel, mChannelModel );
- mChannelNode = channel;
-
- mPlaylistManager = playlistManager;
-
- initGUI();
+ init();
}
-
- private void initGUI()
+
+ private void init()
{
- setLayout( new MigLayout( "fill,wrap 2", "[right,grow][grow]", "[][][][][grow][]" ) );
+ setLayout( new MigLayout( "fill,wrap 3", "[grow,fill][grow,fill][grow,fill]", "[grow,fill][]" ) );
- add( new JLabel( "Channel Configuration" ), "span, align center" );
-
- add( new JLabel( "Enabled:" ) );
-
- mChannelEnabled.setSelected(
- mChannelNode.getChannel().getEnabled() );
- mChannelEnabled.setComponentOrientation( ComponentOrientation.RIGHT_TO_LEFT );
- add( mChannelEnabled );
-
- add( new JLabel( "Name:" ) );
-
- mChannelName = new JTextField( mChannelNode.getChannel().getName() );
- add( mChannelName, "growx,push" );
-
- /**
- * ComboBox: Alias Lists
- */
- add( new JLabel( "Alias List:" ) );
-
- mComboAliasLists = new JComboBox();
-
- List lists = mChannelNode.getModel().getResourceManager()
- .getPlaylistManager().getPlayist().getAliasDirectory().getAliasList();
-
- Collections.sort( lists );
-
- mComboAliasLists.setModel( new DefaultComboBoxModel(
- lists.toArray( new AliasList[ lists.size() ] ) ) );
-
- String aliasListName =
- mChannelNode.getChannel().getAliasListName();
-
- if( aliasListName != null )
- {
- AliasList selected = mChannelNode.getModel().getResourceManager()
- .getPlaylistManager().getPlayist().getAliasDirectory()
- .getAliasList( aliasListName );
-
- mComboAliasLists.setSelectedItem( selected );
- }
-
- add( mComboAliasLists, "growx,push" );
-
JideTabbedPane tabs = new JideTabbedPane();
tabs.setFont( this.getFont() );
tabs.setForeground( Color.BLACK );
- mSourceEditor = new SourceComponentEditor( mChannelNode );
- tabs.addTab( "Source", mSourceEditor );
+ tabs.setTabTrailingComponent( mChannelName );
+
+ mNameConfigurationEditor.setSaveRequestListener( this );
+ tabs.addTab( "Name/Alias", mNameConfigurationEditor );
+
+ mSourceConfigurationEditor = new SourceConfigurationEditor( mSourceManager );
+ mSourceConfigurationEditor.setSaveRequestListener( this );
+ tabs.addTab( "Source", mSourceConfigurationEditor );
- mDecodeEditor = new DecodeComponentEditor( mChannelNode, mPlaylistManager );
- tabs.addTab( "Decoder", mDecodeEditor );
+ mDecodeConfigurationEditor = new DecodeConfigurationEditor( mChannelMapModel );
+ mDecodeConfigurationEditor.setSaveRequestListener( this );
+ tabs.addTab( "Decoder", mDecodeConfigurationEditor );
- mAuxDecodeEditor = new AuxDecodeComponentEditor( mChannelNode );
- tabs.addTab( "Aux", mAuxDecodeEditor );
+ mAuxDecodeConfigurationEditor = new AuxDecodeConfigurationEditor();
+ mAuxDecodeConfigurationEditor.setSaveRequestListener( this );
+ tabs.addTab( "Aux Decoders", mAuxDecodeConfigurationEditor );
- mEventLogEditor = new EventLogComponentEditor( mChannelNode );
- tabs.addTab( "Event Log", mEventLogEditor );
+ mEventLogConfigurationEditor = new EventLogConfigurationEditor();
+ mEventLogConfigurationEditor.setSaveRequestListener( this );
+ tabs.addTab( "Logging", mEventLogConfigurationEditor );
- mRecordEditor = new RecordComponentEditor( mChannelNode );
- tabs.addTab( "Record", mRecordEditor );
+ 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 );
- add( tabs, "span,grow,push" );
-
JButton btnSave = new JButton( "Save" );
+ btnSave.setToolTipText( "Save changes to the currently selected channel" );
btnSave.addActionListener( ChannelEditor.this );
- add( btnSave, "growx,push" );
+ add( btnSave );
JButton btnReset = new JButton( "Reset" );
+ btnReset.setToolTipText( "Reload the currently selected channel" );
btnReset.addActionListener( ChannelEditor.this );
- add( btnReset, "growx,push" );
+ 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
@@ -165,115 +163,95 @@ public class ChannelEditor extends Editor implements ActionListener
{
String command = e.getActionCommand();
- if( command.contentEquals( "Save" ) )
+ if( command.contentEquals( "Enable" ) )
+ {
+ if( hasItem() )
+ {
+ save();
+
+ mChannelEnableRequested = true;
+ mChannelModel.broadcast( new ChannelEvent( getItem(), Event.REQUEST_ENABLE ) );
+ }
+ }
+ else if( command.contentEquals( "Disable" ) )
+ {
+ if( hasItem() )
+ {
+ mChannelModel.broadcast( new ChannelEvent( getItem(), Event.REQUEST_DISABLE ) );
+ }
+ }
+ else if( command.contentEquals( "Save" ) )
{
save();
}
else if( command.contentEquals( "Reset" ) )
{
- reset();
+ setItem( getItem() );
}
-
- if( mTreeModel != null )
- {
- mTreeModel.nodeChanged( mChannelNode );
- }
- }
-
- @Override
- public void save()
- {
- try
- {
- /**
- * Validate the source configuration against the other component
- * configuration editors
- */
- validate( mSourceEditor.getSourceEditor() );
-
- boolean expanded = mChannelNode.getModel().getTree()
- .isExpanded( new TreePath( mChannelNode ) );
-
- mSourceEditor.save();
- mDecodeEditor.save();
- mAuxDecodeEditor.save();
- mEventLogEditor.save();
- mRecordEditor.save();
-
- mChannelNode.getChannel().setName( mChannelName.getText(), false );
- mChannelNode.getChannel().setEnabled( mChannelEnabled.isSelected(), false );
-
- AliasList selected = mComboAliasLists.getItemAt(
- mComboAliasLists.getSelectedIndex() );
- if( selected != null )
- {
- mChannelNode.getChannel().setAliasListName( selected.getName(), false );
- }
-
-
- mChannelNode.save();
- mChannelNode.show();
-
- if( expanded )
- {
- mChannelNode.getModel().getTree().expandPath( new TreePath( mChannelNode ) );
- }
- }
- catch ( ChannelValidationException validationException )
- {
- /* ChannelValidationException can be thrown by any of the component
- * editors. Show the validation error text in a popup menu to the
- * user */
- JOptionPane.showMessageDialog( ChannelEditor.this,
- validationException.getMessage(),
- "Channel Configuration Error",
- JOptionPane.ERROR_MESSAGE );
- }
- }
-
- @Override
- public void reset()
- {
- mChannelName.setText(
- mChannelNode.getChannel().getName() );
- mChannelName.requestFocus();
- mChannelName.requestFocusInWindow();
-
- mChannelEnabled.setSelected(
- mChannelNode.getChannel().getEnabled() );
- mChannelEnabled.requestFocus();
- mChannelEnabled.requestFocusInWindow();
-
- String aliasListName =
- mChannelNode.getChannel().getAliasListName();
-
- if( aliasListName != null )
- {
- AliasList selected = mChannelNode.getModel().getResourceManager()
- .getPlaylistManager().getPlayist().getAliasDirectory()
- .getAliasList( aliasListName );
-
- mComboAliasLists.setSelectedItem( selected );
- }
-
-
- mSourceEditor.reset();
- mDecodeEditor.reset();
- mAuxDecodeEditor.reset();
- mEventLogEditor.reset();
- mRecordEditor.reset();
}
/**
- * Validate the specified editor against each of the component editors
+ * Sets the channel configuration in each of the channel editor components
+ * Note: this method must be invoked on the swing event dispatch thread
*/
- @Override
- public void validate( Editor editor ) throws ChannelValidationException
+ public void setItem( final Channel channel )
{
- mSourceEditor.validate( editor );
- mDecodeEditor.validate( editor );
- mAuxDecodeEditor.validate( editor );
- mEventLogEditor.validate( editor );
- mRecordEditor.validate( editor );
+ 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() );
+ mEnableButton.setText( channel.getEnabled() ? "Disable" : "Enable" );
+ mEnableButton.setEnabled( true );
+ mEnableButton.setBackground( channel.getEnabled() ? Color.GREEN : getBackground() );
+ }
+ else
+ {
+ mChannelName.setText( "Channel: " );
+ mEnableButton.setText( "Enable" );
+ mEnableButton.setEnabled( false );
+ mEnableButton.setBackground( getBackground() );
+ }
}
+
+ public void save()
+ {
+ if( hasItem() )
+ {
+ mNameConfigurationEditor.save();
+ mSourceConfigurationEditor.save();
+ mDecodeConfigurationEditor.save();
+ mAuxDecodeConfigurationEditor.save();
+ mEventLogConfigurationEditor.save();
+ mRecordConfigurationEditor.save();
+
+ try
+ {
+ mDecodeConfigurationEditor.validate( mSourceConfigurationEditor );
+ mDecodeConfigurationEditor.validate( mAuxDecodeConfigurationEditor );
+ }
+ catch( EditorValidationException e )
+ {
+ e.getEditor().requestFocus();
+
+ JOptionPane.showMessageDialog( e.getEditor(), e.getReason(),
+ "Configuration Error", JOptionPane.ERROR_MESSAGE );
+
+ return;
+ }
+
+
+ mChannelModel.broadcast( new ChannelEvent( getItem(),
+ Event.NOTIFICATION_CONFIGURATION_CHANGE ) );
+ }
+
+ setModified( false );
+ }
}
diff --git a/src/controller/channel/ChannelEvent.java b/src/controller/channel/ChannelEvent.java
index a8b88eab..b71b5c94 100644
--- a/src/controller/channel/ChannelEvent.java
+++ b/src/controller/channel/ChannelEvent.java
@@ -1,3 +1,20 @@
+/*******************************************************************************
+ * 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
+ ******************************************************************************/
package controller.channel;
public class ChannelEvent
@@ -27,33 +44,35 @@ public class ChannelEvent
}
/**
- * Channel Events - used to specify channel events and changes to channel
- * configurations and settings.
+ * Channel events to describe the specific event
*/
public enum Event
{
- CHANGE_ALIAS_LIST,
- CHANGE_DECODER,
- CHANGE_EVENT_LOGGER,
- CHANGE_NAME,
- CHANGE_SELECTED,
- CHANGE_SITE,
- CHANGE_RECORDER,
- CHANGE_SOURCE,
- CHANGE_SYSTEM,
-
- CHANNEL_ADDED,
- CHANNEL_DELETED,
-
- CHANNEL_ENABLED,
- CHANNEL_DISABLED,
-
- CHANNEL_PROCESSING_STARTED,
- CHANNEL_PROCESSING_STOPPED,
-
- CHANNEL_STATE_RESET,
-
- TRAFFIC_CHANNEL_ADDED,
- TRAFFIC_CHANNEL_DELETED;
+ //Channel is added
+ NOTIFICATION_ADD,
+ //Channel configuration has changed
+ NOTIFICATION_CONFIGURATION_CHANGE,
+ //Channel is deleted/removed
+ NOTIFICATION_DELETE,
+ //Channel enable request was rejected
+ NOTIFICATION_ENABLE_REJECTED,
+ //Channel has started processing/decoding
+ NOTIFICATION_PROCESSING_START,
+ //Channel has stopped processing/decoding
+ NOTIFICATION_PROCESSING_STOP,
+ //Channel's selection state has changed
+ NOTIFICATION_SELECTION_CHANGE,
+ //Channel's channel-state has been reset
+ NOTIFICATION_STATE_RESET,
+ //Request to delete the channel
+ REQUEST_DELETE,
+ //Request to deselect the channel
+ REQUEST_DESELECT,
+ //Request to disable a channel - response will be a PROCESSING_STOP_NOTIFICATION
+ REQUEST_DISABLE,
+ //Request to enable a channel - response will be a PROCESSING_START_NOTIFICATION
+ REQUEST_ENABLE,
+ //Request to select the channel
+ REQUEST_SELECT;
}
}
diff --git a/src/controller/channel/ChannelEventListener.java b/src/controller/channel/ChannelEventListener.java
index 7c5405db..ad223435 100644
--- a/src/controller/channel/ChannelEventListener.java
+++ b/src/controller/channel/ChannelEventListener.java
@@ -1,6 +1,6 @@
/*******************************************************************************
* SDR Trunk
- * Copyright (C) 2014 Dennis Sheirer
+ * 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
diff --git a/src/controller/channel/ChannelManager.java b/src/controller/channel/ChannelManager.java
deleted file mode 100644
index 4c594824..00000000
--- a/src/controller/channel/ChannelManager.java
+++ /dev/null
@@ -1,143 +0,0 @@
-/*******************************************************************************
- * SDR Trunk
- * Copyright (C) 2014,2015 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
- ******************************************************************************/
-package controller.channel;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import message.Message;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import sample.Listener;
-import alias.action.AliasActionManager;
-import controller.ResourceManager;
-
-public class ChannelManager implements ChannelEventListener
-{
- private final static Logger mLog = LoggerFactory.getLogger( ChannelManager.class );
-
- private List mChannels = new ArrayList<>();
- private List mChannelListeners = new ArrayList<>();
- private List> mMessageListeners = new ArrayList<>();
-
- private AliasActionManager mAliasActionManager;
-
- public ChannelManager( ResourceManager resourceManager )
- {
- mAliasActionManager = new AliasActionManager( resourceManager );
-
- mChannelListeners.add( this );
-
- /* Add alias action manager as listener to all channels/messages */
- mMessageListeners.add( mAliasActionManager );
- }
-
- /**
- * Notification that a channel has been added or deleted, so that we can
- * keep track of all channels and manage system-wide listeners for each
- * of those channels
- */
- @SuppressWarnings( "incomplete-switch" )
- @Override
- public void channelChanged( ChannelEvent event )
- {
- switch( event.getEvent() )
- {
- case CHANNEL_ADDED:
- if( !mChannels.contains( event.getChannel() ) )
- {
- mChannels.add( event.getChannel() );
- }
- break;
- case CHANNEL_DELETED:
- mChannels.remove( event.getChannel() );
- break;
- }
- }
-
- /**
- * Adds a channel listener that will be added to all channels to receive
- * any channel change events. This listener will automatically receive
- * a channel add event as it is added to each of the existing channels.
- */
- public void addListener( ChannelEventListener listener )
- {
- mChannelListeners.add( listener );
-
- for( Channel channel: mChannels )
- {
- channel.addListener( listener );
- }
- }
-
- public List getChannels()
- {
- return mChannels;
- }
-
- /**
- * Removes a channel listener from being automatically added to all channels
- * to receive channel change events.
- */
- public void removeListener( ChannelEventListener listener )
- {
- mChannelListeners.remove( listener );
-
- for( Channel channel: mChannels )
- {
- channel.removeListener( listener );
- }
- }
-
- /**
- * Returns the list of channel change listeners that will be automatically
- * added to all channels to receive channel change events
- */
- public List getChannelListeners()
- {
- return mChannelListeners;
- }
-
- /**
- * Returns the list of channel change listeners that will be automatically
- * added to all channels to receive channel change events
- */
- public List> getMessageListeners()
- {
- return mMessageListeners;
- }
-
- /**
- * Adds a message listener that will be added to all channels to receive
- * any messages.
- */
- public void addListener( Listener listener )
- {
- mMessageListeners.add( listener );
- }
-
- /**
- * Removes a message listener.
- */
- public void removeListener( Listener listener )
- {
- mMessageListeners.remove( listener );
- }
-}
diff --git a/src/controller/channel/ChannelModel.java b/src/controller/channel/ChannelModel.java
new file mode 100644
index 00000000..9efe8cc2
--- /dev/null
+++ b/src/controller/channel/ChannelModel.java
@@ -0,0 +1,343 @@
+/*******************************************************************************
+ * 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
+ ******************************************************************************/
+package controller.channel;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.concurrent.CopyOnWriteArrayList;
+
+import javax.swing.table.AbstractTableModel;
+
+import module.decode.DecoderType;
+import controller.channel.Channel.ChannelType;
+import controller.channel.ChannelEvent.Event;
+
+/**
+ * Channel Model
+ */
+public class ChannelModel extends AbstractTableModel implements IChannelEventBroadcaster
+{
+ private static final long serialVersionUID = 1L;
+
+ private List mChannels = new CopyOnWriteArrayList<>();
+ private List mTrafficChannels = new CopyOnWriteArrayList<>();
+ private List mListeners = new CopyOnWriteArrayList<>();
+
+ public ChannelModel()
+ {
+ }
+
+ /**
+ * Unmodifiable list of non-traffic channels currently in the model
+ */
+ public List getChannels()
+ {
+ return Collections.unmodifiableList( mChannels );
+ }
+
+ public Channel getChannelAtIndex( int row )
+ {
+ if( mChannels.size() >= row )
+ {
+ return mChannels.get( row );
+ }
+
+ return null;
+ }
+
+ /**
+ * Returns a list of unique system values from across the channel set
+ */
+ public List getSystems()
+ {
+ List systems = new ArrayList<>();
+
+ for( Channel channel: mChannels )
+ {
+ if( channel.hasSystem() && !systems.contains( channel.getSystem() ) )
+ {
+ systems.add( channel.getSystem() );
+ }
+ }
+
+ Collections.sort( systems );
+
+ return systems;
+ }
+
+ /**
+ * Returns a list of unique site values for all channels that have a matching
+ * system value
+ */
+ public List getSites( String system )
+ {
+ List sites = new ArrayList<>();
+
+ if( system != null )
+ {
+ for( Channel channel: mChannels )
+ {
+ if( channel.hasSystem() &&
+ system.equals( channel.getSystem() ) &&
+ channel.hasSite() &&
+ !sites.contains( channel.getSite() ) )
+ {
+ sites.add( channel.getSite() );
+ }
+ }
+ }
+
+ Collections.sort( sites );
+
+ return sites;
+ }
+
+ /**
+ * Broadcasts the channel event to all registered listeners
+ */
+ @Override
+ public void broadcast( ChannelEvent event )
+ {
+ for( ChannelEventListener listener: mListeners )
+ {
+ listener.channelChanged( event );
+ }
+
+ if( event.getChannel().getChannelType() == ChannelType.STANDARD )
+ {
+ switch( event.getEvent() )
+ {
+ case NOTIFICATION_CONFIGURATION_CHANGE:
+ case NOTIFICATION_PROCESSING_START:
+ case NOTIFICATION_PROCESSING_STOP:
+ case NOTIFICATION_SELECTION_CHANGE:
+ int index = mChannels.indexOf( event.getChannel() );
+ fireTableRowsUpdated( index, index );
+ break;
+ default:
+ break;
+ }
+ }
+
+ if( event.getEvent() == Event.REQUEST_DELETE )
+ {
+ removeChannel( event.getChannel() );
+ }
+ }
+
+ /**
+ * Bulk loading of channel list. Each channel is added and a channel add
+ * event is broadcast.
+ */
+ public void addChannels( List channels )
+ {
+ for( Channel channel: channels )
+ {
+ addChannel( channel );
+ }
+ }
+
+ /**
+ * Adds the channel to the model and broadcasts a channel add event
+ */
+ public int addChannel( Channel channel )
+ {
+ int index = -1;
+
+ switch( channel.getChannelType() )
+ {
+ case STANDARD:
+ mChannels.add( channel );
+
+ index = mChannels.size() - 1;
+
+ fireTableRowsInserted( index, index );
+ break;
+ case TRAFFIC:
+ mTrafficChannels.add( channel );
+ index = mChannels.size() - 1;
+ break;
+ default:
+ break;
+ }
+
+ broadcast( new ChannelEvent( channel, Event.NOTIFICATION_ADD ) );
+
+ if( channel.getEnabled() )
+ {
+ broadcast( new ChannelEvent( channel, Event.REQUEST_ENABLE ) );
+ }
+
+ return index;
+ }
+
+ /**
+ * Removes the channel from the model and broadcasts a channel remove event
+ */
+ public void removeChannel( Channel channel )
+ {
+ if( channel != null )
+ {
+ switch( channel.getChannelType() )
+ {
+ case STANDARD:
+ int index = mChannels.indexOf( channel );
+
+ mChannels.remove( channel );
+
+ if( index >= 0 )
+ {
+ fireTableRowsDeleted( index, index );
+ }
+ break;
+ case TRAFFIC:
+ mTrafficChannels.remove( channel );
+ break;
+ default:
+ break;
+ }
+
+ broadcast( new ChannelEvent( channel, Event.NOTIFICATION_DELETE ) );
+ }
+ }
+
+ /**
+ * Returns a list of channels that fall within the frequency range
+ *
+ * @param start frequency of the range
+ * @param stop frequency of the range
+ *
+ * @return list of channels or an empty list if none fall within the range
+ */
+ public List getChannelsInFrequencyRange( long start, long stop )
+ {
+ List channels = new ArrayList<>();
+
+ for( Channel channel: mChannels )
+ {
+ if( channel.isWithin( start, stop ) )
+ {
+ channels.add( channel );
+ }
+ }
+
+ for( Channel channel: mTrafficChannels )
+ {
+ if( channel.isWithin( start, stop ) )
+ {
+ channels.add( channel );
+ }
+ }
+
+ return channels;
+ }
+
+ public void addListener( ChannelEventListener listener )
+ {
+ mListeners.add( listener );
+ }
+
+ public void removeListener( ChannelEventListener listener )
+ {
+ mListeners.remove( listener );
+ }
+
+ //Table Model Interface Methods - standard channels only
+ @Override
+ public int getRowCount()
+ {
+ return mChannels.size();
+ }
+
+ @Override
+ public int getColumnCount()
+ {
+ return 6;
+ }
+
+ @Override
+ public String getColumnName( int columnIndex )
+ {
+ switch( columnIndex )
+ {
+ case 0:
+ return "System";
+ case 1:
+ return "Site";
+ case 2:
+ return "Name";
+ case 3:
+ return "Alias List";
+ case 4:
+ return "Source";
+ case 5:
+ return "Decoder";
+ }
+
+ 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 0:
+ return channel.getSystem();
+ case 1:
+ return channel.getSite();
+ case 2:
+ return channel.getName();
+ case 3:
+ return channel.getAliasListName();
+ case 4:
+ return channel.getSourceConfiguration().getDescription();
+ case 5:
+ return channel.getDecodeConfiguration()
+ .getDecoderType().getShortDisplayString();
+ }
+
+ return null;
+ }
+
+ @Override
+ public void setValueAt( Object aValue, int rowIndex, int columnIndex )
+ {
+ throw new IllegalArgumentException( "Not yet implemented" );
+ }
+
+ public void createChannel( DecoderType decoderType, long frequency )
+ {
+ throw new UnsupportedOperationException( "Not yet implemented" );
+ }
+}
diff --git a/src/controller/channel/ChannelNode.java b/src/controller/channel/ChannelNode.java
deleted file mode 100644
index 3c5d70ab..00000000
--- a/src/controller/channel/ChannelNode.java
+++ /dev/null
@@ -1,187 +0,0 @@
-/*******************************************************************************
- * SDR Trunk
- * Copyright (C) 2014 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
- ******************************************************************************/
-package controller.channel;
-
-import java.awt.Color;
-import java.text.DecimalFormat;
-
-import javax.swing.JMenu;
-import javax.swing.JPanel;
-import javax.swing.JPopupMenu;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import source.tuner.TunerChannel;
-import controller.ConfigurableNode;
-import controller.site.SiteNode;
-import controller.system.SystemNode;
-
-/**
- * A channel has all of the pieces needed to wire together a source, decoder,
- * event logger and recorder and be started and stopped.
- */
-public class ChannelNode extends ConfigurableNode implements ChannelEventListener
-{
- private static final long serialVersionUID = 1L;
-
- private final static Logger mLog = LoggerFactory.getLogger( ChannelNode.class );
-
- private static DecimalFormat sFORMAT = new DecimalFormat( "0.0000" );
-
- public ChannelNode( Channel channel )
- {
- super( channel );
- }
-
- public void init()
- {
- /* Set the owning system and site names for the channel */
- SiteNode siteNode = (SiteNode)getParent();
-
- if( siteNode != null )
- {
- getChannel().setSite( siteNode.getSite(), false );
-
- SystemNode systemNode = (SystemNode)siteNode.getParent();
-
- if( systemNode != null )
- {
- getChannel().setSystem( systemNode.getSystem(), false );
- }
- }
-
- /* Add this node as listener to receive changes from underlying channel */
- getChannel().addListener( this );
-
- /* Add the resource manager to the channel so that the channel
- * can provide channel change events to all system resources */
- getChannel().setResourceManager( getModel().getResourceManager() );
- }
-
- /**
- * Listener method to receive channel changes from the underlying chnannel
- */
- @Override
- public void channelChanged( ChannelEvent event )
- {
- switch( event.getEvent() )
- {
- /* Refresh the node for each of these events */
- case CHANGE_DECODER:
- case CHANGE_NAME:
- case CHANGE_SELECTED:
- case CHANGE_SITE:
- case CHANGE_SYSTEM:
- case CHANNEL_ENABLED:
- case CHANNEL_DISABLED:
- case CHANNEL_PROCESSING_STARTED:
- case CHANNEL_PROCESSING_STOPPED:
- this.refresh();
- break;
- /* We're being deleted, so cleanup */
- case CHANNEL_DELETED:
- delete();
- break;
- default:
- break;
- }
- }
-
- @Override
- public String getIconPath()
- {
- return getChannel().getDecodeConfiguration().getDecoderType()
- .getIconFilename();
- }
-
- @Override
- public Color getBackgroundColor()
- {
- if( getChannel().getEnabled() )
- {
- if( getChannel().isProcessing() )
- {
- return Color.GREEN;
- }
- else
- {
- return Color.RED;
- }
- }
-
- return null;
- }
-
- @Override
- public JPanel getEditor()
- {
- return new ChannelEditor( this,
- getChannel().getResourceManager() == null ? null :
- getChannel().getResourceManager().getPlaylistManager() );
- }
-
- public Channel getChannel()
- {
- return (Channel)this.getUserObject();
- }
-
- public String toString()
- {
- StringBuilder sb = new StringBuilder();
-
- sb.append( getChannel().getName() );
-
- TunerChannel channel = getChannel().getTunerChannel();
-
- if( channel != null )
- {
- sb.append( " (" );
- sb.append( sFORMAT.format( (double)channel.getFrequency() / 1000000.0d ) + " MHz" );
- sb.append( ")" );
-
- }
-
- return sb.toString();
- }
-
- public JPopupMenu getContextMenu()
- {
- JPopupMenu popupMenu = new JPopupMenu();
-
- JMenu channelMenu = ChannelUtils.getContextMenu(
- getModel().getResourceManager().getPlaylistManager(),
- getChannel(), getModel().getTree() );
-
- if( channelMenu != null )
- {
- popupMenu.add( channelMenu );
- }
-
- return popupMenu;
- }
-
- public void delete()
- {
- ((SiteNode)getParent()).getSite().removeChannel( getChannel() );
-
- save();
-
- getModel().removeNodeFromParent( ChannelNode.this );
- }
-}
diff --git a/src/controller/channel/ChannelProcessingManager.java b/src/controller/channel/ChannelProcessingManager.java
new file mode 100644
index 00000000..91ca81f2
--- /dev/null
+++ b/src/controller/channel/ChannelProcessingManager.java
@@ -0,0 +1,334 @@
+/*******************************************************************************
+ * 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
+ ******************************************************************************/
+package controller.channel;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.CopyOnWriteArrayList;
+
+import message.Message;
+import module.Module;
+import module.ProcessingChain;
+import module.decode.DecoderFactory;
+import module.decode.event.MessageActivityModel;
+import module.log.EventLogManager;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import record.RecorderManager;
+import record.RecorderType;
+import sample.Listener;
+import source.Source;
+import source.SourceException;
+import source.SourceManager;
+import alias.AliasModel;
+import audio.AudioPacket;
+import audio.metadata.Metadata;
+import audio.metadata.MetadataType;
+import controller.channel.Channel.ChannelType;
+import controller.channel.ChannelEvent.Event;
+import controller.channel.map.ChannelMapModel;
+import filter.FilterSet;
+
+public class ChannelProcessingManager implements ChannelEventListener
+{
+ private final static Logger mLog = LoggerFactory.getLogger( ChannelProcessingManager.class );
+
+ private Map mProcessingChains = new HashMap<>();
+
+ private List> mAudioPacketListeners = new CopyOnWriteArrayList<>();
+ private List> mMessageListeners = new CopyOnWriteArrayList<>();
+
+ private ChannelModel mChannelModel;
+ private ChannelMapModel mChannelMapModel;
+ private AliasModel mAliasModel;
+ private EventLogManager mEventLogManager;
+ private RecorderManager mRecorderManager;
+ private SourceManager mSourceManager;
+
+ public ChannelProcessingManager( ChannelModel channelModel,
+ ChannelMapModel channelMapModel,
+ AliasModel aliasModel,
+ EventLogManager eventLogManager,
+ RecorderManager recorderManager,
+ SourceManager sourceManager )
+ {
+ mChannelModel = channelModel;
+ mChannelMapModel = channelMapModel;
+ mAliasModel = aliasModel;
+ mEventLogManager = eventLogManager;
+ mRecorderManager = recorderManager;
+ mSourceManager = sourceManager;
+ }
+
+ /**
+ * Indicates if a processing chain is constructed for the channel and that
+ * the processing chain is currently processing.
+ */
+ public boolean isProcessing( Channel channel )
+ {
+ if( mProcessingChains.containsKey( channel.getChannelID() ) )
+ {
+ return mProcessingChains.get( channel.getChannelID() ).isProcessing();
+ }
+
+ return false;
+ }
+
+ /**
+ * Returns the current processing chain associated with the channel, or
+ * null if a processing chain is not currently setup for the channel
+ */
+ public ProcessingChain getProcessingChain( Channel channel )
+ {
+ return mProcessingChains.get( channel.getChannelID() );
+ }
+
+ @Override
+ public synchronized void channelChanged( ChannelEvent event )
+ {
+ Channel channel = event.getChannel();
+
+ switch( event.getEvent() )
+ {
+ case REQUEST_ENABLE:
+ if( !mProcessingChains.containsKey( channel.getChannelID() ) ||
+ !mProcessingChains.get( channel.getChannelID() ).isProcessing() )
+ {
+ startProcessing( event );
+ }
+ break;
+ case REQUEST_DISABLE:
+ if( channel.getEnabled() )
+ {
+ switch( channel.getChannelType() )
+ {
+ case STANDARD:
+ stopProcessing( channel, true );
+ break;
+ case TRAFFIC:
+ //Don't remove traffic channel processing chains
+ //until explicitly deleted, so that we can reuse them
+ stopProcessing( channel, false );
+ default:
+ break;
+ }
+ }
+ case NOTIFICATION_DELETE:
+ if( channel.getEnabled() )
+ {
+ stopProcessing( channel, true );
+ }
+ break;
+ case NOTIFICATION_CONFIGURATION_CHANGE:
+ if( isProcessing( channel ) )
+ {
+ stopProcessing( channel, false );
+ startProcessing( event );
+ }
+ break;
+ default:
+ break;
+ }
+ }
+
+ private void startProcessing( ChannelEvent event )
+ {
+ Channel channel = event.getChannel();
+
+ ProcessingChain processingChain = mProcessingChains.get( channel.getChannelID() );
+
+ //If we're already processing, ignore the request
+ if( processingChain != null && processingChain.isProcessing() )
+ {
+ return;
+ }
+
+ //Ensure that we can get a source before we construct a new processing chain
+ Source source = null;
+
+ try
+ {
+ source = mSourceManager.getSource( channel.getSourceConfiguration(),
+ channel.getDecodeConfiguration().getDecoderType().getChannelBandwidth() );
+ }
+ catch( SourceException se )
+ {
+ mLog.debug( "Error obtaining source for channel [" + channel.getName() + "]", se );
+ }
+
+ if( source == null )
+ {
+ channel.setEnabled( false );
+
+ mChannelModel.broadcast(
+ new ChannelEvent( channel, Event.NOTIFICATION_ENABLE_REJECTED ) );
+
+ return;
+ }
+
+ if( processingChain == null )
+ {
+ processingChain = new ProcessingChain( channel.getName(),
+ channel.getChannelType() );
+
+ /* Register global listeners */
+ for( Listener listener: mAudioPacketListeners )
+ {
+ processingChain.addAudioPacketListener( listener );
+ }
+
+ for( Listener listener: mMessageListeners )
+ {
+ processingChain.addMessageListener( listener );
+ }
+
+ /* Processing Modules */
+ List modules = DecoderFactory.getModules( mChannelModel,
+ mChannelMapModel, this, mAliasModel, channel );
+ processingChain.addModules( modules );
+
+ /* Setup message activity model with filtering */
+ FilterSet messageFilter = DecoderFactory.getMessageFilters( modules );
+ MessageActivityModel messageModel = new MessageActivityModel( messageFilter );
+ processingChain.setMessageActivityModel( messageModel );
+
+ /* Setup event logging */
+ List loggers = mEventLogManager.getLoggers(
+ channel.getEventLogConfiguration(), channel.getName() );
+
+ if( !loggers.isEmpty() )
+ {
+ processingChain.addModules( loggers );
+ }
+
+ /* Setup recorders */
+ List recorders = channel.getRecordConfiguration().getRecorders();
+
+ if( !recorders.isEmpty() )
+ {
+ if( recorders.contains( RecorderType.AUDIO ) )
+ {
+ processingChain.addAudioPacketListener( mRecorderManager );
+ }
+
+ /* Add baseband recorder */
+ if( ( recorders.contains( RecorderType.BASEBAND ) &&
+ channel.getChannelType() == ChannelType.STANDARD ) )
+ {
+ processingChain.addModule( mRecorderManager.getBasebandRecorder(
+ channel.toString() ) );
+ }
+
+ /* Add traffic channel baseband recorder */
+ if( recorders.contains( RecorderType.TRAFFIC_BASEBAND ) &&
+ channel.getChannelType() == ChannelType.TRAFFIC )
+ {
+ processingChain.addModule( mRecorderManager
+ .getBasebandRecorder( channel.toString() ) );
+ }
+ }
+
+ /* Inject channel metadata that will be inserted into audio packets
+ * for the recorder manager and streaming */
+ processingChain.broadcast(
+ new Metadata( MetadataType.SYSTEM, channel.getSystem() ) );
+ processingChain.broadcast(
+ new Metadata( MetadataType.SITE_ID, channel.getSite() ) );
+ processingChain.broadcast(
+ new Metadata( MetadataType.CHANNEL_NAME, channel.getName() ) );
+ }
+
+ processingChain.setSource( source );
+
+ if( event instanceof TrafficChannelEvent )
+ {
+ TrafficChannelEvent trafficChannelEvent = (TrafficChannelEvent)event;
+
+ processingChain.getChannelState().configureAsTrafficChannel(
+ trafficChannelEvent.getTrafficChannelManager(),
+ trafficChannelEvent.getCallEvent() );
+ }
+
+ processingChain.start();
+
+ channel.setEnabled( true );
+
+ mProcessingChains.put( channel.getChannelID(), processingChain );
+
+ mChannelModel.broadcast(
+ new ChannelEvent( channel, Event.NOTIFICATION_PROCESSING_START ) );
+ }
+
+ private void stopProcessing( Channel channel, boolean remove )
+ {
+ channel.setEnabled( false );
+
+ if( mProcessingChains.containsKey( channel.getChannelID() ) )
+ {
+ ProcessingChain chain = mProcessingChains.get( channel.getChannelID() );
+
+ chain.stop();
+
+ mChannelModel.broadcast( new ChannelEvent( channel, Event.NOTIFICATION_PROCESSING_STOP ) );
+
+ if( remove )
+ {
+ mProcessingChains.remove( channel.getChannelID() );
+
+ chain.dispose();
+ }
+ }
+ }
+
+ /**
+ * Adds a message listener that will be added to all channels to receive
+ * any messages.
+ */
+ public void addAudioPacketListener( Listener listener )
+ {
+ mAudioPacketListeners.add( listener );
+ }
+
+ /**
+ * Removes a message listener.
+ */
+ public void removeAudioPacketListener( Listener listener )
+ {
+ mAudioPacketListeners.remove( listener );
+ }
+
+ /**
+ * Adds a message listener that will be added to all channels to receive
+ * any messages.
+ */
+ public void addMessageListener( Listener listener )
+ {
+ mMessageListeners.add( listener );
+ }
+
+ /**
+ * Removes a message listener.
+ */
+ public void removeMessageListener( Listener listener )
+ {
+ mMessageListeners.remove( listener );
+ }
+}
diff --git a/src/controller/channel/ChannelSelectionManager.java b/src/controller/channel/ChannelSelectionManager.java
new file mode 100644
index 00000000..65ac9b91
--- /dev/null
+++ b/src/controller/channel/ChannelSelectionManager.java
@@ -0,0 +1,71 @@
+/*******************************************************************************
+ * SDR Trunk
+ * Copyright (C) 2015 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
+ ******************************************************************************/
+package controller.channel;
+
+import controller.channel.ChannelEvent.Event;
+
+public class ChannelSelectionManager implements ChannelEventListener
+{
+ private IChannelEventBroadcaster mChannelEventBroadcaster;
+ private Channel mSelectedChannel = null;
+
+ /**
+ * Manages channel selection state to ensure that only one channel is ever
+ * in a selected state.
+ */
+ public ChannelSelectionManager( IChannelEventBroadcaster broadcaster )
+ {
+ mChannelEventBroadcaster = broadcaster;
+ }
+
+ @Override
+ public void channelChanged( ChannelEvent event )
+ {
+ switch( event.getEvent() )
+ {
+ case REQUEST_SELECT:
+ if( mSelectedChannel != null )
+ {
+ mSelectedChannel.setSelected( false );
+ mChannelEventBroadcaster.broadcast( new ChannelEvent( mSelectedChannel,
+ Event.NOTIFICATION_SELECTION_CHANGE ) );
+ mSelectedChannel = null;
+ }
+
+ mSelectedChannel = event.getChannel();
+ mSelectedChannel.setSelected( true );
+ mChannelEventBroadcaster.broadcast( new ChannelEvent( event.getChannel(),
+ Event.NOTIFICATION_SELECTION_CHANGE ) );
+ break;
+ case NOTIFICATION_PROCESSING_STOP:
+ case REQUEST_DESELECT:
+ if( mSelectedChannel != null &&
+ mSelectedChannel == event.getChannel() )
+ {
+ mSelectedChannel.setSelected( false );
+ mSelectedChannel = null;
+ mChannelEventBroadcaster.broadcast( new ChannelEvent( event.getChannel(),
+ Event.NOTIFICATION_SELECTION_CHANGE ) );
+ }
+ break;
+ default:
+ break;
+ }
+ }
+
+}
diff --git a/src/controller/channel/ChannelUtils.java b/src/controller/channel/ChannelUtils.java
index 5f50f6e6..6434eb4c 100644
--- a/src/controller/channel/ChannelUtils.java
+++ b/src/controller/channel/ChannelUtils.java
@@ -9,23 +9,20 @@ import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JSeparator;
+import module.ProcessingChain;
import module.decode.event.ActivitySummaryFrame;
import module.decode.state.DecoderState;
-import playlist.PlaylistManager;
import controller.channel.ChannelEvent.Event;
public class ChannelUtils
{
- private static final boolean ENABLED = true;
- private static final boolean DISABLED = false;
- private static final boolean BROADCAST_CHANGE = true;
-
/**
* Creates a context menu for the channel argument
*/
- public static JMenu getContextMenu( final PlaylistManager playlistManager,
- final Channel channel,
- final Component anchor )
+ public static JMenu getContextMenu( final ChannelModel channelModel,
+ final ChannelProcessingManager channelProcessingManager,
+ final Channel channel,
+ final Component anchor )
{
if( channel != null )
{
@@ -39,12 +36,8 @@ public class ChannelUtils
@Override
public void actionPerformed( ActionEvent e )
{
- channel.setEnabled( DISABLED, BROADCAST_CHANGE );
-
- if( playlistManager != null )
- {
- playlistManager.save();
- }
+ channelModel.broadcast(
+ new ChannelEvent( channel, Event.REQUEST_DISABLE ) );
}
} );
@@ -62,10 +55,14 @@ public class ChannelUtils
{
StringBuilder sb = new StringBuilder();
- for( DecoderState decoderState: channel
- .getProcessingChain().getDecoderStates() )
+ ProcessingChain chain = channelProcessingManager.getProcessingChain( channel );
+
+ if( chain != null )
{
- sb.append( decoderState.getActivitySummary() );
+ for( DecoderState decoderState: chain.getDecoderStates() )
+ {
+ sb.append( decoderState.getActivitySummary() );
+ }
}
new ActivitySummaryFrame( sb.toString(), anchor );
@@ -82,12 +79,8 @@ public class ChannelUtils
@Override
public void actionPerformed( ActionEvent e )
{
- channel.setEnabled( ENABLED, BROADCAST_CHANGE );
-
- if( playlistManager != null )
- {
- playlistManager.save();
- }
+ channelModel.broadcast(
+ new ChannelEvent( channel, Event.REQUEST_ENABLE ) );
}
} );
@@ -108,16 +101,8 @@ public class ChannelUtils
if( response == JOptionPane.YES_OPTION )
{
- /* Disable the channel */
- channel.setEnabled( DISABLED, BROADCAST_CHANGE );
-
- /* Broadcast channel deleted event */
- channel.fireChannelEvent( Event.CHANNEL_DELETED );
-
- if( playlistManager != null )
- {
- playlistManager.save();
- }
+ channelModel.broadcast(
+ new ChannelEvent( channel, Event.REQUEST_DELETE ) );
}
}
} );
diff --git a/src/controller/channel/ChannelValidationException.java b/src/controller/channel/ChannelValidationException.java
deleted file mode 100644
index 53422a11..00000000
--- a/src/controller/channel/ChannelValidationException.java
+++ /dev/null
@@ -1,11 +0,0 @@
-package controller.channel;
-
-public class ChannelValidationException extends Exception
-{
- private static final long serialVersionUID = 1L;
-
- public ChannelValidationException( String validationMessage )
- {
- super( validationMessage );
- }
-}
diff --git a/src/controller/channel/ConfigurationValidationException.java b/src/controller/channel/ConfigurationValidationException.java
new file mode 100644
index 00000000..a61862c5
--- /dev/null
+++ b/src/controller/channel/ConfigurationValidationException.java
@@ -0,0 +1,23 @@
+package controller.channel;
+
+import java.awt.Component;
+
+public class ConfigurationValidationException extends Exception
+{
+ private static final long serialVersionUID = 1L;
+
+ private Component mComponent;
+
+ public ConfigurationValidationException( Component component,
+ String validationMessage )
+ {
+ super( validationMessage );
+
+ mComponent = component;
+ }
+
+ public Component getComponent()
+ {
+ return mComponent;
+ }
+}
diff --git a/src/source/SampleProvider.java b/src/controller/channel/IChannelEventBroadcaster.java
similarity index 78%
rename from src/source/SampleProvider.java
rename to src/controller/channel/IChannelEventBroadcaster.java
index 3cc84749..ef7c89fd 100644
--- a/src/source/SampleProvider.java
+++ b/src/controller/channel/IChannelEventBroadcaster.java
@@ -1,6 +1,6 @@
/*******************************************************************************
* SDR Trunk
- * Copyright (C) 2014 Dennis Sheirer
+ * Copyright (C) 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
@@ -15,14 +15,15 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see
******************************************************************************/
-package source;
+package controller.channel;
-import sample.SampleType;
-
-public interface SampleProvider
+/**
+ * Provides channel event broadcasting capability.
+ */
+public interface IChannelEventBroadcaster
{
/**
- * Real or Complex
+ * Broadcasts the channel event
*/
- public abstract SampleType getSampleType();
+ public void broadcast( ChannelEvent event );
}
diff --git a/src/controller/channel/NameConfigurationEditor.java b/src/controller/channel/NameConfigurationEditor.java
new file mode 100644
index 00000000..e0525bdf
--- /dev/null
+++ b/src/controller/channel/NameConfigurationEditor.java
@@ -0,0 +1,269 @@
+/*******************************************************************************
+ * 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
+ ******************************************************************************/
+package controller.channel;
+
+import gui.editor.Editor;
+
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.util.Collections;
+import java.util.List;
+
+import javax.swing.ComboBoxModel;
+import javax.swing.DefaultComboBoxModel;
+import javax.swing.JComboBox;
+import javax.swing.JLabel;
+import javax.swing.JTextField;
+import javax.swing.event.DocumentEvent;
+import javax.swing.event.DocumentListener;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import net.miginfocom.swing.MigLayout;
+import alias.AliasModel;
+
+public class NameConfigurationEditor extends Editor implements DocumentListener
+{
+ private static final long serialVersionUID = 1L;
+
+ private final static Logger mLog = LoggerFactory.getLogger( NameConfigurationEditor.class );
+
+ private static ComboBoxModel EMPTY_MODEL = new DefaultComboBoxModel<>();
+
+ private AliasModel mAliasModel;
+ private ChannelModel mChannelModel;
+
+ private JTextField mChannelName;
+ private JComboBox mSystemNameCombo;
+ private JComboBox mSiteNameCombo;
+ private JComboBox mAliasListCombo;
+
+ public NameConfigurationEditor( AliasModel aliasModel, ChannelModel model )
+ {
+ mAliasModel = aliasModel;
+ mChannelModel = model;
+
+ init();
+ }
+
+ private void init()
+ {
+ setLayout( new MigLayout( "fill,wrap 4", "[right][grow,fill][right][grow,fill]", "[][][grow]" ) );
+
+ mChannelName = new JTextField( "" );
+ mChannelName.getDocument().addDocumentListener( this );
+ mChannelName.setEnabled( false );
+
+ add( new JLabel( "Name:" ) );
+ add( mChannelName );
+
+ mSystemNameCombo = new JComboBox<>( EMPTY_MODEL );
+ mSystemNameCombo.setEnabled( false );
+ mSystemNameCombo.setEditable( true );
+ mSystemNameCombo.addActionListener( new ActionListener()
+ {
+ @Override
+ public void actionPerformed( ActionEvent e )
+ {
+ Object selected = mSystemNameCombo.getSelectedItem();
+
+ if( selected != null )
+ {
+ String system = (String)selected;
+
+ List sites = mChannelModel.getSites( system );
+
+ if( !sites.contains( getItem().getSite() ) )
+ {
+ sites.add( getItem().getSite() );
+ }
+
+ if( sites.isEmpty() )
+ {
+ mSiteNameCombo.setModel( EMPTY_MODEL );
+ }
+ else
+ {
+ mSiteNameCombo.setModel( new DefaultComboBoxModel(
+ sites.toArray( new String[ sites.size() ] ) ) );;
+ }
+
+ mSiteNameCombo.setSelectedItem( getItem().getSite() );
+ }
+
+ setModified( true );
+ }
+ } );
+
+ add( new JLabel( "System:" ) );
+ add( mSystemNameCombo );
+
+ mAliasListCombo = new JComboBox();
+ mAliasListCombo.setEnabled( false );
+ mAliasListCombo.setEditable( true );
+ mAliasListCombo.addActionListener( new ActionListener()
+ {
+ @Override
+ public void actionPerformed( ActionEvent e )
+ {
+ setModified( true );
+ }
+ } );
+
+ add( new JLabel( "Alias List:" ) );
+ add( mAliasListCombo );
+
+ mSiteNameCombo = new JComboBox<>( EMPTY_MODEL );
+ mSiteNameCombo.setEnabled( false );
+ mSiteNameCombo.setEditable( true );
+ mSiteNameCombo.addActionListener( new ActionListener()
+ {
+ @Override
+ public void actionPerformed( ActionEvent e )
+ {
+ setModified( true );
+ }
+ } );
+
+ add( new JLabel( "Site:" ) );
+ add( mSiteNameCombo );
+ }
+
+ @Override
+ public void save()
+ {
+ if( hasItem() && isModified() )
+ {
+ getItem().setName( mChannelName.getText() );
+
+ Object system = mSystemNameCombo.getSelectedItem();
+ getItem().setSystem( ( system == null ? null : (String)system ) );
+
+ Object site = mSiteNameCombo.getSelectedItem();
+ getItem().setSite( ( site == null ? null : (String)site ) );
+
+ Object aliasList = mAliasListCombo.getSelectedItem();
+ getItem().setAliasListName( aliasList == null ? null : (String)aliasList );
+ }
+
+ setModified( false );
+ }
+
+ private void setControlsEnabled( boolean enabled )
+ {
+ if( mChannelName.isEnabled() != enabled )
+ {
+ mChannelName.setEnabled( enabled );
+ }
+
+ if( mAliasListCombo.isEnabled() != enabled )
+ {
+ mAliasListCombo.setEnabled( enabled );
+ }
+
+ if( mSystemNameCombo.isEnabled() != enabled )
+ {
+ mSystemNameCombo.setEnabled( enabled );
+ }
+
+ if( mSiteNameCombo.isEnabled() != enabled )
+ {
+ mSiteNameCombo.setEnabled( enabled );
+ }
+ }
+
+ @Override
+ public void setItem( Channel channel )
+ {
+ super.setItem( channel );
+
+ if( hasItem() )
+ {
+ setControlsEnabled( true );
+
+ mChannelName.setText( channel.getName() );
+
+ List systems = mChannelModel.getSystems();
+
+ if( systems.isEmpty() )
+ {
+ mSystemNameCombo.setModel( EMPTY_MODEL );
+ }
+ else
+ {
+ mSystemNameCombo.setModel( new DefaultComboBoxModel(
+ systems.toArray( new String[ systems.size() ] ) ) );;
+ }
+
+ mSystemNameCombo.setSelectedItem( channel.getSystem() );
+
+ List sites = mChannelModel.getSites( channel.getSystem() );
+
+ if( sites.isEmpty() )
+ {
+ mSiteNameCombo.setModel( EMPTY_MODEL );
+ }
+ else
+ {
+ mSiteNameCombo.setModel( new DefaultComboBoxModel(
+ sites.toArray( new String[ sites.size() ] ) ) );;
+ }
+
+ if( channel.getSite() != null )
+ {
+ mSiteNameCombo.setSelectedItem( channel.getSite() );
+ }
+
+ List lists = mAliasModel.getListNames();
+ Collections.sort( lists );
+ mAliasListCombo.setModel( new DefaultComboBoxModel(
+ lists.toArray( new String[ lists.size() ] ) ) );
+
+ if( channel.getAliasListName() != null )
+ {
+ mAliasListCombo.setSelectedItem( channel.getAliasListName() );
+ }
+ }
+ else
+ {
+ setControlsEnabled( false );
+ mChannelName.setText( null );
+ }
+
+ setModified( false );
+ }
+
+ @Override
+ public void insertUpdate( DocumentEvent e )
+ {
+ setModified( true );
+ }
+
+ @Override
+ public void changedUpdate( DocumentEvent e )
+ {
+ setModified( true );
+ }
+
+ @Override
+ public void removeUpdate( DocumentEvent e )
+ {
+ setModified( true );
+ }
+}
diff --git a/src/controller/channel/TrafficChannelEvent.java b/src/controller/channel/TrafficChannelEvent.java
new file mode 100644
index 00000000..20c4aade
--- /dev/null
+++ b/src/controller/channel/TrafficChannelEvent.java
@@ -0,0 +1,34 @@
+package controller.channel;
+
+import module.decode.event.CallEvent;
+import module.decode.state.TrafficChannelManager;
+
+public class TrafficChannelEvent extends ChannelEvent
+{
+ private TrafficChannelManager mTrafficChannelManager;
+ private CallEvent mCallEvent;
+
+ /**
+ * Call event with traffic channel manager for call back, and original creation event.
+ */
+ public TrafficChannelEvent( TrafficChannelManager trafficChannelManager,
+ Channel channel,
+ Event event,
+ CallEvent callEvent )
+ {
+ super( channel, event );
+
+ mTrafficChannelManager = trafficChannelManager;
+ mCallEvent = callEvent;
+ }
+
+ public TrafficChannelManager getTrafficChannelManager()
+ {
+ return mTrafficChannelManager;
+ }
+
+ public CallEvent getCallEvent()
+ {
+ return mCallEvent;
+ }
+}
diff --git a/src/controller/channel/map/ChannelMap.java b/src/controller/channel/map/ChannelMap.java
index 3ec928a0..107280f8 100644
--- a/src/controller/channel/map/ChannelMap.java
+++ b/src/controller/channel/map/ChannelMap.java
@@ -18,17 +18,20 @@
package controller.channel.map;
import java.util.ArrayList;
+import java.util.List;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlSeeAlso;
+import javax.xml.bind.annotation.XmlTransient;
@XmlSeeAlso( { ChannelRange.class } )
public class ChannelMap
{
private String mName;
+ private boolean mInvalid = false;
- private ArrayList mRanges = new ArrayList();
+ private List mRanges = new ArrayList();
public ChannelMap()
{
@@ -39,17 +42,10 @@ public class ChannelMap
{
mName = name;
}
-
- public String toString()
- {
- return mName;
- }
- /**
- * Creates a copy of this channel map
- */
+
public ChannelMap copyOf()
{
- ChannelMap map = new ChannelMap( mName );
+ ChannelMap map = new ChannelMap( new String( mName ) );
for( ChannelRange range: mRanges )
{
@@ -58,29 +54,29 @@ public class ChannelMap
return map;
}
-
- public int getInvalidRange()
+
+ @XmlTransient
+ public boolean isInvalid()
{
- for( int x = 0; x < mRanges.size(); x++ )
- {
- if( !mRanges.get( x ).isValid() )
- {
- return x;
- }
- }
-
- return -1;
+ return mInvalid;
+ }
+
+ public String toString()
+ {
+ return mName + ( mInvalid ? " - Error" : "" );
}
- public ArrayList getRanges()
+ @XmlElement( name = "range" )
+ public List getRanges()
{
return mRanges;
}
- @XmlElement( name = "range" )
- public void setRanges( ArrayList ranges )
+ public void setRanges( List ranges )
{
mRanges = ranges;
+
+ validate();
}
@XmlAttribute
@@ -106,15 +102,44 @@ public class ChannelMap
return 0;
}
+
+ /**
+ * Validates each of the channel ranges for overlap
+ */
+ private void validate()
+ {
+ mInvalid = false;
+
+ for( int x = 0; x < mRanges.size(); x++ )
+ {
+ if( !mRanges.get( x ).isValid() )
+ {
+ mInvalid = true;
+ }
+
+ for( int y = x + 1; y < mRanges.size(); y++ )
+ {
+ if( mRanges.get( x ).overlaps( mRanges.get( y ) ) )
+ {
+ mInvalid = true;
+ }
+ }
+ }
+ }
+
public void addRange( ChannelRange range )
{
mRanges.add( range );
+
+ validate();
}
- public void deleteRange( ChannelRange range )
+ public void removeRange( ChannelRange range )
{
mRanges.remove( range );
+
+ validate();
}
}
diff --git a/src/controller/channel/map/ChannelMapEditor.java b/src/controller/channel/map/ChannelMapEditor.java
index 11ede472..ef7c4374 100644
--- a/src/controller/channel/map/ChannelMapEditor.java
+++ b/src/controller/channel/map/ChannelMapEditor.java
@@ -1,6 +1,6 @@
/*******************************************************************************
* SDR Trunk
- * Copyright (C) 2014 Dennis Sheirer
+ * 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
@@ -17,75 +17,112 @@
******************************************************************************/
package controller.channel.map;
+import gui.editor.DocumentListenerEditor;
+
import java.awt.Component;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
-import java.awt.event.FocusEvent;
-import java.awt.event.FocusListener;
import javax.swing.AbstractCellEditor;
import javax.swing.JButton;
import javax.swing.JFormattedTextField;
import javax.swing.JLabel;
+import javax.swing.JList;
import javax.swing.JOptionPane;
-import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.JTextField;
+import javax.swing.event.ListSelectionEvent;
+import javax.swing.event.ListSelectionListener;
import javax.swing.table.TableCellEditor;
import javax.swing.text.InternationalFormatter;
import net.miginfocom.swing.MigLayout;
+import controller.channel.map.ChannelMapEvent.Event;
+import controller.channel.map.ChannelRangeModel.ChannelRangeEventListener;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-public class ChannelMapEditor extends JPanel implements ActionListener
+public class ChannelMapEditor extends DocumentListenerEditor
+ implements ListSelectionListener, ChannelRangeEventListener
{
private static final long serialVersionUID = 1L;
- private final static Logger mLog =
- LoggerFactory.getLogger( ChannelMapEditor.class );
- private ChannelMapNode mChannelMapNode;
-
private JLabel mNameLabel;
private JTextField mNameText;
- private ChannelMapModel mRangeModel;
+ private ChannelRangeModel mRangeModel;
private JTable mRangeTable;
- private JButton mDelete;
- private ChannelMap mChannelMapCopy;
+ private JButton mNewButton;
+ private JButton mDeleteButton;
+ private JButton mSaveButton;
+ private JLabel mRangeDescription;
+ private ChannelMapModel mChannelMapModel;
- public ChannelMapEditor( ChannelMapNode ChannelMap )
+ public ChannelMapEditor( ChannelMapModel channelMapModel )
{
- mChannelMapNode = ChannelMap;
- mChannelMapCopy = mChannelMapNode.getChannelMap().copyOf();
- initGUI();
+ mChannelMapModel = channelMapModel;
+ init();
}
- private void initGUI()
+ public ChannelMap getChannelMap()
{
- setLayout( new MigLayout( "fill,wrap 2", "[right][grow]", "[][][grow][][]" ) );
+ if( hasItem() )
+ {
+ return (ChannelMap)getItem();
+ }
- add( new JLabel( "Channel Map" ), "span, align center" );
+ return null;
+ }
+
+ @Override
+ public void setItem( ChannelMap item )
+ {
+ super.setItem( item );
- mNameLabel = new JLabel( "Name:" );
+ if( hasItem() )
+ {
+ ChannelMap map = getChannelMap();
+
+ mNameText.setText( map.getName() );
+
+ mRangeModel.clear();
+ mRangeModel.addRanges( map.getRanges() );
+
+ mNewButton.setEnabled( true );
+ }
+ else
+ {
+ mNameText.setText( "" );
+
+ mRangeModel.clear();
+
+ mNewButton.setEnabled( false );
+ }
+
+ setModified( false );
+ }
+
+ @Override
+ public void setModified( boolean modified )
+ {
+ super.setModified( modified );
+
+ mSaveButton.setEnabled( modified );
+ }
+
+ private void init()
+ {
+ setLayout( new MigLayout( "fill,wrap 3",
+ "[grow,fill][grow,fill][grow,fill]", "[][grow][][]" ) );
+
+ mNameLabel = new JLabel( "Channel Map:" );
add( mNameLabel );
- mNameText = new JTextField( mChannelMapCopy.getName() );
- mNameText.addFocusListener( new FocusListener()
- {
- @Override public void focusGained( FocusEvent arg0 ) {}
+ mNameText = new JTextField();
+ mNameText.getDocument().addDocumentListener( this );
+ add( mNameText, "span" );
- @Override
- public void focusLost( FocusEvent arg0 )
- {
- mChannelMapCopy.setName( mNameText.getText() );
- }
- } );
- add( mNameText, "growx, push" );
-
- mRangeModel = new ChannelMapModel( mChannelMapCopy );
+ mRangeModel = new ChannelRangeModel();
+ mRangeModel.setListener( this );
mRangeTable = new JTable( mRangeModel );
mRangeTable.setPreferredScrollableViewportSize( new Dimension( 300, 150 ) );
@@ -96,129 +133,201 @@ public class ChannelMapEditor extends JPanel implements ActionListener
mRangeTable.getColumnModel().getColumn( x )
.setCellEditor( new RangeTableEditor() );
}
+
+ mRangeTable.getSelectionModel().addListSelectionListener( new ListSelectionListener()
+ {
+ @Override
+ public void valueChanged( ListSelectionEvent event )
+ {
+ if( !event.getValueIsAdjusting() )
+ {
+ int index = event.getFirstIndex();
+
+ mDeleteButton.setEnabled( index >= 0 );
+
+ if( index >= 0 )
+ {
+ int modelIndex = mRangeTable.convertRowIndexToModel( index );
+
+ if( modelIndex >= 0 )
+ {
+ ChannelRange selected = mRangeModel.getChannelRange( modelIndex );
+
+ if( selected != null )
+ {
+ mRangeDescription.setText( selected.getDescription() );
+ }
+ else
+ {
+ mRangeDescription.setText( "" );
+ }
+ }
+ }
+ }
+ }
+ } );
JScrollPane scrollPane = new JScrollPane( mRangeTable );
add( scrollPane, "span,grow,push");
- JButton btnNew = new JButton( "New Range" );
- btnNew.addActionListener( ChannelMapEditor.this );
- add( btnNew );
-
- mDelete = new JButton( "Delete" );
- mDelete.addActionListener( ChannelMapEditor.this );
- add( mDelete, "wrap" );
-
- JButton btnSave = new JButton( "Save" );
- btnSave.addActionListener( ChannelMapEditor.this );
- add( btnSave, "growx" );
-
- JButton btnReset = new JButton( "Reset " );
- btnReset.addActionListener( ChannelMapEditor.this );
- add( btnReset, "wrap" );
- }
-
- private boolean checkForValidRanges()
- {
- int invalidRow = mChannelMapCopy.getInvalidRange();
-
- if( invalidRow != -1 && invalidRow <= mRangeTable.getRowCount() )
- {
- mRangeTable.setRowSelectionInterval( invalidRow, invalidRow );
-
- JOptionPane.showMessageDialog( this,
- "Starting channel number must be less than ending channel number",
- "Channel number range error",
- JOptionPane.WARNING_MESSAGE );
-
- return false;
- }
+ mRangeDescription = new JLabel( " " );
+ add( mRangeDescription, "span" );
- return true;
+ mSaveButton = new JButton( "Save" );
+ mSaveButton.setEnabled( false );
+ mSaveButton.addActionListener( new ActionListener()
+ {
+ @Override
+ public void actionPerformed( ActionEvent e )
+ {
+ save();
+ }
+ } );
+ add( mSaveButton );
+
+ mNewButton = new JButton( "New Range" );
+ mNewButton.setEnabled( false );
+ mNewButton.addActionListener( new ActionListener()
+ {
+ @Override
+ public void actionPerformed( ActionEvent e )
+ {
+ mRangeModel.addRange( new ChannelRange() );
+ }
+ } );
+ add( mNewButton );
+
+ mDeleteButton = new JButton( "DeleteRange" );
+ mDeleteButton.setEnabled( false );
+ mDeleteButton.addActionListener( new ActionListener()
+ {
+ @Override
+ public void actionPerformed( ActionEvent e )
+ {
+ int selectedRow = mRangeTable.getSelectedRow();
+
+ if( selectedRow >= 0 )
+ {
+ int modelRow = mRangeTable.convertRowIndexToModel( selectedRow );
+
+ ChannelRange selectedRange = mRangeModel.getChannelRange( modelRow );
+
+ if( selectedRange != null )
+ {
+ int choice = JOptionPane.showConfirmDialog( mRangeTable,
+ "Do you want to delete this channel range?",
+ "Delete Channel Range?", JOptionPane.YES_NO_OPTION );
+
+ if( choice == JOptionPane.YES_OPTION )
+ {
+ mRangeModel.removeRange( selectedRange );
+ mDeleteButton.setEnabled( false );
+ }
+ }
+ }
+ }
+ } );
+ add( mDeleteButton, "wrap" );
}
@Override
- public void actionPerformed( ActionEvent e )
- {
- String command = e.getActionCommand();
-
- switch( command )
+ public void save()
+ {
+ if( isModified() && hasItem() )
{
- case "New Range":
- mRangeModel.addRange( new ChannelRange() );
- break;
- case "Save":
- if( checkForValidRanges() )
+ ChannelMap channelMap = getChannelMap();
+
+ channelMap.setName( mNameText.getText() );
+ channelMap.setRanges( mRangeModel.getChannelRanges() );
+
+ mChannelMapModel.broadcast( new ChannelMapEvent( getChannelMap(), Event.CHANGE ) );
+ setModified( false );
+ }
+ }
+
+ @Override
+ public void valueChanged( ListSelectionEvent e )
+ {
+ if( !e.getValueIsAdjusting() )
+ {
+ if( e.getSource() instanceof JList )
+ {
+ JList> list = (JList>)e.getSource();
+
+ if( list.getSelectedValue() instanceof ChannelMap )
{
- mChannelMapNode.getChannelMap()
- .setName( mChannelMapCopy.getName() );
- mChannelMapNode.getChannelMap()
- .setRanges( mChannelMapCopy.getRanges() );
-
- mChannelMapNode.save();
-
- mChannelMapNode.refresh();
-
- mChannelMapNode.show();
- }
- case "Reset":
- mChannelMapCopy = mChannelMapNode.getChannelMap();
- mRangeModel.setChannelMap( mChannelMapCopy );
- mNameText.setText( mChannelMapCopy.getName() );
- break;
- case "Delete":
- int row = mRangeTable.getSelectedRow();
-
- if( row < 0 )
- {
- JOptionPane.showMessageDialog( this,
- "Please select a channel range to delete",
- "Select a range",
- JOptionPane.WARNING_MESSAGE );
+ setItem( (ChannelMap)list.getSelectedValue() );
}
else
{
- mRangeModel.removeRange(
- mRangeTable.convertRowIndexToModel( row ) );
+ setItem( null );
}
- break;
+ }
}
- }
+ }
+
+ @Override
+ public void channelRangesChanged()
+ {
+ setModified( true );
+
+ //Update description
+ int index = mRangeTable.getSelectedRow();
+
+ if( index >= 0 )
+ {
+ ChannelRange selected = mRangeModel.getChannelRange(
+ mRangeTable.convertRowIndexToModel( index ) );
+
+ if( selected != null )
+ {
+ mRangeDescription.setText( selected.getDescription() );
+ }
+ else
+ {
+ mRangeDescription.setText( "" );
+ }
+ }
+ }
public class RangeTableEditor extends AbstractCellEditor
- implements TableCellEditor, ActionListener
+ implements TableCellEditor, ActionListener
{
+ private static final long serialVersionUID = 1L;
+
JFormattedTextField mEditor;
-
+
public RangeTableEditor()
{
InternationalFormatter nf = new InternationalFormatter();
nf.setMinimum( 1 );
- nf.setAllowsInvalid( false );
+ nf.setAllowsInvalid( true );
mEditor = new JFormattedTextField( nf );
}
@Override
- public Object getCellEditorValue()
- {
- return mEditor.getText();
- }
-
+ public Object getCellEditorValue()
+ {
+ return mEditor.getText();
+ }
+
@Override
- public void actionPerformed( ActionEvent e )
- {
- }
-
+ public void actionPerformed( ActionEvent e )
+ {
+ }
+
@Override
- public Component getTableCellEditorComponent( JTable table,
- Object value,
- boolean isSelected,
- int row,
- int column )
- {
+ public Component getTableCellEditorComponent( JTable table,
+ Object value,
+ boolean isSelected,
+ int row,
+ int column )
+ {
mEditor.setValue( value );
return mEditor;
- }
- }
+ }
+ }
+
}
diff --git a/src/controller/channel/map/ChannelMapEvent.java b/src/controller/channel/map/ChannelMapEvent.java
new file mode 100644
index 00000000..a3ca8821
--- /dev/null
+++ b/src/controller/channel/map/ChannelMapEvent.java
@@ -0,0 +1,76 @@
+/*******************************************************************************
+ * 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
+ ******************************************************************************/
+package controller.channel.map;
+
+public class ChannelMapEvent
+{
+ private ChannelMap mChannelMap;
+ private Event mEvent;
+ private String mPreviousChannelMapName;
+
+ /**
+ * ChannelMapEvent - event describing any changes to a channel map
+ *
+ * @param channelMap that changed
+ * @param event describing the change
+ */
+ public ChannelMapEvent( ChannelMap channelMap, Event event )
+ {
+ mChannelMap = channelMap;
+ mEvent = event;
+ }
+
+ /**
+ * ChannelMapEvent - channel map renaming event.
+ *
+ * @param channelMap that changed
+ * @param previousName of the channel map
+ */
+ public ChannelMapEvent( ChannelMap channelMap, String previousName )
+ {
+ this( channelMap, Event.RENAME );
+
+ mPreviousChannelMapName = previousName;
+ }
+
+ public ChannelMap getChannelMap()
+ {
+ return mChannelMap;
+ }
+
+ public String getPreviousChannelMapName()
+ {
+ return mPreviousChannelMapName;
+ }
+
+ public Event getEvent()
+ {
+ return mEvent;
+ }
+
+ /**
+ * Events
+ */
+ public enum Event
+ {
+ ADD,
+ CHANGE,
+ RENAME,
+ DELETE;
+ }
+}
diff --git a/src/controller/channel/map/ChannelMapListNode.java b/src/controller/channel/map/ChannelMapListNode.java
deleted file mode 100644
index 208e22a4..00000000
--- a/src/controller/channel/map/ChannelMapListNode.java
+++ /dev/null
@@ -1,87 +0,0 @@
-/*******************************************************************************
- * SDR Trunk
- * Copyright (C) 2014 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
- ******************************************************************************/
-package controller.channel.map;
-
-import java.awt.event.ActionEvent;
-import java.awt.event.ActionListener;
-
-import javax.swing.JMenuItem;
-import javax.swing.JPopupMenu;
-
-import controller.ConfigurableNode;
-
-public class ChannelMapListNode extends ConfigurableNode
-{
- private static final long serialVersionUID = 1L;
-
- public ChannelMapListNode( ChannelMapList list )
- {
- super( list );
- }
-
- public ChannelMapList getChannelMapList()
- {
- return (ChannelMapList)getUserObject();
- }
-
- public void init()
- {
- for( ChannelMap channelMap: getChannelMapList().getChannelMap() )
- {
- ChannelMapNode node = new ChannelMapNode( channelMap );
-
- getModel().addNode( node, ChannelMapListNode.this, getChildCount() );
-
- node.init();
- }
-
- sort();
- }
-
- public String toString()
- {
- return "Channel Maps [" + this.getChildCount() + "]";
- }
-
- public JPopupMenu getContextMenu()
- {
- JPopupMenu retVal = new JPopupMenu();
-
- JMenuItem addSystemItem = new JMenuItem( "Add Channel Map" );
- addSystemItem.addActionListener( new ActionListener()
- {
- @Override
- public void actionPerformed( ActionEvent e )
- {
- ChannelMap channelMap = new ChannelMap();
-
- getChannelMapList().addChannelMap( channelMap );
-
- getModel().addNode( new ChannelMapNode( channelMap ),
- ChannelMapListNode.this,
- ChannelMapListNode.this.getChildCount() );
-
- save();
- }
- } );
-
- retVal.add( addSystemItem );
-
- return retVal;
- }
-}
diff --git a/src/controller/channel/map/ChannelMapManager.java b/src/controller/channel/map/ChannelMapManager.java
new file mode 100644
index 00000000..78e0608e
--- /dev/null
+++ b/src/controller/channel/map/ChannelMapManager.java
@@ -0,0 +1,135 @@
+package controller.channel.map;
+
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+
+import javax.swing.JButton;
+import javax.swing.JList;
+import javax.swing.JOptionPane;
+import javax.swing.JPanel;
+import javax.swing.JScrollPane;
+import javax.swing.ListSelectionModel;
+import javax.swing.event.ListSelectionEvent;
+import javax.swing.event.ListSelectionListener;
+
+import net.miginfocom.swing.MigLayout;
+
+import com.jidesoft.swing.JideSplitPane;
+
+public class ChannelMapManager extends JPanel
+{
+ private static final long serialVersionUID = 1L;
+
+ private ChannelMapModel mChannelMapModel;
+ private JList mChannelMapList;
+ private ChannelMapEditor mChannelMapEditor;
+ private JButton mDeleteMapButton;
+ private JButton mCloneMapButton;
+
+ public ChannelMapManager( ChannelMapModel channelMapModel )
+ {
+ mChannelMapModel = channelMapModel;
+ mChannelMapList = new JList<>( mChannelMapModel );
+ mChannelMapEditor = new ChannelMapEditor( mChannelMapModel );
+
+ init();
+ }
+
+ private void init()
+ {
+ setLayout( new MigLayout( "insets 1 1 1 1", "[fill,grow]", "[fill,grow]" ) );
+
+ JPanel listPanel = new JPanel();
+ listPanel.setLayout( new MigLayout( "",
+ "[fill,grow][fill,grow][fill,grow]", "[fill,grow][]" ) );
+
+ mChannelMapList.addListSelectionListener( mChannelMapEditor );
+ mChannelMapList.addListSelectionListener( new ListSelectionListener()
+ {
+ @Override
+ public void valueChanged( ListSelectionEvent e )
+ {
+ if( mChannelMapList.getSelectedValue() != null )
+ {
+ mCloneMapButton.setEnabled( true );
+ mDeleteMapButton.setEnabled( true );
+ }
+ else
+ {
+ mCloneMapButton.setEnabled( false );
+ mDeleteMapButton.setEnabled( false );
+ }
+ }
+ } );
+ mChannelMapList.setLayoutOrientation( JList.VERTICAL );
+ mChannelMapList.setSelectionMode( ListSelectionModel.SINGLE_SELECTION );
+
+ JScrollPane listScroller = new JScrollPane( mChannelMapList );
+
+ listPanel.add( listScroller, "span" );
+
+ JButton newMapButton = new JButton( "New" );
+ newMapButton.addActionListener( new ActionListener()
+ {
+ @Override
+ public void actionPerformed( ActionEvent e )
+ {
+ ChannelMap channelMap = new ChannelMap( "New Channel Map" );
+ mChannelMapModel.addChannelMap( channelMap );
+ mChannelMapList.setSelectedValue( channelMap, true );
+ }
+ } );
+
+ listPanel.add( newMapButton );
+
+ mCloneMapButton = new JButton( "Clone" );
+ mCloneMapButton.addActionListener( new ActionListener()
+ {
+ @Override
+ public void actionPerformed( ActionEvent e )
+ {
+ ChannelMap selected = mChannelMapList.getSelectedValue();
+
+ if( selected != null )
+ {
+ ChannelMap copy = selected.copyOf();
+ mChannelMapModel.addChannelMap( copy );
+ mChannelMapList.setSelectedValue( copy, true );
+ }
+ }
+ } );
+ listPanel.add( mCloneMapButton );
+
+ mDeleteMapButton = new JButton( "Delete" );
+ mDeleteMapButton.addActionListener( new ActionListener()
+ {
+ @Override
+ public void actionPerformed( ActionEvent e )
+ {
+ ChannelMap channelMap = mChannelMapList.getSelectedValue();
+
+ if( channelMap != null )
+ {
+ int choice = JOptionPane.showConfirmDialog( mChannelMapList,
+ "Do you want to delete this channel map?",
+ "Delete Channel Map?", JOptionPane.YES_NO_OPTION );
+
+ if( choice == JOptionPane.YES_OPTION )
+ {
+ mChannelMapModel.removeChannelMap( channelMap );
+ }
+ }
+ }
+ } );
+ listPanel.add( mDeleteMapButton );
+
+ JScrollPane editorScroller = new JScrollPane( mChannelMapEditor );
+
+ JideSplitPane splitPane = new JideSplitPane();
+ splitPane.setOrientation( JideSplitPane.HORIZONTAL_SPLIT );
+ splitPane.add( listPanel );
+ splitPane.add( editorScroller );
+
+ add( splitPane );
+ }
+}
diff --git a/src/controller/channel/map/ChannelMapManagerFrame.java b/src/controller/channel/map/ChannelMapManagerFrame.java
new file mode 100644
index 00000000..04812979
--- /dev/null
+++ b/src/controller/channel/map/ChannelMapManagerFrame.java
@@ -0,0 +1,61 @@
+/*******************************************************************************
+ * 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
+ ******************************************************************************/
+package controller.channel.map;
+
+import java.awt.EventQueue;
+
+import javax.swing.JFrame;
+
+import net.miginfocom.swing.MigLayout;
+
+public class ChannelMapManagerFrame extends JFrame
+{
+ private static final long serialVersionUID = 1L;
+
+ public ChannelMapManagerFrame( ChannelMapModel channelMapModel )
+ {
+ init( new ChannelMapManager( channelMapModel ) );
+ }
+
+ private void init( ChannelMapManager channelMapManager )
+ {
+ setTitle( "Channel Map Manager" );
+ setSize( 800, 400 );
+ setDefaultCloseOperation( JFrame.DISPOSE_ON_CLOSE );
+ setLayout( new MigLayout( "", "[grow,fill]", "[grow,fill]" ) );
+ add( channelMapManager );
+ setLocationRelativeTo( null );
+ }
+
+ public static void main( String[] args )
+ {
+ ChannelMapModel channelMapModel = new ChannelMapModel();
+
+ final ChannelMapManagerFrame frame =
+ new ChannelMapManagerFrame( channelMapModel );
+
+ EventQueue.invokeLater( new Runnable()
+ {
+ @Override
+ public void run()
+ {
+ frame.setVisible( true );
+ }
+ } );
+ }
+}
diff --git a/src/controller/channel/map/ChannelMapModel.java b/src/controller/channel/map/ChannelMapModel.java
index 50da7dfe..e0b486d4 100644
--- a/src/controller/channel/map/ChannelMapModel.java
+++ b/src/controller/channel/map/ChannelMapModel.java
@@ -1,171 +1,155 @@
-/*******************************************************************************
- * SDR Trunk
- * Copyright (C) 2014 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
- ******************************************************************************/
package controller.channel.map;
-import javax.swing.table.AbstractTableModel;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
+import javax.swing.AbstractListModel;
-public class ChannelMapModel extends AbstractTableModel
+import controller.channel.map.ChannelMapEvent.Event;
+import sample.Broadcaster;
+import sample.Listener;
+
+public class ChannelMapModel extends AbstractListModel
{
- private static final long serialVersionUID = 1L;
- private final static Logger mLog =
- LoggerFactory.getLogger( ChannelMapModel.class );
+ private static final long serialVersionUID = 1L;
+
+ private List