WIP #34 - new channel map manager/editor is complete. Channel map

manager is now a separate window launched from the decoder tab,
specifically the MPT1327 decoder tab.
This commit is contained in:
denny
2016-03-06 19:22:54 -05:00
parent 2394ac2fa9
commit b6e4c4e7aa
6 changed files with 619 additions and 201 deletions

View File

@@ -18,17 +18,20 @@
package controller.channel.map; package controller.channel.map;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List;
import javax.xml.bind.annotation.XmlAttribute; import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlSeeAlso; import javax.xml.bind.annotation.XmlSeeAlso;
import javax.xml.bind.annotation.XmlTransient;
@XmlSeeAlso( { ChannelRange.class } ) @XmlSeeAlso( { ChannelRange.class } )
public class ChannelMap public class ChannelMap
{ {
private String mName; private String mName;
private boolean mInvalid = false;
private ArrayList<ChannelRange> mRanges = new ArrayList<ChannelRange>(); private List<ChannelRange> mRanges = new ArrayList<ChannelRange>();
public ChannelMap() public ChannelMap()
{ {
@@ -40,16 +43,9 @@ public class ChannelMap
mName = name; mName = name;
} }
public String toString()
{
return mName;
}
/**
* Creates a copy of this channel map
*/
public ChannelMap copyOf() public ChannelMap copyOf()
{ {
ChannelMap map = new ChannelMap( mName ); ChannelMap map = new ChannelMap( new String( mName ) );
for( ChannelRange range: mRanges ) for( ChannelRange range: mRanges )
{ {
@@ -59,28 +55,33 @@ public class ChannelMap
return map; return map;
} }
public int getInvalidRange() @XmlTransient
public boolean isInvalid()
{ {
for( int x = 0; x < mRanges.size(); x++ ) return mInvalid;
{
if( !mRanges.get( x ).isValid() )
{
return x;
}
}
return -1;
} }
public ArrayList<ChannelRange> getRanges() public String toString()
{
return mName + ( mInvalid ? " - Error" : "" );
}
public List<ChannelRange> getRanges()
{ {
return mRanges; return mRanges;
} }
@XmlElement( name = "range" ) @XmlElement( name = "range" )
public void setRanges( ArrayList<ChannelRange> ranges ) public void setRanges( List<ChannelRange> ranges )
{ {
mRanges = ranges; mRanges.clear();
for( ChannelRange range: ranges )
{
mRanges.add( range );
}
validate();
} }
@XmlAttribute @XmlAttribute
@@ -107,14 +108,43 @@ public class ChannelMap
return 0; 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 ) public void addRange( ChannelRange range )
{ {
mRanges.add( range ); mRanges.add( range );
validate();
} }
public void deleteRange( ChannelRange range ) public void deleteRange( ChannelRange range )
{ {
mRanges.remove( range ); mRanges.remove( range );
validate();
} }
} }

View File

@@ -28,6 +28,8 @@ import javax.swing.AbstractCellEditor;
import javax.swing.JButton; import javax.swing.JButton;
import javax.swing.JFormattedTextField; import javax.swing.JFormattedTextField;
import javax.swing.JLabel; import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JOptionPane;
import javax.swing.JScrollPane; import javax.swing.JScrollPane;
import javax.swing.JTable; import javax.swing.JTable;
import javax.swing.JTextField; import javax.swing.JTextField;
@@ -41,8 +43,11 @@ import net.miginfocom.swing.MigLayout;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import controller.channel.map.ChannelMapEvent.Event;
import controller.channel.map.ChannelRangeModel.ChannelRangeEventListener;
public class ChannelMapEditor extends DocumentListenerEditor<ChannelMap> public class ChannelMapEditor extends DocumentListenerEditor<ChannelMap>
implements ListSelectionListener implements ListSelectionListener, ChannelRangeEventListener
{ {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
private final static Logger mLog = private final static Logger mLog =
@@ -52,14 +57,19 @@ public class ChannelMapEditor extends DocumentListenerEditor<ChannelMap>
private JTextField mNameText; private JTextField mNameText;
private ChannelRangeModel mRangeModel; private ChannelRangeModel mRangeModel;
private JTable mRangeTable; private JTable mRangeTable;
private JButton mDelete; private JButton mNewButton;
private JButton mDeleteButton;
private JButton mSaveButton;
private JLabel mRangeDescription;
private ChannelMapModel mChannelMapModel;
public ChannelMapEditor() public ChannelMapEditor( ChannelMapModel channelMapModel )
{ {
initGUI(); mChannelMapModel = channelMapModel;
init();
} }
private ChannelMap getChannelMap() public ChannelMap getChannelMap()
{ {
if( hasItem() ) if( hasItem() )
{ {
@@ -76,24 +86,49 @@ public class ChannelMapEditor extends DocumentListenerEditor<ChannelMap>
if( hasItem() ) 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 );
} }
private void initGUI() @Override
public void setModified( boolean modified )
{ {
setLayout( new MigLayout( "fill,wrap 2", "[right][grow]", "[][][grow][][]" ) ); super.setModified( modified );
add( new JLabel( "Channel Map" ), "span, align center" ); mSaveButton.setEnabled( modified );
}
mNameLabel = new JLabel( "Name:" ); private void init()
{
setLayout( new MigLayout( "fill,wrap 3",
"[grow,fill][grow,fill][grow,fill]", "[][grow][][]" ) );
mNameLabel = new JLabel( "Channel Map:" );
add( mNameLabel ); add( mNameLabel );
mNameText = new JTextField(); mNameText = new JTextField();
mNameText.getDocument().addDocumentListener( this ); mNameText.getDocument().addDocumentListener( this );
add( mNameText, "growx, push" ); add( mNameText, "span" );
mRangeModel = new ChannelRangeModel(); mRangeModel = new ChannelRangeModel();
mRangeModel.setListener( this );
mRangeTable = new JTable( mRangeModel ); mRangeTable = new JTable( mRangeModel );
mRangeTable.setPreferredScrollableViewportSize( new Dimension( 300, 150 ) ); mRangeTable.setPreferredScrollableViewportSize( new Dimension( 300, 150 ) );
@@ -105,142 +140,200 @@ public class ChannelMapEditor extends DocumentListenerEditor<ChannelMap>
.setCellEditor( new RangeTableEditor() ); .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 ); JScrollPane scrollPane = new JScrollPane( mRangeTable );
add( scrollPane, "span,grow,push"); add( scrollPane, "span,grow,push");
JButton btnNew = new JButton( "New Range" ); mRangeDescription = new JLabel( " " );
// btnNew.addActionListener( ChannelMapEditor.this ); add( mRangeDescription, "span" );
add( btnNew );
mDelete = new JButton( "Delete" ); mSaveButton = new JButton( "Save" );
// mDelete.addActionListener( ChannelMapEditor.this ); mSaveButton.setEnabled( false );
add( mDelete, "wrap" ); mSaveButton.addActionListener( new ActionListener()
{
@Override
public void actionPerformed( ActionEvent e )
{
save();
}
} );
add( mSaveButton );
JButton btnSave = new JButton( "Save" ); mNewButton = new JButton( "New Range" );
// btnSave.addActionListener( ChannelMapEditor.this ); mNewButton.setEnabled( false );
add( btnSave, "growx" ); mNewButton.addActionListener( new ActionListener()
{
@Override
public void actionPerformed( ActionEvent e )
{
mRangeModel.addRange( new ChannelRange() );
}
} );
add( mNewButton );
JButton btnReset = new JButton( "Reset " ); mDeleteButton = new JButton( "DeleteRange" );
// btnReset.addActionListener( ChannelMapEditor.this ); mDeleteButton.setEnabled( false );
add( btnReset, "wrap" ); 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" );
} }
private boolean checkForValidRanges() @Override
public void save()
{ {
mLog.error( "Fix Me!" ); if( isModified() && hasItem() )
// int invalidRow = mChannelMapCopy.getInvalidRange(); {
// ChannelMap channelMap = getChannelMap();
// 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;
// }
return true; channelMap.setName( mNameText.getText() );
channelMap.setRanges( mRangeModel.getChannelRanges() );
mChannelMapModel.broadcast( new ChannelMapEvent( getChannelMap(), Event.CHANGE ) );
setModified( false );
}
} }
// @Override @Override
// public void actionPerformed( ActionEvent e ) public void valueChanged( ListSelectionEvent e )
// { {
// String command = e.getActionCommand(); if( !e.getValueIsAdjusting() )
// {
// switch( command ) if( e.getSource() instanceof JList )
// { {
// case "New Range": JList<?> list = (JList<?>)e.getSource();
// mRangeModel.addRange( new ChannelRange() );
// break; if( list.getSelectedValue() instanceof ChannelMap )
// case "Save": {
// if( checkForValidRanges() ) setItem( (ChannelMap)list.getSelectedValue() );
// { }
// mChannelMapNode.getChannelMap() else
// .setName( mChannelMapCopy.getName() ); {
// mChannelMapNode.getChannelMap() setItem( null );
// .setRanges( mChannelMapCopy.getRanges() ); }
// }
// mChannelMapNode.save(); }
// }
// mChannelMapNode.refresh();
// @Override
// mChannelMapNode.show(); public void channelRangesChanged()
// } {
// case "Reset": setModified( true );
// mChannelMapCopy = mChannelMapNode.getChannelMap();
// mRangeModel.setChannelMap( mChannelMapCopy ); //Update description
// mNameText.setText( mChannelMapCopy.getName() ); int index = mRangeTable.getSelectedRow();
// break;
// case "Delete": if( index >= 0 )
// int row = mRangeTable.getSelectedRow(); {
// ChannelRange selected = mRangeModel.getChannelRange(
// if( row < 0 ) mRangeTable.convertRowIndexToModel( index ) );
// {
// JOptionPane.showMessageDialog( this, if( selected != null )
// "Please select a channel range to delete", {
// "Select a range", mRangeDescription.setText( selected.getDescription() );
// JOptionPane.WARNING_MESSAGE ); }
// } else
// else {
// { mRangeDescription.setText( "" );
// mRangeModel.removeRange( }
// mRangeTable.convertRowIndexToModel( row ) ); }
// } }
// break;
// }
// }
public class RangeTableEditor extends AbstractCellEditor public class RangeTableEditor extends AbstractCellEditor
implements TableCellEditor, ActionListener implements TableCellEditor, ActionListener
{ {
private static final long serialVersionUID = 1L;
JFormattedTextField mEditor; JFormattedTextField mEditor;
public RangeTableEditor() public RangeTableEditor()
{ {
InternationalFormatter nf = new InternationalFormatter(); InternationalFormatter nf = new InternationalFormatter();
nf.setMinimum( 1 ); nf.setMinimum( 1 );
nf.setAllowsInvalid( false ); nf.setAllowsInvalid( true );
mEditor = new JFormattedTextField( nf ); mEditor = new JFormattedTextField( nf );
} }
@Override @Override
public Object getCellEditorValue() public Object getCellEditorValue()
{ {
return mEditor.getText(); return mEditor.getText();
} }
@Override @Override
public void actionPerformed( ActionEvent e ) public void actionPerformed( ActionEvent e )
{ {
} }
@Override @Override
public Component getTableCellEditorComponent( JTable table, public Component getTableCellEditorComponent( JTable table,
Object value, Object value,
boolean isSelected, boolean isSelected,
int row, int row,
int column ) int column )
{ {
mEditor.setValue( value ); mEditor.setValue( value );
return mEditor; return mEditor;
} }
}
@Override
public void save()
{
// TODO Auto-generated method stub
} }
@Override
public void valueChanged( ListSelectionEvent e )
{
mLog.debug( "List Select Event from: " + e.getSource().getClass() );
}
} }

View File

@@ -1,9 +1,16 @@
package controller.channel.map; 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.JList;
import javax.swing.JOptionPane;
import javax.swing.JPanel; import javax.swing.JPanel;
import javax.swing.JScrollPane; import javax.swing.JScrollPane;
import javax.swing.ListSelectionModel; import javax.swing.ListSelectionModel;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import net.miginfocom.swing.MigLayout; import net.miginfocom.swing.MigLayout;
@@ -16,30 +23,111 @@ public class ChannelMapManager extends JPanel
private ChannelMapModel mChannelMapModel; private ChannelMapModel mChannelMapModel;
private JList<ChannelMap> mChannelMapList; private JList<ChannelMap> mChannelMapList;
private ChannelMapEditor mChannelMapEditor; private ChannelMapEditor mChannelMapEditor;
private JButton mDeleteMapButton;
private JButton mCloneMapButton;
public ChannelMapManager( ChannelMapModel channelMapModel ) public ChannelMapManager( ChannelMapModel channelMapModel )
{ {
mChannelMapModel = channelMapModel; mChannelMapModel = channelMapModel;
mChannelMapList = new JList( mChannelMapModel ); mChannelMapList = new JList<>( mChannelMapModel );
mChannelMapEditor = new ChannelMapEditor(); mChannelMapEditor = new ChannelMapEditor( mChannelMapModel );
init(); init();
} }
private void init() private void init()
{ {
setLayout( new MigLayout( "", "[fill,grow]", "[fill,grow]" ) ); 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( 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.setLayoutOrientation( JList.VERTICAL );
mChannelMapList.setSelectionMode( ListSelectionModel.SINGLE_SELECTION ); mChannelMapList.setSelectionMode( ListSelectionModel.SINGLE_SELECTION );
JScrollPane listScroller = new JScrollPane( mChannelMapList ); 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 ); JScrollPane editorScroller = new JScrollPane( mChannelMapEditor );
JideSplitPane splitPane = new JideSplitPane(); JideSplitPane splitPane = new JideSplitPane();
splitPane.setOrientation( JideSplitPane.HORIZONTAL_SPLIT ); splitPane.setOrientation( JideSplitPane.HORIZONTAL_SPLIT );
splitPane.add( listScroller ); splitPane.add( listPanel );
splitPane.add( editorScroller ); splitPane.add( editorScroller );
add( splitPane ); add( splitPane );

View File

@@ -17,6 +17,8 @@
******************************************************************************/ ******************************************************************************/
package controller.channel.map; package controller.channel.map;
import java.awt.EventQueue;
import javax.swing.JFrame; import javax.swing.JFrame;
import net.miginfocom.swing.MigLayout; import net.miginfocom.swing.MigLayout;
@@ -39,4 +41,21 @@ public class ChannelMapManagerFrame extends JFrame
add( channelMapManager ); add( channelMapManager );
setLocationRelativeTo( null ); 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 );
}
} );
}
} }

View File

@@ -17,21 +17,35 @@
******************************************************************************/ ******************************************************************************/
package controller.channel.map; package controller.channel.map;
import java.text.DecimalFormat;
import javax.xml.bind.annotation.XmlAttribute; import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlRootElement; import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlTransient;
import javax.xml.bind.annotation.XmlType; import javax.xml.bind.annotation.XmlType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@XmlRootElement @XmlRootElement
@XmlType( name = "range" ) @XmlType( name = "range" )
public class ChannelRange public class ChannelRange
{ {
private final static Logger mLog = LoggerFactory.getLogger( ChannelRange.class );
private static final DecimalFormat FREQUENCY_FORMATTER =
new DecimalFormat( "#.0000" );
private int mFirst = 1; private int mFirst = 1;
private int mLast = 1023; private int mLast = 1023;
private int mBase = 450000000; private int mBase = 450000000;
private int mSize = 12500; private int mSize = 12500;
/* JAXB requirement - do not remove */ private boolean mOverlapping = false;
public ChannelRange() {}
public ChannelRange()
{
}
public ChannelRange( int first, int last, int base, int size ) public ChannelRange( int first, int last, int base, int size )
{ {
@@ -46,11 +60,67 @@ public class ChannelRange
return new ChannelRange( mFirst, mLast, mBase, mSize ); 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() public boolean isValid()
{ {
return mFirst < mLast; 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 ) public boolean hasChannel( int channel )
{ {
return mFirst <= channel && channel <= mLast; return mFirst <= channel && channel <= mLast;

View File

@@ -17,6 +17,11 @@
******************************************************************************/ ******************************************************************************/
package controller.channel.map; package controller.channel.map;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
import javax.swing.table.AbstractTableModel; import javax.swing.table.AbstractTableModel;
import org.slf4j.Logger; import org.slf4j.Logger;
@@ -32,49 +37,91 @@ public class ChannelRangeModel extends AbstractTableModel
private static final int STOP = 1; private static final int STOP = 1;
private static final int BASE = 2; private static final int BASE = 2;
private static final int SIZE = 3; private static final int SIZE = 3;
private static final int ERROR = 4;
protected int[] mColumnWidths = { 110, 110, 110, -1 }; protected int[] mColumnWidths = { 110, 110, 110, -1, 80 };
protected String[] mHeaders = new String[] { "Begin", protected String[] mHeaders = new String[] { "Begin",
"End", "End",
"Base", "Base",
"Size" }; "Size",
"Error" };
private ChannelMap mChannelMap; private List<ChannelRange> mRanges = new CopyOnWriteArrayList<>();
private ChannelRangeEventListener mListener;
public ChannelRangeModel() public ChannelRangeModel()
{ {
} }
public void setChannelMap( ChannelMap channelMap ) public void setListener( ChannelRangeEventListener listener )
{ {
mChannelMap = channelMap; mListener = listener;
fireTableDataChanged(); }
private void broadcastChange()
{
if( mListener != null )
{
mListener.channelRangesChanged();
}
}
public void clear()
{
for( ChannelRange range: mRanges )
{
removeRange( range );
}
broadcastChange();
}
public List<ChannelRange> getChannelRanges()
{
return Collections.unmodifiableList( mRanges );
}
public void addRanges( List<ChannelRange> ranges )
{
for( ChannelRange range: ranges )
{
addRange( range );
}
broadcastChange();
} }
public void addRange( ChannelRange range ) public void addRange( ChannelRange range )
{ {
if( mChannelMap != null ) if( range != null && !mRanges.contains( range ) )
{ {
mChannelMap.addRange( range ); mRanges.add( range );
int index = mChannelMap.getRanges().size() - 1; int index = mRanges.indexOf( range );
fireTableRowsInserted( index, index ); fireTableRowsInserted( index, index );
} }
validate();
broadcastChange();
} }
public void removeRange( int rangeIndex ) public void removeRange( ChannelRange range )
{ {
if( mChannelMap != null ) if( range != null && mRanges.contains( range ) )
{ {
if( rangeIndex < mChannelMap.getRanges().size() ) int index = mRanges.indexOf( range );
{
mChannelMap.getRanges().remove( rangeIndex );
}
fireTableRowsDeleted( rangeIndex, rangeIndex ); mRanges.remove( range );
fireTableRowsDeleted( index, index );
} }
validate();
broadcastChange();
} }
public int[] getColumnWidths() public int[] getColumnWidths()
@@ -84,10 +131,11 @@ public class ChannelRangeModel extends AbstractTableModel
public void setColumnWidths( int[] widths ) public void setColumnWidths( int[] widths )
{ {
if( widths.length != 4 ) if( widths.length != mHeaders.length )
{ {
throw new IllegalArgumentException( "ChannelMapRangeModel - " throw new IllegalArgumentException( "ChannelMapRangeModel - "
+ "column widths array should have 5 elements" ); + "column widths array should have " +
mHeaders.length + " elements" );
} }
else else
{ {
@@ -98,12 +146,7 @@ public class ChannelRangeModel extends AbstractTableModel
@Override @Override
public int getRowCount() public int getRowCount()
{ {
if( mChannelMap != null ) return mRanges.size();
{
return mChannelMap.getRanges().size();
}
return 0;
} }
@Override @Override
@@ -117,13 +160,23 @@ public class ChannelRangeModel extends AbstractTableModel
return mHeaders[ column ]; return mHeaders[ column ];
} }
public ChannelRange getChannelRange( int index )
{
if( index < mRanges.size() )
{
return mRanges.get( index );
}
return null;
}
@Override @Override
public Object getValueAt( int rowIndex, int columnIndex ) public Object getValueAt( int rowIndex, int columnIndex )
{ {
if( mChannelMap != null ) ChannelRange range = getChannelRange( rowIndex );
{
ChannelRange range = mChannelMap.getRanges().get( rowIndex );
if( range != null )
{
switch( columnIndex ) switch( columnIndex )
{ {
case START: case START:
@@ -134,27 +187,84 @@ public class ChannelRangeModel extends AbstractTableModel
return range.getBase(); return range.getBase();
case SIZE: case SIZE:
return range.getSize(); 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; 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 ) public boolean isCellEditable( int row, int column )
{ {
return mChannelMap != null; return row < mRanges.size() && column != ERROR;
} }
public void setValueAt( Object value, int row, int column ) public void setValueAt( Object value, int row, int column )
{ {
if( mChannelMap != null ) int parsedValue = -1;
try
{ {
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 )
{ {
int parsedValue = Integer.parseInt( (String)value );
ChannelRange range = mChannelMap.getRanges().get( row );
switch( column ) switch( column )
{ {
case START: case START:
@@ -170,12 +280,20 @@ public class ChannelRangeModel extends AbstractTableModel
range.setSize( parsedValue ); range.setSize( parsedValue );
break; break;
} }
validate();
broadcastChange();
} }
catch( Exception e ) else
{ {
mLog.error( "ChannelMapModel - couldn't parse integer value " throw new IllegalArgumentException( "Invalid channel range row index" );
+ "for [" + value + "]", e );
} }
} }
} }
public interface ChannelRangeEventListener
{
public void channelRangesChanged();
}
} }