mirror of
https://github.com/DSheirer/sdrtrunk.git
synced 2026-11-12 08:37:25 -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:
@@ -19,71 +19,53 @@
|
||||
package io.github.dsheirer.controller;
|
||||
|
||||
import com.jidesoft.swing.JideTabbedPane;
|
||||
import io.github.dsheirer.alias.AliasController;
|
||||
import io.github.dsheirer.alias.AliasModel;
|
||||
import io.github.dsheirer.audio.broadcast.BroadcastModel;
|
||||
import io.github.dsheirer.audio.broadcast.BroadcastPanel;
|
||||
import io.github.dsheirer.audio.playback.AudioPanel;
|
||||
import io.github.dsheirer.audio.playback.AudioPlaybackManager;
|
||||
import io.github.dsheirer.channel.metadata.NowPlayingPanel;
|
||||
import io.github.dsheirer.controller.channel.ChannelController;
|
||||
import io.github.dsheirer.controller.channel.ChannelModel;
|
||||
import io.github.dsheirer.controller.channel.ChannelProcessingManager;
|
||||
import io.github.dsheirer.controller.channel.map.ChannelMapModel;
|
||||
import io.github.dsheirer.eventbus.MyEventBus;
|
||||
import io.github.dsheirer.gui.playlist.ViewPlaylistRequest;
|
||||
import io.github.dsheirer.icon.IconManager;
|
||||
import io.github.dsheirer.map.MapPanel;
|
||||
import io.github.dsheirer.map.MapService;
|
||||
import io.github.dsheirer.playlist.PlaylistManager;
|
||||
import io.github.dsheirer.preference.UserPreferences;
|
||||
import io.github.dsheirer.settings.SettingsManager;
|
||||
import io.github.dsheirer.source.SourceManager;
|
||||
import io.github.dsheirer.source.tuner.TunerModel;
|
||||
import io.github.dsheirer.source.tuner.TunerViewPanel;
|
||||
import jiconfont.icons.font_awesome.FontAwesome;
|
||||
import jiconfont.swing.IconFontSwing;
|
||||
import net.miginfocom.swing.MigLayout;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.swing.Icon;
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.JPanel;
|
||||
import java.awt.Color;
|
||||
import java.awt.Dimension;
|
||||
|
||||
public class ControllerPanel extends JPanel
|
||||
{
|
||||
private final static Logger mLog = LoggerFactory.getLogger(ControllerPanel.class);
|
||||
private static final long serialVersionUID = 1L;
|
||||
private int mSettingsTabIndex = -1;
|
||||
|
||||
private AliasController mAliasController;
|
||||
private AudioPanel mAudioPanel;
|
||||
private BroadcastPanel mBroadcastPanel;
|
||||
private ChannelController mChannelController;
|
||||
private NowPlayingPanel mNowPlayingPanel;
|
||||
private ChannelModel mChannelModel;
|
||||
private MapPanel mMapPanel;
|
||||
private TunerViewPanel mTunerManagerPanel;
|
||||
private BroadcastModel mBroadcastModel;
|
||||
|
||||
private JideTabbedPane mTabbedPane;
|
||||
|
||||
public ControllerPanel(AudioPlaybackManager audioPlaybackManager, AliasModel aliasModel, BroadcastModel broadcastModel,
|
||||
ChannelModel channelModel, ChannelMapModel channelMapModel, ChannelProcessingManager channelProcessingManager,
|
||||
public ControllerPanel(PlaylistManager playlistManager, AudioPlaybackManager audioPlaybackManager,
|
||||
IconManager iconManager, MapService mapService, SettingsManager settingsManager,
|
||||
SourceManager sourceManager, TunerModel tunerModel, UserPreferences userPreferences)
|
||||
SourceManager sourceManager, UserPreferences userPreferences)
|
||||
{
|
||||
mBroadcastModel = broadcastModel;
|
||||
mChannelModel = channelModel;
|
||||
|
||||
mAudioPanel = new AudioPanel(iconManager, userPreferences, settingsManager, sourceManager, audioPlaybackManager,
|
||||
aliasModel);
|
||||
|
||||
mNowPlayingPanel = new NowPlayingPanel(channelModel, channelProcessingManager, iconManager,
|
||||
aliasModel, userPreferences);
|
||||
|
||||
mMapPanel = new MapPanel(mapService, aliasModel, iconManager, settingsManager);
|
||||
|
||||
mBroadcastPanel = new BroadcastPanel(broadcastModel, aliasModel, iconManager, userPreferences);
|
||||
|
||||
mChannelController = new ChannelController(channelModel, channelProcessingManager, channelMapModel,
|
||||
sourceManager, aliasModel, userPreferences);
|
||||
|
||||
mAliasController = new AliasController(aliasModel, broadcastModel, iconManager, userPreferences);
|
||||
|
||||
mTunerManagerPanel = new TunerViewPanel(tunerModel, userPreferences);
|
||||
playlistManager.getAliasModel());
|
||||
mNowPlayingPanel = new NowPlayingPanel(playlistManager, iconManager, userPreferences);
|
||||
mMapPanel = new MapPanel(mapService, playlistManager.getAliasModel(), iconManager, settingsManager);
|
||||
mTunerManagerPanel = new TunerViewPanel(sourceManager.getTunerModel(), userPreferences);
|
||||
|
||||
init();
|
||||
}
|
||||
@@ -94,20 +76,34 @@ public class ControllerPanel extends JPanel
|
||||
|
||||
add(mAudioPanel, "wrap");
|
||||
|
||||
//Tabbed View - configuration, calls, messages, map
|
||||
mTabbedPane = new JideTabbedPane();
|
||||
mTabbedPane = new JideTabbedPane()
|
||||
{
|
||||
@Override
|
||||
public void setSelectedIndex(int index)
|
||||
{
|
||||
if(index == mSettingsTabIndex)
|
||||
{
|
||||
MyEventBus.getEventBus().post(new ViewPlaylistRequest());
|
||||
}
|
||||
else
|
||||
{
|
||||
super.setSelectedIndex(index);
|
||||
}
|
||||
}
|
||||
};
|
||||
mTabbedPane.setFont(this.getFont());
|
||||
mTabbedPane.setForeground(Color.BLACK);
|
||||
mTabbedPane.addTab("Now Playing", mNowPlayingPanel);
|
||||
mTabbedPane.addTab("Aliases", mAliasController);
|
||||
mTabbedPane.addTab("Channels", mChannelController);
|
||||
mTabbedPane.addTab("Map", mMapPanel);
|
||||
mTabbedPane.addTab("Streaming", mBroadcastPanel);
|
||||
mTabbedPane.addTab("Tuners", mTunerManagerPanel);
|
||||
|
||||
Icon gearIcon = IconFontSwing.buildIcon(FontAwesome.COG, 20, Color.DARK_GRAY);
|
||||
mTabbedPane.addTab("Playlist Editor", gearIcon, new JLabel("Show Playlist Manager"));
|
||||
mSettingsTabIndex = mTabbedPane.getTabCount() - 1;
|
||||
|
||||
//Set preferred size to influence the split between these panels
|
||||
mTabbedPane.setPreferredSize(new Dimension(880, 500));
|
||||
|
||||
add(mTabbedPane,"wrap");
|
||||
add(mTabbedPane, "wrap");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,21 +1,23 @@
|
||||
/*
|
||||
* ******************************************************************************
|
||||
* sdrtrunk
|
||||
* Copyright (C) 2014-2018 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;
|
||||
|
||||
@@ -39,6 +41,14 @@ import io.github.dsheirer.source.config.SourceConfigRecording;
|
||||
import io.github.dsheirer.source.config.SourceConfigTuner;
|
||||
import io.github.dsheirer.source.config.SourceConfiguration;
|
||||
import io.github.dsheirer.source.tuner.channel.TunerChannel;
|
||||
import javafx.beans.Observable;
|
||||
import javafx.beans.property.BooleanProperty;
|
||||
import javafx.beans.property.IntegerProperty;
|
||||
import javafx.beans.property.SimpleBooleanProperty;
|
||||
import javafx.beans.property.SimpleIntegerProperty;
|
||||
import javafx.beans.property.SimpleStringProperty;
|
||||
import javafx.beans.property.StringProperty;
|
||||
import javafx.util.Callback;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@@ -64,14 +74,14 @@ public class Channel extends Configuration implements Listener<SourceEvent>
|
||||
private EventLogConfiguration mEventLogConfiguration = new EventLogConfiguration();
|
||||
private RecordConfiguration mRecordConfiguration = new RecordConfiguration();
|
||||
|
||||
private String mAliasListName;
|
||||
private String mSystem = "System";
|
||||
private String mSite = "Site";
|
||||
private String mName = "Channel";
|
||||
private StringProperty mAliasListName = new SimpleStringProperty();
|
||||
private StringProperty mSystem = new SimpleStringProperty();
|
||||
private StringProperty mSite = new SimpleStringProperty();
|
||||
private StringProperty mName = new SimpleStringProperty();
|
||||
|
||||
private boolean mProcessing;
|
||||
private boolean mAutoStart;
|
||||
private Integer mAutoStartOrder;
|
||||
private BooleanProperty mProcessing = new SimpleBooleanProperty();
|
||||
private BooleanProperty mAutoStart = new SimpleBooleanProperty();
|
||||
private IntegerProperty mAutoStartOrder = new SimpleIntegerProperty();
|
||||
private boolean mSelected;
|
||||
private TunerChannel mTunerChannel = null;
|
||||
|
||||
@@ -100,7 +110,7 @@ public class Channel extends Configuration implements Listener<SourceEvent>
|
||||
public Channel(String channelName)
|
||||
{
|
||||
this();
|
||||
mName = channelName;
|
||||
mName.set(channelName);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -111,52 +121,119 @@ public class Channel extends Configuration implements Listener<SourceEvent>
|
||||
mChannelID = UNIQUE_ID++;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Creates a (new) deep copy of this channel
|
||||
*/
|
||||
public Channel copyOf()
|
||||
{
|
||||
Channel channel = new Channel(mName);
|
||||
channel.setSystem(mSystem);
|
||||
channel.setSite(mSite);
|
||||
channel.setAliasListName(mAliasListName);
|
||||
channel.setAutoStart(mAutoStart);
|
||||
channel.setAutoStartOrder(mAutoStartOrder);
|
||||
Channel channel = new Channel(mName.get());
|
||||
channel.setSystem(mSystem.get());
|
||||
channel.setSite(mSite.get());
|
||||
channel.setAliasListName(mAliasListName.get());
|
||||
channel.setAutoStart(mAutoStart.get());
|
||||
channel.setAutoStartOrder(mAutoStartOrder.get());
|
||||
|
||||
AuxDecodeConfiguration aux = new AuxDecodeConfiguration();
|
||||
AuxDecodeConfiguration auxCopy = new AuxDecodeConfiguration();
|
||||
|
||||
for(DecoderType auxType : aux.getAuxDecoders())
|
||||
if(getAuxDecodeConfiguration() != null)
|
||||
{
|
||||
aux.addAuxDecoder(auxType);
|
||||
for(DecoderType auxType : getAuxDecodeConfiguration().getAuxDecoders())
|
||||
{
|
||||
auxCopy.addAuxDecoder(auxType);
|
||||
}
|
||||
}
|
||||
|
||||
channel.setAuxDecodeConfiguration(aux);
|
||||
channel.setAuxDecodeConfiguration(auxCopy);
|
||||
|
||||
channel.setDecodeConfiguration(DecoderFactory.copy(mDecodeConfiguration));
|
||||
|
||||
EventLogConfiguration log = new EventLogConfiguration();
|
||||
EventLogConfiguration logCopy = new EventLogConfiguration();
|
||||
|
||||
for(EventLogType logType : mEventLogConfiguration.getLoggers())
|
||||
if(mEventLogConfiguration != null)
|
||||
{
|
||||
log.addLogger(logType);
|
||||
for(EventLogType logType : mEventLogConfiguration.getLoggers())
|
||||
{
|
||||
logCopy.addLogger(logType);
|
||||
}
|
||||
}
|
||||
|
||||
channel.setEventLogConfiguration(log);
|
||||
channel.setEventLogConfiguration(logCopy);
|
||||
|
||||
RecordConfiguration record = new RecordConfiguration();
|
||||
RecordConfiguration recordCopy = new RecordConfiguration();
|
||||
|
||||
for(RecorderType recordType : mRecordConfiguration.getRecorders())
|
||||
if(mRecordConfiguration != null)
|
||||
{
|
||||
record.addRecorder(recordType);
|
||||
for(RecorderType recordType : mRecordConfiguration.getRecorders())
|
||||
{
|
||||
recordCopy.addRecorder(recordType);
|
||||
}
|
||||
}
|
||||
|
||||
channel.setRecordConfiguration(record);
|
||||
channel.setRecordConfiguration(recordCopy);
|
||||
|
||||
channel.setSourceConfiguration(SourceConfigFactory.copy(mSourceConfiguration));
|
||||
|
||||
return channel;
|
||||
}
|
||||
|
||||
/**
|
||||
* Alias list name property
|
||||
*/
|
||||
public StringProperty aliasListNameProperty()
|
||||
{
|
||||
return mAliasListName;
|
||||
}
|
||||
|
||||
/**
|
||||
* System property
|
||||
*/
|
||||
public StringProperty systemProperty()
|
||||
{
|
||||
return mSystem;
|
||||
}
|
||||
|
||||
/**
|
||||
* Site property
|
||||
*/
|
||||
public StringProperty siteProperty()
|
||||
{
|
||||
return mSite;
|
||||
}
|
||||
|
||||
/**
|
||||
* Channel name property
|
||||
*/
|
||||
public StringProperty nameProperty()
|
||||
{
|
||||
return mName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Processing property. Indicates if this channel configuration is currently processing.
|
||||
*/
|
||||
public BooleanProperty processingProperty()
|
||||
{
|
||||
return mProcessing;
|
||||
}
|
||||
|
||||
/**
|
||||
* Auto-Start property. Indicates if this channel is setup for auto-start
|
||||
*/
|
||||
public BooleanProperty autoStartProperty()
|
||||
{
|
||||
return mAutoStart;
|
||||
}
|
||||
|
||||
/**
|
||||
* Auto-start order property. Indicates the order for starting channels that are flagged for auto-start.
|
||||
*/
|
||||
public IntegerProperty autoStartOrderProperty()
|
||||
{
|
||||
return mAutoStartOrder;
|
||||
}
|
||||
|
||||
/**
|
||||
* Unique identifier for this channel. Value is transient (not persisted).
|
||||
*/
|
||||
@@ -214,17 +291,17 @@ public class Channel extends Configuration implements Listener<SourceEvent>
|
||||
@JacksonXmlProperty(isAttribute = true, localName = "system")
|
||||
public String getSystem()
|
||||
{
|
||||
return mSystem;
|
||||
return mSystem.get();
|
||||
}
|
||||
|
||||
public void setSystem(String system)
|
||||
{
|
||||
mSystem = system;
|
||||
mSystem.set(system);
|
||||
}
|
||||
|
||||
public boolean hasSystem()
|
||||
{
|
||||
return mSystem != null;
|
||||
return mSystem != null && mSystem.get() != null;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -233,17 +310,17 @@ public class Channel extends Configuration implements Listener<SourceEvent>
|
||||
@JacksonXmlProperty(isAttribute = true, localName = "site")
|
||||
public String getSite()
|
||||
{
|
||||
return mSite;
|
||||
return mSite.get();
|
||||
}
|
||||
|
||||
public void setSite(String site)
|
||||
{
|
||||
mSite = site;
|
||||
mSite.set(site);
|
||||
}
|
||||
|
||||
public boolean hasSite()
|
||||
{
|
||||
return mSite != null;
|
||||
return mSite != null && mSite.get() != null;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -252,7 +329,7 @@ public class Channel extends Configuration implements Listener<SourceEvent>
|
||||
@JacksonXmlProperty(isAttribute = true, localName = "name")
|
||||
public String getName()
|
||||
{
|
||||
return mName;
|
||||
return mName.get();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -260,7 +337,7 @@ public class Channel extends Configuration implements Listener<SourceEvent>
|
||||
*/
|
||||
public void setName(String name)
|
||||
{
|
||||
mName = name;
|
||||
mName.set(name);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -284,7 +361,7 @@ public class Channel extends Configuration implements Listener<SourceEvent>
|
||||
@JsonIgnore
|
||||
public boolean isProcessing()
|
||||
{
|
||||
return mProcessing;
|
||||
return mProcessing.get();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -296,7 +373,7 @@ public class Channel extends Configuration implements Listener<SourceEvent>
|
||||
*/
|
||||
void setProcessing(boolean processing)
|
||||
{
|
||||
mProcessing = processing;
|
||||
mProcessing.set(processing);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -310,7 +387,7 @@ public class Channel extends Configuration implements Listener<SourceEvent>
|
||||
@JacksonXmlProperty(isAttribute = true, localName = "enabled")
|
||||
public boolean getAutoStart()
|
||||
{
|
||||
return mAutoStart;
|
||||
return mAutoStart.get();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -319,7 +396,7 @@ public class Channel extends Configuration implements Listener<SourceEvent>
|
||||
@JsonIgnore
|
||||
public boolean isAutoStart()
|
||||
{
|
||||
return mAutoStart;
|
||||
return mAutoStart.get();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -329,7 +406,7 @@ public class Channel extends Configuration implements Listener<SourceEvent>
|
||||
*/
|
||||
public void setAutoStart(boolean autoStart)
|
||||
{
|
||||
mAutoStart = autoStart;
|
||||
mAutoStart.set(autoStart);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -341,7 +418,7 @@ public class Channel extends Configuration implements Listener<SourceEvent>
|
||||
@JacksonXmlProperty(isAttribute = true, localName = "order")
|
||||
public Integer getAutoStartOrder()
|
||||
{
|
||||
return mAutoStartOrder;
|
||||
return mAutoStartOrder.get();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -351,7 +428,14 @@ public class Channel extends Configuration implements Listener<SourceEvent>
|
||||
*/
|
||||
public void setAutoStartOrder(Integer order)
|
||||
{
|
||||
mAutoStartOrder = order;
|
||||
if(order != null)
|
||||
{
|
||||
mAutoStartOrder.set(order);
|
||||
}
|
||||
else
|
||||
{
|
||||
mAutoStartOrder.setValue(null);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -362,7 +446,7 @@ public class Channel extends Configuration implements Listener<SourceEvent>
|
||||
@JsonIgnore
|
||||
public boolean hasAutoStartOrder()
|
||||
{
|
||||
return mAutoStartOrder != null;
|
||||
return mAutoStartOrder != null && mAutoStartOrder.getValue() != null;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -372,7 +456,7 @@ public class Channel extends Configuration implements Listener<SourceEvent>
|
||||
@JacksonXmlProperty(isAttribute = false, localName = "alias_list_name")
|
||||
public String getAliasListName()
|
||||
{
|
||||
return mAliasListName;
|
||||
return mAliasListName.get();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -381,15 +465,7 @@ public class Channel extends Configuration implements Listener<SourceEvent>
|
||||
*/
|
||||
public void setAliasListName(String name)
|
||||
{
|
||||
mAliasListName = name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates if this channel has a non-null, non-empty alias list name specified.
|
||||
*/
|
||||
public boolean hasAliasList()
|
||||
{
|
||||
return mAliasListName != null && !mAliasListName.isEmpty();
|
||||
mAliasListName.set(name);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -558,8 +634,6 @@ public class Channel extends Configuration implements Listener<SourceEvent>
|
||||
|
||||
/**
|
||||
* Current channel frequency correction value (when this channel is processing)
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@JsonIgnore
|
||||
public int getChannelFrequencyCorrection()
|
||||
@@ -595,4 +669,13 @@ public class Channel extends Configuration implements Listener<SourceEvent>
|
||||
{
|
||||
return Objects.hash(getChannelID());
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an observable property extractor for use with observable lists to detect changes internal to this object.
|
||||
*/
|
||||
public static Callback<Channel,Observable[]> extractor()
|
||||
{
|
||||
return (Channel c) -> new Observable[] {c.processingProperty(), c.nameProperty(), c.aliasListNameProperty(),
|
||||
c.autoStartOrderProperty(), c.autoStartProperty(), c.siteProperty(), c.systemProperty()};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,306 +0,0 @@
|
||||
/*
|
||||
* ******************************************************************************
|
||||
* 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.controller.channel;
|
||||
|
||||
import com.jidesoft.swing.JideSplitPane;
|
||||
import io.github.dsheirer.alias.AliasModel;
|
||||
import io.github.dsheirer.controller.channel.map.ChannelMapModel;
|
||||
import io.github.dsheirer.preference.UserPreferences;
|
||||
import io.github.dsheirer.preference.swing.JTableColumnWidthMonitor;
|
||||
import io.github.dsheirer.source.SourceManager;
|
||||
import net.coderazzi.filters.gui.AutoChoices;
|
||||
import net.coderazzi.filters.gui.TableFilterHeader;
|
||||
import net.miginfocom.swing.MigLayout;
|
||||
|
||||
import javax.swing.JButton;
|
||||
import javax.swing.JOptionPane;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.JScrollPane;
|
||||
import javax.swing.JTable;
|
||||
import javax.swing.ListSelectionModel;
|
||||
import javax.swing.event.ListSelectionEvent;
|
||||
import javax.swing.event.ListSelectionListener;
|
||||
import javax.swing.table.DefaultTableCellRenderer;
|
||||
import java.awt.Color;
|
||||
import java.awt.Component;
|
||||
import java.awt.Dimension;
|
||||
import java.awt.Rectangle;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.ActionListener;
|
||||
|
||||
public class ChannelController extends JPanel implements ActionListener, ListSelectionListener
|
||||
{
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private static final String TABLE_PREFERENCE_KEY = "channel.controller";
|
||||
private ChannelModel mChannelModel;
|
||||
private ChannelProcessingManager mChannelProcessingManager;
|
||||
private JTable mChannelTable;
|
||||
private JTableColumnWidthMonitor mColumnWidthMonitor;
|
||||
private TableFilterHeader mTableFilterHeader;
|
||||
private ChannelEditor mEditor;
|
||||
private UserPreferences mUserPreferences;
|
||||
|
||||
private static final String NEW_CHANNEL = "New";
|
||||
private static final String COPY_CHANNEL = "Copy";
|
||||
private static final String DELETE_CHANNEL = "Delete";
|
||||
|
||||
private JButton mNewChannelButton = new JButton(NEW_CHANNEL);
|
||||
private JButton mCopyChannelButton = new JButton(COPY_CHANNEL);
|
||||
private JButton mDeleteChannelButton = new JButton(DELETE_CHANNEL);
|
||||
|
||||
public ChannelController(ChannelModel channelModel, ChannelProcessingManager channelProcessingManager,
|
||||
ChannelMapModel channelMapModel, SourceManager sourceManager, AliasModel aliasModel,
|
||||
UserPreferences userPreferences)
|
||||
{
|
||||
mChannelModel = channelModel;
|
||||
mChannelProcessingManager = channelProcessingManager;
|
||||
mEditor = new ChannelEditor(channelModel, channelProcessingManager, channelMapModel, sourceManager, aliasModel);
|
||||
mChannelModel.addListener(mEditor);
|
||||
mChannelProcessingManager.addChannelEventListener(mEditor);
|
||||
mUserPreferences = userPreferences;
|
||||
|
||||
init();
|
||||
}
|
||||
|
||||
private void init()
|
||||
{
|
||||
setLayout(new MigLayout("", "[grow,fill]", "[grow,fill]"));
|
||||
|
||||
//System Configuration View and Editor
|
||||
mChannelTable = new JTable(mChannelModel);
|
||||
mChannelTable.setDefaultRenderer(String.class, new ChannelTableCellRenderer());
|
||||
mChannelTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
|
||||
mChannelTable.getSelectionModel().addListSelectionListener(this);
|
||||
mChannelTable.setAutoCreateRowSorter(true);
|
||||
mColumnWidthMonitor = new JTableColumnWidthMonitor(mUserPreferences, mChannelTable, TABLE_PREFERENCE_KEY);
|
||||
|
||||
mTableFilterHeader = new TableFilterHeader(mChannelTable, AutoChoices.ENABLED);
|
||||
mTableFilterHeader.setFilterOnUpdates(true);
|
||||
|
||||
JScrollPane channelScroller = new JScrollPane(mChannelTable);
|
||||
|
||||
JPanel buttonsPanel = new JPanel();
|
||||
|
||||
buttonsPanel.setLayout(
|
||||
new MigLayout("insets 0 0 0 0", "[grow,fill][grow,fill][grow,fill]", "[]"));
|
||||
|
||||
mNewChannelButton.addActionListener(this);
|
||||
mNewChannelButton.setToolTipText("Create a new channel configuration");
|
||||
buttonsPanel.add(mNewChannelButton);
|
||||
|
||||
mCopyChannelButton.addActionListener(this);
|
||||
mCopyChannelButton.setEnabled(false);
|
||||
mCopyChannelButton.setToolTipText("Create a copy of the currently selected channel configuration");
|
||||
buttonsPanel.add(mCopyChannelButton);
|
||||
|
||||
mDeleteChannelButton.addActionListener(this);
|
||||
mDeleteChannelButton.setEnabled(false);
|
||||
mDeleteChannelButton.setToolTipText("Delete the currently selected channel configuration");
|
||||
buttonsPanel.add(mDeleteChannelButton);
|
||||
|
||||
JPanel listAndButtonsPanel = new JPanel();
|
||||
|
||||
listAndButtonsPanel.setLayout(
|
||||
new MigLayout("insets 0 0 0 0", "[grow,fill]", "[grow,fill][]"));
|
||||
|
||||
listAndButtonsPanel.add(channelScroller, "wrap");
|
||||
listAndButtonsPanel.add(buttonsPanel);
|
||||
|
||||
JideSplitPane splitPane = new JideSplitPane(JideSplitPane.VERTICAL_SPLIT);
|
||||
splitPane.setDividerSize(5);
|
||||
splitPane.setShowGripper(true);
|
||||
|
||||
//Shrink this guy so that he'll eventually be expanded to the scroller preferred size
|
||||
listAndButtonsPanel.setPreferredSize(new Dimension(10, 10));
|
||||
|
||||
//Attempt to get a 60:40 vertical sizing preference
|
||||
JScrollPane listScroller = new JScrollPane(listAndButtonsPanel);
|
||||
listScroller.setPreferredSize(new Dimension(800, 58));
|
||||
|
||||
JScrollPane editorScroller = new JScrollPane(mEditor);
|
||||
editorScroller.setPreferredSize(new Dimension(800, 42));
|
||||
|
||||
splitPane.add(listScroller);
|
||||
splitPane.add(editorScroller);
|
||||
|
||||
add(splitPane);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void valueChanged(ListSelectionEvent event)
|
||||
{
|
||||
//This limits event firing to only when selection is complete
|
||||
if(!event.getValueIsAdjusting())
|
||||
{
|
||||
int selectedRow = mChannelTable.getSelectedRow();
|
||||
|
||||
if(selectedRow != -1)
|
||||
{
|
||||
int index = mChannelTable.convertRowIndexToModel(mChannelTable.getSelectedRow());
|
||||
|
||||
Channel channel = mChannelModel.getChannelAtIndex(index);
|
||||
|
||||
if(channel != null)
|
||||
{
|
||||
mEditor.setItem(channel);
|
||||
mCopyChannelButton.setEnabled(true);
|
||||
mDeleteChannelButton.setEnabled(true);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
mEditor.setItem(null);
|
||||
mCopyChannelButton.setEnabled(false);
|
||||
mDeleteChannelButton.setEnabled(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the channel to the channel model/table and scrolls to view it
|
||||
*/
|
||||
private void addChannel(Channel channel)
|
||||
{
|
||||
//HACK: when inserting a row to the model, the JTable gets
|
||||
//notified and attempts to tell the coderazzi table filter
|
||||
//adaptive choices filter to refresh before the table filter is
|
||||
//notified of the row additions, causing an index out of bounds
|
||||
//exception. We turn off adaptive choices temporarily, add the
|
||||
//channel, and turn on adaptive choices again.
|
||||
mTableFilterHeader.setAdaptiveChoices(false);
|
||||
|
||||
int index = mChannelModel.addChannel(channel);
|
||||
|
||||
mTableFilterHeader.setAdaptiveChoices(true);
|
||||
|
||||
if(index >= 0)
|
||||
{
|
||||
int translatedIndex = mChannelTable.convertRowIndexToView(index);
|
||||
|
||||
if(translatedIndex >= 0)
|
||||
{
|
||||
mChannelTable.setRowSelectionInterval(translatedIndex, translatedIndex);
|
||||
mChannelTable.scrollRectToVisible(
|
||||
new Rectangle(mChannelTable.getCellRect(translatedIndex, 0, true)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Channel getSelectedChannel()
|
||||
{
|
||||
int index = mChannelTable.getSelectedRow();
|
||||
|
||||
if(index >= 0)
|
||||
{
|
||||
return mChannelModel.getChannelAtIndex(
|
||||
mChannelTable.convertRowIndexToModel(index));
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Responds to New, Copy and Delete Channel button invocations
|
||||
*/
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent event)
|
||||
{
|
||||
switch(event.getActionCommand())
|
||||
{
|
||||
case NEW_CHANNEL:
|
||||
addChannel(new Channel("New Channel"));
|
||||
break;
|
||||
case COPY_CHANNEL:
|
||||
Channel selected = getSelectedChannel();
|
||||
|
||||
if(selected != null)
|
||||
{
|
||||
addChannel(selected.copyOf());
|
||||
}
|
||||
break;
|
||||
case DELETE_CHANNEL:
|
||||
Channel toDelete = getSelectedChannel();
|
||||
|
||||
if(toDelete != null)
|
||||
{
|
||||
int choice = JOptionPane.showConfirmDialog(ChannelController.this,
|
||||
"Do you want to delete this channel?", "Delete Channel?",
|
||||
JOptionPane.YES_NO_OPTION, JOptionPane.WARNING_MESSAGE);
|
||||
|
||||
if(choice == JOptionPane.YES_OPTION)
|
||||
{
|
||||
mChannelModel.removeChannel(toDelete);
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public class ChannelTableCellRenderer extends DefaultTableCellRenderer
|
||||
{
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Override
|
||||
public Component getTableCellRendererComponent(JTable table,
|
||||
Object value, boolean isSelected, boolean hasFocus, int row,
|
||||
int column)
|
||||
{
|
||||
Component component = super.getTableCellRendererComponent(
|
||||
table, value, isSelected, hasFocus, row, column);
|
||||
|
||||
ChannelModel model = (ChannelModel)table.getModel();
|
||||
|
||||
int index = table.convertRowIndexToModel(row);
|
||||
|
||||
Channel channel = model.getChannelAtIndex(index);
|
||||
|
||||
boolean enabled = channel.isProcessing();
|
||||
|
||||
if(isSelected)
|
||||
{
|
||||
if(enabled)
|
||||
{
|
||||
component.setBackground(Color.YELLOW);
|
||||
}
|
||||
else
|
||||
{
|
||||
component.setBackground(table.getSelectionBackground());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if(enabled)
|
||||
{
|
||||
component.setBackground(Color.GREEN);
|
||||
}
|
||||
else
|
||||
{
|
||||
component.setBackground(table.getBackground());
|
||||
}
|
||||
}
|
||||
|
||||
return component;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,262 +0,0 @@
|
||||
/*
|
||||
* ******************************************************************************
|
||||
* sdrtrunk
|
||||
* Copyright (C) 2014-2018 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;
|
||||
|
||||
import com.jidesoft.swing.JideTabbedPane;
|
||||
import io.github.dsheirer.alias.AliasModel;
|
||||
import io.github.dsheirer.controller.channel.map.ChannelMapModel;
|
||||
import io.github.dsheirer.gui.editor.Editor;
|
||||
import io.github.dsheirer.gui.editor.EditorValidationException;
|
||||
import io.github.dsheirer.module.decode.AuxDecodeConfigurationEditor;
|
||||
import io.github.dsheirer.module.decode.DecodeConfigurationEditor;
|
||||
import io.github.dsheirer.module.log.EventLogConfigurationEditor;
|
||||
import io.github.dsheirer.record.RecordConfigurationEditor;
|
||||
import io.github.dsheirer.sample.Listener;
|
||||
import io.github.dsheirer.source.SourceConfigurationEditor;
|
||||
import io.github.dsheirer.source.SourceManager;
|
||||
import net.miginfocom.swing.MigLayout;
|
||||
|
||||
import javax.swing.JButton;
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.JOptionPane;
|
||||
import java.awt.Color;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.ActionListener;
|
||||
|
||||
public class ChannelEditor extends Editor<Channel> implements ActionListener, Listener<ChannelEvent>
|
||||
{
|
||||
private static final long serialVersionUID = 1L;
|
||||
private static final String ACTION_START = "Start";
|
||||
private static final String ACTION_STOP = "Stop";
|
||||
private static final String ACTION_SAVE = "Save";
|
||||
private static final String ACTION_RESET = "Reset";
|
||||
|
||||
private NameConfigurationEditor mNameConfigurationEditor;
|
||||
private SourceConfigurationEditor mSourceConfigurationEditor;
|
||||
private DecodeConfigurationEditor mDecodeConfigurationEditor;
|
||||
private AuxDecodeConfigurationEditor mAuxDecodeConfigurationEditor;
|
||||
private EventLogConfigurationEditor mEventLogConfigurationEditor;
|
||||
private RecordConfigurationEditor mRecordConfigurationEditor;
|
||||
|
||||
private JButton mProcessingStateButton = new JButton(ACTION_START);
|
||||
private JLabel mChannelName = new JLabel("Channel:");
|
||||
|
||||
private ChannelModel mChannelModel;
|
||||
private ChannelProcessingManager mChannelProcessingManager;
|
||||
private ChannelMapModel mChannelMapModel;
|
||||
private SourceManager mSourceManager;
|
||||
|
||||
private boolean mChannelStartRequested = false;
|
||||
|
||||
public ChannelEditor(ChannelModel channelModel,
|
||||
ChannelProcessingManager channelProcessingManager,
|
||||
ChannelMapModel channelMapModel,
|
||||
SourceManager sourceManager,
|
||||
AliasModel aliasModel)
|
||||
{
|
||||
mChannelModel = channelModel;
|
||||
mChannelProcessingManager = channelProcessingManager;
|
||||
mChannelMapModel = channelMapModel;
|
||||
mSourceManager = sourceManager;
|
||||
mNameConfigurationEditor = new NameConfigurationEditor(aliasModel, mChannelModel);
|
||||
|
||||
init();
|
||||
}
|
||||
|
||||
private void init()
|
||||
{
|
||||
setLayout(new MigLayout("fill,wrap 3", "[grow,fill][grow,fill][grow,fill]", "[grow,fill][]"));
|
||||
|
||||
JideTabbedPane tabs = new JideTabbedPane();
|
||||
tabs.setFont(this.getFont());
|
||||
tabs.setForeground(Color.BLACK);
|
||||
|
||||
tabs.setTabTrailingComponent(mChannelName);
|
||||
|
||||
mNameConfigurationEditor.setSaveRequestListener(this);
|
||||
tabs.addTab("Name/Alias", mNameConfigurationEditor);
|
||||
|
||||
mSourceConfigurationEditor = new SourceConfigurationEditor(mSourceManager);
|
||||
mSourceConfigurationEditor.setSaveRequestListener(this);
|
||||
tabs.addTab("Source", mSourceConfigurationEditor);
|
||||
|
||||
mDecodeConfigurationEditor = new DecodeConfigurationEditor(mChannelMapModel);
|
||||
mDecodeConfigurationEditor.setSaveRequestListener(this);
|
||||
tabs.addTab("Decoder", mDecodeConfigurationEditor);
|
||||
|
||||
mAuxDecodeConfigurationEditor = new AuxDecodeConfigurationEditor();
|
||||
mAuxDecodeConfigurationEditor.setSaveRequestListener(this);
|
||||
tabs.addTab("Aux Decoders", mAuxDecodeConfigurationEditor);
|
||||
|
||||
mEventLogConfigurationEditor = new EventLogConfigurationEditor();
|
||||
mEventLogConfigurationEditor.setSaveRequestListener(this);
|
||||
tabs.addTab("Logging", mEventLogConfigurationEditor);
|
||||
|
||||
mRecordConfigurationEditor = new RecordConfigurationEditor();
|
||||
mRecordConfigurationEditor.setSaveRequestListener(this);
|
||||
tabs.addTab("Recording", mRecordConfigurationEditor);
|
||||
|
||||
add(tabs, "span");
|
||||
|
||||
mProcessingStateButton.addActionListener(this);
|
||||
mProcessingStateButton.setEnabled(false);
|
||||
mProcessingStateButton.setToolTipText("Start/Stop the currently selected channel");
|
||||
add(mProcessingStateButton);
|
||||
|
||||
JButton btnSave = new JButton(ACTION_SAVE);
|
||||
btnSave.setToolTipText("Save changes to the currently selected channel");
|
||||
btnSave.addActionListener(ChannelEditor.this);
|
||||
add(btnSave);
|
||||
|
||||
JButton btnReset = new JButton(ACTION_RESET);
|
||||
btnReset.setToolTipText("Reload the currently selected channel");
|
||||
btnReset.addActionListener(ChannelEditor.this);
|
||||
add(btnReset);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void receive(ChannelEvent event)
|
||||
{
|
||||
if(hasItem() && getItem() == event.getChannel())
|
||||
{
|
||||
switch(event.getEvent())
|
||||
{
|
||||
case NOTIFICATION_CONFIGURATION_CHANGE:
|
||||
case NOTIFICATION_PROCESSING_START:
|
||||
case NOTIFICATION_PROCESSING_STOP:
|
||||
setItem(getItem());
|
||||
mChannelStartRequested = false;
|
||||
break;
|
||||
case NOTIFICATION_DELETE:
|
||||
setItem(null);
|
||||
break;
|
||||
case NOTIFICATION_PROCESSING_START_REJECTED:
|
||||
if(mChannelStartRequested)
|
||||
{
|
||||
JOptionPane.showMessageDialog(ChannelEditor.this, "Channel could not be "
|
||||
+ "started. This is likely because there are no tuner channels "
|
||||
+ "available", "Couldn't start channel", JOptionPane.INFORMATION_MESSAGE);
|
||||
|
||||
mChannelStartRequested = false;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e)
|
||||
{
|
||||
String command = e.getActionCommand();
|
||||
|
||||
if(command.contentEquals(ACTION_START))
|
||||
{
|
||||
if(hasItem())
|
||||
{
|
||||
save();
|
||||
|
||||
mChannelStartRequested = true;
|
||||
mChannelProcessingManager.receive(new ChannelEvent(getItem(), ChannelEvent.Event.REQUEST_ENABLE));
|
||||
}
|
||||
}
|
||||
else if(command.contentEquals(ACTION_STOP))
|
||||
{
|
||||
if(hasItem())
|
||||
{
|
||||
mChannelProcessingManager.receive(new ChannelEvent(getItem(), ChannelEvent.Event.REQUEST_DISABLE));
|
||||
}
|
||||
}
|
||||
else if(command.contentEquals(ACTION_SAVE))
|
||||
{
|
||||
save();
|
||||
}
|
||||
else if(command.contentEquals(ACTION_RESET))
|
||||
{
|
||||
setItem(getItem());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the channel configuration in each of the channel editor components
|
||||
* Note: this method must be invoked on the swing event dispatch thread
|
||||
*/
|
||||
public void setItem(final Channel channel)
|
||||
{
|
||||
super.setItem(channel);
|
||||
|
||||
mNameConfigurationEditor.setItem(channel);
|
||||
mSourceConfigurationEditor.setItem(channel);
|
||||
mDecodeConfigurationEditor.setItem(channel);
|
||||
mAuxDecodeConfigurationEditor.setItem(channel);
|
||||
mEventLogConfigurationEditor.setItem(channel);
|
||||
mRecordConfigurationEditor.setItem(channel);
|
||||
|
||||
if(channel != null)
|
||||
{
|
||||
mChannelName.setText("Channel: " + channel.getName());
|
||||
mProcessingStateButton.setText(channel.isProcessing() ? ACTION_STOP : ACTION_START);
|
||||
mProcessingStateButton.setEnabled(true);
|
||||
mProcessingStateButton.setBackground(channel.isProcessing() ? Color.GREEN : getBackground());
|
||||
}
|
||||
else
|
||||
{
|
||||
mChannelName.setText("Channel: ");
|
||||
mProcessingStateButton.setText(ACTION_START);
|
||||
mProcessingStateButton.setEnabled(false);
|
||||
mProcessingStateButton.setBackground(getBackground());
|
||||
}
|
||||
}
|
||||
|
||||
public void save()
|
||||
{
|
||||
if(hasItem())
|
||||
{
|
||||
mNameConfigurationEditor.save();
|
||||
mSourceConfigurationEditor.save();
|
||||
mDecodeConfigurationEditor.save();
|
||||
mAuxDecodeConfigurationEditor.save();
|
||||
mEventLogConfigurationEditor.save();
|
||||
mRecordConfigurationEditor.save();
|
||||
|
||||
try
|
||||
{
|
||||
mDecodeConfigurationEditor.validate(mSourceConfigurationEditor);
|
||||
mDecodeConfigurationEditor.validate(mAuxDecodeConfigurationEditor);
|
||||
}
|
||||
catch(EditorValidationException e)
|
||||
{
|
||||
e.getEditor().requestFocus();
|
||||
|
||||
JOptionPane.showMessageDialog(e.getEditor(), e.getReason(),
|
||||
"Configuration Error", JOptionPane.ERROR_MESSAGE);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
ChannelEvent update = new ChannelEvent(getItem(), ChannelEvent.Event.NOTIFICATION_CONFIGURATION_CHANGE);
|
||||
|
||||
mChannelModel.receive(update);
|
||||
}
|
||||
|
||||
setModified(false);
|
||||
}
|
||||
}
|
||||
@@ -80,6 +80,22 @@ public class ChannelEvent
|
||||
(mDescription != null ? " Description:" + mDescription : "");
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a request enable channel event
|
||||
*/
|
||||
public static ChannelEvent requestEnable(Channel channel)
|
||||
{
|
||||
return new ChannelEvent(channel, Event.REQUEST_ENABLE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new request disable channel event
|
||||
*/
|
||||
public static ChannelEvent requestDisable(Channel channel)
|
||||
{
|
||||
return new ChannelEvent(channel, Event.REQUEST_DISABLE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Channel events to describe the specific event
|
||||
*/
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
/*******************************************************************************
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* Exception for channel start/stop errors
|
||||
*/
|
||||
public class ChannelException extends Exception
|
||||
{
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public ChannelException(String message )
|
||||
{
|
||||
this( message, null );
|
||||
}
|
||||
|
||||
public ChannelException(String message, Throwable cause )
|
||||
{
|
||||
super( message, cause );
|
||||
}
|
||||
}
|
||||
@@ -1,62 +1,94 @@
|
||||
/*******************************************************************************
|
||||
* sdrtrunk
|
||||
* Copyright (C) 2014-2017 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;
|
||||
|
||||
import io.github.dsheirer.alias.AliasModel;
|
||||
import io.github.dsheirer.controller.channel.Channel.ChannelType;
|
||||
import io.github.dsheirer.controller.channel.ChannelEvent.Event;
|
||||
import io.github.dsheirer.module.decode.DecoderType;
|
||||
import io.github.dsheirer.sample.Broadcaster;
|
||||
import io.github.dsheirer.sample.Listener;
|
||||
import javafx.collections.FXCollections;
|
||||
import javafx.collections.ListChangeListener;
|
||||
import javafx.collections.ObservableList;
|
||||
|
||||
import javax.swing.table.AbstractTableModel;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
|
||||
/**
|
||||
* Channel Model
|
||||
*/
|
||||
public class ChannelModel extends AbstractTableModel implements Listener<ChannelEvent>
|
||||
public class ChannelModel implements Listener<ChannelEvent>
|
||||
{
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public static final int COLUMN_ENABLED = 0;
|
||||
public static final int COLUMN_SYSTEM = 1;
|
||||
public static final int COLUMN_SITE = 2;
|
||||
public static final int COLUMN_NAME = 3;
|
||||
public static final int COLUMN_ALIAS_LIST = 4;
|
||||
public static final int COLUMN_SOURCE = 5;
|
||||
public static final int COLUMN_DECODER = 6;
|
||||
public static final int COLUMN_AUTO_START = 7;
|
||||
|
||||
private static final String[] COLUMN_NAMES = new String[] {"Playing", "System", "Site", "Name", "Alias List",
|
||||
"Source", "Decoder", "Auto-Start"};
|
||||
private static final String VALUE_YES = "Yes";
|
||||
|
||||
private List<Channel> mChannels = new CopyOnWriteArrayList<>();
|
||||
private List<Channel> mTrafficChannels = new CopyOnWriteArrayList<>();
|
||||
private ObservableList<Channel> mChannels = FXCollections.observableArrayList(Channel.extractor());
|
||||
private ObservableList<Channel> mTrafficChannels = FXCollections.observableArrayList(Channel.extractor());
|
||||
private Broadcaster<ChannelEvent> mChannelEventBroadcaster = new Broadcaster();
|
||||
private AliasModel mAliasModel;
|
||||
|
||||
public ChannelModel()
|
||||
public ChannelModel(AliasModel aliasModel)
|
||||
{
|
||||
mAliasModel = aliasModel;
|
||||
|
||||
//Register a listener to detect channel changes and broadcast change events to cause playlist save requests
|
||||
ChannelListChangeListener changeListener = new ChannelListChangeListener();
|
||||
mChannels.addListener(changeListener);
|
||||
mTrafficChannels.addListener(changeListener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Observable list of channel configurations managed by this model
|
||||
*/
|
||||
public ObservableList<Channel> channelList()
|
||||
{
|
||||
return mChannels;
|
||||
}
|
||||
|
||||
/**
|
||||
* Observable list of traffic channel configurations managed by this model
|
||||
*/
|
||||
public ObservableList<Channel> trafficChannelList()
|
||||
{
|
||||
return mTrafficChannels;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes all channels and traffic channels and fires remove event for each.
|
||||
*/
|
||||
public void clear()
|
||||
{
|
||||
List<Channel> trafficChannels = new ArrayList<>(mTrafficChannels);
|
||||
|
||||
for(Channel trafficChannel: trafficChannels)
|
||||
{
|
||||
removeChannel(trafficChannel);
|
||||
}
|
||||
|
||||
List<Channel> channels = new ArrayList<>(mChannels);
|
||||
|
||||
for(Channel channel: channels)
|
||||
{
|
||||
removeChannel(channel);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -80,7 +112,7 @@ public class ChannelModel extends AbstractTableModel implements Listener<Channel
|
||||
/**
|
||||
* Returns a list of unique system values from across the channel set
|
||||
*/
|
||||
public List<String> getSystems()
|
||||
public List<String> getSystemNames()
|
||||
{
|
||||
List<String> systems = new ArrayList<>();
|
||||
|
||||
@@ -97,33 +129,6 @@ public class ChannelModel extends AbstractTableModel implements Listener<Channel
|
||||
return systems;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of unique site values for all channels that have a matching
|
||||
* system value
|
||||
*/
|
||||
public List<String> getSites(String system)
|
||||
{
|
||||
List<String> sites = new ArrayList<>();
|
||||
|
||||
if(system != null)
|
||||
{
|
||||
for(Channel channel : mChannels)
|
||||
{
|
||||
if(channel.hasSystem() &&
|
||||
system.equals(channel.getSystem()) &&
|
||||
channel.hasSite() &&
|
||||
!sites.contains(channel.getSite()))
|
||||
{
|
||||
sites.add(channel.getSite());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Collections.sort(sites);
|
||||
|
||||
return sites;
|
||||
}
|
||||
|
||||
/**
|
||||
* Broadcasts the channel event to all registered listeners
|
||||
*/
|
||||
@@ -136,22 +141,9 @@ public class ChannelModel extends AbstractTableModel implements Listener<Channel
|
||||
{
|
||||
case NOTIFICATION_CONFIGURATION_CHANGE:
|
||||
case NOTIFICATION_SELECTION_CHANGE:
|
||||
index = mChannels.indexOf(event.getChannel());
|
||||
if(index > 0)
|
||||
{
|
||||
fireTableRowsUpdated(index, index);
|
||||
}
|
||||
//rebroadcast this event to any listeners
|
||||
mChannelEventBroadcaster.broadcast(event);
|
||||
break;
|
||||
case NOTIFICATION_PROCESSING_START:
|
||||
case NOTIFICATION_PROCESSING_STOP:
|
||||
index = mChannels.indexOf(event.getChannel());
|
||||
if(index > 0)
|
||||
{
|
||||
fireTableRowsUpdated(index, index);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -178,30 +170,21 @@ public class ChannelModel extends AbstractTableModel implements Listener<Channel
|
||||
/**
|
||||
* Adds the channel to the model and broadcasts a channel add event
|
||||
*/
|
||||
public int addChannel(Channel channel)
|
||||
public void addChannel(Channel channel)
|
||||
{
|
||||
int index = -1;
|
||||
|
||||
switch(channel.getChannelType())
|
||||
{
|
||||
case STANDARD:
|
||||
mChannels.add(channel);
|
||||
|
||||
index = mChannels.size() - 1;
|
||||
|
||||
fireTableRowsInserted(index, index);
|
||||
//Make sure the alias model has the alias list referred to by the channel
|
||||
mAliasModel.addAliasList(channel.getAliasListName());
|
||||
break;
|
||||
case TRAFFIC:
|
||||
mTrafficChannels.add(channel);
|
||||
index = mChannels.size() - 1;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
mChannelEventBroadcaster.broadcast(new ChannelEvent(channel, Event.NOTIFICATION_ADD));
|
||||
|
||||
return index;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -214,14 +197,7 @@ public class ChannelModel extends AbstractTableModel implements Listener<Channel
|
||||
switch(channel.getChannelType())
|
||||
{
|
||||
case STANDARD:
|
||||
int index = mChannels.indexOf(channel);
|
||||
|
||||
mChannels.remove(channel);
|
||||
|
||||
if(index >= 0)
|
||||
{
|
||||
fireTableRowsDeleted(index, index);
|
||||
}
|
||||
break;
|
||||
case TRAFFIC:
|
||||
mTrafficChannels.remove(channel);
|
||||
@@ -229,8 +205,6 @@ public class ChannelModel extends AbstractTableModel implements Listener<Channel
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
mChannelEventBroadcaster.broadcast(new ChannelEvent(channel, Event.NOTIFICATION_DELETE));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -312,89 +286,59 @@ public class ChannelModel extends AbstractTableModel implements Listener<Channel
|
||||
mChannelEventBroadcaster.removeListener(listener);
|
||||
}
|
||||
|
||||
//Table Model Interface Methods - standard channels only
|
||||
@Override
|
||||
public int getRowCount()
|
||||
/**
|
||||
* List of site names from all channels in the model
|
||||
*/
|
||||
public List<String> getSiteNames()
|
||||
{
|
||||
return mChannels.size();
|
||||
}
|
||||
List<String> sites = new ArrayList<>();
|
||||
|
||||
@Override
|
||||
public int getColumnCount()
|
||||
{
|
||||
return COLUMN_NAMES.length;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getColumnName(int columnIndex)
|
||||
{
|
||||
if(columnIndex < COLUMN_NAMES.length)
|
||||
for(Channel channel: mChannels)
|
||||
{
|
||||
return COLUMN_NAMES[columnIndex];
|
||||
String site = channel.getSite();
|
||||
|
||||
if(site != null && !site.isEmpty() && !sites.contains(site))
|
||||
{
|
||||
sites.add(site);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
return sites;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> getColumnClass(int columnIndex)
|
||||
/**
|
||||
* Observable list change listener for both channels and traffic channels lists
|
||||
*/
|
||||
public class ChannelListChangeListener implements ListChangeListener<Channel>
|
||||
{
|
||||
return String.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCellEditable(int rowIndex, int columnIndex)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getValueAt(int rowIndex, int columnIndex)
|
||||
{
|
||||
Channel channel = mChannels.get(rowIndex);
|
||||
|
||||
switch(columnIndex)
|
||||
@Override
|
||||
public void onChanged(Change<? extends Channel> change)
|
||||
{
|
||||
case COLUMN_ENABLED:
|
||||
return channel.isProcessing() ? VALUE_YES : null;
|
||||
case COLUMN_SYSTEM:
|
||||
return channel.getSystem();
|
||||
case COLUMN_SITE:
|
||||
return channel.getSite();
|
||||
case COLUMN_NAME:
|
||||
return channel.getName();
|
||||
case COLUMN_ALIAS_LIST:
|
||||
return channel.getAliasListName();
|
||||
case COLUMN_SOURCE:
|
||||
return channel.getSourceConfiguration().getDescription();
|
||||
case COLUMN_DECODER:
|
||||
return channel.getDecodeConfiguration().getDecoderType().getShortDisplayString();
|
||||
case COLUMN_AUTO_START:
|
||||
if(channel.isAutoStart())
|
||||
while(change.next())
|
||||
{
|
||||
if(change.wasAdded())
|
||||
{
|
||||
if(channel.hasAutoStartOrder())
|
||||
for(Channel channel: change.getAddedSubList())
|
||||
{
|
||||
return channel.getAutoStartOrder();
|
||||
}
|
||||
else
|
||||
{
|
||||
return VALUE_YES;
|
||||
mChannelEventBroadcaster.broadcast(new ChannelEvent(channel, Event.NOTIFICATION_ADD));
|
||||
}
|
||||
}
|
||||
break;
|
||||
else if(change.wasRemoved())
|
||||
{
|
||||
for(Channel channel: change.getRemoved())
|
||||
{
|
||||
mChannelEventBroadcaster.broadcast(new ChannelEvent(channel, Event.NOTIFICATION_DELETE));
|
||||
}
|
||||
}
|
||||
else if(change.wasUpdated())
|
||||
{
|
||||
for(int x = change.getFrom(); x < change.getTo(); x++)
|
||||
{
|
||||
mChannelEventBroadcaster.broadcast(new ChannelEvent(change.getList().get(x),
|
||||
Event.NOTIFICATION_CONFIGURATION_CHANGE));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setValueAt(Object aValue, int rowIndex, int columnIndex)
|
||||
{
|
||||
throw new IllegalArgumentException("Not yet implemented");
|
||||
}
|
||||
|
||||
public void createChannel(DecoderType decoderType, long frequency)
|
||||
{
|
||||
throw new UnsupportedOperationException("Not yet implemented");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -118,6 +118,15 @@ public class ChannelProcessingManager implements Listener<ChannelEvent>
|
||||
return mProcessingChains.containsKey(channel) && mProcessingChains.get(channel).isProcessing();
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates if any channels are currently processing.
|
||||
* @return true if channels are processing.
|
||||
*/
|
||||
public boolean isProcessing()
|
||||
{
|
||||
return !mProcessingChains.isEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current processing chain associated with the channel, or
|
||||
* null if a processing chain is not currently setup for the channel
|
||||
@@ -164,38 +173,52 @@ public class ChannelProcessingManager implements Listener<ChannelEvent>
|
||||
case REQUEST_ENABLE:
|
||||
if(!isProcessing(channel))
|
||||
{
|
||||
startProcessing(event);
|
||||
try
|
||||
{
|
||||
startProcessing(event);
|
||||
}
|
||||
catch(ChannelException ce)
|
||||
{
|
||||
mLog.error("Error starting requested channel [" + channel.getName() + "] - " + ce.getMessage());
|
||||
}
|
||||
}
|
||||
break;
|
||||
case REQUEST_DISABLE:
|
||||
if(channel.isProcessing())
|
||||
{
|
||||
switch(channel.getChannelType())
|
||||
try
|
||||
{
|
||||
case STANDARD:
|
||||
stopProcessing(channel, true);
|
||||
break;
|
||||
case TRAFFIC:
|
||||
//Don't remove traffic channel processing chains
|
||||
//until explicitly deleted, so that we can reuse them
|
||||
stopProcessing(channel, false);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
switch(channel.getChannelType())
|
||||
{
|
||||
case STANDARD:
|
||||
stopProcessing(channel, true);
|
||||
break;
|
||||
case TRAFFIC:
|
||||
//Don't remove traffic channel processing chains
|
||||
//until explicitly deleted, so that we can reuse them
|
||||
stopProcessing(channel, false);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
catch(ChannelException ce)
|
||||
{
|
||||
mLog.error("Error stopping channel [" + channel.getName() + "] - " + ce.getMessage());
|
||||
}
|
||||
}
|
||||
break;
|
||||
case NOTIFICATION_DELETE:
|
||||
if(channel.isProcessing())
|
||||
{
|
||||
stopProcessing(channel, true);
|
||||
}
|
||||
break;
|
||||
case NOTIFICATION_CONFIGURATION_CHANGE:
|
||||
if(isProcessing(channel))
|
||||
{
|
||||
stopProcessing(channel, true);
|
||||
startProcessing(event);
|
||||
try
|
||||
{
|
||||
stopProcessing(channel, true);
|
||||
}
|
||||
catch(ChannelException ce)
|
||||
{
|
||||
mLog.error("Error stopping deleted channel [" + channel.getName() + "] - " + ce.getMessage());
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
@@ -203,12 +226,32 @@ public class ChannelProcessingManager implements Listener<ChannelEvent>
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts the specified channel.
|
||||
* @param channel to start
|
||||
* @throws ChannelException if the channel can't be started
|
||||
*/
|
||||
public void start(Channel channel) throws ChannelException
|
||||
{
|
||||
startProcessing(new ChannelEvent(channel, ChannelEvent.Event.REQUEST_ENABLE));
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Stops the specified channel
|
||||
* @param channel to stop
|
||||
*/
|
||||
public void stop(Channel channel) throws ChannelException
|
||||
{
|
||||
stopProcessing(channel, !channel.isTrafficChannel());
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts a channel/processing chain
|
||||
*
|
||||
* @param event that requested the channel start
|
||||
*/
|
||||
private void startProcessing(ChannelEvent event)
|
||||
private void startProcessing(ChannelEvent event) throws ChannelException
|
||||
{
|
||||
Channel channel = event.getChannel();
|
||||
|
||||
@@ -217,7 +260,7 @@ public class ChannelProcessingManager implements Listener<ChannelEvent>
|
||||
//If we're already processing, ignore the request
|
||||
if(processingChain != null && processingChain.isProcessing())
|
||||
{
|
||||
return;
|
||||
throw new ChannelException("Channel is already playing");
|
||||
}
|
||||
|
||||
//Ensure that we can get a source before we construct a new processing chain
|
||||
@@ -240,7 +283,7 @@ public class ChannelProcessingManager implements Listener<ChannelEvent>
|
||||
mChannelEventBroadcaster.broadcast(new ChannelEvent(channel,
|
||||
ChannelEvent.Event.NOTIFICATION_PROCESSING_START_REJECTED, TUNER_UNAVAILABLE_DESCRIPTION));
|
||||
|
||||
return;
|
||||
throw new ChannelException("No Tuner Available");
|
||||
}
|
||||
|
||||
if(processingChain == null)
|
||||
@@ -277,8 +320,17 @@ public class ChannelProcessingManager implements Listener<ChannelEvent>
|
||||
|
||||
if(toShutdown != null)
|
||||
{
|
||||
mLog.info("Channel source error detected - stopping channel [" + toShutdown.getName() + "]");
|
||||
stopProcessing(toShutdown, true);
|
||||
mLog.warn("Channel source error detected - stopping channel [" + toShutdown.getName() + "]");
|
||||
|
||||
try
|
||||
{
|
||||
stopProcessing(toShutdown, true);
|
||||
}
|
||||
catch(ChannelException ce)
|
||||
{
|
||||
mLog.error("Error stopping channel [" + channel.getName() + "] with source error - " +
|
||||
ce.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -380,7 +432,7 @@ public class ChannelProcessingManager implements Listener<ChannelEvent>
|
||||
* @param channel to stop
|
||||
* @param remove set to true to remove the associated processing chain.
|
||||
*/
|
||||
private void stopProcessing(Channel channel, boolean remove)
|
||||
private void stopProcessing(Channel channel, boolean remove) throws ChannelException
|
||||
{
|
||||
channel.setProcessing(false);
|
||||
|
||||
@@ -412,6 +464,10 @@ public class ChannelProcessingManager implements Listener<ChannelEvent>
|
||||
processingChain.dispose();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new ChannelException("Channel is not currently playing");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -423,7 +479,14 @@ public class ChannelProcessingManager implements Listener<ChannelEvent>
|
||||
|
||||
for(Channel channel : channelsToStop)
|
||||
{
|
||||
stopProcessing(channel, true);
|
||||
try
|
||||
{
|
||||
stopProcessing(channel, true);
|
||||
}
|
||||
catch(ChannelException ce)
|
||||
{
|
||||
mLog.error("Error stopping channel [" + channel.getName() + "] - " + ce.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -30,9 +30,10 @@ public class ChannelSelectionManager implements Listener<ChannelEvent>
|
||||
* Manages channel selection state to ensure that only one channel is ever
|
||||
* in a selected state.
|
||||
*/
|
||||
public ChannelSelectionManager(Listener<ChannelEvent> listener)
|
||||
public ChannelSelectionManager(ChannelModel channelModel)
|
||||
{
|
||||
mChannelEventListener = listener;
|
||||
mChannelEventListener = channelModel;
|
||||
channelModel.addListener(this::receive);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -1,344 +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;
|
||||
|
||||
import io.github.dsheirer.alias.AliasModel;
|
||||
import io.github.dsheirer.gui.editor.Editor;
|
||||
import net.miginfocom.swing.MigLayout;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.swing.ComboBoxModel;
|
||||
import javax.swing.DefaultComboBoxModel;
|
||||
import javax.swing.JCheckBox;
|
||||
import javax.swing.JComboBox;
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.JSpinner;
|
||||
import javax.swing.JTextField;
|
||||
import javax.swing.SpinnerModel;
|
||||
import javax.swing.SpinnerNumberModel;
|
||||
import javax.swing.event.ChangeEvent;
|
||||
import javax.swing.event.ChangeListener;
|
||||
import javax.swing.event.DocumentEvent;
|
||||
import javax.swing.event.DocumentListener;
|
||||
import java.awt.Dimension;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.ActionListener;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
public class NameConfigurationEditor extends Editor<Channel> implements DocumentListener
|
||||
{
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private final static Logger mLog = LoggerFactory.getLogger(NameConfigurationEditor.class);
|
||||
|
||||
private static ComboBoxModel<String> EMPTY_SYSTEM_MODEL = new DefaultComboBoxModel<>();
|
||||
private static ComboBoxModel<String> EMPTY_SITE_MODEL = new DefaultComboBoxModel<>();
|
||||
private static ComboBoxModel<String> EMPTY_ALIAS_LIST_MODEL = new DefaultComboBoxModel<>();
|
||||
|
||||
private AliasModel mAliasModel;
|
||||
private ChannelModel mChannelModel;
|
||||
|
||||
private JTextField mChannelName;
|
||||
private JComboBox<String> mSystemNameCombo;
|
||||
private JComboBox<String> mSiteNameCombo;
|
||||
private JComboBox<String> mAliasListCombo;
|
||||
private JCheckBox mAutoStartCheckBox;
|
||||
private JSpinner mAutoStartOrder;
|
||||
|
||||
public NameConfigurationEditor(AliasModel aliasModel, ChannelModel model)
|
||||
{
|
||||
mAliasModel = aliasModel;
|
||||
mChannelModel = model;
|
||||
|
||||
init();
|
||||
}
|
||||
|
||||
private void init()
|
||||
{
|
||||
setLayout(new MigLayout("fill,wrap 4", "[right][grow,fill][right][grow,fill]", "[][][grow]"));
|
||||
|
||||
mChannelName = new JTextField("");
|
||||
mChannelName.getDocument().addDocumentListener(this);
|
||||
mChannelName.setEnabled(false);
|
||||
|
||||
add(new JLabel("Name:"));
|
||||
add(mChannelName);
|
||||
|
||||
mSystemNameCombo = new JComboBox<>(EMPTY_SYSTEM_MODEL);
|
||||
mSystemNameCombo.setEnabled(false);
|
||||
mSystemNameCombo.setEditable(true);
|
||||
mSystemNameCombo.addActionListener(new ActionListener()
|
||||
{
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e)
|
||||
{
|
||||
Object selected = mSystemNameCombo.getSelectedItem();
|
||||
|
||||
if(selected != null)
|
||||
{
|
||||
String system = (String)selected;
|
||||
|
||||
List<String> sites = mChannelModel.getSites(system);
|
||||
|
||||
if(!sites.contains(getItem().getSite()))
|
||||
{
|
||||
sites.add(getItem().getSite());
|
||||
}
|
||||
|
||||
if(sites.isEmpty())
|
||||
{
|
||||
mSiteNameCombo.setModel(EMPTY_SITE_MODEL);
|
||||
}
|
||||
else
|
||||
{
|
||||
mSiteNameCombo.setModel(new DefaultComboBoxModel<String>(
|
||||
sites.toArray(new String[0])));
|
||||
;
|
||||
}
|
||||
|
||||
mSiteNameCombo.setSelectedItem(getItem().getSite());
|
||||
}
|
||||
|
||||
setModified(true);
|
||||
}
|
||||
});
|
||||
|
||||
add(new JLabel("System:"));
|
||||
add(mSystemNameCombo);
|
||||
|
||||
mAliasListCombo = new JComboBox<String>(EMPTY_ALIAS_LIST_MODEL);
|
||||
mAliasListCombo.setEnabled(false);
|
||||
mAliasListCombo.setEditable(true);
|
||||
mAliasListCombo.addActionListener(new ActionListener()
|
||||
{
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e)
|
||||
{
|
||||
setModified(true);
|
||||
}
|
||||
});
|
||||
|
||||
add(new JLabel("Alias List:"));
|
||||
add(mAliasListCombo);
|
||||
|
||||
mSiteNameCombo = new JComboBox<>(EMPTY_SITE_MODEL);
|
||||
mSiteNameCombo.setEnabled(false);
|
||||
mSiteNameCombo.setEditable(true);
|
||||
mSiteNameCombo.addActionListener(new ActionListener()
|
||||
{
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e)
|
||||
{
|
||||
setModified(true);
|
||||
}
|
||||
});
|
||||
|
||||
add(new JLabel("Site:"));
|
||||
add(mSiteNameCombo);
|
||||
|
||||
JPanel autoStartPanel = new JPanel();
|
||||
|
||||
mAutoStartCheckBox = new JCheckBox("Auto-Start");
|
||||
mAutoStartCheckBox.setToolTipText("Automatically start this channel each time the application runs.");
|
||||
mAutoStartCheckBox.addActionListener(new ActionListener()
|
||||
{
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e)
|
||||
{
|
||||
mAutoStartOrder.setEnabled(mAutoStartCheckBox.isSelected());
|
||||
setModified(true);
|
||||
}
|
||||
});
|
||||
autoStartPanel.add(mAutoStartCheckBox);
|
||||
|
||||
SpinnerModel model = new SpinnerNumberModel(0,0,500,1);
|
||||
mAutoStartOrder = new JSpinner(model);
|
||||
mAutoStartOrder.setEnabled(false);
|
||||
mAutoStartOrder.setPreferredSize(new Dimension(60, mAutoStartOrder.getPreferredSize().height));
|
||||
mAutoStartOrder.setToolTipText("Auto-start order: 1 (high) <> 500 (low). 0 = no ordering");
|
||||
mAutoStartOrder.addChangeListener(new ChangeListener()
|
||||
{
|
||||
@Override
|
||||
public void stateChanged(ChangeEvent e)
|
||||
{
|
||||
setModified(true);
|
||||
}
|
||||
});
|
||||
|
||||
autoStartPanel.add(new JLabel("Order:"));
|
||||
autoStartPanel.add(mAutoStartOrder);
|
||||
add(new JLabel()); //Empty space holder
|
||||
add(autoStartPanel);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void save()
|
||||
{
|
||||
if(hasItem() && isModified())
|
||||
{
|
||||
getItem().setName(mChannelName.getText());
|
||||
|
||||
Object system = mSystemNameCombo.getSelectedItem();
|
||||
getItem().setSystem((system == null ? null : (String)system));
|
||||
|
||||
Object site = mSiteNameCombo.getSelectedItem();
|
||||
getItem().setSite((site == null ? null : (String)site));
|
||||
|
||||
Object aliasList = mAliasListCombo.getSelectedItem();
|
||||
getItem().setAliasListName(aliasList == null ? null : (String)aliasList);
|
||||
|
||||
getItem().setAutoStart(mAutoStartCheckBox.isSelected());
|
||||
|
||||
int order = ((Number)mAutoStartOrder.getValue()).intValue();
|
||||
getItem().setAutoStartOrder((order > 0 ? order : null));
|
||||
}
|
||||
|
||||
setModified(false);
|
||||
}
|
||||
|
||||
private void setControlsEnabled(boolean enabled)
|
||||
{
|
||||
if(mChannelName.isEnabled() != enabled)
|
||||
{
|
||||
mChannelName.setEnabled(enabled);
|
||||
}
|
||||
|
||||
if(mAliasListCombo.isEnabled() != enabled)
|
||||
{
|
||||
mAliasListCombo.setEnabled(enabled);
|
||||
}
|
||||
|
||||
if(mSystemNameCombo.isEnabled() != enabled)
|
||||
{
|
||||
mSystemNameCombo.setEnabled(enabled);
|
||||
}
|
||||
|
||||
if(mSiteNameCombo.isEnabled() != enabled)
|
||||
{
|
||||
mSiteNameCombo.setEnabled(enabled);
|
||||
}
|
||||
|
||||
if(mAutoStartCheckBox.isEnabled() != enabled)
|
||||
{
|
||||
mAutoStartCheckBox.setEnabled(enabled);
|
||||
}
|
||||
|
||||
if(mAutoStartOrder.isEnabled() != enabled)
|
||||
{
|
||||
mAutoStartOrder.setEnabled(enabled);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setItem(Channel channel)
|
||||
{
|
||||
super.setItem(channel);
|
||||
|
||||
if(hasItem())
|
||||
{
|
||||
setControlsEnabled(true);
|
||||
|
||||
mChannelName.setText(channel.getName());
|
||||
|
||||
List<String> systems = mChannelModel.getSystems();
|
||||
|
||||
if(systems.isEmpty())
|
||||
{
|
||||
mSystemNameCombo.setModel(EMPTY_SYSTEM_MODEL);
|
||||
}
|
||||
else
|
||||
{
|
||||
mSystemNameCombo.setModel(new DefaultComboBoxModel<String>(
|
||||
systems.toArray(new String[0])));
|
||||
;
|
||||
}
|
||||
|
||||
mSystemNameCombo.setSelectedItem(channel.getSystem());
|
||||
|
||||
List<String> sites = mChannelModel.getSites(channel.getSystem());
|
||||
|
||||
if(sites.isEmpty())
|
||||
{
|
||||
mSiteNameCombo.setModel(EMPTY_SITE_MODEL);
|
||||
}
|
||||
else
|
||||
{
|
||||
mSiteNameCombo.setModel(new DefaultComboBoxModel<String>(
|
||||
sites.toArray(new String[0])));
|
||||
;
|
||||
}
|
||||
|
||||
if(channel.getSite() != null)
|
||||
{
|
||||
mSiteNameCombo.setSelectedItem(channel.getSite());
|
||||
}
|
||||
|
||||
List<String> lists = mAliasModel.getListNames();
|
||||
Collections.sort(lists);
|
||||
mAliasListCombo.setModel(new DefaultComboBoxModel<String>(
|
||||
lists.toArray(new String[0])));
|
||||
|
||||
if(channel.getAliasListName() != null)
|
||||
{
|
||||
mAliasListCombo.setSelectedItem(channel.getAliasListName());
|
||||
}
|
||||
|
||||
if(channel.isAutoStart())
|
||||
{
|
||||
mAutoStartCheckBox.setSelected(channel.isAutoStart());
|
||||
mAutoStartOrder.setEnabled(true);
|
||||
}
|
||||
else
|
||||
{
|
||||
mAutoStartCheckBox.setSelected(false);
|
||||
mAutoStartOrder.setEnabled(false);
|
||||
}
|
||||
|
||||
mAutoStartOrder.setValue(channel.hasAutoStartOrder() ? channel.getAutoStartOrder() : 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
setControlsEnabled(false);
|
||||
mChannelName.setText(null);
|
||||
}
|
||||
|
||||
|
||||
setModified(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void insertUpdate(DocumentEvent e)
|
||||
{
|
||||
setModified(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void changedUpdate(DocumentEvent e)
|
||||
{
|
||||
setModified(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeUpdate(DocumentEvent e)
|
||||
{
|
||||
setModified(true);
|
||||
}
|
||||
}
|
||||
@@ -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