mirror of
https://github.com/DSheirer/sdrtrunk.git
synced 2026-11-12 16:47:31 -05:00
61 Rotating Control Channel Support - P25 Decoder (#475)
* Initial work on add support for rotating control channels. * Creates multiple frequency editor control. * Updates channel source editor to allow user to select multi-frequency configuration and add multiple frequencies. * Adds full support for rotating control channels to the P25 channelizer. Users define multiple frequencies in the channel source configuration. The decoder monitors the state of the channel for a CONTROL state. If the state does not stay in the CONTROL state for a user-specified delay value, then the decoder rotates to the next frequency in the list. This behavior continues until the decoder state once again changes to CONTROL. This is NOT a 'scanner' feature.
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* ******************************************************************************
|
||||
* sdrtrunk
|
||||
* Copyright (C) 2014-2018 Dennis Sheirer
|
||||
* Copyright (C) 2014-2019 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
|
||||
@@ -154,7 +154,7 @@ public class SDRTrunk implements Listener<TunerEvent>
|
||||
mSourceManager = new SourceManager(tunerModel, mSettingsManager, mUserPreferences);
|
||||
|
||||
mChannelProcessingManager = new ChannelProcessingManager(channelMapModel, eventLogManager, recorderManager,
|
||||
mSourceManager, aliasModel);
|
||||
mSourceManager, aliasModel, mUserPreferences);
|
||||
|
||||
mChannelModel.addListener(mChannelProcessingManager);
|
||||
mChannelProcessingManager.addChannelEventListener(mChannelModel);
|
||||
|
||||
@@ -0,0 +1,494 @@
|
||||
/*
|
||||
* ******************************************************************************
|
||||
* sdrtrunk
|
||||
* Copyright (C) 2014-2019 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.gui.control;
|
||||
|
||||
import net.miginfocom.swing.MigLayout;
|
||||
import org.kordamp.ikonli.fontawesome.FontAwesome;
|
||||
import org.kordamp.ikonli.swing.FontIcon;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.swing.AbstractAction;
|
||||
import javax.swing.DefaultCellEditor;
|
||||
import javax.swing.JButton;
|
||||
import javax.swing.JFormattedTextField;
|
||||
import javax.swing.JFrame;
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.JScrollPane;
|
||||
import javax.swing.JTable;
|
||||
import javax.swing.JTextField;
|
||||
import javax.swing.KeyStroke;
|
||||
import javax.swing.ListSelectionModel;
|
||||
import javax.swing.event.ListSelectionEvent;
|
||||
import javax.swing.event.ListSelectionListener;
|
||||
import javax.swing.table.AbstractTableModel;
|
||||
import javax.swing.table.DefaultTableCellRenderer;
|
||||
import javax.swing.text.DefaultFormatterFactory;
|
||||
import javax.swing.text.NumberFormatter;
|
||||
import java.awt.Component;
|
||||
import java.awt.Dimension;
|
||||
import java.awt.EventQueue;
|
||||
import java.awt.Rectangle;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.KeyEvent;
|
||||
import java.text.DecimalFormat;
|
||||
import java.text.NumberFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Multiple frequency editor control for editing an ordered list of frequencies. Frequency values are persisted as
|
||||
* long values in hertz, but presented to the user as double values in megahertz.
|
||||
*
|
||||
*/
|
||||
public class MultipleFrequencyEditor extends JPanel
|
||||
{
|
||||
private final static Logger mLog = LoggerFactory.getLogger(MultipleFrequencyEditor.class);
|
||||
private static final Long DEFAULT_FREQUENCY = 0l;
|
||||
private DecimalFormat mDecimalFormat = new DecimalFormat("0.000000");
|
||||
private FrequencyModel mFrequencyModel = new FrequencyModel();
|
||||
private JTable mFrequencyTable;
|
||||
private JButton mAddButton;
|
||||
private JButton mRemoveButton;
|
||||
private JButton mMoveUpButton;
|
||||
private JButton mMoveDownButton;
|
||||
|
||||
/**
|
||||
* Constructs the editor
|
||||
* @param minimum allowable frequency value
|
||||
* @param maximum allowable frequency value
|
||||
*/
|
||||
public MultipleFrequencyEditor(double minimum, double maximum)
|
||||
{
|
||||
setLayout(new MigLayout("insets 0 0 0 0", "[]1[grow,fill]1[]", "[]1[]1[top,grow,fill]"));
|
||||
|
||||
mMoveUpButton = new JButton();
|
||||
mMoveUpButton.setIcon(FontIcon.of(FontAwesome.ARROW_UP, 10));
|
||||
mMoveUpButton.setEnabled(false);
|
||||
mMoveUpButton.addActionListener(e -> {
|
||||
int selectedRow = mFrequencyTable.getSelectedRow();
|
||||
int index = mFrequencyTable.convertRowIndexToModel(selectedRow);
|
||||
mFrequencyModel.moveUp(index);
|
||||
mFrequencyTable.getSelectionModel().setLeadSelectionIndex(index - 1);
|
||||
mFrequencyTable.scrollRectToVisible(new Rectangle(mFrequencyTable.getCellRect(selectedRow, 0, true)));
|
||||
|
||||
updateButtons();
|
||||
});
|
||||
add(mMoveUpButton);
|
||||
|
||||
mFrequencyTable = new JTable(mFrequencyModel);
|
||||
//testing ...
|
||||
mFrequencyTable.setPreferredScrollableViewportSize(new Dimension(100, 65));
|
||||
mFrequencyTable.setFillsViewportHeight(true);
|
||||
|
||||
mFrequencyTable.getSelectionModel().setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
|
||||
mFrequencyTable.getSelectionModel().addListSelectionListener(new SelectionListener());
|
||||
mFrequencyTable.setDefaultEditor(Long.class, new FrequencyEditor(minimum, maximum));
|
||||
mFrequencyTable.setDefaultRenderer(Long.class, new FrequencyRenderer());
|
||||
JScrollPane listScrollPane = new JScrollPane(mFrequencyTable);
|
||||
add(listScrollPane, "span 1 3");
|
||||
|
||||
mAddButton = new JButton();
|
||||
mAddButton.setIcon(FontIcon.of(FontAwesome.PLUS, 10));
|
||||
mAddButton.addActionListener(e -> {
|
||||
int selectedRow = mFrequencyTable.getSelectedRow();
|
||||
if(selectedRow >= 0)
|
||||
{
|
||||
int index = mFrequencyTable.convertRowIndexToModel(selectedRow);
|
||||
int nextIndex = index + 1;
|
||||
mFrequencyModel.add(DEFAULT_FREQUENCY, nextIndex);
|
||||
mFrequencyTable.requestFocus();
|
||||
mFrequencyTable.setRowSelectionInterval(nextIndex, nextIndex);
|
||||
mFrequencyTable.scrollRectToVisible(new Rectangle(mFrequencyTable.getCellRect(nextIndex, 0, true)));
|
||||
mFrequencyTable.editCellAt(nextIndex, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
mFrequencyModel.add(DEFAULT_FREQUENCY);
|
||||
int lastIndex = mFrequencyModel.getRowCount() - 1;
|
||||
mFrequencyTable.setRowSelectionInterval(lastIndex, lastIndex);
|
||||
mFrequencyTable.editCellAt(lastIndex, 0);
|
||||
}
|
||||
|
||||
mFrequencyTable.setSurrendersFocusOnKeystroke(true);
|
||||
mFrequencyTable.getEditorComponent().requestFocus();
|
||||
|
||||
updateButtons();
|
||||
});
|
||||
add(mAddButton, "wrap");
|
||||
|
||||
mMoveDownButton = new JButton();
|
||||
mMoveDownButton.setIcon(FontIcon.of(FontAwesome.ARROW_DOWN, 10));
|
||||
mMoveDownButton.setEnabled(false);
|
||||
mMoveDownButton.addActionListener(e -> {
|
||||
int selectedRow = mFrequencyTable.getSelectedRow();
|
||||
int index = mFrequencyTable.convertRowIndexToModel(selectedRow);
|
||||
mFrequencyModel.moveDown(index);
|
||||
mFrequencyTable.getSelectionModel().setLeadSelectionIndex(index + 1);
|
||||
mFrequencyTable.scrollRectToVisible(new Rectangle(mFrequencyTable.getCellRect(selectedRow, 0, true)));
|
||||
|
||||
updateButtons();
|
||||
});
|
||||
add(mMoveDownButton);
|
||||
|
||||
mRemoveButton = new JButton();
|
||||
mRemoveButton.setIcon(FontIcon.of(FontAwesome.MINUS, 10));
|
||||
mRemoveButton.setEnabled(false);
|
||||
mRemoveButton.addActionListener(e -> {
|
||||
int selectedRow = mFrequencyTable.getSelectedRow();
|
||||
int selectedIndex = mFrequencyTable.convertRowIndexToModel(selectedRow);
|
||||
mFrequencyModel.remove(selectedIndex);
|
||||
|
||||
if(selectedIndex < mFrequencyModel.getRowCount())
|
||||
{
|
||||
int reselectedRow = mFrequencyTable.convertRowIndexToView(selectedIndex);
|
||||
mFrequencyTable.setRowSelectionInterval(reselectedRow, reselectedRow);
|
||||
mFrequencyTable.scrollRectToVisible(new Rectangle(mFrequencyTable.getCellRect(reselectedRow, 0, true)));
|
||||
}
|
||||
|
||||
updateButtons();
|
||||
});
|
||||
add(mRemoveButton);
|
||||
}
|
||||
|
||||
public FrequencyModel getFrequencyModel()
|
||||
{
|
||||
return mFrequencyModel;
|
||||
}
|
||||
|
||||
private void updateButtons()
|
||||
{
|
||||
int index = mFrequencyTable.getSelectedRow();
|
||||
|
||||
if(index >= 0)
|
||||
{
|
||||
mMoveUpButton.setEnabled(index > 0);
|
||||
mMoveDownButton.setEnabled(index < mFrequencyModel.getRowCount() - 1);
|
||||
mRemoveButton.setEnabled(true);
|
||||
}
|
||||
else
|
||||
{
|
||||
mRemoveButton.setEnabled(false);
|
||||
mMoveUpButton.setEnabled(false);
|
||||
mMoveDownButton.setEnabled(false);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats the frequency value as MHz
|
||||
*/
|
||||
private String format(long frequency)
|
||||
{
|
||||
return mDecimalFormat.format(frequency / 1e6d);
|
||||
}
|
||||
|
||||
public class FrequencyModel extends AbstractTableModel
|
||||
{
|
||||
private List<Long> mFrequencies = new ArrayList<>();
|
||||
|
||||
@Override
|
||||
public String getColumnName(int column)
|
||||
{
|
||||
return "Frequencies (MHz)";
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getRowCount()
|
||||
{
|
||||
return mFrequencies.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getColumnCount()
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getValueAt(int rowIndex, int columnIndex)
|
||||
{
|
||||
return mFrequencies.get(rowIndex);
|
||||
}
|
||||
|
||||
public void setValueAt(int index, Long value)
|
||||
{
|
||||
mFrequencies.set(index, value);
|
||||
fireTableRowsUpdated(index, index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCellEditable(int rowIndex, int columnIndex)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> getColumnClass(int columnIndex)
|
||||
{
|
||||
return Long.class;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a frequency to the model
|
||||
*/
|
||||
public void add(long frequency, int index)
|
||||
{
|
||||
mFrequencies.add(index, frequency);
|
||||
fireTableRowsInserted(index, index);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a frequency to the model
|
||||
*/
|
||||
public void add(long frequency)
|
||||
{
|
||||
mFrequencies.add(frequency);
|
||||
fireTableRowsInserted(mFrequencies.size() - 1, mFrequencies.size() - 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes an indexed value from the model
|
||||
*/
|
||||
public Long remove(int index)
|
||||
{
|
||||
Long value = mFrequencies.remove(index);
|
||||
fireTableRowsDeleted(index, index);
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes all frequencies from the model
|
||||
*/
|
||||
public void clear()
|
||||
{
|
||||
mFrequencies.clear();
|
||||
fireTableDataChanged();
|
||||
}
|
||||
|
||||
/**
|
||||
* Moves the frequency up in the list
|
||||
*/
|
||||
public void moveUp(int index)
|
||||
{
|
||||
if(index > 0 && index < mFrequencies.size())
|
||||
{
|
||||
Long valueA = mFrequencies.get(index);
|
||||
Long valueB = mFrequencies.get(index - 1);
|
||||
mFrequencies.set(index, valueB);
|
||||
mFrequencies.set(index - 1, valueA);
|
||||
|
||||
fireTableRowsUpdated(index - 1, index);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Moves the frequency down in the list
|
||||
*/
|
||||
public void moveDown(int index)
|
||||
{
|
||||
if(index >= 0 && index + 1 < mFrequencies.size())
|
||||
{
|
||||
Long valueA = mFrequencies.get(index);
|
||||
Long valueB = mFrequencies.get(index + 1);
|
||||
mFrequencies.set(index, valueB);
|
||||
mFrequencies.set(index + 1, valueA);
|
||||
|
||||
fireTableRowsUpdated(index, index + 1);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the frequencies for the model
|
||||
*/
|
||||
public void setFrequencies(List<Long> frequencies)
|
||||
{
|
||||
mFrequencies.clear();
|
||||
mFrequencies.addAll(frequencies);
|
||||
fireTableDataChanged();
|
||||
}
|
||||
|
||||
/**
|
||||
* List of long frequency values contained in the model
|
||||
*/
|
||||
public List<Long> getFrequencies()
|
||||
{
|
||||
List<Long> frequencies = new ArrayList<>();
|
||||
frequencies.addAll(mFrequencies);
|
||||
return frequencies;
|
||||
}
|
||||
}
|
||||
|
||||
public class SelectionListener implements ListSelectionListener
|
||||
{
|
||||
@Override
|
||||
public void valueChanged(ListSelectionEvent event)
|
||||
{
|
||||
updateButtons();
|
||||
}
|
||||
}
|
||||
|
||||
public class FrequencyEditor extends DefaultCellEditor
|
||||
{
|
||||
private JFormattedTextField mTextField;
|
||||
private NumberFormat mNumberFormat;
|
||||
private Double mMinimum;
|
||||
private Double mMaximum;
|
||||
|
||||
public FrequencyEditor(double minValue, double maxValue)
|
||||
{
|
||||
super(new JFormattedTextField());
|
||||
mTextField = (JFormattedTextField)getComponent();
|
||||
mMinimum = minValue;
|
||||
mMaximum = maxValue;
|
||||
|
||||
mNumberFormat = NumberFormat.getNumberInstance();
|
||||
mNumberFormat.setMinimumFractionDigits(1);
|
||||
mNumberFormat.setMaximumFractionDigits(6);
|
||||
mNumberFormat.setGroupingUsed(false);
|
||||
NumberFormatter numberFormatter = new NumberFormatter(mNumberFormat);
|
||||
numberFormatter.setFormat(mNumberFormat);
|
||||
numberFormatter.setMinimum(mMinimum);
|
||||
numberFormatter.setMaximum(mMaximum);
|
||||
numberFormatter.setAllowsInvalid(false);
|
||||
|
||||
mTextField.setFormatterFactory(new DefaultFormatterFactory(numberFormatter));
|
||||
mTextField.setValue(mMinimum);
|
||||
mTextField.setHorizontalAlignment(JTextField.TRAILING);
|
||||
mTextField.setFocusLostBehavior(JFormattedTextField.PERSIST);
|
||||
mTextField.getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0), "check");
|
||||
mTextField.getActionMap().put("check", new AbstractAction()
|
||||
{
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent event)
|
||||
{
|
||||
//This should always be true since we use formatter.setAllowsInvalid(false) above.
|
||||
if(mTextField.isEditValid())
|
||||
{
|
||||
int selectedRow = mFrequencyTable.getSelectedRow();
|
||||
int selectedIndex = mFrequencyTable.convertRowIndexToModel(selectedRow);
|
||||
|
||||
try
|
||||
{
|
||||
Double frequencyMHz = Double.parseDouble(mTextField.getText());
|
||||
|
||||
long frequency = (long)(frequencyMHz * 1e6d);
|
||||
mFrequencyModel.setValueAt(selectedIndex, frequency);
|
||||
}
|
||||
catch(Exception err)
|
||||
{
|
||||
mLog.error("Error parsing frequency: " + mTextField.getText(), err);
|
||||
}
|
||||
|
||||
fireEditingStopped();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
//Override to invoke setValue on the formatted text field.
|
||||
public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column)
|
||||
{
|
||||
JFormattedTextField ftf = (JFormattedTextField)super.getTableCellEditorComponent(table, value, isSelected, row, column);
|
||||
|
||||
if(value instanceof Long)
|
||||
{
|
||||
ftf.setValue(((Long)value).doubleValue() / 1e6d);
|
||||
}
|
||||
else
|
||||
{
|
||||
ftf.setValue(value);
|
||||
}
|
||||
|
||||
ftf.requestFocus();
|
||||
ftf.selectAll();
|
||||
ftf.setCaretPosition(0);
|
||||
return ftf;
|
||||
}
|
||||
|
||||
//Override to ensure that the value remains a Double.
|
||||
public Object getCellEditorValue()
|
||||
{
|
||||
JFormattedTextField ftf = (JFormattedTextField)getComponent();
|
||||
Object o = ftf.getValue();
|
||||
if(o instanceof Double)
|
||||
{
|
||||
return o;
|
||||
}
|
||||
else if(o instanceof Number)
|
||||
{
|
||||
return ((Number)o).doubleValue();
|
||||
}
|
||||
else
|
||||
{
|
||||
try
|
||||
{
|
||||
return Double.parseDouble(o.toString());
|
||||
}
|
||||
catch(Exception exc)
|
||||
{
|
||||
return new Double(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Table cell renderer to display long hertz values as double megahertz values
|
||||
*/
|
||||
public class FrequencyRenderer extends DefaultTableCellRenderer
|
||||
{
|
||||
|
||||
public FrequencyRenderer()
|
||||
{
|
||||
super.setHorizontalAlignment(JLabel.RIGHT);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column)
|
||||
{
|
||||
JLabel cell = (JLabel)super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
|
||||
|
||||
if(value instanceof Long)
|
||||
{
|
||||
cell.setText(format((long)value));
|
||||
}
|
||||
else
|
||||
{
|
||||
cell.setText("??");
|
||||
}
|
||||
|
||||
return cell;
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String[] args)
|
||||
{
|
||||
EventQueue.invokeLater(() -> {
|
||||
final JFrame frame = new JFrame();
|
||||
frame.setSize(new Dimension(400, 400));
|
||||
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
|
||||
frame.add(new MultipleFrequencyEditor(1.0, 6000.0));
|
||||
frame.setLocationRelativeTo(null);
|
||||
frame.setVisible(true);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* ******************************************************************************
|
||||
* sdrtrunk
|
||||
* Copyright (C) 2014-2018 Dennis Sheirer
|
||||
* Copyright (C) 2014-2019 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
|
||||
@@ -21,6 +21,7 @@
|
||||
package io.github.dsheirer.gui.preference;
|
||||
|
||||
import io.github.dsheirer.gui.preference.directory.DirectoryPreferenceEditor;
|
||||
import io.github.dsheirer.gui.preference.tuner.ChannelMultipleFrequencyPreferenceEditor;
|
||||
import io.github.dsheirer.gui.preference.tuner.TunerPreferenceEditor;
|
||||
import io.github.dsheirer.preference.UserPreferences;
|
||||
import javafx.scene.Node;
|
||||
@@ -38,9 +39,11 @@ public class PreferenceEditorFactory
|
||||
return new DecodeEventViewPreferenceEditor(userPreferences);
|
||||
case DIRECTORY:
|
||||
return new DirectoryPreferenceEditor(userPreferences);
|
||||
case SOURCE_CHANNEL_MULTIPLE_FREQUENCY:
|
||||
return new ChannelMultipleFrequencyPreferenceEditor(userPreferences);
|
||||
case TALKGROUP_FORMAT:
|
||||
return new TalkgroupFormatPreferenceEditor(userPreferences);
|
||||
case TUNER:
|
||||
case SOURCE_TUNER_CHANNELIZER:
|
||||
return new TunerPreferenceEditor(userPreferences);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* ******************************************************************************
|
||||
* sdrtrunk
|
||||
* Copyright (C) 2014-2018 Dennis Sheirer
|
||||
* Copyright (C) 2014-2019 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
|
||||
@@ -27,8 +27,9 @@ public enum PreferenceEditorType
|
||||
{
|
||||
CHANNEL_EVENT("Channel Events"),
|
||||
DIRECTORY("Directories"),
|
||||
TALKGROUP_FORMAT("Talkgroups"),
|
||||
TUNER("Tuners");
|
||||
SOURCE_CHANNEL_MULTIPLE_FREQUENCY("Channel - Multiple Frequency"),
|
||||
SOURCE_TUNER_CHANNELIZER("Tuner Channelizer"),
|
||||
TALKGROUP_FORMAT("Talkgroups");
|
||||
|
||||
private String mLabel;
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* ******************************************************************************
|
||||
* sdrtrunk
|
||||
* Copyright (C) 2014-2018 Dennis Sheirer
|
||||
* Copyright (C) 2014-2019 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
|
||||
@@ -201,7 +201,8 @@ public class PreferencesEditor extends Application
|
||||
storageItem.setExpanded(true);
|
||||
|
||||
TreeItem<String> sourceItem = new TreeItem<>("Source");
|
||||
sourceItem.getChildren().add(new TreeItem(PreferenceEditorType.TUNER));
|
||||
sourceItem.getChildren().add(new TreeItem(PreferenceEditorType.SOURCE_CHANNEL_MULTIPLE_FREQUENCY));
|
||||
sourceItem.getChildren().add(new TreeItem(PreferenceEditorType.SOURCE_TUNER_CHANNELIZER));
|
||||
treeRoot.getChildren().add(sourceItem);
|
||||
sourceItem.setExpanded(true);
|
||||
|
||||
|
||||
@@ -0,0 +1,123 @@
|
||||
/*
|
||||
* ******************************************************************************
|
||||
* sdrtrunk
|
||||
* Copyright (C) 2014-2019 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.gui.preference.tuner;
|
||||
|
||||
import io.github.dsheirer.preference.UserPreferences;
|
||||
import io.github.dsheirer.preference.source.ChannelMultiFrequencyPreference;
|
||||
import javafx.geometry.Insets;
|
||||
import javafx.scene.control.Button;
|
||||
import javafx.scene.control.Label;
|
||||
import javafx.scene.control.Spinner;
|
||||
import javafx.scene.layout.GridPane;
|
||||
import javafx.scene.layout.HBox;
|
||||
|
||||
|
||||
/**
|
||||
* Preference settings for channel source configurations containing multiple frequencies
|
||||
*/
|
||||
public class ChannelMultipleFrequencyPreferenceEditor extends HBox
|
||||
{
|
||||
private static final String HELP_TEXT = "Rotation delay determines how long a decoder will wait before rotating " +
|
||||
"to the next frequency in a list for a rotating control channel configuration.";
|
||||
|
||||
private static final Insets MARGINS = new Insets(5,5,5,5);
|
||||
private ChannelMultiFrequencyPreference mChannelMultiFrequencyPreference;
|
||||
private GridPane mEditorPane;
|
||||
private Label mRotationDelayLabel;
|
||||
private Spinner<Integer> mRotationDelaySpinner;
|
||||
private Button mResetButton;
|
||||
private Label mHelpText;
|
||||
|
||||
public ChannelMultipleFrequencyPreferenceEditor(UserPreferences userPreferences)
|
||||
{
|
||||
mChannelMultiFrequencyPreference = userPreferences.getChannelMultiFrequencyPreference();
|
||||
getChildren().add(getEditorPane());
|
||||
}
|
||||
|
||||
private GridPane getEditorPane()
|
||||
{
|
||||
if(mEditorPane == null)
|
||||
{
|
||||
mEditorPane = new GridPane();
|
||||
GridPane.setMargin(getRotationDelaySpinner(), new Insets(5, 5, 5, 5));
|
||||
mEditorPane.add(getRotationDelaySpinner(), 0, 0);
|
||||
GridPane.setMargin(getRotationDelayLabel(), new Insets(5, 5, 5, 5));
|
||||
mEditorPane.add(getRotationDelayLabel(), 1, 0);
|
||||
GridPane.setMargin(getResetButton(), MARGINS);
|
||||
mEditorPane.add(getResetButton(), 2, 0);
|
||||
GridPane.setMargin(getHelpText(), MARGINS);
|
||||
mEditorPane.add(getHelpText(), 0, 1, 3, 1);
|
||||
}
|
||||
|
||||
return mEditorPane;
|
||||
}
|
||||
|
||||
public Spinner<Integer> getRotationDelaySpinner()
|
||||
{
|
||||
if(mRotationDelaySpinner == null)
|
||||
{
|
||||
int currentDelay = (int)(mChannelMultiFrequencyPreference.getRotationDelay() / 1000);
|
||||
mRotationDelaySpinner = new Spinner<>(1, 60, currentDelay, 1);
|
||||
mRotationDelaySpinner.setEditable(true);
|
||||
mRotationDelaySpinner.valueProperty().addListener((observable, oldValue, newValue) -> {
|
||||
mChannelMultiFrequencyPreference.setRotationDelay(newValue * 1000);
|
||||
});
|
||||
}
|
||||
|
||||
return mRotationDelaySpinner;
|
||||
}
|
||||
|
||||
public Label getRotationDelayLabel()
|
||||
{
|
||||
if(mRotationDelayLabel == null)
|
||||
{
|
||||
mRotationDelayLabel = new Label("Rotation Delay (seconds)");
|
||||
}
|
||||
|
||||
return mRotationDelayLabel;
|
||||
}
|
||||
|
||||
public Button getResetButton()
|
||||
{
|
||||
if(mResetButton == null)
|
||||
{
|
||||
mResetButton = new Button("Reset");
|
||||
mResetButton.setOnAction(event -> {
|
||||
mChannelMultiFrequencyPreference.resetRotationDelay();
|
||||
int currentDelay = (int)(mChannelMultiFrequencyPreference.getRotationDelay() / 1000);
|
||||
getRotationDelaySpinner().getValueFactory().setValue(currentDelay);
|
||||
});
|
||||
}
|
||||
|
||||
return mResetButton;
|
||||
}
|
||||
|
||||
public Label getHelpText()
|
||||
{
|
||||
if(mHelpText == null)
|
||||
{
|
||||
mHelpText = new Label(HELP_TEXT);
|
||||
mHelpText.setWrapText(true);
|
||||
}
|
||||
|
||||
return mHelpText;
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* ******************************************************************************
|
||||
* sdrtrunk
|
||||
* Copyright (C) 2014-2018 Dennis Sheirer
|
||||
* Copyright (C) 2014-2019 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
|
||||
@@ -21,8 +21,8 @@
|
||||
package io.github.dsheirer.gui.preference.tuner;
|
||||
|
||||
import io.github.dsheirer.preference.UserPreferences;
|
||||
import io.github.dsheirer.preference.tuner.ChannelizerType;
|
||||
import io.github.dsheirer.preference.tuner.TunerPreference;
|
||||
import io.github.dsheirer.preference.source.ChannelizerType;
|
||||
import io.github.dsheirer.preference.source.TunerPreference;
|
||||
import javafx.geometry.HPos;
|
||||
import javafx.geometry.Insets;
|
||||
import javafx.geometry.Orientation;
|
||||
|
||||
Reference in New Issue
Block a user