mirror of
https://github.com/DSheirer/sdrtrunk.git
synced 2026-11-15 01:57:39 -05:00
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:
@@ -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();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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() );
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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 );
|
||||||
|
|||||||
@@ -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 );
|
||||||
|
}
|
||||||
|
} );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user