mirror of
https://github.com/DSheirer/sdrtrunk.git
synced 2026-11-12 16:47:31 -05:00
Merges #34 Major redesign of Tuner, Channel and Alias management.
Changes to model, view and controller components and updates all gui editors. Removes the configuration panel and tree and replaces with 3 new panels. Updates playlist.xml to a new playlist_v2.xml format.
This commit is contained in:
File diff suppressed because it is too large
Load Diff
279
src/controller/channel/ChannelController.java
Normal file
279
src/controller/channel/ChannelController.java
Normal file
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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<Channel> 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<AliasList> 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<AliasList>();
|
||||
|
||||
List<AliasList> lists = mChannelNode.getModel().getResourceManager()
|
||||
.getPlaylistManager().getPlayist().getAliasDirectory().getAliasList();
|
||||
|
||||
Collections.sort( lists );
|
||||
|
||||
mComboAliasLists.setModel( new DefaultComboBoxModel<AliasList>(
|
||||
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 );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 <http://www.gnu.org/licenses/>
|
||||
******************************************************************************/
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 <http://www.gnu.org/licenses/>
|
||||
******************************************************************************/
|
||||
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<Channel> mChannels = new ArrayList<>();
|
||||
private List<ChannelEventListener> mChannelListeners = new ArrayList<>();
|
||||
private List<Listener<Message>> 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<Channel> 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<ChannelEventListener> 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<Listener<Message>> getMessageListeners()
|
||||
{
|
||||
return mMessageListeners;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a message listener that will be added to all channels to receive
|
||||
* any messages.
|
||||
*/
|
||||
public void addListener( Listener<Message> listener )
|
||||
{
|
||||
mMessageListeners.add( listener );
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a message listener.
|
||||
*/
|
||||
public void removeListener( Listener<Message> listener )
|
||||
{
|
||||
mMessageListeners.remove( listener );
|
||||
}
|
||||
}
|
||||
343
src/controller/channel/ChannelModel.java
Normal file
343
src/controller/channel/ChannelModel.java
Normal file
@@ -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 <http://www.gnu.org/licenses/>
|
||||
******************************************************************************/
|
||||
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<Channel> mChannels = new CopyOnWriteArrayList<>();
|
||||
private List<Channel> mTrafficChannels = new CopyOnWriteArrayList<>();
|
||||
private List<ChannelEventListener> mListeners = new CopyOnWriteArrayList<>();
|
||||
|
||||
public ChannelModel()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Unmodifiable list of non-traffic channels currently in the model
|
||||
*/
|
||||
public List<Channel> 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<String> getSystems()
|
||||
{
|
||||
List<String> 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<String> getSites( String system )
|
||||
{
|
||||
List<String> 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<Channel> 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<Channel> getChannelsInFrequencyRange( long start, long stop )
|
||||
{
|
||||
List<Channel> 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" );
|
||||
}
|
||||
}
|
||||
@@ -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 <http://www.gnu.org/licenses/>
|
||||
******************************************************************************/
|
||||
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 );
|
||||
}
|
||||
}
|
||||
334
src/controller/channel/ChannelProcessingManager.java
Normal file
334
src/controller/channel/ChannelProcessingManager.java
Normal file
@@ -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 <http://www.gnu.org/licenses/>
|
||||
******************************************************************************/
|
||||
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<Integer,ProcessingChain> mProcessingChains = new HashMap<>();
|
||||
|
||||
private List<Listener<AudioPacket>> mAudioPacketListeners = new CopyOnWriteArrayList<>();
|
||||
private List<Listener<Message>> 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<AudioPacket> listener: mAudioPacketListeners )
|
||||
{
|
||||
processingChain.addAudioPacketListener( listener );
|
||||
}
|
||||
|
||||
for( Listener<Message> listener: mMessageListeners )
|
||||
{
|
||||
processingChain.addMessageListener( listener );
|
||||
}
|
||||
|
||||
/* Processing Modules */
|
||||
List<Module> modules = DecoderFactory.getModules( mChannelModel,
|
||||
mChannelMapModel, this, mAliasModel, channel );
|
||||
processingChain.addModules( modules );
|
||||
|
||||
/* Setup message activity model with filtering */
|
||||
FilterSet<Message> messageFilter = DecoderFactory.getMessageFilters( modules );
|
||||
MessageActivityModel messageModel = new MessageActivityModel( messageFilter );
|
||||
processingChain.setMessageActivityModel( messageModel );
|
||||
|
||||
/* Setup event logging */
|
||||
List<Module> loggers = mEventLogManager.getLoggers(
|
||||
channel.getEventLogConfiguration(), channel.getName() );
|
||||
|
||||
if( !loggers.isEmpty() )
|
||||
{
|
||||
processingChain.addModules( loggers );
|
||||
}
|
||||
|
||||
/* Setup recorders */
|
||||
List<RecorderType> 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<AudioPacket> listener )
|
||||
{
|
||||
mAudioPacketListeners.add( listener );
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a message listener.
|
||||
*/
|
||||
public void removeAudioPacketListener( Listener<AudioPacket> listener )
|
||||
{
|
||||
mAudioPacketListeners.remove( listener );
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a message listener that will be added to all channels to receive
|
||||
* any messages.
|
||||
*/
|
||||
public void addMessageListener( Listener<Message> listener )
|
||||
{
|
||||
mMessageListeners.add( listener );
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a message listener.
|
||||
*/
|
||||
public void removeMessageListener( Listener<Message> listener )
|
||||
{
|
||||
mMessageListeners.remove( listener );
|
||||
}
|
||||
}
|
||||
71
src/controller/channel/ChannelSelectionManager.java
Normal file
71
src/controller/channel/ChannelSelectionManager.java
Normal file
@@ -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 <http://www.gnu.org/licenses/>
|
||||
******************************************************************************/
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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 ) );
|
||||
}
|
||||
}
|
||||
} );
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
package controller.channel;
|
||||
|
||||
public class ChannelValidationException extends Exception
|
||||
{
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public ChannelValidationException( String validationMessage )
|
||||
{
|
||||
super( validationMessage );
|
||||
}
|
||||
}
|
||||
23
src/controller/channel/ConfigurationValidationException.java
Normal file
23
src/controller/channel/ConfigurationValidationException.java
Normal file
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
@@ -17,26 +17,13 @@
|
||||
******************************************************************************/
|
||||
package controller.channel;
|
||||
|
||||
import controller.Editor;
|
||||
|
||||
public abstract class AbstractChannelEditor extends Editor
|
||||
/**
|
||||
* Provides channel event broadcasting capability.
|
||||
*/
|
||||
public interface IChannelEventBroadcaster
|
||||
{
|
||||
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();
|
||||
/**
|
||||
* Broadcasts the channel event
|
||||
*/
|
||||
public void broadcast( ChannelEvent event );
|
||||
}
|
||||
269
src/controller/channel/NameConfigurationEditor.java
Normal file
269
src/controller/channel/NameConfigurationEditor.java
Normal file
@@ -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 <http://www.gnu.org/licenses/>
|
||||
******************************************************************************/
|
||||
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<Channel> implements DocumentListener
|
||||
{
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private final static Logger mLog = LoggerFactory.getLogger( NameConfigurationEditor.class );
|
||||
|
||||
private static ComboBoxModel<String> EMPTY_MODEL = new DefaultComboBoxModel<>();
|
||||
|
||||
private AliasModel mAliasModel;
|
||||
private ChannelModel mChannelModel;
|
||||
|
||||
private JTextField mChannelName;
|
||||
private JComboBox<String> mSystemNameCombo;
|
||||
private JComboBox<String> mSiteNameCombo;
|
||||
private JComboBox<String> 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<String> 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<String>(
|
||||
sites.toArray( new String[ sites.size() ] ) ) );;
|
||||
}
|
||||
|
||||
mSiteNameCombo.setSelectedItem( getItem().getSite() );
|
||||
}
|
||||
|
||||
setModified( true );
|
||||
}
|
||||
} );
|
||||
|
||||
add( new JLabel( "System:" ) );
|
||||
add( mSystemNameCombo );
|
||||
|
||||
mAliasListCombo = new JComboBox<String>();
|
||||
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<String> systems = mChannelModel.getSystems();
|
||||
|
||||
if( systems.isEmpty() )
|
||||
{
|
||||
mSystemNameCombo.setModel( EMPTY_MODEL );
|
||||
}
|
||||
else
|
||||
{
|
||||
mSystemNameCombo.setModel( new DefaultComboBoxModel<String>(
|
||||
systems.toArray( new String[ systems.size() ] ) ) );;
|
||||
}
|
||||
|
||||
mSystemNameCombo.setSelectedItem( channel.getSystem() );
|
||||
|
||||
List<String> sites = mChannelModel.getSites( channel.getSystem() );
|
||||
|
||||
if( sites.isEmpty() )
|
||||
{
|
||||
mSiteNameCombo.setModel( EMPTY_MODEL );
|
||||
}
|
||||
else
|
||||
{
|
||||
mSiteNameCombo.setModel( new DefaultComboBoxModel<String>(
|
||||
sites.toArray( new String[ sites.size() ] ) ) );;
|
||||
}
|
||||
|
||||
if( channel.getSite() != null )
|
||||
{
|
||||
mSiteNameCombo.setSelectedItem( channel.getSite() );
|
||||
}
|
||||
|
||||
List<String> lists = mAliasModel.getListNames();
|
||||
Collections.sort( lists );
|
||||
mAliasListCombo.setModel( new DefaultComboBoxModel<String>(
|
||||
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 );
|
||||
}
|
||||
}
|
||||
34
src/controller/channel/TrafficChannelEvent.java
Normal file
34
src/controller/channel/TrafficChannelEvent.java
Normal file
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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<ChannelRange> mRanges = new ArrayList<ChannelRange>();
|
||||
private List<ChannelRange> mRanges = new ArrayList<ChannelRange>();
|
||||
|
||||
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<ChannelRange> getRanges()
|
||||
@XmlElement( name = "range" )
|
||||
public List<ChannelRange> getRanges()
|
||||
{
|
||||
return mRanges;
|
||||
}
|
||||
|
||||
@XmlElement( name = "range" )
|
||||
public void setRanges( ArrayList<ChannelRange> ranges )
|
||||
public void setRanges( List<ChannelRange> 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();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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<ChannelMap>
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
76
src/controller/channel/map/ChannelMapEvent.java
Normal file
76
src/controller/channel/map/ChannelMapEvent.java
Normal file
@@ -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 <http://www.gnu.org/licenses/>
|
||||
******************************************************************************/
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -1,76 +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 <http://www.gnu.org/licenses/>
|
||||
******************************************************************************/
|
||||
package controller.channel.map;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
import javax.xml.bind.annotation.XmlElement;
|
||||
import javax.xml.bind.annotation.XmlSeeAlso;
|
||||
|
||||
@XmlSeeAlso( ChannelMap.class )
|
||||
public class ChannelMapList
|
||||
{
|
||||
private ArrayList<ChannelMap> mChannelMap = new ArrayList<ChannelMap>();
|
||||
|
||||
public ChannelMapList()
|
||||
{
|
||||
/* Empty constructor for JAXB */
|
||||
}
|
||||
|
||||
@XmlElement( name = "channel_map" )
|
||||
public ArrayList<ChannelMap> getChannelMap()
|
||||
{
|
||||
return mChannelMap;
|
||||
}
|
||||
|
||||
public void setChannelMap( ArrayList<ChannelMap> ChannelMaps )
|
||||
{
|
||||
mChannelMap = ChannelMaps;
|
||||
}
|
||||
|
||||
public void addChannelMap( ChannelMap ChannelMap )
|
||||
{
|
||||
mChannelMap.add( ChannelMap );
|
||||
}
|
||||
|
||||
public void removeChannelMap( ChannelMap ChannelMap )
|
||||
{
|
||||
mChannelMap.remove( ChannelMap );
|
||||
}
|
||||
|
||||
public void clearChannelMaps()
|
||||
{
|
||||
mChannelMap.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the named channel map, or returns an empty map
|
||||
*/
|
||||
public ChannelMap getChannelMap( String name )
|
||||
{
|
||||
for( ChannelMap map: mChannelMap )
|
||||
{
|
||||
if( map.getName().equalsIgnoreCase( name ) )
|
||||
{
|
||||
return map;
|
||||
}
|
||||
}
|
||||
|
||||
return new ChannelMap();
|
||||
}
|
||||
}
|
||||
@@ -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 <http://www.gnu.org/licenses/>
|
||||
******************************************************************************/
|
||||
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;
|
||||
}
|
||||
}
|
||||
135
src/controller/channel/map/ChannelMapManager.java
Normal file
135
src/controller/channel/map/ChannelMapManager.java
Normal file
@@ -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<ChannelMap> 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 );
|
||||
}
|
||||
}
|
||||
61
src/controller/channel/map/ChannelMapManagerFrame.java
Normal file
61
src/controller/channel/map/ChannelMapManagerFrame.java
Normal file
@@ -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 <http://www.gnu.org/licenses/>
|
||||
******************************************************************************/
|
||||
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 );
|
||||
}
|
||||
} );
|
||||
}
|
||||
}
|
||||
@@ -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 <http://www.gnu.org/licenses/>
|
||||
******************************************************************************/
|
||||
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<ChannelMap>
|
||||
{
|
||||
private static final long serialVersionUID = 1L;
|
||||
private final static Logger mLog =
|
||||
LoggerFactory.getLogger( ChannelMapModel.class );
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private List<ChannelMap> mChannelMaps = new ArrayList<>();
|
||||
private Broadcaster<ChannelMapEvent> mEventBroadcaster = new Broadcaster<>();
|
||||
|
||||
private static final int sSTART = 0;
|
||||
private static final int sSTOP = 1;
|
||||
private static final int sBASE = 2;
|
||||
private static final int sSIZE = 3;
|
||||
public ChannelMapModel()
|
||||
{
|
||||
}
|
||||
|
||||
protected int[] mColumnWidths = { 110, 110, 110, -1 };
|
||||
/**
|
||||
* Returns an unmodifiable list of channel maps currently in the model
|
||||
*/
|
||||
public List<ChannelMap> getChannelMaps()
|
||||
{
|
||||
return Collections.unmodifiableList( mChannelMaps );
|
||||
}
|
||||
|
||||
protected String[] mHeaders = new String[] { "Begin",
|
||||
"End",
|
||||
"Base",
|
||||
"Size" };
|
||||
|
||||
private ChannelMap mChannelMap;
|
||||
|
||||
public ChannelMapModel( ChannelMap map )
|
||||
/**
|
||||
* Returns the channel map with a matching name or null
|
||||
*/
|
||||
public ChannelMap getChannelMap( String name )
|
||||
{
|
||||
mChannelMap = map;
|
||||
}
|
||||
|
||||
public ChannelMap getChannelMap()
|
||||
{
|
||||
return mChannelMap;
|
||||
}
|
||||
|
||||
public void setChannelMap( ChannelMap map )
|
||||
{
|
||||
mChannelMap = map;
|
||||
|
||||
fireTableDataChanged();
|
||||
}
|
||||
|
||||
public void addRange( ChannelRange range )
|
||||
{
|
||||
mChannelMap.addRange( range );
|
||||
|
||||
int index = mChannelMap.getRanges().size() - 1;
|
||||
|
||||
fireTableRowsInserted( index, index );
|
||||
}
|
||||
|
||||
public void removeRange( int rangeIndex )
|
||||
{
|
||||
if( rangeIndex < mChannelMap.getRanges().size() )
|
||||
for( ChannelMap channelMap: mChannelMaps )
|
||||
{
|
||||
mChannelMap.getRanges().remove( rangeIndex );
|
||||
}
|
||||
|
||||
fireTableRowsDeleted( rangeIndex, rangeIndex );
|
||||
}
|
||||
|
||||
public int[] getColumnWidths()
|
||||
{
|
||||
return mColumnWidths;
|
||||
}
|
||||
|
||||
public void setColumnWidths( int[] widths )
|
||||
{
|
||||
if( widths.length != 4 )
|
||||
{
|
||||
throw new IllegalArgumentException( "ChannelMapRangeModel - "
|
||||
+ "column widths array should have 5 elements" );
|
||||
}
|
||||
else
|
||||
{
|
||||
mColumnWidths = widths;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getRowCount()
|
||||
{
|
||||
return mChannelMap.getRanges().size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getColumnCount()
|
||||
{
|
||||
return mHeaders.length;
|
||||
}
|
||||
|
||||
public String getColumnName( int column )
|
||||
{
|
||||
return mHeaders[ column ];
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getValueAt( int rowIndex, int columnIndex )
|
||||
{
|
||||
ChannelRange range = mChannelMap.getRanges().get( rowIndex );
|
||||
|
||||
switch( columnIndex )
|
||||
{
|
||||
case sSTART:
|
||||
return range.getFirstChannelNumber();
|
||||
case sSTOP:
|
||||
return range.getLastChannelNumber();
|
||||
case sBASE:
|
||||
return range.getBase();
|
||||
case sSIZE:
|
||||
return range.getSize();
|
||||
if( channelMap.getName().equalsIgnoreCase( name ) )
|
||||
{
|
||||
return channelMap;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public boolean isCellEditable( int row, int column )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public void setValueAt( Object value, int row, int column )
|
||||
|
||||
/**
|
||||
* Adds a listener to receive notifications when channel map updates occur
|
||||
*/
|
||||
public void addListener( Listener<ChannelMapEvent> listener )
|
||||
{
|
||||
try
|
||||
mEventBroadcaster.addListener( listener );
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the channel map update notification listener
|
||||
*/
|
||||
public void removeListener( Listener<ChannelMapEvent> listener )
|
||||
{
|
||||
mEventBroadcaster.removeListener( listener );
|
||||
}
|
||||
|
||||
/**
|
||||
* Broadcasts a channel map change.
|
||||
*
|
||||
* Note: use the add/remove methods to add or remove channel maps from this
|
||||
* model. When using those methods, an add or delete event will automatically
|
||||
* be generated.
|
||||
*/
|
||||
public void broadcast( ChannelMapEvent event )
|
||||
{
|
||||
if( event.getEvent() == ChannelMapEvent.Event.CHANGE ||
|
||||
event.getEvent() == ChannelMapEvent.Event.RENAME )
|
||||
{
|
||||
int parsedValue = Integer.parseInt( (String)value );
|
||||
int index = mChannelMaps.indexOf( event.getChannelMap() );
|
||||
|
||||
ChannelRange range = mChannelMap.getRanges().get( row );
|
||||
|
||||
switch( column )
|
||||
if( index >= 0 )
|
||||
{
|
||||
case sSTART:
|
||||
range.setFirstChannelNumber( parsedValue );
|
||||
break;
|
||||
case sSTOP:
|
||||
range.setLastChannelNumber( parsedValue );
|
||||
break;
|
||||
case sBASE:
|
||||
range.setBase( parsedValue );
|
||||
break;
|
||||
case sSIZE:
|
||||
range.setSize( parsedValue );
|
||||
break;
|
||||
fireContentsChanged( this, index, index );
|
||||
}
|
||||
}
|
||||
catch( Exception e )
|
||||
|
||||
mEventBroadcaster.broadcast( event );
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a list of channel maps to this model
|
||||
*/
|
||||
public void addChannelMaps( List<ChannelMap> channelMaps )
|
||||
{
|
||||
for( ChannelMap channelMap: channelMaps )
|
||||
{
|
||||
mLog.error( "ChannelMapModel - couldn't parse integer value "
|
||||
+ "for [" + value + "]", e );
|
||||
addChannelMap( channelMap );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the channel map to this model
|
||||
*/
|
||||
public void addChannelMap( ChannelMap channelMap )
|
||||
{
|
||||
if( !mChannelMaps.contains( channelMap ) )
|
||||
{
|
||||
mChannelMaps.add( channelMap );
|
||||
|
||||
int index = mChannelMaps.indexOf( channelMap );
|
||||
|
||||
fireIntervalAdded( this, index, index );
|
||||
|
||||
broadcast( new ChannelMapEvent( channelMap, Event.ADD ) );
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the channel map from this model
|
||||
*/
|
||||
public void removeChannelMap( ChannelMap channelMap )
|
||||
{
|
||||
if( mChannelMaps.contains( channelMap ) )
|
||||
{
|
||||
int index = mChannelMaps.indexOf( channelMap );
|
||||
|
||||
mChannelMaps.remove( channelMap );
|
||||
|
||||
fireIntervalRemoved( this, index, index );
|
||||
|
||||
broadcast( new ChannelMapEvent( channelMap, Event.DELETE ) );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Size/Number of channel maps in this model
|
||||
*/
|
||||
@Override
|
||||
public int getSize()
|
||||
{
|
||||
return mChannelMaps.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the channel map at the specified index
|
||||
*/
|
||||
@Override
|
||||
public ChannelMap getElementAt( int index )
|
||||
{
|
||||
if( index <= mChannelMaps.size() )
|
||||
{
|
||||
return mChannelMaps.get( index );
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,102 +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 <http://www.gnu.org/licenses/>
|
||||
******************************************************************************/
|
||||
package controller.channel.map;
|
||||
|
||||
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.ActionListener;
|
||||
|
||||
import javax.swing.JMenuItem;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.JPopupMenu;
|
||||
|
||||
import controller.ConfigurableNode;
|
||||
|
||||
public class ChannelMapNode extends ConfigurableNode
|
||||
{
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public ChannelMapNode( ChannelMap ChannelMap )
|
||||
{
|
||||
super( ChannelMap );
|
||||
}
|
||||
|
||||
public void init()
|
||||
{
|
||||
//For consistency, but not used
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getIconPath()
|
||||
{
|
||||
return "images/ChannelMap.png";
|
||||
}
|
||||
|
||||
@Override
|
||||
public JPanel getEditor()
|
||||
{
|
||||
return new ChannelMapEditor( this );
|
||||
}
|
||||
|
||||
public ChannelMap getChannelMap()
|
||||
{
|
||||
return (ChannelMap)getUserObject();
|
||||
}
|
||||
|
||||
public String toString()
|
||||
{
|
||||
return getChannelMap().getName();
|
||||
}
|
||||
|
||||
public void delete()
|
||||
{
|
||||
ChannelMapListNode parent = (ChannelMapListNode)getParent();
|
||||
|
||||
parent.getChannelMapList().removeChannelMap( getChannelMap() );
|
||||
|
||||
save();
|
||||
|
||||
getModel().removeNodeFromParent( ChannelMapNode.this );
|
||||
|
||||
/* Show the first child, if one exists so that we don't have a
|
||||
* collapsed list */
|
||||
if( parent.getChildCount() > 0 )
|
||||
{
|
||||
((ConfigurableNode)parent.getFirstChild()).show();
|
||||
}
|
||||
}
|
||||
|
||||
public JPopupMenu getContextMenu()
|
||||
{
|
||||
JPopupMenu retVal = new JPopupMenu();
|
||||
|
||||
JMenuItem deleteItem = new JMenuItem( "Delete" );
|
||||
deleteItem.addActionListener( new ActionListener()
|
||||
{
|
||||
@Override
|
||||
public void actionPerformed( ActionEvent e )
|
||||
{
|
||||
delete();
|
||||
}
|
||||
} );
|
||||
|
||||
retVal.add( deleteItem );
|
||||
|
||||
return retVal;
|
||||
}
|
||||
}
|
||||
@@ -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,21 +17,29 @@
|
||||
******************************************************************************/
|
||||
package controller.channel.map;
|
||||
|
||||
import java.text.DecimalFormat;
|
||||
|
||||
import javax.xml.bind.annotation.XmlAttribute;
|
||||
import javax.xml.bind.annotation.XmlRootElement;
|
||||
import javax.xml.bind.annotation.XmlTransient;
|
||||
import javax.xml.bind.annotation.XmlType;
|
||||
|
||||
@XmlRootElement
|
||||
@XmlType( name = "range" )
|
||||
public class ChannelRange
|
||||
{
|
||||
private static final DecimalFormat FREQUENCY_FORMATTER = new DecimalFormat( "#.0000" );
|
||||
|
||||
private int mFirst = 1;
|
||||
private int mLast = 1023;
|
||||
private int mBase = 450000000;
|
||||
private int mSize = 12500;
|
||||
|
||||
private boolean mOverlapping = false;
|
||||
|
||||
/* JAXB requirement - do not remove */
|
||||
public ChannelRange() {}
|
||||
public ChannelRange()
|
||||
{
|
||||
}
|
||||
|
||||
public ChannelRange( int first, int last, int base, int size )
|
||||
{
|
||||
@@ -46,11 +54,67 @@ public class ChannelRange
|
||||
return new ChannelRange( mFirst, mLast, mBase, mSize );
|
||||
}
|
||||
|
||||
public String getDescription()
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
if( isValid() )
|
||||
{
|
||||
sb.append( "First: " );
|
||||
sb.append( mFirst + "=" );
|
||||
|
||||
long frequency = getFrequency( mFirst );
|
||||
|
||||
sb.append( FREQUENCY_FORMATTER.format( (double)frequency / 1E6D ) );
|
||||
sb.append( " Last: " );
|
||||
sb.append( mLast + "=" );
|
||||
|
||||
frequency = getFrequency( mLast );
|
||||
sb.append( FREQUENCY_FORMATTER.format( (double)frequency / 1E6D ) );
|
||||
sb.append( " MHz" );
|
||||
}
|
||||
else
|
||||
{
|
||||
sb.append( "First channel must be smaller than last channel" );
|
||||
}
|
||||
|
||||
if( isOverlapping() )
|
||||
{
|
||||
if( sb.length() > 0 )
|
||||
{
|
||||
sb.append( ", " );
|
||||
}
|
||||
|
||||
sb.append( "Overlap!" );
|
||||
}
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
public boolean isValid()
|
||||
{
|
||||
return mFirst < mLast;
|
||||
}
|
||||
|
||||
public boolean overlaps( ChannelRange other )
|
||||
{
|
||||
return ( mFirst <= other.mFirst && other.mFirst <= mLast ) ||
|
||||
( mFirst <= other.mLast && other.mLast <= mLast ) ||
|
||||
( mFirst <= other.mFirst && other.mLast <= mLast ) ||
|
||||
( other.mFirst <= mFirst && mLast <= other.mLast );
|
||||
}
|
||||
|
||||
@XmlTransient
|
||||
public boolean isOverlapping()
|
||||
{
|
||||
return mOverlapping;
|
||||
}
|
||||
|
||||
public void setOverlapping( boolean overlapping )
|
||||
{
|
||||
mOverlapping = overlapping;
|
||||
}
|
||||
|
||||
public boolean hasChannel( int channel )
|
||||
{
|
||||
return mFirst <= channel && channel <= mLast;
|
||||
|
||||
292
src/controller/channel/map/ChannelRangeModel.java
Normal file
292
src/controller/channel/map/ChannelRangeModel.java
Normal file
@@ -0,0 +1,292 @@
|
||||
/*******************************************************************************
|
||||
* SDR Trunk
|
||||
* Copyright (C) 2014-2016 Dennis Sheirer
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
******************************************************************************/
|
||||
package controller.channel.map;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
|
||||
import javax.swing.table.AbstractTableModel;
|
||||
|
||||
public class ChannelRangeModel extends AbstractTableModel
|
||||
{
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private static final int START = 0;
|
||||
private static final int STOP = 1;
|
||||
private static final int BASE = 2;
|
||||
private static final int SIZE = 3;
|
||||
private static final int ERROR = 4;
|
||||
|
||||
protected int[] mColumnWidths = { 110, 110, 110, -1, 80 };
|
||||
|
||||
protected String[] mHeaders = new String[] { "Begin",
|
||||
"End",
|
||||
"Base",
|
||||
"Size",
|
||||
"Error" };
|
||||
|
||||
private List<ChannelRange> mRanges = new CopyOnWriteArrayList<>();
|
||||
private ChannelRangeEventListener mListener;
|
||||
|
||||
public ChannelRangeModel()
|
||||
{
|
||||
}
|
||||
|
||||
public void setListener( ChannelRangeEventListener listener )
|
||||
{
|
||||
mListener = listener;
|
||||
}
|
||||
|
||||
private void broadcastChange()
|
||||
{
|
||||
if( mListener != null )
|
||||
{
|
||||
mListener.channelRangesChanged();
|
||||
}
|
||||
}
|
||||
|
||||
public void clear()
|
||||
{
|
||||
for( ChannelRange range: mRanges )
|
||||
{
|
||||
removeRange( range );
|
||||
}
|
||||
|
||||
broadcastChange();
|
||||
}
|
||||
|
||||
public List<ChannelRange> getChannelRanges()
|
||||
{
|
||||
return mRanges;
|
||||
}
|
||||
|
||||
public void addRanges( List<ChannelRange> ranges )
|
||||
{
|
||||
for( ChannelRange range: ranges )
|
||||
{
|
||||
addRange( range );
|
||||
}
|
||||
|
||||
broadcastChange();
|
||||
}
|
||||
|
||||
public void addRange( ChannelRange range )
|
||||
{
|
||||
if( range != null && !mRanges.contains( range ) )
|
||||
{
|
||||
mRanges.add( range );
|
||||
|
||||
int index = mRanges.indexOf( range );
|
||||
|
||||
fireTableRowsInserted( index, index );
|
||||
}
|
||||
|
||||
validate();
|
||||
|
||||
broadcastChange();
|
||||
}
|
||||
|
||||
public void removeRange( ChannelRange range )
|
||||
{
|
||||
if( range != null && mRanges.contains( range ) )
|
||||
{
|
||||
int index = mRanges.indexOf( range );
|
||||
|
||||
mRanges.remove( range );
|
||||
|
||||
fireTableRowsDeleted( index, index );
|
||||
}
|
||||
|
||||
validate();
|
||||
|
||||
broadcastChange();
|
||||
}
|
||||
|
||||
public int[] getColumnWidths()
|
||||
{
|
||||
return mColumnWidths;
|
||||
}
|
||||
|
||||
public void setColumnWidths( int[] widths )
|
||||
{
|
||||
if( widths.length != mHeaders.length )
|
||||
{
|
||||
throw new IllegalArgumentException( "ChannelMapRangeModel - "
|
||||
+ "column widths array should have " +
|
||||
mHeaders.length + " elements" );
|
||||
}
|
||||
else
|
||||
{
|
||||
mColumnWidths = widths;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getRowCount()
|
||||
{
|
||||
return mRanges.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getColumnCount()
|
||||
{
|
||||
return mHeaders.length;
|
||||
}
|
||||
|
||||
public String getColumnName( int column )
|
||||
{
|
||||
return mHeaders[ column ];
|
||||
}
|
||||
|
||||
public ChannelRange getChannelRange( int index )
|
||||
{
|
||||
if( index < mRanges.size() )
|
||||
{
|
||||
return mRanges.get( index );
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getValueAt( int rowIndex, int columnIndex )
|
||||
{
|
||||
ChannelRange range = getChannelRange( rowIndex );
|
||||
|
||||
if( range != null )
|
||||
{
|
||||
switch( columnIndex )
|
||||
{
|
||||
case START:
|
||||
return range.getFirstChannelNumber();
|
||||
case STOP:
|
||||
return range.getLastChannelNumber();
|
||||
case BASE:
|
||||
return range.getBase();
|
||||
case SIZE:
|
||||
return range.getSize();
|
||||
case ERROR:
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
if( !range.isValid() )
|
||||
{
|
||||
sb.append( "Sequence" );
|
||||
}
|
||||
|
||||
if( range.isOverlapping() )
|
||||
{
|
||||
if( sb.length() > 0 )
|
||||
{
|
||||
sb.append( "," );
|
||||
}
|
||||
|
||||
sb.append( "Overlap" );
|
||||
}
|
||||
|
||||
return sb.toString();
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates each of the channel ranges for overlap
|
||||
*/
|
||||
private void validate()
|
||||
{
|
||||
for( int x = 0; x < mRanges.size(); x++ )
|
||||
{
|
||||
mRanges.get( x ).setOverlapping( false );
|
||||
fireTableCellUpdated( x, ERROR );
|
||||
}
|
||||
|
||||
for( int x = 0; x < mRanges.size(); x++ )
|
||||
{
|
||||
for( int y = x + 1; y < mRanges.size(); y++ )
|
||||
{
|
||||
if( mRanges.get( x ).overlaps( mRanges.get( y ) ) )
|
||||
{
|
||||
mRanges.get( x ).setOverlapping( true );
|
||||
fireTableCellUpdated( x, ERROR );
|
||||
|
||||
mRanges.get( y ).setOverlapping( true );
|
||||
fireTableCellUpdated( y, ERROR );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isCellEditable( int row, int column )
|
||||
{
|
||||
return row < mRanges.size() && column != ERROR;
|
||||
}
|
||||
|
||||
public void setValueAt( Object value, int row, int column )
|
||||
{
|
||||
int parsedValue = -1;
|
||||
|
||||
try
|
||||
{
|
||||
parsedValue = Integer.parseInt( (String)value );
|
||||
}
|
||||
catch( Exception e )
|
||||
{
|
||||
//Do nothing ... we'll use 0
|
||||
}
|
||||
|
||||
if( parsedValue >= 0 )
|
||||
{
|
||||
ChannelRange range = getChannelRange( row );
|
||||
|
||||
if( range != null )
|
||||
{
|
||||
switch( column )
|
||||
{
|
||||
case START:
|
||||
range.setFirstChannelNumber( parsedValue );
|
||||
break;
|
||||
case STOP:
|
||||
range.setLastChannelNumber( parsedValue );
|
||||
break;
|
||||
case BASE:
|
||||
range.setBase( parsedValue );
|
||||
break;
|
||||
case SIZE:
|
||||
range.setSize( parsedValue );
|
||||
break;
|
||||
}
|
||||
|
||||
validate();
|
||||
|
||||
broadcastChange();
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new IllegalArgumentException( "Invalid channel range row index" );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public interface ChannelRangeEventListener
|
||||
{
|
||||
public void channelRangesChanged();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user