mirror of
https://github.com/DSheirer/sdrtrunk.git
synced 2026-11-12 16:47:31 -05:00
Updates pull request from Roman Tsypuk to convert the build system from
ant to gradle. Removes linux and windows application launch scripts and produces an executable jar with all resources included.
This commit is contained in:
@@ -0,0 +1,144 @@
|
||||
/*******************************************************************************
|
||||
* SDR Trunk
|
||||
* Copyright (C) 2014 Dennis Sheirer
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
******************************************************************************/
|
||||
package io.github.dsheirer.controller.channel.map;
|
||||
|
||||
import javax.xml.bind.annotation.XmlAttribute;
|
||||
import javax.xml.bind.annotation.XmlElement;
|
||||
import javax.xml.bind.annotation.XmlSeeAlso;
|
||||
import javax.xml.bind.annotation.XmlTransient;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@XmlSeeAlso( { ChannelRange.class } )
|
||||
public class ChannelMap
|
||||
{
|
||||
private String mName;
|
||||
private boolean mInvalid = false;
|
||||
|
||||
private List<ChannelRange> mRanges = new ArrayList<ChannelRange>();
|
||||
|
||||
public ChannelMap()
|
||||
{
|
||||
this( "New Channel Map" );
|
||||
}
|
||||
|
||||
public ChannelMap( String name )
|
||||
{
|
||||
mName = name;
|
||||
}
|
||||
|
||||
public ChannelMap copyOf()
|
||||
{
|
||||
ChannelMap map = new ChannelMap( new String( mName ) );
|
||||
|
||||
for( ChannelRange range: mRanges )
|
||||
{
|
||||
map.addRange( range.copyOf() );
|
||||
}
|
||||
|
||||
return map;
|
||||
}
|
||||
|
||||
@XmlTransient
|
||||
public boolean isInvalid()
|
||||
{
|
||||
return mInvalid;
|
||||
}
|
||||
|
||||
public String toString()
|
||||
{
|
||||
return mName + ( mInvalid ? " - Error" : "" );
|
||||
}
|
||||
|
||||
@XmlElement( name = "range" )
|
||||
public List<ChannelRange> getRanges()
|
||||
{
|
||||
return mRanges;
|
||||
}
|
||||
|
||||
public void setRanges( List<ChannelRange> ranges )
|
||||
{
|
||||
mRanges = ranges;
|
||||
|
||||
validate();
|
||||
}
|
||||
|
||||
@XmlAttribute
|
||||
public String getName()
|
||||
{
|
||||
return mName;
|
||||
}
|
||||
|
||||
public void setName( String name )
|
||||
{
|
||||
mName = name;
|
||||
}
|
||||
|
||||
public long getFrequency( int channelNumber )
|
||||
{
|
||||
for( ChannelRange range: mRanges )
|
||||
{
|
||||
if( range.hasChannel( channelNumber ) )
|
||||
{
|
||||
return range.getFrequency( channelNumber );
|
||||
}
|
||||
}
|
||||
|
||||
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 removeRange( ChannelRange range )
|
||||
{
|
||||
mRanges.remove( range );
|
||||
|
||||
validate();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,322 @@
|
||||
/*******************************************************************************
|
||||
* SDR Trunk
|
||||
* Copyright (C) 2014-2016 Dennis Sheirer
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
******************************************************************************/
|
||||
package io.github.dsheirer.controller.channel.map;
|
||||
|
||||
import io.github.dsheirer.controller.channel.map.ChannelMapEvent.Event;
|
||||
import io.github.dsheirer.controller.channel.map.ChannelRangeModel.ChannelRangeEventListener;
|
||||
import io.github.dsheirer.gui.editor.DocumentListenerEditor;
|
||||
import net.miginfocom.swing.MigLayout;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.event.ListSelectionEvent;
|
||||
import javax.swing.event.ListSelectionListener;
|
||||
import javax.swing.table.TableCellEditor;
|
||||
import javax.swing.text.InternationalFormatter;
|
||||
import java.awt.*;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.ActionListener;
|
||||
|
||||
public class ChannelMapEditor extends DocumentListenerEditor<ChannelMap>
|
||||
implements ListSelectionListener, ChannelRangeEventListener
|
||||
{
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private JLabel mNameLabel;
|
||||
private JTextField mNameText;
|
||||
private ChannelRangeModel mRangeModel;
|
||||
private JTable mRangeTable;
|
||||
private JButton mNewButton;
|
||||
private JButton mDeleteButton;
|
||||
private JButton mSaveButton;
|
||||
private JLabel mRangeDescription;
|
||||
private ChannelMapModel mChannelMapModel;
|
||||
|
||||
public ChannelMapEditor( ChannelMapModel channelMapModel )
|
||||
{
|
||||
mChannelMapModel = channelMapModel;
|
||||
init();
|
||||
}
|
||||
|
||||
public ChannelMap getChannelMap()
|
||||
{
|
||||
if( hasItem() )
|
||||
{
|
||||
return (ChannelMap)getItem();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setItem( ChannelMap item )
|
||||
{
|
||||
super.setItem( item );
|
||||
|
||||
if( hasItem() )
|
||||
{
|
||||
ChannelMap map = getChannelMap();
|
||||
|
||||
mNameText.setText( map.getName() );
|
||||
|
||||
mRangeModel.clear();
|
||||
mRangeModel.addRanges( map.getRanges() );
|
||||
|
||||
mNewButton.setEnabled( true );
|
||||
}
|
||||
else
|
||||
{
|
||||
mNameText.setText( "" );
|
||||
|
||||
mRangeModel.clear();
|
||||
|
||||
mNewButton.setEnabled( false );
|
||||
}
|
||||
|
||||
setModified( false );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setModified( boolean modified )
|
||||
{
|
||||
super.setModified( modified );
|
||||
|
||||
mSaveButton.setEnabled( modified );
|
||||
}
|
||||
|
||||
private void init()
|
||||
{
|
||||
setLayout( new MigLayout( "fill,wrap 3",
|
||||
"[grow,fill][grow,fill][grow,fill]", "[][grow][][]" ) );
|
||||
|
||||
mNameLabel = new JLabel( "Channel Map:" );
|
||||
add( mNameLabel );
|
||||
|
||||
mNameText = new JTextField();
|
||||
mNameText.getDocument().addDocumentListener( this );
|
||||
add( mNameText, "span" );
|
||||
|
||||
mRangeModel = new ChannelRangeModel();
|
||||
mRangeModel.setListener( this );
|
||||
|
||||
mRangeTable = new JTable( mRangeModel );
|
||||
mRangeTable.setPreferredScrollableViewportSize( new Dimension( 300, 150 ) );
|
||||
mRangeTable.setFillsViewportHeight( true );
|
||||
mRangeTable.setAutoCreateRowSorter( true );
|
||||
for( int x = 0; x < mRangeTable.getColumnCount(); x++ )
|
||||
{
|
||||
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");
|
||||
|
||||
mRangeDescription = new JLabel( " " );
|
||||
add( mRangeDescription, "span" );
|
||||
|
||||
mSaveButton = new JButton( "Save" );
|
||||
mSaveButton.setEnabled( false );
|
||||
mSaveButton.addActionListener( new ActionListener()
|
||||
{
|
||||
@Override
|
||||
public void actionPerformed( ActionEvent e )
|
||||
{
|
||||
save();
|
||||
}
|
||||
} );
|
||||
add( mSaveButton );
|
||||
|
||||
mNewButton = new JButton( "New Range" );
|
||||
mNewButton.setEnabled( false );
|
||||
mNewButton.addActionListener( new ActionListener()
|
||||
{
|
||||
@Override
|
||||
public void actionPerformed( ActionEvent e )
|
||||
{
|
||||
mRangeModel.addRange( new ChannelRange() );
|
||||
}
|
||||
} );
|
||||
add( mNewButton );
|
||||
|
||||
mDeleteButton = new JButton( "DeleteRange" );
|
||||
mDeleteButton.setEnabled( false );
|
||||
mDeleteButton.addActionListener( new ActionListener()
|
||||
{
|
||||
@Override
|
||||
public void actionPerformed( ActionEvent e )
|
||||
{
|
||||
int selectedRow = mRangeTable.getSelectedRow();
|
||||
|
||||
if( selectedRow >= 0 )
|
||||
{
|
||||
int modelRow = mRangeTable.convertRowIndexToModel( selectedRow );
|
||||
|
||||
ChannelRange selectedRange = mRangeModel.getChannelRange( modelRow );
|
||||
|
||||
if( selectedRange != null )
|
||||
{
|
||||
int choice = JOptionPane.showConfirmDialog( mRangeTable,
|
||||
"Do you want to delete this channel range?",
|
||||
"Delete Channel Range?", JOptionPane.YES_NO_OPTION );
|
||||
|
||||
if( choice == JOptionPane.YES_OPTION )
|
||||
{
|
||||
mRangeModel.removeRange( selectedRange );
|
||||
mDeleteButton.setEnabled( false );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} );
|
||||
add( mDeleteButton, "wrap" );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void save()
|
||||
{
|
||||
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 )
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
/*******************************************************************************
|
||||
* SDR Trunk
|
||||
* Copyright (C) 2014-2016 Dennis Sheirer
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
******************************************************************************/
|
||||
package io.github.dsheirer.controller.channel.map;
|
||||
|
||||
public class ChannelMapEvent
|
||||
{
|
||||
private ChannelMap mChannelMap;
|
||||
private Event mEvent;
|
||||
private String mPreviousChannelMapName;
|
||||
|
||||
/**
|
||||
* ChannelMapEvent - event describing any changes to a channel map
|
||||
*
|
||||
* @param channelMap that changed
|
||||
* @param event describing the change
|
||||
*/
|
||||
public ChannelMapEvent( ChannelMap channelMap, Event event )
|
||||
{
|
||||
mChannelMap = channelMap;
|
||||
mEvent = event;
|
||||
}
|
||||
|
||||
/**
|
||||
* ChannelMapEvent - channel map renaming event.
|
||||
*
|
||||
* @param channelMap that changed
|
||||
* @param previousName of the channel map
|
||||
*/
|
||||
public ChannelMapEvent( ChannelMap channelMap, String previousName )
|
||||
{
|
||||
this( channelMap, Event.RENAME );
|
||||
|
||||
mPreviousChannelMapName = previousName;
|
||||
}
|
||||
|
||||
public ChannelMap getChannelMap()
|
||||
{
|
||||
return mChannelMap;
|
||||
}
|
||||
|
||||
public String getPreviousChannelMapName()
|
||||
{
|
||||
return mPreviousChannelMapName;
|
||||
}
|
||||
|
||||
public Event getEvent()
|
||||
{
|
||||
return mEvent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Events
|
||||
*/
|
||||
public enum Event
|
||||
{
|
||||
ADD,
|
||||
CHANGE,
|
||||
RENAME,
|
||||
DELETE;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,128 @@
|
||||
package io.github.dsheirer.controller.channel.map;
|
||||
|
||||
import com.jidesoft.swing.JideSplitPane;
|
||||
import net.miginfocom.swing.MigLayout;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.event.ListSelectionEvent;
|
||||
import javax.swing.event.ListSelectionListener;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.ActionListener;
|
||||
|
||||
public class ChannelMapManager extends JPanel
|
||||
{
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private ChannelMapModel mChannelMapModel;
|
||||
private JList<ChannelMap> mChannelMapList;
|
||||
private ChannelMapEditor mChannelMapEditor;
|
||||
private JButton mDeleteMapButton;
|
||||
private JButton mCloneMapButton;
|
||||
|
||||
public ChannelMapManager( ChannelMapModel channelMapModel )
|
||||
{
|
||||
mChannelMapModel = channelMapModel;
|
||||
mChannelMapList = new JList<>( mChannelMapModel );
|
||||
mChannelMapEditor = new ChannelMapEditor( mChannelMapModel );
|
||||
|
||||
init();
|
||||
}
|
||||
|
||||
private void init()
|
||||
{
|
||||
setLayout( new MigLayout( "insets 1 1 1 1", "[fill,grow]", "[fill,grow]" ) );
|
||||
|
||||
JPanel listPanel = new JPanel();
|
||||
listPanel.setLayout( new MigLayout( "",
|
||||
"[fill,grow][fill,grow][fill,grow]", "[fill,grow][]" ) );
|
||||
|
||||
mChannelMapList.addListSelectionListener( mChannelMapEditor );
|
||||
mChannelMapList.addListSelectionListener( new ListSelectionListener()
|
||||
{
|
||||
@Override
|
||||
public void valueChanged( ListSelectionEvent e )
|
||||
{
|
||||
if( mChannelMapList.getSelectedValue() != null )
|
||||
{
|
||||
mCloneMapButton.setEnabled( true );
|
||||
mDeleteMapButton.setEnabled( true );
|
||||
}
|
||||
else
|
||||
{
|
||||
mCloneMapButton.setEnabled( false );
|
||||
mDeleteMapButton.setEnabled( false );
|
||||
}
|
||||
}
|
||||
} );
|
||||
mChannelMapList.setLayoutOrientation( JList.VERTICAL );
|
||||
mChannelMapList.setSelectionMode( ListSelectionModel.SINGLE_SELECTION );
|
||||
|
||||
JScrollPane listScroller = new JScrollPane( mChannelMapList );
|
||||
|
||||
listPanel.add( listScroller, "span" );
|
||||
|
||||
JButton newMapButton = new JButton( "New" );
|
||||
newMapButton.addActionListener( new ActionListener()
|
||||
{
|
||||
@Override
|
||||
public void actionPerformed( ActionEvent e )
|
||||
{
|
||||
ChannelMap channelMap = new ChannelMap( "New Channel Map" );
|
||||
mChannelMapModel.addChannelMap( channelMap );
|
||||
mChannelMapList.setSelectedValue( channelMap, true );
|
||||
}
|
||||
} );
|
||||
|
||||
listPanel.add( newMapButton );
|
||||
|
||||
mCloneMapButton = new JButton( "Clone" );
|
||||
mCloneMapButton.addActionListener( new ActionListener()
|
||||
{
|
||||
@Override
|
||||
public void actionPerformed( ActionEvent e )
|
||||
{
|
||||
ChannelMap selected = mChannelMapList.getSelectedValue();
|
||||
|
||||
if( selected != null )
|
||||
{
|
||||
ChannelMap copy = selected.copyOf();
|
||||
mChannelMapModel.addChannelMap( copy );
|
||||
mChannelMapList.setSelectedValue( copy, true );
|
||||
}
|
||||
}
|
||||
} );
|
||||
listPanel.add( mCloneMapButton );
|
||||
|
||||
mDeleteMapButton = new JButton( "Delete" );
|
||||
mDeleteMapButton.addActionListener( new ActionListener()
|
||||
{
|
||||
@Override
|
||||
public void actionPerformed( ActionEvent e )
|
||||
{
|
||||
ChannelMap channelMap = mChannelMapList.getSelectedValue();
|
||||
|
||||
if( channelMap != null )
|
||||
{
|
||||
int choice = JOptionPane.showConfirmDialog( mChannelMapList,
|
||||
"Do you want to delete this channel map?",
|
||||
"Delete Channel Map?", JOptionPane.YES_NO_OPTION );
|
||||
|
||||
if( choice == JOptionPane.YES_OPTION )
|
||||
{
|
||||
mChannelMapModel.removeChannelMap( channelMap );
|
||||
}
|
||||
}
|
||||
}
|
||||
} );
|
||||
listPanel.add( mDeleteMapButton );
|
||||
|
||||
JScrollPane editorScroller = new JScrollPane( mChannelMapEditor );
|
||||
|
||||
JideSplitPane splitPane = new JideSplitPane();
|
||||
splitPane.setOrientation( JideSplitPane.HORIZONTAL_SPLIT );
|
||||
splitPane.add( listPanel );
|
||||
splitPane.add( editorScroller );
|
||||
|
||||
add( splitPane );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
/*******************************************************************************
|
||||
* SDR Trunk
|
||||
* Copyright (C) 2014-2016 Dennis Sheirer
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
******************************************************************************/
|
||||
package io.github.dsheirer.controller.channel.map;
|
||||
|
||||
import net.miginfocom.swing.MigLayout;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
|
||||
public class ChannelMapManagerFrame extends JFrame
|
||||
{
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public ChannelMapManagerFrame( ChannelMapModel channelMapModel )
|
||||
{
|
||||
init( new ChannelMapManager( channelMapModel ) );
|
||||
}
|
||||
|
||||
private void init( ChannelMapManager channelMapManager )
|
||||
{
|
||||
setTitle( "Channel Map Manager" );
|
||||
setSize( 800, 400 );
|
||||
setDefaultCloseOperation( JFrame.DISPOSE_ON_CLOSE );
|
||||
setLayout( new MigLayout( "", "[grow,fill]", "[grow,fill]" ) );
|
||||
add( channelMapManager );
|
||||
setLocationRelativeTo( null );
|
||||
}
|
||||
|
||||
public static void main( String[] args )
|
||||
{
|
||||
ChannelMapModel channelMapModel = new ChannelMapModel();
|
||||
|
||||
final ChannelMapManagerFrame frame =
|
||||
new ChannelMapManagerFrame( channelMapModel );
|
||||
|
||||
EventQueue.invokeLater( new Runnable()
|
||||
{
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
frame.setVisible( true );
|
||||
}
|
||||
} );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,157 @@
|
||||
package io.github.dsheirer.controller.channel.map;
|
||||
|
||||
import io.github.dsheirer.controller.channel.map.ChannelMapEvent.Event;
|
||||
import io.github.dsheirer.sample.Broadcaster;
|
||||
import io.github.dsheirer.sample.Listener;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
public class ChannelMapModel extends AbstractListModel<ChannelMap>
|
||||
{
|
||||
private static final long serialVersionUID = 1L;
|
||||
private final static Logger mLog = LoggerFactory.getLogger(ChannelMapModel.class);
|
||||
|
||||
private List<ChannelMap> mChannelMaps = new ArrayList<>();
|
||||
private Broadcaster<ChannelMapEvent> mEventBroadcaster = new Broadcaster<>();
|
||||
|
||||
public ChannelMapModel()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an unmodifiable list of channel maps currently in the model
|
||||
*/
|
||||
public List<ChannelMap> getChannelMaps()
|
||||
{
|
||||
return Collections.unmodifiableList(mChannelMaps);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the channel map with a matching name or null
|
||||
*/
|
||||
public ChannelMap getChannelMap(String name)
|
||||
{
|
||||
for(ChannelMap channelMap : mChannelMaps)
|
||||
{
|
||||
if(channelMap.getName().equalsIgnoreCase(name))
|
||||
{
|
||||
return channelMap;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a listener to receive notifications when channel map updates occur
|
||||
*/
|
||||
public void addListener(Listener<ChannelMapEvent> listener)
|
||||
{
|
||||
mEventBroadcaster.addListener(listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the channel map update notification listener
|
||||
*/
|
||||
public void removeListener(Listener<ChannelMapEvent> listener)
|
||||
{
|
||||
mEventBroadcaster.removeListener(listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Broadcasts a channel map change.
|
||||
*
|
||||
* Note: use the add/remove methods to add or remove channel maps from this
|
||||
* model. When using those methods, an add or delete event will automatically
|
||||
* be generated.
|
||||
*/
|
||||
public void broadcast(ChannelMapEvent event)
|
||||
{
|
||||
if(event.getEvent() == ChannelMapEvent.Event.CHANGE ||
|
||||
event.getEvent() == ChannelMapEvent.Event.RENAME)
|
||||
{
|
||||
int index = mChannelMaps.indexOf(event.getChannelMap());
|
||||
|
||||
if(index >= 0)
|
||||
{
|
||||
fireContentsChanged(this, index, index);
|
||||
}
|
||||
}
|
||||
|
||||
mEventBroadcaster.broadcast(event);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a list of channel maps to this model
|
||||
*/
|
||||
public void addChannelMaps(List<ChannelMap> channelMaps)
|
||||
{
|
||||
for(ChannelMap channelMap : channelMaps)
|
||||
{
|
||||
addChannelMap(channelMap);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the channel map to this model
|
||||
*/
|
||||
public void addChannelMap(ChannelMap channelMap)
|
||||
{
|
||||
if(!mChannelMaps.contains(channelMap))
|
||||
{
|
||||
mChannelMaps.add(channelMap);
|
||||
|
||||
int index = mChannelMaps.indexOf(channelMap);
|
||||
|
||||
fireIntervalAdded(this, index, index);
|
||||
|
||||
broadcast(new ChannelMapEvent(channelMap, Event.ADD));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the channel map from this model
|
||||
*/
|
||||
public void removeChannelMap(ChannelMap channelMap)
|
||||
{
|
||||
if(mChannelMaps.contains(channelMap))
|
||||
{
|
||||
int index = mChannelMaps.indexOf(channelMap);
|
||||
|
||||
mChannelMaps.remove(channelMap);
|
||||
|
||||
fireIntervalRemoved(this, index, index);
|
||||
|
||||
broadcast(new ChannelMapEvent(channelMap, Event.DELETE));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Size/Number of channel maps in this model
|
||||
*/
|
||||
@Override
|
||||
public int getSize()
|
||||
{
|
||||
return mChannelMaps.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the channel map at the specified index
|
||||
*/
|
||||
@Override
|
||||
public ChannelMap getElementAt(int index)
|
||||
{
|
||||
if(index <= mChannelMaps.size())
|
||||
{
|
||||
return mChannelMaps.get(index);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,185 @@
|
||||
/*******************************************************************************
|
||||
* SDR Trunk
|
||||
* Copyright (C) 2014-2016 Dennis Sheirer
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
******************************************************************************/
|
||||
package io.github.dsheirer.controller.channel.map;
|
||||
|
||||
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 java.text.DecimalFormat;
|
||||
|
||||
@XmlRootElement
|
||||
@XmlType( name = "range" )
|
||||
public class ChannelRange
|
||||
{
|
||||
private static final DecimalFormat FREQUENCY_FORMATTER = new DecimalFormat( "#.0000" );
|
||||
|
||||
private int mFirst = 1;
|
||||
private int mLast = 1023;
|
||||
private int mBase = 450000000;
|
||||
private int mSize = 12500;
|
||||
|
||||
private boolean mOverlapping = false;
|
||||
|
||||
public ChannelRange()
|
||||
{
|
||||
}
|
||||
|
||||
public ChannelRange( int first, int last, int base, int size )
|
||||
{
|
||||
mFirst = first;
|
||||
mLast = last;
|
||||
mBase = base;
|
||||
mSize = size;
|
||||
}
|
||||
|
||||
public ChannelRange copyOf()
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
public long getFrequency( int channel )
|
||||
{
|
||||
if( hasChannel( channel ) )
|
||||
{
|
||||
return mBase + ( ( channel - mFirst ) * mSize );
|
||||
}
|
||||
else
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
@XmlAttribute( name = "first" )
|
||||
public int getFirstChannelNumber()
|
||||
{
|
||||
return mFirst;
|
||||
}
|
||||
|
||||
public void setFirstChannelNumber( int first )
|
||||
{
|
||||
mFirst = first;
|
||||
}
|
||||
|
||||
@XmlAttribute( name = "last" )
|
||||
public void setLastChannelNumber( int last )
|
||||
{
|
||||
mLast = last;
|
||||
}
|
||||
|
||||
public int getLastChannelNumber()
|
||||
{
|
||||
return mLast;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the base frequency
|
||||
* @param base frequency in hertz
|
||||
*/
|
||||
@XmlAttribute( name = "base" )
|
||||
public void setBase( int base )
|
||||
{
|
||||
mBase = base;
|
||||
}
|
||||
|
||||
public int getBase()
|
||||
{
|
||||
return mBase;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the channel size
|
||||
* @param size in hertz
|
||||
*/
|
||||
@XmlAttribute( name = "size" )
|
||||
public void setSize( int size )
|
||||
{
|
||||
mSize = size;
|
||||
}
|
||||
|
||||
public int getSize()
|
||||
{
|
||||
return mSize;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,291 @@
|
||||
/*******************************************************************************
|
||||
* SDR Trunk
|
||||
* Copyright (C) 2014-2016 Dennis Sheirer
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
******************************************************************************/
|
||||
package io.github.dsheirer.controller.channel.map;
|
||||
|
||||
import javax.swing.table.AbstractTableModel;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
|
||||
public class ChannelRangeModel extends AbstractTableModel
|
||||
{
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private static final int START = 0;
|
||||
private static final int STOP = 1;
|
||||
private static final int BASE = 2;
|
||||
private static final int SIZE = 3;
|
||||
private static final int ERROR = 4;
|
||||
|
||||
protected int[] mColumnWidths = { 110, 110, 110, -1, 80 };
|
||||
|
||||
protected String[] mHeaders = new String[] { "Begin",
|
||||
"End",
|
||||
"Base",
|
||||
"Size",
|
||||
"Error" };
|
||||
|
||||
private List<ChannelRange> mRanges = new CopyOnWriteArrayList<>();
|
||||
private ChannelRangeEventListener mListener;
|
||||
|
||||
public ChannelRangeModel()
|
||||
{
|
||||
}
|
||||
|
||||
public void setListener( ChannelRangeEventListener listener )
|
||||
{
|
||||
mListener = listener;
|
||||
}
|
||||
|
||||
private void broadcastChange()
|
||||
{
|
||||
if( mListener != null )
|
||||
{
|
||||
mListener.channelRangesChanged();
|
||||
}
|
||||
}
|
||||
|
||||
public void clear()
|
||||
{
|
||||
for( ChannelRange range: mRanges )
|
||||
{
|
||||
removeRange( range );
|
||||
}
|
||||
|
||||
broadcastChange();
|
||||
}
|
||||
|
||||
public List<ChannelRange> getChannelRanges()
|
||||
{
|
||||
return mRanges;
|
||||
}
|
||||
|
||||
public void addRanges( List<ChannelRange> ranges )
|
||||
{
|
||||
for( ChannelRange range: ranges )
|
||||
{
|
||||
addRange( range );
|
||||
}
|
||||
|
||||
broadcastChange();
|
||||
}
|
||||
|
||||
public void addRange( ChannelRange range )
|
||||
{
|
||||
if( range != null && !mRanges.contains( range ) )
|
||||
{
|
||||
mRanges.add( range );
|
||||
|
||||
int index = mRanges.indexOf( range );
|
||||
|
||||
fireTableRowsInserted( index, index );
|
||||
}
|
||||
|
||||
validate();
|
||||
|
||||
broadcastChange();
|
||||
}
|
||||
|
||||
public void removeRange( ChannelRange range )
|
||||
{
|
||||
if( range != null && mRanges.contains( range ) )
|
||||
{
|
||||
int index = mRanges.indexOf( range );
|
||||
|
||||
mRanges.remove( range );
|
||||
|
||||
fireTableRowsDeleted( index, index );
|
||||
}
|
||||
|
||||
validate();
|
||||
|
||||
broadcastChange();
|
||||
}
|
||||
|
||||
public int[] getColumnWidths()
|
||||
{
|
||||
return mColumnWidths;
|
||||
}
|
||||
|
||||
public void setColumnWidths( int[] widths )
|
||||
{
|
||||
if( widths.length != mHeaders.length )
|
||||
{
|
||||
throw new IllegalArgumentException( "ChannelMapRangeModel - "
|
||||
+ "column widths array should have " +
|
||||
mHeaders.length + " elements" );
|
||||
}
|
||||
else
|
||||
{
|
||||
mColumnWidths = widths;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getRowCount()
|
||||
{
|
||||
return mRanges.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getColumnCount()
|
||||
{
|
||||
return mHeaders.length;
|
||||
}
|
||||
|
||||
public String getColumnName( int column )
|
||||
{
|
||||
return mHeaders[ column ];
|
||||
}
|
||||
|
||||
public ChannelRange getChannelRange( int index )
|
||||
{
|
||||
if( index < mRanges.size() )
|
||||
{
|
||||
return mRanges.get( index );
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getValueAt( int rowIndex, int columnIndex )
|
||||
{
|
||||
ChannelRange range = getChannelRange( rowIndex );
|
||||
|
||||
if( range != null )
|
||||
{
|
||||
switch( columnIndex )
|
||||
{
|
||||
case START:
|
||||
return range.getFirstChannelNumber();
|
||||
case STOP:
|
||||
return range.getLastChannelNumber();
|
||||
case BASE:
|
||||
return range.getBase();
|
||||
case SIZE:
|
||||
return range.getSize();
|
||||
case ERROR:
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
if( !range.isValid() )
|
||||
{
|
||||
sb.append( "Sequence" );
|
||||
}
|
||||
|
||||
if( range.isOverlapping() )
|
||||
{
|
||||
if( sb.length() > 0 )
|
||||
{
|
||||
sb.append( "," );
|
||||
}
|
||||
|
||||
sb.append( "Overlap" );
|
||||
}
|
||||
|
||||
return sb.toString();
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates each of the channel ranges for overlap
|
||||
*/
|
||||
private void validate()
|
||||
{
|
||||
for( int x = 0; x < mRanges.size(); x++ )
|
||||
{
|
||||
mRanges.get( x ).setOverlapping( false );
|
||||
fireTableCellUpdated( x, ERROR );
|
||||
}
|
||||
|
||||
for( int x = 0; x < mRanges.size(); x++ )
|
||||
{
|
||||
for( int y = x + 1; y < mRanges.size(); y++ )
|
||||
{
|
||||
if( mRanges.get( x ).overlaps( mRanges.get( y ) ) )
|
||||
{
|
||||
mRanges.get( x ).setOverlapping( true );
|
||||
fireTableCellUpdated( x, ERROR );
|
||||
|
||||
mRanges.get( y ).setOverlapping( true );
|
||||
fireTableCellUpdated( y, ERROR );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isCellEditable( int row, int column )
|
||||
{
|
||||
return row < mRanges.size() && column != ERROR;
|
||||
}
|
||||
|
||||
public void setValueAt( Object value, int row, int column )
|
||||
{
|
||||
int parsedValue = -1;
|
||||
|
||||
try
|
||||
{
|
||||
parsedValue = Integer.parseInt( (String)value );
|
||||
}
|
||||
catch( Exception e )
|
||||
{
|
||||
//Do nothing ... we'll use 0
|
||||
}
|
||||
|
||||
if( parsedValue >= 0 )
|
||||
{
|
||||
ChannelRange range = getChannelRange( row );
|
||||
|
||||
if( range != null )
|
||||
{
|
||||
switch( column )
|
||||
{
|
||||
case START:
|
||||
range.setFirstChannelNumber( parsedValue );
|
||||
break;
|
||||
case STOP:
|
||||
range.setLastChannelNumber( parsedValue );
|
||||
break;
|
||||
case BASE:
|
||||
range.setBase( parsedValue );
|
||||
break;
|
||||
case SIZE:
|
||||
range.setSize( parsedValue );
|
||||
break;
|
||||
}
|
||||
|
||||
validate();
|
||||
|
||||
broadcastChange();
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new IllegalArgumentException( "Invalid channel range row index" );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public interface ChannelRangeEventListener
|
||||
{
|
||||
public void channelRangesChanged();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user