From da924e8b3aae886ddef28c09dc092feadcfdd446 Mon Sep 17 00:00:00 2001 From: denny Date: Sun, 24 Jan 2016 10:31:31 -0500 Subject: [PATCH] Updates playlist editor with Add, Copy/Clone and Delete channel functionality --- src/controller/channel/Channel.java | 47 ++++++ src/controller/channel/ChannelEditor.java | 37 ----- src/controller/channel/ChannelModel.java | 64 ++++++--- .../decode/config/DecodeConfigFactory.java | 79 +++++++++- src/playlist/PlaylistEditor.java | 136 +++++++++++++++++- src/playlist/PlaylistManager.java | 7 + src/source/config/SourceConfigFactory.java | 35 +++++ 7 files changed, 337 insertions(+), 68 deletions(-) diff --git a/src/controller/channel/Channel.java b/src/controller/channel/Channel.java index b7d290a8..a86d4aae 100644 --- a/src/controller/channel/Channel.java +++ b/src/controller/channel/Channel.java @@ -23,14 +23,17 @@ import javax.xml.bind.annotation.XmlRootElement; import javax.xml.bind.annotation.XmlSeeAlso; import javax.xml.bind.annotation.XmlTransient; +import module.decode.DecoderType; import module.decode.config.AuxDecodeConfiguration; import module.decode.config.DecodeConfigFactory; import module.decode.config.DecodeConfiguration; +import module.log.EventLogType; import module.log.config.EventLogConfiguration; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import record.RecorderType; import record.config.RecordConfiguration; import source.SourceType; import source.config.SourceConfigFactory; @@ -115,6 +118,50 @@ public class Channel extends Configuration implements IFrequencyChangeProcessor { 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 ); + + AuxDecodeConfiguration aux = new AuxDecodeConfiguration(); + + for( DecoderType auxType: aux.getAuxDecoders() ) + { + aux.addAuxDecoder( auxType ); + } + + channel.setAuxDecodeConfiguration( aux ); + + channel.setDecodeConfiguration( DecodeConfigFactory.copy( mDecodeConfiguration ) ); + + EventLogConfiguration log = new EventLogConfiguration(); + + for( EventLogType logType: mEventLogConfiguration.getLoggers() ) + { + log.addLogger( logType ); + } + + channel.setEventLogConfiguration( log ); + + RecordConfiguration record = new RecordConfiguration(); + + for( RecorderType recordType: mRecordConfiguration.getRecorders() ) + { + record.addRecorder( recordType ); + } + + channel.setRecordConfiguration( record ); + + channel.setSourceConfiguration( SourceConfigFactory.copy( mSourceConfiguration ) ); + + return channel; + } //TODO: remove this /** diff --git a/src/controller/channel/ChannelEditor.java b/src/controller/channel/ChannelEditor.java index 7879b03d..4e6c85e9 100644 --- a/src/controller/channel/ChannelEditor.java +++ b/src/controller/channel/ChannelEditor.java @@ -17,21 +17,16 @@ ******************************************************************************/ package controller.channel; - import java.awt.Color; import java.awt.Component; -import java.awt.EventQueue; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import javax.swing.JButton; -import javax.swing.JCheckBox; -import javax.swing.JComboBox; import javax.swing.JLabel; import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JSeparator; -import javax.swing.JTextField; import module.decode.AuxDecodeComponentEditor; import module.decode.DecodeComponentEditor; @@ -45,7 +40,6 @@ import playlist.PlaylistManager; import record.RecordComponentEditor; import source.SourceComponentEditor; import source.SourceManager; -import alias.AliasList; import com.jidesoft.swing.JideTabbedPane; @@ -169,7 +163,6 @@ public class ChannelEditor extends JPanel @Override public void channelChanged( ChannelEvent event ) { - mLog.debug( "Received channel event: " + event.getEvent() ); if( mChannel != null && mChannel == event.getChannel() ) { switch( event.getEvent() ) @@ -302,36 +295,6 @@ public class ChannelEditor extends JPanel mChannelModel.broadcast( new ChannelEvent( mChannel, Event.NOTIFICATION_CONFIGURATION_CHANGE ) ); } - -// @Override -// public void reset() -// { -// mChannelName.setText( mChannel.getName() ); -// mChannelName.requestFocus(); -// mChannelName.requestFocusInWindow(); -// -// mChannelEnabled.setSelected( mChannel.getEnabled() ); -// mChannelEnabled.requestFocus(); -// mChannelEnabled.requestFocusInWindow(); -// -// String aliasListName = mChannel.getAliasListName(); -// -// if( aliasListName != null ) -// { -// AliasList selected = mPlaylistManager.getPlayist().getAliasDirectory() -// .getAliasList( aliasListName ); -// -// mComboAliasLists.setSelectedItem( selected ); -// } -// -// -// mSourceEditor.reset(); -// mDecodeEditor.reset(); -// mAuxDecodeEditor.reset(); -// mEventLogEditor.reset(); -// mRecordEditor.reset(); -// } - // /** // * Validate the specified editor against each of the component editors // */ diff --git a/src/controller/channel/ChannelModel.java b/src/controller/channel/ChannelModel.java index 751c374e..ab1e3a88 100644 --- a/src/controller/channel/ChannelModel.java +++ b/src/controller/channel/ChannelModel.java @@ -26,6 +26,10 @@ import javax.swing.event.TableModelEvent; import javax.swing.event.TableModelListener; import javax.swing.table.TableModel; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import playlist.PlaylistEditor; import module.decode.DecoderType; import controller.channel.Channel.ChannelType; import controller.channel.ChannelEvent.Event; @@ -35,6 +39,8 @@ import controller.channel.ChannelEvent.Event; */ public class ChannelModel implements IChannelEventBroadcaster, TableModel { + private final static Logger mLog = LoggerFactory.getLogger( ChannelModel.class ); + private ChannelSelectionManager mChannelSelectionManager; private List mChannels = new ArrayList<>(); private List mTrafficChannels = new ArrayList<>(); @@ -55,6 +61,16 @@ public class ChannelModel implements IChannelEventBroadcaster, TableModel return Collections.unmodifiableList( mChannels ); } + public Channel getChannelAtIndex( int row ) + { + if( mChannels.size() >= row ) + { + return mChannels.get( row ); + } + + return null; + } + /** * Returns a list of unique system values from across the channel set */ @@ -150,20 +166,23 @@ public class ChannelModel implements IChannelEventBroadcaster, TableModel /** * Adds the channel to the model and broadcasts a channel add event */ - public void addChannel( Channel channel ) + public int addChannel( Channel channel ) { + int index = -1; + switch( channel.getChannelType() ) { case STANDARD: mChannels.add( channel ); - int index = mChannels.size() - 1; + index = mChannels.size() - 1; broadcast( new TableModelEvent( this, index, index, TableModelEvent.ALL_COLUMNS, TableModelEvent.INSERT ) ); break; case TRAFFIC: mTrafficChannels.add( channel ); + index = mChannels.size() - 1; break; default: break; @@ -175,6 +194,8 @@ public class ChannelModel implements IChannelEventBroadcaster, TableModel { broadcast( new ChannelEvent( channel, Event.REQUEST_ENABLE ) ); } + + return index; } /** @@ -182,27 +203,30 @@ public class ChannelModel implements IChannelEventBroadcaster, TableModel */ public void removeChannel( Channel channel ) { - switch( channel.getChannelType() ) + if( channel != null ) { - case STANDARD: - int index = mChannels.indexOf( channel ); - - mChannels.remove( channel ); + switch( channel.getChannelType() ) + { + case STANDARD: + int index = mChannels.indexOf( channel ); + + mChannels.remove( channel ); - if( index >= 0 ) - { - broadcast( new TableModelEvent( this, index, index, - TableModelEvent.ALL_COLUMNS, TableModelEvent.DELETE ) ); - } - break; - case TRAFFIC: - mTrafficChannels.remove( channel ); - break; - default: - break; + if( index >= 0 ) + { + broadcast( new TableModelEvent( this, index, index, + TableModelEvent.ALL_COLUMNS, TableModelEvent.DELETE ) ); + } + break; + case TRAFFIC: + mTrafficChannels.remove( channel ); + break; + default: + break; + } + + broadcast( new ChannelEvent( channel, Event.NOTIFICATION_DELETE ) ); } - - broadcast( new ChannelEvent( channel, Event.NOTIFICATION_DELETE ) ); } /** diff --git a/src/module/decode/config/DecodeConfigFactory.java b/src/module/decode/config/DecodeConfigFactory.java index a4a88398..8c0a0f0d 100644 --- a/src/module/decode/config/DecodeConfigFactory.java +++ b/src/module/decode/config/DecodeConfigFactory.java @@ -43,18 +43,18 @@ public class DecodeConfigFactory case AM: retVal = new DecodeConfigAM(); break; - case NBFM: - retVal = new DecodeConfigNBFM(); + case LTR_NET: + retVal = new DecodeConfigLTRNet(); break; case LTR_STANDARD: retVal = new DecodeConfigLTRStandard(); break; - case LTR_NET: - retVal = new DecodeConfigLTRNet(); - break; case MPT1327: retVal = new DecodeConfigMPT1327(); break; + case NBFM: + retVal = new DecodeConfigNBFM(); + break; case PASSPORT: retVal = new DecodeConfigPassport(); break; @@ -68,4 +68,73 @@ public class DecodeConfigFactory return retVal; } + + /** + * Creates a copy of the configuration + */ + public static DecodeConfiguration copy( DecodeConfiguration config ) + { + if( config != null ) + { + switch( config.getDecoderType() ) + { + case AM: + DecodeConfigAM originalAM = (DecodeConfigAM)config; + DecodeConfigAM copyAM = new DecodeConfigAM(); + copyAM.setAFC( originalAM.getAFC() ); + copyAM.setAFCMaximumCorrection( originalAM.getAFCMaximumCorrection() ); + return copyAM; + case LTR_NET: + DecodeConfigLTRNet originalLTRNet = (DecodeConfigLTRNet)config; + DecodeConfigLTRNet copyLTRNet = new DecodeConfigLTRNet(); + copyLTRNet.setAFC( originalLTRNet.getAFC() ); + copyLTRNet.setAFCMaximumCorrection( originalLTRNet.getAFCMaximumCorrection() ); + copyLTRNet.setMessageDirection( originalLTRNet.getMessageDirection() ); + return copyLTRNet; + case LTR_STANDARD: + DecodeConfigLTRStandard originalLTRStandard = (DecodeConfigLTRStandard)config; + DecodeConfigLTRStandard copyLTRStandard = new DecodeConfigLTRStandard(); + copyLTRStandard.setAFC( originalLTRStandard.getAFC() ); + copyLTRStandard.setAFCMaximumCorrection( originalLTRStandard.getAFCMaximumCorrection() ); + copyLTRStandard.setMessageDirection( originalLTRStandard.getMessageDirection() ); + return copyLTRStandard; + case MPT1327: + DecodeConfigMPT1327 originalMPT = (DecodeConfigMPT1327)config; + DecodeConfigMPT1327 copyMPT = new DecodeConfigMPT1327(); + copyMPT.setAFC( originalMPT.getAFC() ); + copyMPT.setAFCMaximumCorrection( originalMPT.getAFCMaximumCorrection() ); + copyMPT.setCallTimeout( originalMPT.getCallTimeout() ); + copyMPT.setChannelMapName( originalMPT.getChannelMapName() ); + copyMPT.setSync( originalMPT.getSync() ); + copyMPT.setTrafficChannelPoolSize( originalMPT.getTrafficChannelPoolSize() ); + return copyMPT; + case NBFM: + DecodeConfigNBFM originalNBFM = (DecodeConfigNBFM)config; + DecodeConfigNBFM copyNBFM = new DecodeConfigNBFM(); + copyNBFM.setAFC( originalNBFM.getAFC() ); + copyNBFM.setAFCMaximumCorrection( originalNBFM.getAFCMaximumCorrection() ); + return copyNBFM; + case P25_PHASE1: + DecodeConfigP25Phase1 originalP25 = (DecodeConfigP25Phase1)config; + DecodeConfigP25Phase1 copyP25 = new DecodeConfigP25Phase1(); + copyP25.setAFC( originalP25.getAFC() ); + copyP25.setAFCMaximumCorrection( originalP25.getAFCMaximumCorrection() ); + copyP25.setIgnoreDataCalls( originalP25.getIgnoreDataCalls() ); + copyP25.setModulation( originalP25.getModulation() ); + copyP25.setTrafficChannelPoolSize( originalP25.getTrafficChannelPoolSize() ); + return copyP25; + case PASSPORT: + DecodeConfigPassport originalPass = (DecodeConfigPassport)config; + DecodeConfigPassport copyPass = new DecodeConfigPassport(); + copyPass.setAFC( originalPass.getAFC() ); + copyPass.setAFCMaximumCorrection( originalPass.getAFCMaximumCorrection() ); + return copyPass; + default: + throw new IllegalArgumentException( "Unrecognized decoder " + + "configuration type:" + config.getDecoderType() ); + } + } + + return null; + } } diff --git a/src/playlist/PlaylistEditor.java b/src/playlist/PlaylistEditor.java index d94a9c6f..f48212cf 100644 --- a/src/playlist/PlaylistEditor.java +++ b/src/playlist/PlaylistEditor.java @@ -1,5 +1,11 @@ package playlist; +import java.awt.Rectangle; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +import javax.swing.JButton; +import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JTable; @@ -22,14 +28,24 @@ import controller.channel.Channel; import controller.channel.ChannelEditor; import controller.channel.ChannelModel; -public class PlaylistEditor extends JPanel implements ListSelectionListener +public class PlaylistEditor extends JPanel + implements ActionListener, ListSelectionListener { private static final long serialVersionUID = 1L; private final static Logger mLog = LoggerFactory.getLogger( PlaylistEditor.class ); private ChannelModel mChannelModel; private JTable mChannelTable; + private TableFilterHeader mTableFilterHeader; private ChannelEditor mEditor; + + 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 PlaylistEditor( ChannelModel channelModel, PlaylistManager playlistManager, @@ -55,16 +71,42 @@ public class PlaylistEditor extends JPanel implements ListSelectionListener mChannelTable.getSelectionModel().addListSelectionListener( this ); mChannelTable.setAutoCreateRowSorter( true ); - TableFilterHeader channelHeaderFilter = - new TableFilterHeader( mChannelTable, AutoChoices.ENABLED ); - channelHeaderFilter.setFilterOnUpdates( true ); + 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( "Adds a new default channel" ); + buttonsPanel.add( mNewChannelButton ); + + mCopyChannelButton.addActionListener( this ); + mCopyChannelButton.setEnabled( false ); + mCopyChannelButton.setToolTipText( "Creates a copy of the currently selected channel and adds it" ); + buttonsPanel.add( mCopyChannelButton ); + + mDeleteChannelButton.addActionListener( this ); + mDeleteChannelButton.setEnabled( false ); + mDeleteChannelButton.setToolTipText( "Deletes the currently selected channel" ); + 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.HORIZONTAL_SPLIT ); splitPane.setDividerSize( 5 ); splitPane.setShowGripper( true ); - splitPane.add( channelScroller ); + splitPane.add( listAndButtonsPanel ); splitPane.add( mEditor ); add( splitPane ); @@ -82,17 +124,99 @@ public class PlaylistEditor extends JPanel implements ListSelectionListener { int index = mChannelTable.convertRowIndexToModel( mChannelTable.getSelectedRow() ); - Channel channel = mChannelModel.getChannels().get( index ); + Channel channel = mChannelModel.getChannelAtIndex( index ); if( channel != null ) { mEditor.setChannel( channel ); + mCopyChannelButton.setEnabled( true ); + mDeleteChannelButton.setEnabled( true ); } } else { mEditor.setChannel( null ); + mCopyChannelButton.setEnabled( false ); + mDeleteChannelButton.setEnabled( false ); } } } + + /** + * Selects the channel's table row and scrolls the table's viewport to show 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 ); + 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( PlaylistEditor.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; + } + } } diff --git a/src/playlist/PlaylistManager.java b/src/playlist/PlaylistManager.java index dea60a8c..c8b4498e 100644 --- a/src/playlist/PlaylistManager.java +++ b/src/playlist/PlaylistManager.java @@ -97,6 +97,9 @@ public class PlaylistManager implements ChannelEventListener */ private void transferPlaylistToModels() { +//TODO: delete the playlist after we've loaded it, since the models will have +//all of the data + mPlaylistLoading = true; mChannelModel.addChannels( mPlaylist.getChannels() ); @@ -167,6 +170,10 @@ public class PlaylistManager implements ChannelEventListener public void save() { +//TODO: recreate the playlist and load values from each of the models + + mPlaylist.setChannels( mChannelModel.getChannels() ); + JAXBContext context = null; SystemProperties props = SystemProperties.getInstance(); diff --git a/src/source/config/SourceConfigFactory.java b/src/source/config/SourceConfigFactory.java index 2cedeaec..f4d02f39 100644 --- a/src/source/config/SourceConfigFactory.java +++ b/src/source/config/SourceConfigFactory.java @@ -50,4 +50,39 @@ public class SourceConfigFactory return retVal; } + + /** + * Creates a copy of the configuration + */ + public static SourceConfiguration copy( SourceConfiguration config ) + { + if( config != null ) + { + switch( config.getSourceType() ) + { + case MIXER: + SourceConfigMixer originalMixer = (SourceConfigMixer)config; + SourceConfigMixer copyMixer = new SourceConfigMixer(); + copyMixer.setChannel( originalMixer.getChannel() ); + copyMixer.setMixer( originalMixer.getMixer() ); + return copyMixer; + case RECORDING: + SourceConfigRecording originalRec = (SourceConfigRecording)config; + SourceConfigRecording copyRec = new SourceConfigRecording(); + copyRec.setFrequency( originalRec.getFrequency() ); + copyRec.setRecordingAlias( originalRec.getRecordingAlias() ); + return copyRec; + case TUNER: + SourceConfigTuner originalTuner = (SourceConfigTuner)config; + SourceConfigTuner copyTuner = new SourceConfigTuner(); + copyTuner.setFrequency( originalTuner.getFrequency() ); + return copyTuner; + case NONE: + default: + return new SourceConfigNone(); + } + } + + return null; + } }