diff --git a/src/controller/channel/map/ChannelMap.java b/src/controller/channel/map/ChannelMap.java index 3ec928a0..a7359a16 100644 --- a/src/controller/channel/map/ChannelMap.java +++ b/src/controller/channel/map/ChannelMap.java @@ -18,17 +18,20 @@ package controller.channel.map; import java.util.ArrayList; +import java.util.List; import javax.xml.bind.annotation.XmlAttribute; import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlSeeAlso; +import javax.xml.bind.annotation.XmlTransient; @XmlSeeAlso( { ChannelRange.class } ) public class ChannelMap { private String mName; + private boolean mInvalid = false; - private ArrayList mRanges = new ArrayList(); + private List mRanges = new ArrayList(); public ChannelMap() { @@ -39,17 +42,10 @@ public class ChannelMap { mName = name; } - - public String toString() - { - return mName; - } - /** - * Creates a copy of this channel map - */ + public ChannelMap copyOf() { - ChannelMap map = new ChannelMap( mName ); + ChannelMap map = new ChannelMap( new String( mName ) ); for( ChannelRange range: mRanges ) { @@ -58,29 +54,34 @@ public class ChannelMap return map; } - - public int getInvalidRange() + + @XmlTransient + public boolean isInvalid() { - for( int x = 0; x < mRanges.size(); x++ ) - { - if( !mRanges.get( x ).isValid() ) - { - return x; - } - } - - return -1; + return mInvalid; + } + + public String toString() + { + return mName + ( mInvalid ? " - Error" : "" ); } - public ArrayList getRanges() + public List getRanges() { return mRanges; } @XmlElement( name = "range" ) - public void setRanges( ArrayList ranges ) + public void setRanges( List ranges ) { - mRanges = ranges; + mRanges.clear(); + + for( ChannelRange range: ranges ) + { + mRanges.add( range ); + } + + validate(); } @XmlAttribute @@ -106,15 +107,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 ) { mRanges.remove( range ); + + validate(); } } diff --git a/src/controller/channel/map/ChannelMapEditor.java b/src/controller/channel/map/ChannelMapEditor.java index 85e39357..b79a220f 100644 --- a/src/controller/channel/map/ChannelMapEditor.java +++ b/src/controller/channel/map/ChannelMapEditor.java @@ -28,6 +28,8 @@ 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.JScrollPane; import javax.swing.JTable; import javax.swing.JTextField; @@ -41,8 +43,11 @@ import net.miginfocom.swing.MigLayout; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import controller.channel.map.ChannelMapEvent.Event; +import controller.channel.map.ChannelRangeModel.ChannelRangeEventListener; + public class ChannelMapEditor extends DocumentListenerEditor - implements ListSelectionListener + implements ListSelectionListener, ChannelRangeEventListener { private static final long serialVersionUID = 1L; private final static Logger mLog = @@ -52,14 +57,19 @@ public class ChannelMapEditor extends DocumentListenerEditor private JTextField mNameText; private ChannelRangeModel mRangeModel; 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() ) { @@ -76,24 +86,49 @@ public class ChannelMapEditor extends DocumentListenerEditor 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 ); + + mSaveButton.setEnabled( modified ); + } + + private void init() + { + setLayout( new MigLayout( "fill,wrap 3", + "[grow,fill][grow,fill][grow,fill]", "[][grow][][]" ) ); - add( new JLabel( "Channel Map" ), "span, align center" ); - - mNameLabel = new JLabel( "Name:" ); + mNameLabel = new JLabel( "Channel Map:" ); add( mNameLabel ); mNameText = new JTextField(); mNameText.getDocument().addDocumentListener( this ); - add( mNameText, "growx, push" ); + add( mNameText, "span" ); mRangeModel = new ChannelRangeModel(); + mRangeModel.setListener( this ); mRangeTable = new JTable( mRangeModel ); mRangeTable.setPreferredScrollableViewportSize( new Dimension( 300, 150 ) ); @@ -104,143 +139,201 @@ public class ChannelMapEditor extends DocumentListenerEditor 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() - { - mLog.error( "Fix Me!" ); -// 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; - } - -// @Override -// public void actionPerformed( ActionEvent e ) -// { -// String command = e.getActionCommand(); -// -// switch( command ) -// { -// case "New Range": -// mRangeModel.addRange( new ChannelRange() ); -// break; -// case "Save": -// if( checkForValidRanges() ) -// { -// 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 ); -// } -// else -// { -// mRangeModel.removeRange( -// mRangeTable.convertRowIndexToModel( row ) ); -// } -// break; -// } -// } - - public class RangeTableEditor extends AbstractCellEditor - implements TableCellEditor, ActionListener - { - JFormattedTextField mEditor; - - public RangeTableEditor() + mSaveButton = new JButton( "Save" ); + mSaveButton.setEnabled( false ); + mSaveButton.addActionListener( new ActionListener() { - InternationalFormatter nf = new InternationalFormatter(); - nf.setMinimum( 1 ); - nf.setAllowsInvalid( false ); - - mEditor = new JFormattedTextField( nf ); - } - - @Override - public Object getCellEditorValue() - { - return mEditor.getText(); - } + @Override + public void actionPerformed( ActionEvent e ) + { + save(); + } + } ); + add( mSaveButton ); - @Override - public void actionPerformed( ActionEvent e ) - { - } + mNewButton = new JButton( "New Range" ); + mNewButton.setEnabled( false ); + mNewButton.addActionListener( new ActionListener() + { + @Override + public void actionPerformed( ActionEvent e ) + { + mRangeModel.addRange( new ChannelRange() ); + } + } ); + add( mNewButton ); - @Override - public Component getTableCellEditorComponent( JTable table, - Object value, - boolean isSelected, - int row, - int column ) - { - mEditor.setValue( value ); - - return mEditor; - } - } + 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 save() { - // TODO Auto-generated method stub - + if( isModified() && hasItem() ) + { + 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 ) { - mLog.debug( "List Select Event from: " + e.getSource().getClass() ); + if( !e.getValueIsAdjusting() ) + { + if( e.getSource() instanceof JList ) + { + JList list = (JList)e.getSource(); + + if( list.getSelectedValue() instanceof ChannelMap ) + { + setItem( (ChannelMap)list.getSelectedValue() ); + } + else + { + setItem( null ); + } + } + } } + + @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 + { + private static final long serialVersionUID = 1L; + + JFormattedTextField mEditor; + + public RangeTableEditor() + { + InternationalFormatter nf = new InternationalFormatter(); + nf.setMinimum( 1 ); + nf.setAllowsInvalid( true ); + + mEditor = new JFormattedTextField( nf ); + } + + @Override + public Object getCellEditorValue() + { + return mEditor.getText(); + } + + @Override + public void actionPerformed( ActionEvent e ) + { + } + + @Override + public Component getTableCellEditorComponent( JTable table, + Object value, + boolean isSelected, + int row, + int column ) + { + mEditor.setValue( value ); + + return mEditor; + } + } + } diff --git a/src/controller/channel/map/ChannelMapManager.java b/src/controller/channel/map/ChannelMapManager.java index d92626a5..78e0608e 100644 --- a/src/controller/channel/map/ChannelMapManager.java +++ b/src/controller/channel/map/ChannelMapManager.java @@ -1,9 +1,16 @@ 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; @@ -16,30 +23,111 @@ public class ChannelMapManager extends JPanel private ChannelMapModel mChannelMapModel; private JList mChannelMapList; private ChannelMapEditor mChannelMapEditor; + private JButton mDeleteMapButton; + private JButton mCloneMapButton; public ChannelMapManager( ChannelMapModel channelMapModel ) { mChannelMapModel = channelMapModel; - mChannelMapList = new JList( mChannelMapModel ); - mChannelMapEditor = new ChannelMapEditor(); + mChannelMapList = new JList<>( mChannelMapModel ); + mChannelMapEditor = new ChannelMapEditor( mChannelMapModel ); 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( 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( listScroller ); + splitPane.add( listPanel ); splitPane.add( editorScroller ); add( splitPane ); diff --git a/src/controller/channel/map/ChannelMapManagerFrame.java b/src/controller/channel/map/ChannelMapManagerFrame.java index c3c48628..04812979 100644 --- a/src/controller/channel/map/ChannelMapManagerFrame.java +++ b/src/controller/channel/map/ChannelMapManagerFrame.java @@ -17,6 +17,8 @@ ******************************************************************************/ package controller.channel.map; +import java.awt.EventQueue; + import javax.swing.JFrame; import net.miginfocom.swing.MigLayout; @@ -39,4 +41,21 @@ public class ChannelMapManagerFrame extends JFrame add( channelMapManager ); setLocationRelativeTo( null ); } + + public static void main( String[] args ) + { + ChannelMapModel channelMapModel = new ChannelMapModel(); + + final ChannelMapManagerFrame frame = + new ChannelMapManagerFrame( channelMapModel ); + + EventQueue.invokeLater( new Runnable() + { + @Override + public void run() + { + frame.setVisible( true ); + } + } ); + } } diff --git a/src/controller/channel/map/ChannelRange.java b/src/controller/channel/map/ChannelRange.java index a57ee199..28b9a5b4 100644 --- a/src/controller/channel/map/ChannelRange.java +++ b/src/controller/channel/map/ChannelRange.java @@ -17,21 +17,35 @@ ******************************************************************************/ 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; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + @XmlRootElement @XmlType( name = "range" ) 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 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 +60,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; diff --git a/src/controller/channel/map/ChannelRangeModel.java b/src/controller/channel/map/ChannelRangeModel.java index 9e13d345..d5269c1d 100644 --- a/src/controller/channel/map/ChannelRangeModel.java +++ b/src/controller/channel/map/ChannelRangeModel.java @@ -17,6 +17,11 @@ ******************************************************************************/ 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 org.slf4j.Logger; @@ -32,49 +37,91 @@ public class ChannelRangeModel extends AbstractTableModel 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 }; + protected int[] mColumnWidths = { 110, 110, 110, -1, 80 }; protected String[] mHeaders = new String[] { "Begin", "End", "Base", - "Size" }; + "Size", + "Error" }; - private ChannelMap mChannelMap; + private List mRanges = new CopyOnWriteArrayList<>(); + private ChannelRangeEventListener mListener; public ChannelRangeModel() { } - public void setChannelMap( ChannelMap channelMap ) + public void setListener( ChannelRangeEventListener listener ) { - mChannelMap = channelMap; - fireTableDataChanged(); + mListener = listener; + } + + private void broadcastChange() + { + if( mListener != null ) + { + mListener.channelRangesChanged(); + } + } + + public void clear() + { + for( ChannelRange range: mRanges ) + { + removeRange( range ); + } + + broadcastChange(); + } + + public List getChannelRanges() + { + return Collections.unmodifiableList( mRanges ); + } + + public void addRanges( List ranges ) + { + for( ChannelRange range: ranges ) + { + addRange( range ); + } + + broadcastChange(); } public void addRange( ChannelRange range ) { - if( mChannelMap != null ) + if( range != null && !mRanges.contains( range ) ) { - mChannelMap.addRange( range ); - - int index = mChannelMap.getRanges().size() - 1; - + mRanges.add( range ); + + int index = mRanges.indexOf( range ); + 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() ) - { - mChannelMap.getRanges().remove( rangeIndex ); - } + int index = mRanges.indexOf( range ); - fireTableRowsDeleted( rangeIndex, rangeIndex ); + mRanges.remove( range ); + + fireTableRowsDeleted( index, index ); } + + validate(); + + broadcastChange(); } public int[] getColumnWidths() @@ -84,10 +131,11 @@ public class ChannelRangeModel extends AbstractTableModel public void setColumnWidths( int[] widths ) { - if( widths.length != 4 ) + if( widths.length != mHeaders.length ) { throw new IllegalArgumentException( "ChannelMapRangeModel - " - + "column widths array should have 5 elements" ); + + "column widths array should have " + + mHeaders.length + " elements" ); } else { @@ -98,12 +146,7 @@ public class ChannelRangeModel extends AbstractTableModel @Override public int getRowCount() { - if( mChannelMap != null ) - { - return mChannelMap.getRanges().size(); - } - - return 0; + return mRanges.size(); } @Override @@ -117,13 +160,23 @@ public class ChannelRangeModel extends AbstractTableModel 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 ) { - if( mChannelMap != null ) + ChannelRange range = getChannelRange( rowIndex ); + + if( range != null ) { - ChannelRange range = mChannelMap.getRanges().get( rowIndex ); - switch( columnIndex ) { case START: @@ -134,27 +187,84 @@ public class ChannelRangeModel extends AbstractTableModel 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 mChannelMap != null; + return row < mRanges.size() && column != ERROR; } 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 ) { case START: @@ -170,12 +280,20 @@ public class ChannelRangeModel extends AbstractTableModel range.setSize( parsedValue ); break; } + + validate(); + + broadcastChange(); } - catch( Exception e ) + else { - mLog.error( "ChannelMapModel - couldn't parse integer value " - + "for [" + value + "]", e ); + throw new IllegalArgumentException( "Invalid channel range row index" ); } } } + + public interface ChannelRangeEventListener + { + public void channelRangesChanged(); + } }