mirror of
https://github.com/DSheirer/sdrtrunk.git
synced 2026-11-12 16:47:31 -05:00
* WIP - Adds support for aliasing P25 Phase 2 tones.Playlist editor - adds editor support for alias actions * Playlist editor implementation and integration into application is complete. Adds JavaFX window monitoring to persist window size/location across application runs. Co-authored-by: Denny <denny@denny-desktop>
This commit is contained in:
@@ -1,34 +1,45 @@
|
||||
/*******************************************************************************
|
||||
* 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.
|
||||
* * ******************************************************************************
|
||||
* * Copyright (C) 2014-2020 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/>
|
||||
* * *****************************************************************************
|
||||
*
|
||||
* 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 com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
|
||||
import javafx.beans.Observable;
|
||||
import javafx.beans.property.BooleanProperty;
|
||||
import javafx.beans.property.SimpleBooleanProperty;
|
||||
import javafx.beans.property.SimpleStringProperty;
|
||||
import javafx.beans.property.StringProperty;
|
||||
import javafx.collections.FXCollections;
|
||||
import javafx.collections.ListChangeListener;
|
||||
import javafx.collections.ObservableList;
|
||||
import javafx.util.Callback;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class ChannelMap
|
||||
{
|
||||
private String mName;
|
||||
private boolean mInvalid = false;
|
||||
|
||||
private List<ChannelRange> mRanges = new ArrayList<ChannelRange>();
|
||||
private StringProperty mName = new SimpleStringProperty();
|
||||
private BooleanProperty mValidProperty = new SimpleBooleanProperty();
|
||||
private ObservableList<ChannelRange> mRanges = FXCollections.observableArrayList(ChannelRange.extractor());
|
||||
|
||||
public ChannelMap()
|
||||
{
|
||||
@@ -37,12 +48,29 @@ public class ChannelMap
|
||||
|
||||
public ChannelMap(String name)
|
||||
{
|
||||
mName = name;
|
||||
mName.set(name);
|
||||
mRanges.addListener((ListChangeListener<ChannelRange>)c -> validate());
|
||||
}
|
||||
|
||||
public StringProperty nameProperty()
|
||||
{
|
||||
return mName;
|
||||
}
|
||||
|
||||
public BooleanProperty validProperty()
|
||||
{
|
||||
return mValidProperty;
|
||||
}
|
||||
|
||||
@JsonIgnore
|
||||
public ObservableList<ChannelRange> getItems()
|
||||
{
|
||||
return mRanges;
|
||||
}
|
||||
|
||||
public ChannelMap copyOf()
|
||||
{
|
||||
ChannelMap map = new ChannelMap(new String(mName));
|
||||
ChannelMap map = new ChannelMap(new String(mName.get()));
|
||||
|
||||
for(ChannelRange range : mRanges)
|
||||
{
|
||||
@@ -53,14 +81,14 @@ public class ChannelMap
|
||||
}
|
||||
|
||||
@JsonIgnore
|
||||
public boolean isInvalid()
|
||||
public boolean isValid()
|
||||
{
|
||||
return mInvalid;
|
||||
return mValidProperty.get();
|
||||
}
|
||||
|
||||
public String toString()
|
||||
{
|
||||
return mName + (mInvalid ? " - Error" : "");
|
||||
return mName.get();
|
||||
}
|
||||
|
||||
@JacksonXmlProperty(isAttribute = false, localName = "range")
|
||||
@@ -71,20 +99,18 @@ public class ChannelMap
|
||||
|
||||
public void setRanges(List<ChannelRange> ranges)
|
||||
{
|
||||
mRanges = ranges;
|
||||
|
||||
validate();
|
||||
mRanges.setAll(ranges);
|
||||
}
|
||||
|
||||
@JacksonXmlProperty(isAttribute = true, localName = "name")
|
||||
public String getName()
|
||||
{
|
||||
return mName;
|
||||
return mName.get();
|
||||
}
|
||||
|
||||
public void setName(String name)
|
||||
{
|
||||
mName = name;
|
||||
mName.set(name);
|
||||
}
|
||||
|
||||
public long getFrequency(int channelNumber)
|
||||
@@ -105,20 +131,27 @@ public class ChannelMap
|
||||
*/
|
||||
private void validate()
|
||||
{
|
||||
mInvalid = false;
|
||||
mValidProperty.set(true);
|
||||
|
||||
for(ChannelRange channelRange: mRanges)
|
||||
{
|
||||
channelRange.setOverlapping(false);
|
||||
}
|
||||
|
||||
for(int x = 0; x < mRanges.size(); x++)
|
||||
{
|
||||
if(!mRanges.get(x).isValid())
|
||||
{
|
||||
mInvalid = true;
|
||||
mValidProperty.set(false);
|
||||
}
|
||||
|
||||
for(int y = x + 1; y < mRanges.size(); y++)
|
||||
{
|
||||
if(mRanges.get(x).overlaps(mRanges.get(y)))
|
||||
{
|
||||
mInvalid = true;
|
||||
mRanges.get(x).setOverlapping(true);
|
||||
mRanges.get(y).setOverlapping(true);
|
||||
mValidProperty.set(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -128,15 +161,19 @@ public class ChannelMap
|
||||
public void addRange(ChannelRange range)
|
||||
{
|
||||
mRanges.add(range);
|
||||
|
||||
validate();
|
||||
}
|
||||
|
||||
public void removeRange(ChannelRange range)
|
||||
{
|
||||
mRanges.remove(range);
|
||||
|
||||
validate();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Creates an observable property extractor for use with observable lists to detect changes internal to this object.
|
||||
*/
|
||||
public static Callback<ChannelMap,Observable[]> extractor()
|
||||
{
|
||||
return (ChannelMap c) -> new Observable[] {c.nameProperty(), c.validProperty(), c.getItems()};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,322 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* 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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,128 +0,0 @@
|
||||
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 );
|
||||
}
|
||||
}
|
||||
@@ -1,60 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* 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 );
|
||||
}
|
||||
} );
|
||||
}
|
||||
}
|
||||
@@ -1,14 +1,37 @@
|
||||
/*
|
||||
*
|
||||
* * ******************************************************************************
|
||||
* * Copyright (C) 2014-2020 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.sample.Broadcaster;
|
||||
import io.github.dsheirer.sample.Listener;
|
||||
import javafx.collections.FXCollections;
|
||||
import javafx.collections.ObservableList;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.AbstractListModel;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
public class ChannelMapModel extends AbstractListModel<ChannelMap>
|
||||
@@ -16,19 +39,32 @@ 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 ObservableList<ChannelMap> mChannelMaps = FXCollections.observableArrayList(ChannelMap.extractor());
|
||||
private Broadcaster<ChannelMapEvent> mEventBroadcaster = new Broadcaster<>();
|
||||
|
||||
public ChannelMapModel()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes all channel maps from this model and broadcasts a remove/delete event for each.
|
||||
*/
|
||||
public void clear()
|
||||
{
|
||||
List<ChannelMap> channelMaps = new ArrayList<>(mChannelMaps);
|
||||
|
||||
for(ChannelMap channelMap: channelMaps)
|
||||
{
|
||||
removeChannelMap(channelMap);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an unmodifiable list of channel maps currently in the model
|
||||
*/
|
||||
public List<ChannelMap> getChannelMaps()
|
||||
public ObservableList<ChannelMap> getChannelMaps()
|
||||
{
|
||||
return Collections.unmodifiableList(mChannelMaps);
|
||||
return mChannelMaps;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -112,7 +148,6 @@ public class ChannelMapModel extends AbstractListModel<ChannelMap>
|
||||
|
||||
broadcast(new ChannelMapEvent(channelMap, Event.ADD));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,25 +1,36 @@
|
||||
/*******************************************************************************
|
||||
* 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.
|
||||
* * ******************************************************************************
|
||||
* * Copyright (C) 2014-2020 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/>
|
||||
* * *****************************************************************************
|
||||
*
|
||||
* 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 com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
|
||||
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlRootElement;
|
||||
import javafx.beans.Observable;
|
||||
import javafx.beans.binding.Bindings;
|
||||
import javafx.beans.property.BooleanProperty;
|
||||
import javafx.beans.property.IntegerProperty;
|
||||
import javafx.beans.property.SimpleBooleanProperty;
|
||||
import javafx.beans.property.SimpleIntegerProperty;
|
||||
import javafx.util.Callback;
|
||||
|
||||
import java.text.DecimalFormat;
|
||||
|
||||
@@ -28,28 +39,64 @@ 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;
|
||||
private IntegerProperty mFirstChannel = new SimpleIntegerProperty(1);
|
||||
private IntegerProperty mLastChannel = new SimpleIntegerProperty(1023);
|
||||
private IntegerProperty mBaseFrequency = new SimpleIntegerProperty(450000000);
|
||||
private IntegerProperty mStepSize = new SimpleIntegerProperty(12500);
|
||||
private BooleanProperty mValid = new SimpleBooleanProperty();
|
||||
private BooleanProperty mOverlapping = new SimpleBooleanProperty();
|
||||
|
||||
public ChannelRange()
|
||||
{
|
||||
//Bind valid to first channel is less than last channel
|
||||
mValid.bind(Bindings.lessThan(mFirstChannel, mLastChannel));
|
||||
}
|
||||
|
||||
public ChannelRange(int first, int last, int base, int size)
|
||||
public ChannelRange(int first, int last, int base, int sizeProperty)
|
||||
{
|
||||
mFirst = first;
|
||||
mLast = last;
|
||||
mBase = base;
|
||||
mSize = size;
|
||||
this();
|
||||
mFirstChannel.set(first);
|
||||
mLastChannel.set(last);
|
||||
mBaseFrequency.set(base);
|
||||
mStepSize.set(sizeProperty);
|
||||
}
|
||||
|
||||
public ChannelRange copyOf()
|
||||
{
|
||||
return new ChannelRange(mFirst, mLast, mBase, mSize);
|
||||
return new ChannelRange(mFirstChannel.get(), mLastChannel.get(), mBaseFrequency.get(), mStepSize.get());
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates if this channel range overlaps with another channel range
|
||||
*/
|
||||
public BooleanProperty overlappingProperty()
|
||||
{
|
||||
return mOverlapping;
|
||||
}
|
||||
|
||||
public BooleanProperty validProperty()
|
||||
{
|
||||
return mValid;
|
||||
}
|
||||
|
||||
public IntegerProperty firstChannelProperty()
|
||||
{
|
||||
return mFirstChannel;
|
||||
}
|
||||
|
||||
public IntegerProperty lastChannelProperty()
|
||||
{
|
||||
return mLastChannel;
|
||||
}
|
||||
|
||||
public IntegerProperty baseFrequencyProperty()
|
||||
{
|
||||
return mBaseFrequency;
|
||||
}
|
||||
|
||||
public IntegerProperty stepSizeProperty()
|
||||
{
|
||||
return mStepSize;
|
||||
}
|
||||
|
||||
@JsonIgnore
|
||||
@@ -60,15 +107,15 @@ public class ChannelRange
|
||||
if(isValid())
|
||||
{
|
||||
sb.append("First: ");
|
||||
sb.append(mFirst).append("=");
|
||||
sb.append(mFirstChannel.get()).append("=");
|
||||
|
||||
long frequency = getFrequency(mFirst);
|
||||
long frequency = getFrequency(mFirstChannel.get());
|
||||
|
||||
sb.append(FREQUENCY_FORMATTER.format((double)frequency / 1E6D));
|
||||
sb.append(" Last: ");
|
||||
sb.append(mLast).append("=");
|
||||
sb.append(mLastChannel.get()).append("=");
|
||||
|
||||
frequency = getFrequency(mLast);
|
||||
frequency = getFrequency(mLastChannel.get());
|
||||
sb.append(FREQUENCY_FORMATTER.format((double)frequency / 1E6D));
|
||||
sb.append(" MHz");
|
||||
}
|
||||
@@ -93,38 +140,38 @@ public class ChannelRange
|
||||
@JsonIgnore
|
||||
public boolean isValid()
|
||||
{
|
||||
return mFirst < mLast;
|
||||
return mValid.get();
|
||||
}
|
||||
|
||||
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);
|
||||
return (mFirstChannel.get() <= other.mFirstChannel.get() && other.mFirstChannel.get() <= mLastChannel.get()) ||
|
||||
(mFirstChannel.get() <= other.mLastChannel.get() && other.mLastChannel.get() <= mLastChannel.get()) ||
|
||||
(mFirstChannel.get() <= other.mFirstChannel.get() && other.mLastChannel.get() <= mLastChannel.get()) ||
|
||||
(other.mFirstChannel.get() <= mFirstChannel.get() && mLastChannel.get() <= other.mLastChannel.get());
|
||||
}
|
||||
|
||||
@JsonIgnore
|
||||
public boolean isOverlapping()
|
||||
{
|
||||
return mOverlapping;
|
||||
return mOverlapping.get();
|
||||
}
|
||||
|
||||
public void setOverlapping(boolean overlapping)
|
||||
{
|
||||
mOverlapping = overlapping;
|
||||
mOverlapping.set(overlapping);
|
||||
}
|
||||
|
||||
public boolean hasChannel(int channel)
|
||||
{
|
||||
return mFirst <= channel && channel <= mLast;
|
||||
return mFirstChannel.get() <= channel && channel <= mLastChannel.get();
|
||||
}
|
||||
|
||||
public long getFrequency(int channel)
|
||||
{
|
||||
if(hasChannel(channel))
|
||||
{
|
||||
return mBase + ((channel - mFirst) * mSize);
|
||||
return mBaseFrequency.get() + ((channel - mFirstChannel.get()) * mStepSize.get());
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -135,54 +182,63 @@ public class ChannelRange
|
||||
@JacksonXmlProperty(isAttribute = true, localName = "first")
|
||||
public int getFirstChannelNumber()
|
||||
{
|
||||
return mFirst;
|
||||
return mFirstChannel.get();
|
||||
}
|
||||
|
||||
public void setFirstChannelNumber(int first)
|
||||
{
|
||||
mFirst = first;
|
||||
mFirstChannel.set(first);;
|
||||
}
|
||||
|
||||
@JacksonXmlProperty(isAttribute = true, localName = "last")
|
||||
public void setLastChannelNumber(int last)
|
||||
{
|
||||
mLast = last;
|
||||
mLastChannel.set(last);;
|
||||
}
|
||||
|
||||
public int getLastChannelNumber()
|
||||
{
|
||||
return mLast;
|
||||
return mLastChannel.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the base frequency
|
||||
*
|
||||
* @param base frequency in hertz
|
||||
* @param baseFrequency frequency in hertz
|
||||
*/
|
||||
@JacksonXmlProperty(isAttribute = true, localName = "base")
|
||||
public void setBase(int base)
|
||||
public void setBaseFrequency(int baseFrequency)
|
||||
{
|
||||
mBase = base;
|
||||
this.mBaseFrequency.set(baseFrequency);
|
||||
}
|
||||
|
||||
public int getBase()
|
||||
public int getBaseFrequency()
|
||||
{
|
||||
return mBase;
|
||||
return mBaseFrequency.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the channel size
|
||||
*
|
||||
* @param size in hertz
|
||||
* @param stepSize in hertz
|
||||
*/
|
||||
@JacksonXmlProperty(isAttribute = true, localName = "size")
|
||||
public void setSize(int size)
|
||||
public void setStepSize(int stepSize)
|
||||
{
|
||||
mSize = size;
|
||||
this.mStepSize.set(stepSize);
|
||||
}
|
||||
|
||||
public int getSize()
|
||||
public int getStepSize()
|
||||
{
|
||||
return mSize;
|
||||
return mStepSize.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an observable property extractor for use with obserable lists to detect changes internal to this object.
|
||||
*/
|
||||
public static Callback<ChannelRange,Observable[]> extractor()
|
||||
{
|
||||
return (ChannelRange c) -> new Observable[] {c.firstChannelProperty(), c.lastChannelProperty(),
|
||||
c.baseFrequencyProperty(), c.stepSizeProperty(), c.validProperty(), c.overlappingProperty()};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,20 +1,24 @@
|
||||
/*******************************************************************************
|
||||
* 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/>
|
||||
******************************************************************************/
|
||||
/*
|
||||
*
|
||||
* * ******************************************************************************
|
||||
* * Copyright (C) 2014-2020 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;
|
||||
@@ -176,9 +180,9 @@ public class ChannelRangeModel extends AbstractTableModel
|
||||
case STOP:
|
||||
return range.getLastChannelNumber();
|
||||
case BASE:
|
||||
return range.getBase();
|
||||
return range.getBaseFrequency();
|
||||
case SIZE:
|
||||
return range.getSize();
|
||||
return range.getStepSize();
|
||||
case ERROR:
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
@@ -266,10 +270,10 @@ public class ChannelRangeModel extends AbstractTableModel
|
||||
range.setLastChannelNumber( parsedValue );
|
||||
break;
|
||||
case BASE:
|
||||
range.setBase( parsedValue );
|
||||
range.setBaseFrequency( parsedValue );
|
||||
break;
|
||||
case SIZE:
|
||||
range.setSize( parsedValue );
|
||||
range.setStepSize( parsedValue );
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user