diff --git a/src/alias/AliasController.java b/src/alias/AliasController.java index 0619672c..7dc27ccf 100644 --- a/src/alias/AliasController.java +++ b/src/alias/AliasController.java @@ -1,369 +1,356 @@ package alias; import audio.broadcast.BroadcastModel; +import com.jidesoft.swing.JideSplitPane; import gui.editor.Editor; +import icon.IconManager; +import net.coderazzi.filters.gui.AutoChoices; +import net.coderazzi.filters.gui.TableFilterHeader; +import net.miginfocom.swing.MigLayout; -import java.awt.Color; -import java.awt.Component; -import java.awt.Dimension; -import java.awt.Rectangle; +import javax.swing.*; +import javax.swing.event.ListSelectionEvent; +import javax.swing.event.ListSelectionListener; +import javax.swing.table.DefaultTableCellRenderer; +import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.util.ArrayList; import java.util.List; -import javax.swing.ImageIcon; -import javax.swing.JButton; -import javax.swing.JLabel; -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 net.coderazzi.filters.gui.AutoChoices; -import net.coderazzi.filters.gui.TableFilterHeader; -import net.miginfocom.swing.MigLayout; -import settings.SettingsManager; - -import com.jidesoft.swing.JideSplitPane; - -public class AliasController extends JPanel - implements ActionListener, ListSelectionListener +public class AliasController extends JPanel + implements ActionListener, ListSelectionListener { - private static final long serialVersionUID = 1L; + private static final long serialVersionUID = 1L; - private AliasModel mAliasModel; - private JTable mAliasTable; - private TableFilterHeader mTableFilterHeader; - private AliasEditor mAliasEditor; - private MultipleAliasEditor mMultipleAliasEditor; - private JideSplitPane mSplitPane; - - private static final String NEW_ALIAS = "New"; - private static final String COPY_ALIAS = "Copy"; - private static final String DELETE_ALIAS = "Delete"; - - private JButton mNewButton = new JButton( NEW_ALIAS ); - private JButton mCopyButton = new JButton( COPY_ALIAS ); - private JButton mDeleteButton = new JButton( DELETE_ALIAS ); - - private IconCellRenderer mIconCellRenderer; + private AliasModel mAliasModel; + private JTable mAliasTable; + private TableFilterHeader mTableFilterHeader; + private AliasEditor mAliasEditor; + private MultipleAliasEditor mMultipleAliasEditor; + private JideSplitPane mSplitPane; - public AliasController(AliasModel aliasModel, BroadcastModel broadcastModel, SettingsManager settingsManager ) - { - mAliasModel = aliasModel; + private static final String NEW_ALIAS = "New"; + private static final String COPY_ALIAS = "Copy"; + private static final String DELETE_ALIAS = "Delete"; - mAliasEditor = new AliasEditor( mAliasModel, broadcastModel, settingsManager ); - mMultipleAliasEditor = new MultipleAliasEditor( mAliasModel, broadcastModel, settingsManager ); + private JButton mNewButton = new JButton(NEW_ALIAS); + private JButton mCopyButton = new JButton(COPY_ALIAS); + private JButton mDeleteButton = new JButton(DELETE_ALIAS); - mIconCellRenderer = new IconCellRenderer( settingsManager ); - - init(); - - mAliasModel.addListener( mAliasEditor ); - } - - private void init() - { - setLayout( new MigLayout( "insets 0 0 0 0", - "[grow,fill]", - "[grow,fill]") ); + private IconCellRenderer mIconCellRenderer; - //System Configuration View and Editor - mAliasTable = new JTable( mAliasModel ); - mAliasTable.setSelectionMode( ListSelectionModel.MULTIPLE_INTERVAL_SELECTION ); - mAliasTable.getSelectionModel().addListSelectionListener( this ); - mAliasTable.setAutoCreateRowSorter( true ); - - mAliasTable.getColumnModel().getColumn( AliasModel.COLUMN_COLOR ) - .setCellRenderer( new ColorCellRenderer() ); - - mAliasTable.getColumnModel().getColumn( AliasModel.COLUMN_ICON ) - .setCellRenderer( mIconCellRenderer ); + public AliasController(AliasModel aliasModel, BroadcastModel broadcastModel, IconManager iconManager) + { + mAliasModel = aliasModel; - mTableFilterHeader = new TableFilterHeader( mAliasTable, AutoChoices.ENABLED ); - mTableFilterHeader.setFilterOnUpdates( true ); - - JScrollPane tableScroller = new JScrollPane( mAliasTable ); + mAliasEditor = new AliasEditor(mAliasModel, broadcastModel, iconManager); + mMultipleAliasEditor = new MultipleAliasEditor(mAliasModel, broadcastModel, iconManager); - JPanel buttonsPanel = new JPanel(); - - buttonsPanel.setLayout( - new MigLayout( "insets 0 0 0 0", "[grow,fill][grow,fill][grow,fill]", "[]") ); + mIconCellRenderer = new IconCellRenderer(iconManager); - mNewButton.addActionListener( this ); - mNewButton.setToolTipText( "Adds a new alias" ); - buttonsPanel.add( mNewButton ); - - mCopyButton.addActionListener( this ); - mCopyButton.setEnabled( false ); - mCopyButton.setToolTipText( "Creates a copy of the currently selected alias and adds it" ); - buttonsPanel.add( mCopyButton ); + init(); - mDeleteButton.addActionListener( this ); - mDeleteButton.setEnabled( false ); - mDeleteButton.setToolTipText( "Deletes the currently selected alias" ); - buttonsPanel.add( mDeleteButton ); - - JPanel listAndButtonsPanel = new JPanel(); + mAliasModel.addListener(mAliasEditor); + } - listAndButtonsPanel.setLayout( - new MigLayout( "insets 0 0 0 0", "[grow,fill]", "[grow,fill][]") ); + private void init() + { + setLayout(new MigLayout("insets 0 0 0 0", + "[grow,fill]", + "[grow,fill]")); - listAndButtonsPanel.add( tableScroller, "wrap" ); - listAndButtonsPanel.add( buttonsPanel ); - - mSplitPane = new JideSplitPane( JideSplitPane.HORIZONTAL_SPLIT ); - mSplitPane.setDividerSize( 5 ); - mSplitPane.setShowGripper( true ); - mSplitPane.add( listAndButtonsPanel ); - mSplitPane.add( mAliasEditor ); + //System Configuration View and Editor + mAliasTable = new JTable(mAliasModel); + mAliasTable.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION); + mAliasTable.getSelectionModel().addListSelectionListener(this); + mAliasTable.setAutoCreateRowSorter(true); - add( mSplitPane ); - } + mAliasTable.getColumnModel().getColumn(AliasModel.COLUMN_COLOR) + .setCellRenderer(new ColorCellRenderer()); - /** - * Sets the editor argument as the currently visible alias editor - */ - private void setEditor( Editor editor ) - { - if( mSplitPane.getComponentCount() == 3 ) - { - Component component = mSplitPane.getComponent( 2 ); - - if( component instanceof Editor && component != editor ) - { - //Get current divider location so that we can reapply it after - //changing out the editors, so that the interface doesn't move - int location = mSplitPane.getDividerLocation( 0 ); - - ((Editor)component).setItem( null ); - - mSplitPane.remove( component ); - editor.setPreferredSize( new Dimension( 100, 100 ) ); - mSplitPane.add( editor ); - - mSplitPane.validate(); - mSplitPane.setDividerLocation( 0, location ); - mSplitPane.repaint(); - } - } - } - - private Alias getAlias( int selectedRow ) - { - if( selectedRow >= 0 ) - { - int index = mAliasTable.convertRowIndexToModel( selectedRow ); - - return mAliasModel.getAliasAtIndex( index ); - } - - return null; - } + mAliasTable.getColumnModel().getColumn(AliasModel.COLUMN_ICON) + .setCellRenderer(mIconCellRenderer); - @Override - public void valueChanged( ListSelectionEvent event ) - { - //Limits event firing to only when selection is complete - if( !event.getValueIsAdjusting() ) - { - int[] selectedRows = mAliasTable.getSelectedRows(); + mTableFilterHeader = new TableFilterHeader(mAliasTable, AutoChoices.ENABLED); + mTableFilterHeader.setFilterOnUpdates(true); - if( selectedRows.length == 0 ) - { - mAliasEditor.setItem( null ); - mCopyButton.setEnabled( false ); - mDeleteButton.setEnabled( false ); - } - else if( selectedRows.length == 1 ) - { - setEditor( mAliasEditor ); - - Alias selectedAlias = getAlias( selectedRows[ 0 ] ); - - mAliasEditor.setItem( selectedAlias ); - - if( selectedAlias != null ) - { - mCopyButton.setEnabled( true ); - mDeleteButton.setEnabled( true ); - } - else - { - mCopyButton.setEnabled( false ); - mDeleteButton.setEnabled( false ); - } - } - else - { - setEditor( mMultipleAliasEditor ); - - mCopyButton.setEnabled( true ); - mDeleteButton.setEnabled( true ); + JScrollPane tableScroller = new JScrollPane(mAliasTable); - List selectedAliases = new ArrayList(); - - for( int selectedRow: selectedRows ) - { - Alias alias = getAlias( selectedRow ); + JPanel buttonsPanel = new JPanel(); - selectedAliases.add( alias ); - } - - mMultipleAliasEditor.setItem( selectedAliases ); - } - } - } + buttonsPanel.setLayout( + new MigLayout("insets 0 0 0 0", "[grow,fill][grow,fill][grow,fill]", "[]")); - /** - * Adds the alias to the alias table/model and scrolls the view it - */ - private void addAlias( Alias alias ) - { - //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 = mAliasModel.addAlias( alias ); + mNewButton.addActionListener(this); + mNewButton.setToolTipText("Adds a new alias"); + buttonsPanel.add(mNewButton); - mTableFilterHeader.setAdaptiveChoices( true ); + mCopyButton.addActionListener(this); + mCopyButton.setEnabled(false); + mCopyButton.setToolTipText("Creates a copy of the currently selected alias and adds it"); + buttonsPanel.add(mCopyButton); - if( index >= 0 ) - { - int translatedIndex = mAliasTable.convertRowIndexToView( index ); - mAliasTable.setRowSelectionInterval( translatedIndex, translatedIndex ); - mAliasTable.scrollRectToVisible( - new Rectangle( mAliasTable.getCellRect( translatedIndex, 0, true ) ) ); - } - } - - private List getSelectedAliases() - { - List selected = new ArrayList<>(); - - int[] rows = mAliasTable.getSelectedRows(); + mDeleteButton.addActionListener(this); + mDeleteButton.setEnabled(false); + mDeleteButton.setToolTipText("Deletes the currently selected alias"); + buttonsPanel.add(mDeleteButton); - for( int row: rows ) - { - if( row >= 0 ) - { - Alias alias = mAliasModel.getAliasAtIndex( mAliasTable.convertRowIndexToModel( row ) ); - - if( alias != null ) - { - selected.add( alias ); - } - } - } - - return selected; - } + JPanel listAndButtonsPanel = new JPanel(); - /** - * Responds to New, Copy and Delete Channel button invocations - */ - @Override - public void actionPerformed( ActionEvent event ) - { - switch( event.getActionCommand() ) - { - case NEW_ALIAS: - addAlias( new Alias( "New Alias" ) ); - break; - case COPY_ALIAS: - for( Alias alias: getSelectedAliases() ) - { - addAlias( AliasFactory.copyOf( alias ) ); - } - break; - case DELETE_ALIAS: - List toDelete = getSelectedAliases(); + listAndButtonsPanel.setLayout( + new MigLayout("insets 0 0 0 0", "[grow,fill]", "[grow,fill][]")); - if( toDelete != null && !toDelete.isEmpty() ) - { - String title = toDelete.size() == 1 ? "Delete Alias?" : "Delete Aliases?"; - String prompt = toDelete.size() == 1 ? "Do you want to delete this alias?" : - "Do you want to delete these " + toDelete.size() + " aliases?"; - - int choice = JOptionPane.showConfirmDialog( AliasController.this, - prompt, title, JOptionPane.YES_NO_OPTION, JOptionPane.WARNING_MESSAGE ); - - if( choice == JOptionPane.YES_OPTION ) - { - for( Alias alias: toDelete ) - { - mAliasModel.removeAlias( alias ); - } - } - } - break; - default: - break; - } - } + listAndButtonsPanel.add(tableScroller, "wrap"); + listAndButtonsPanel.add(buttonsPanel); - /** - * Colorizes the cell based on the cell's integer value - */ - public class ColorCellRenderer extends DefaultTableCellRenderer - { - private static final long serialVersionUID = 1L; + mSplitPane = new JideSplitPane(JideSplitPane.HORIZONTAL_SPLIT); + mSplitPane.setDividerSize(5); + mSplitPane.setShowGripper(true); + mSplitPane.add(listAndButtonsPanel); + mSplitPane.add(mAliasEditor); - @Override - public Component getTableCellRendererComponent( JTable table, - Object value, boolean isSelected, boolean hasFocus, int row, - int column ) - { - JLabel label = (JLabel)super.getTableCellRendererComponent( table, - value, isSelected, hasFocus, row, column ); + add(mSplitPane); + } - label.setBackground( new Color( (int)value ) ); - - label.setText( "" ); - - return label; - } - } - - /** - * Displays cell's icon and name - */ - public class IconCellRenderer extends DefaultTableCellRenderer - { - private static final long serialVersionUID = 1L; - - private SettingsManager mSettingsManager; - - public IconCellRenderer( SettingsManager settingsManager ) - { - mSettingsManager = settingsManager; - } + /** + * Sets the editor argument as the currently visible alias editor + */ + private void setEditor(Editor editor) + { + if(mSplitPane.getComponentCount() == 3) + { + Component component = mSplitPane.getComponent(2); - @Override - public Component getTableCellRendererComponent( JTable table, - Object value, boolean isSelected, boolean hasFocus, int row, - int column ) - { - JLabel label = (JLabel)super.getTableCellRendererComponent( table, - value, isSelected, hasFocus, row, column ); + if(component instanceof Editor && component != editor) + { + //Get current divider location so that we can reapply it after + //changing out the editors, so that the interface doesn't move + int location = mSplitPane.getDividerLocation(0); - ImageIcon icon = mSettingsManager.getImageIcon( label.getText(), 12 ); + ((Editor) component).setItem(null); - if( icon != null ) - { - label.setIcon( icon ); - } - - return label; - } - } + mSplitPane.remove(component); + editor.setPreferredSize(new Dimension(100, 100)); + mSplitPane.add(editor); + + mSplitPane.validate(); + mSplitPane.setDividerLocation(0, location); + mSplitPane.repaint(); + } + } + } + + private Alias getAlias(int selectedRow) + { + if(selectedRow >= 0) + { + int index = mAliasTable.convertRowIndexToModel(selectedRow); + + return mAliasModel.getAliasAtIndex(index); + } + + return null; + } + + @Override + public void valueChanged(ListSelectionEvent event) + { + //Limits event firing to only when selection is complete + if(!event.getValueIsAdjusting()) + { + int[] selectedRows = mAliasTable.getSelectedRows(); + + if(selectedRows.length == 0) + { + mAliasEditor.setItem(null); + mCopyButton.setEnabled(false); + mDeleteButton.setEnabled(false); + } + else if(selectedRows.length == 1) + { + setEditor(mAliasEditor); + + Alias selectedAlias = getAlias(selectedRows[0]); + + mAliasEditor.setItem(selectedAlias); + + if(selectedAlias != null) + { + mCopyButton.setEnabled(true); + mDeleteButton.setEnabled(true); + } + else + { + mCopyButton.setEnabled(false); + mDeleteButton.setEnabled(false); + } + } + else + { + setEditor(mMultipleAliasEditor); + + mCopyButton.setEnabled(true); + mDeleteButton.setEnabled(true); + + List selectedAliases = new ArrayList(); + + for(int selectedRow : selectedRows) + { + Alias alias = getAlias(selectedRow); + + selectedAliases.add(alias); + } + + mMultipleAliasEditor.setItem(selectedAliases); + } + } + } + + /** + * Adds the alias to the alias table/model and scrolls the view it + */ + private void addAlias(Alias alias) + { + //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 = mAliasModel.addAlias(alias); + + mTableFilterHeader.setAdaptiveChoices(true); + + if(index >= 0) + { + int translatedIndex = mAliasTable.convertRowIndexToView(index); + mAliasTable.setRowSelectionInterval(translatedIndex, translatedIndex); + mAliasTable.scrollRectToVisible( + new Rectangle(mAliasTable.getCellRect(translatedIndex, 0, true))); + } + } + + private List getSelectedAliases() + { + List selected = new ArrayList<>(); + + int[] rows = mAliasTable.getSelectedRows(); + + for(int row : rows) + { + if(row >= 0) + { + Alias alias = mAliasModel.getAliasAtIndex(mAliasTable.convertRowIndexToModel(row)); + + if(alias != null) + { + selected.add(alias); + } + } + } + + return selected; + } + + /** + * Responds to New, Copy and Delete Channel button invocations + */ + @Override + public void actionPerformed(ActionEvent event) + { + switch(event.getActionCommand()) + { + case NEW_ALIAS: + addAlias(new Alias("New Alias")); + break; + case COPY_ALIAS: + for(Alias alias : getSelectedAliases()) + { + addAlias(AliasFactory.copyOf(alias)); + } + break; + case DELETE_ALIAS: + List toDelete = getSelectedAliases(); + + if(toDelete != null && !toDelete.isEmpty()) + { + String title = toDelete.size() == 1 ? "Delete Alias?" : "Delete Aliases?"; + String prompt = toDelete.size() == 1 ? "Do you want to delete this alias?" : + "Do you want to delete these " + toDelete.size() + " aliases?"; + + int choice = JOptionPane.showConfirmDialog(AliasController.this, + prompt, title, JOptionPane.YES_NO_OPTION, JOptionPane.WARNING_MESSAGE); + + if(choice == JOptionPane.YES_OPTION) + { + for(Alias alias : toDelete) + { + mAliasModel.removeAlias(alias); + } + } + } + break; + default: + break; + } + } + + /** + * Colorizes the cell based on the cell's integer value + */ + public class ColorCellRenderer extends DefaultTableCellRenderer + { + private static final long serialVersionUID = 1L; + + @Override + public Component getTableCellRendererComponent(JTable table, + Object value, boolean isSelected, boolean hasFocus, int row, + int column) + { + JLabel label = (JLabel) super.getTableCellRendererComponent(table, + value, isSelected, hasFocus, row, column); + + label.setBackground(new Color((int) value)); + + label.setText(""); + + return label; + } + } + + /** + * Displays cell's icon and name + */ + public class IconCellRenderer extends DefaultTableCellRenderer + { + private static final long serialVersionUID = 1L; + + private IconManager mIconManager; + + public IconCellRenderer(IconManager iconManager) + { + mIconManager = iconManager; + } + + @Override + public Component getTableCellRendererComponent(JTable table, + Object value, boolean isSelected, boolean hasFocus, int row, + int column) + { + JLabel label = (JLabel) super.getTableCellRendererComponent(table, + value, isSelected, hasFocus, row, column); + + ImageIcon icon = mIconManager.getIcon(label.getText(), 12); + + if(icon != null) + { + label.setIcon(icon); + } + + return label; + } + } } diff --git a/src/alias/AliasEditor.java b/src/alias/AliasEditor.java index 9cdd4632..706ef5e4 100644 --- a/src/alias/AliasEditor.java +++ b/src/alias/AliasEditor.java @@ -28,6 +28,7 @@ import javax.swing.JButton; import javax.swing.JLabel; import javax.swing.JSeparator; +import icon.IconManager; import net.miginfocom.swing.MigLayout; import sample.Listener; import settings.SettingsManager; @@ -50,10 +51,10 @@ public class AliasEditor extends Editor private AliasIdentifierEditor mAliasIdentifierEditor; private AliasActionEditor mAliasActionEditor; - public AliasEditor(AliasModel aliasModel, BroadcastModel broadcastModel, SettingsManager settingsManager ) + public AliasEditor(AliasModel aliasModel, BroadcastModel broadcastModel, IconManager iconManager ) { mAliasModel = aliasModel; - mAliasNameEditor = new AliasNameEditor( mAliasModel, settingsManager ); + mAliasNameEditor = new AliasNameEditor( mAliasModel, iconManager ); mAliasIdentifierEditor = new AliasIdentifierEditor( aliasModel, broadcastModel ); mAliasActionEditor = new AliasActionEditor( aliasModel ); diff --git a/src/alias/AliasNameEditor.java b/src/alias/AliasNameEditor.java index f628cd59..06fb6881 100644 --- a/src/alias/AliasNameEditor.java +++ b/src/alias/AliasNameEditor.java @@ -1,291 +1,278 @@ package alias; +import alias.AliasEvent.Event; import gui.editor.Editor; +import icon.Icon; +import icon.IconCellRenderer; +import icon.IconManager; +import map.MapIcon; +import net.miginfocom.swing.MigLayout; +import settings.SettingsManager; -import java.awt.Color; -import java.awt.Dimension; -import java.awt.EventQueue; +import javax.swing.*; +import javax.swing.event.DocumentEvent; +import javax.swing.event.DocumentListener; +import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.util.List; -import javax.swing.ComboBoxModel; -import javax.swing.DefaultComboBoxModel; -import javax.swing.JButton; -import javax.swing.JColorChooser; -import javax.swing.JComboBox; -import javax.swing.JLabel; -import javax.swing.JTextField; -import javax.swing.event.DocumentEvent; -import javax.swing.event.DocumentListener; - -import map.IconManager; -import map.MapIcon; -import map.MapIconListCellRenderer; -import net.miginfocom.swing.MigLayout; -import settings.SettingsManager; -import alias.AliasEvent.Event; - public class AliasNameEditor extends Editor { - private static final long serialVersionUID = 1L; + private static final long serialVersionUID = 1L; - private static ComboBoxModel EMPTY_MODEL = new DefaultComboBoxModel<>(); + private static ComboBoxModel EMPTY_MODEL = new DefaultComboBoxModel<>(); - private JComboBox mListCombo = new JComboBox<>( EMPTY_MODEL ); - private JComboBox mGroupCombo = new JComboBox<>( EMPTY_MODEL ); - private JComboBox mMapIconCombo; + private JComboBox mListCombo = new JComboBox<>(EMPTY_MODEL); + private JComboBox mGroupCombo = new JComboBox<>(EMPTY_MODEL); + private JComboBox mIconCombo; private JTextField mName; private JButton mButtonColor; private JButton mBtnIconManager; - + private AliasModel mAliasModel; - private SettingsManager mSettingsManager; - - public AliasNameEditor( AliasModel aliasModel, SettingsManager settingsManager ) - { - mAliasModel = aliasModel; - mSettingsManager = settingsManager; - init(); - } - - private void init() - { - setLayout( new MigLayout( "fill,wrap 2", "[right][grow,fill]", - "[][][][][][][grow]" ) ); + private IconManager mIconManager; - add( new JLabel( "Name:" ) ); - mName = new JTextField(); - mName.getDocument().addDocumentListener( new DocumentListener() - { - @Override - public void removeUpdate( DocumentEvent e ) - { - setModified( true ); - } - - @Override - public void insertUpdate( DocumentEvent e ) - { - setModified( true ); - } - - @Override - public void changedUpdate( DocumentEvent e ){} - } ); - add( mName, "wrap" ); + public AliasNameEditor(AliasModel aliasModel, IconManager iconManager) + { + mAliasModel = aliasModel; + mIconManager = iconManager; - add( new JLabel( "List:" ) ); - mListCombo.setEditable( true ); - mListCombo.addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent e ) - { - if( mListCombo.getSelectedItem() != null ) - { - List groups = mAliasModel - .getGroupNames( (String)mListCombo.getSelectedItem() ); - - if( groups.isEmpty() ) - { - mGroupCombo.setModel( EMPTY_MODEL ); - } - else - { - mGroupCombo.setModel( new DefaultComboBoxModel( - groups.toArray( new String[ groups.size() ] ) ) );; - } - } - - setModified( true ); - } - } ); - add( mListCombo, "wrap" ); + init(); + } - add( new JLabel( "Group:" ) ); - mGroupCombo.setEditable( true ); - mGroupCombo.addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent e ) - { - setModified( true ); - } - } ); - add( mGroupCombo, "wrap" ); + private void init() + { + setLayout(new MigLayout("fill,wrap 2", "[right][grow,fill]", + "[][][][][][][grow]")); - add( new JLabel( "Color:" ) ); - - mButtonColor = new JButton( "Select ..." ); - mButtonColor.addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent e ) + add(new JLabel("Name:")); + mName = new JTextField(); + mName.getDocument().addDocumentListener(new DocumentListener() + { + @Override + public void removeUpdate(DocumentEvent e) { - Color newColor = JColorChooser.showDialog( - AliasNameEditor.this, - "Choose color for this alias", - ( hasItem() ? getItem().getMapColor() : null ) ); - - if( newColor != null ) - { - mButtonColor.setForeground( newColor ); - mButtonColor.setBackground( newColor ); - - setModified( true ); - } + setModified(true); } - } ); - add( mButtonColor, "wrap" ); - - add( new JLabel( "Icon:" ) ); - - mMapIconCombo = new JComboBox( mSettingsManager.getMapIcons() ); - MapIconListCellRenderer renderer = new MapIconListCellRenderer(); - renderer.setPreferredSize( new Dimension( 200, 30 ) ); - mMapIconCombo.setRenderer( renderer ); - mMapIconCombo.addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent e ) - { - setModified( true ); - } - } ); - - add( mMapIconCombo, "wrap" ); - - //Dummy place holder - add( new JLabel() ); - - mBtnIconManager = new JButton( "Icon Manager" ); - mBtnIconManager.addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent arg0 ) + @Override + public void insertUpdate(DocumentEvent e) { - final IconManager iconManager = - new IconManager( mSettingsManager, AliasNameEditor.this ); - - EventQueue.invokeLater( new Runnable() - { - @Override - public void run() - { - iconManager.setVisible( true ); - } - } ); + setModified(true); } - } ); - - add( mBtnIconManager, "span 2,wrap" ); - setModified( false ); - } + @Override + public void changedUpdate(DocumentEvent e) + { + } + }); + add(mName, "wrap"); - @Override - public void setItem( Alias alias ) - { - super.setItem( alias ); - - if( hasItem() ) - { - mName.setText( alias.getName() ); - - List listNames = mAliasModel.getListNames(); - - if( listNames.isEmpty() ) - { - mListCombo.setModel( EMPTY_MODEL ); - } - else - { - mListCombo.setModel( new DefaultComboBoxModel( - listNames.toArray( new String[ listNames.size() ] ) ) );; - } - - mListCombo.setSelectedItem( alias.getList() ); - - List groupNames = mAliasModel.getGroupNames( alias.getList() ); - - if( groupNames.isEmpty() ) - { - mGroupCombo.setModel( EMPTY_MODEL ); - } - else - { - mGroupCombo.setModel( new DefaultComboBoxModel( - groupNames.toArray( new String[ groupNames.size() ] ) ) );; - } - - mGroupCombo.setSelectedItem( alias.getGroup() ); - - Color color = alias.getMapColor(); - - mButtonColor.setBackground( color ); - mButtonColor.setForeground( color ); - - String iconName = alias.getIconName(); - - if( iconName == null ) - { - iconName = SettingsManager.DEFAULT_ICON; - } - - MapIcon savedIcon = mSettingsManager.getMapIcon( iconName ); - - if( savedIcon != null ) - { - mMapIconCombo.setSelectedItem( savedIcon ); - } - } - else - { - mListCombo.setModel( EMPTY_MODEL ); - mGroupCombo.setModel( EMPTY_MODEL ); - mName.setText( null ); - - mButtonColor.setBackground( getBackground() ); - mButtonColor.setForeground( getForeground() ); - } - - repaint(); - - setModified( false ); - } + add(new JLabel("List:")); + mListCombo.setEditable(true); + mListCombo.addActionListener(new ActionListener() + { + @Override + public void actionPerformed(ActionEvent e) + { + if(mListCombo.getSelectedItem() != null) + { + List groups = mAliasModel + .getGroupNames((String) mListCombo.getSelectedItem()); - @Override - public void save() - { - if( hasItem() && isModified() ) - { - Alias alias = getItem(); - - if( mListCombo.getSelectedItem() != null ) - { - alias.setList( (String)mListCombo.getSelectedItem() ); - } - - if( mGroupCombo.getSelectedItem() != null ) - { - alias.setGroup( (String)mGroupCombo.getSelectedItem() ); - } + if(groups.isEmpty()) + { + mGroupCombo.setModel(EMPTY_MODEL); + } + else + { + mGroupCombo.setModel(new DefaultComboBoxModel( + groups.toArray(new String[groups.size()]))); + ; + } + } - alias.setName( mName.getText() ); + setModified(true); + } + }); + add(mListCombo, "wrap"); - alias.setColor( mButtonColor.getBackground().getRGB() ); + add(new JLabel("Group:")); + mGroupCombo.setEditable(true); + mGroupCombo.addActionListener(new ActionListener() + { + @Override + public void actionPerformed(ActionEvent e) + { + setModified(true); + } + }); + add(mGroupCombo, "wrap"); - if( mMapIconCombo.getSelectedItem() != null ) - { - alias.setIconName( ((MapIcon)mMapIconCombo.getSelectedItem()).getName() ); - } - - setModified( false ); + add(new JLabel("Color:")); - //Broadcast an alias change event to save the updates - mAliasModel.broadcast( new AliasEvent( getItem(), Event.CHANGE ) ); - } - } + mButtonColor = new JButton("Select ..."); + mButtonColor.addActionListener(new ActionListener() + { + @Override + public void actionPerformed(ActionEvent e) + { + Color newColor = JColorChooser.showDialog( + AliasNameEditor.this, + "Choose color for this alias", + (hasItem() ? getItem().getMapColor() : null)); + + if(newColor != null) + { + mButtonColor.setForeground(newColor); + mButtonColor.setBackground(newColor); + + setModified(true); + } + } + }); + add(mButtonColor, "wrap"); + + add(new JLabel("Icon:")); + + mIconCombo = new JComboBox(mIconManager.getIcons()); + + IconCellRenderer renderer = new IconCellRenderer(mIconManager); + renderer.setPreferredSize(new Dimension(200, 30)); + mIconCombo.setRenderer( renderer ); + mIconCombo.addActionListener(new ActionListener() + { + @Override + public void actionPerformed(ActionEvent e) + { + setModified(true); + } + }); + + add(mIconCombo, "wrap"); + + //Dummy place holder + add(new JLabel()); + + mBtnIconManager = new JButton("Icon Manager"); + mBtnIconManager.addActionListener(new ActionListener() + { + @Override + public void actionPerformed(ActionEvent arg0) + { + mIconManager.showEditor(AliasNameEditor.this); + } + }); + + add(mBtnIconManager, "span 2,wrap"); + + setModified(false); + } + + @Override + public void setItem(Alias alias) + { + super.setItem(alias); + + if(hasItem()) + { + mName.setText(alias.getName()); + + List listNames = mAliasModel.getListNames(); + + if(listNames.isEmpty()) + { + mListCombo.setModel(EMPTY_MODEL); + } + else + { + mListCombo.setModel(new DefaultComboBoxModel( + listNames.toArray(new String[listNames.size()]))); + ; + } + + mListCombo.setSelectedItem(alias.getList()); + + List groupNames = mAliasModel.getGroupNames(alias.getList()); + + if(groupNames.isEmpty()) + { + mGroupCombo.setModel(EMPTY_MODEL); + } + else + { + mGroupCombo.setModel(new DefaultComboBoxModel( + groupNames.toArray(new String[groupNames.size()]))); + ; + } + + mGroupCombo.setSelectedItem(alias.getGroup()); + + Color color = alias.getMapColor(); + + mButtonColor.setBackground(color); + mButtonColor.setForeground(color); + + String iconName = alias.getIconName(); + + if(iconName == null) + { + iconName = SettingsManager.DEFAULT_ICON; + } + + Icon savedIcon = mIconManager.getModel().getIcon(iconName); + + if(savedIcon != null) + { + mIconCombo.setSelectedItem(savedIcon); + } + } + else + { + mListCombo.setModel(EMPTY_MODEL); + mGroupCombo.setModel(EMPTY_MODEL); + mName.setText(null); + + mButtonColor.setBackground(getBackground()); + mButtonColor.setForeground(getForeground()); + } + + repaint(); + + setModified(false); + } + + @Override + public void save() + { + if(hasItem() && isModified()) + { + Alias alias = getItem(); + + if(mListCombo.getSelectedItem() != null) + { + alias.setList((String) mListCombo.getSelectedItem()); + } + + if(mGroupCombo.getSelectedItem() != null) + { + alias.setGroup((String) mGroupCombo.getSelectedItem()); + } + + alias.setName(mName.getText()); + + alias.setColor(mButtonColor.getBackground().getRGB()); + + if(mIconCombo.getSelectedItem() != null) + { + alias.setIconName(((MapIcon) mIconCombo.getSelectedItem()).getName()); + } + + setModified(false); + + //Broadcast an alias change event to save the updates + mAliasModel.broadcast(new AliasEvent(getItem(), Event.CHANGE)); + } + } } diff --git a/src/alias/MultipleAliasEditor.java b/src/alias/MultipleAliasEditor.java index ba6861d1..3bcc34ce 100644 --- a/src/alias/MultipleAliasEditor.java +++ b/src/alias/MultipleAliasEditor.java @@ -3,14 +3,13 @@ package alias; import alias.id.AliasIDType; import alias.id.broadcast.BroadcastChannel; import audio.broadcast.BroadcastModel; +import com.sun.org.apache.xpath.internal.operations.Mult; import gui.editor.Editor; import java.awt.Color; import java.awt.Dimension; -import java.awt.EventQueue; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; -import java.util.Arrays; import java.util.List; import javax.swing.DefaultComboBoxModel; @@ -24,16 +23,16 @@ import javax.swing.JSlider; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; -import map.IconManager; +import icon.Icon; +import icon.IconManager; import map.MapIcon; -import map.MapIconListCellRenderer; +import icon.IconCellRenderer; import net.miginfocom.swing.MigLayout; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import sample.Listener; -import settings.SettingsManager; import alias.AliasEvent.Event; import alias.id.priority.Priority; @@ -54,7 +53,7 @@ public class MultipleAliasEditor extends Editor> private JCheckBox mGroupCheckBox = new JCheckBox("Group"); private JComboBox mGroupCombo = new JComboBox<>(); private JCheckBox mIconCheckBox = new JCheckBox("Icon"); - private JComboBox mMapIconCombo; + private JComboBox mIconCombo; private JCheckBox mColorCheckBox = new JCheckBox("Color"); private JButton mButtonColor; private JButton mBtnIconManager; @@ -68,13 +67,13 @@ public class MultipleAliasEditor extends Editor> private AliasModel mAliasModel; private BroadcastModel mBroadcastModel; - private SettingsManager mSettingsManager; + private IconManager mIconManager; - public MultipleAliasEditor(AliasModel aliasModel, BroadcastModel broadcastModel, SettingsManager settingsManager) + public MultipleAliasEditor(AliasModel aliasModel, BroadcastModel broadcastModel, IconManager iconManager) { mAliasModel = aliasModel; mBroadcastModel = broadcastModel; - mSettingsManager = settingsManager; + mIconManager = iconManager; mAliasModel.addListener(this); @@ -139,12 +138,12 @@ public class MultipleAliasEditor extends Editor> add(mIconCheckBox); - mMapIconCombo = new JComboBox(mSettingsManager.getMapIcons()); + mIconCombo = new JComboBox(mIconManager.getIcons()); - MapIconListCellRenderer renderer = new MapIconListCellRenderer(); + IconCellRenderer renderer = new IconCellRenderer(mIconManager); renderer.setPreferredSize(new Dimension(200, 30)); - mMapIconCombo.setRenderer(renderer); - add(mMapIconCombo, "wrap"); + mIconCombo.setRenderer(renderer); + add(mIconCombo, "wrap"); //Dummy place holder add(new JLabel()); @@ -155,17 +154,7 @@ public class MultipleAliasEditor extends Editor> @Override public void actionPerformed(ActionEvent arg0) { - final IconManager iconManager = - new IconManager(mSettingsManager, MultipleAliasEditor.this); - - EventQueue.invokeLater(new Runnable() - { - @Override - public void run() - { - iconManager.setVisible(true); - } - }); + mIconManager.showEditor(MultipleAliasEditor.this); } }); @@ -363,9 +352,9 @@ public class MultipleAliasEditor extends Editor> if (mIconCheckBox.isSelected()) { - if (mMapIconCombo.getSelectedItem() != null) + if (mIconCombo.getSelectedItem() != null) { - alias.setIconName(((MapIcon) mMapIconCombo.getSelectedItem()).getName()); + alias.setIconName(((MapIcon) mIconCombo.getSelectedItem()).getName()); } else { diff --git a/src/controller/ControllerPanel.java b/src/controller/ControllerPanel.java index a66a994c..dd85b50f 100644 --- a/src/controller/ControllerPanel.java +++ b/src/controller/ControllerPanel.java @@ -29,6 +29,7 @@ import javax.swing.event.ChangeListener; import audio.broadcast.BroadcastModel; import audio.broadcast.BroadcastPanel; +import icon.IconManager; import map.MapPanel; import map.MapService; import module.decode.event.CallEventPanel; @@ -78,6 +79,7 @@ public class ControllerPanel extends JPanel ChannelModel channelModel, ChannelMapModel channelMapModel, ChannelProcessingManager channelProcessingManager, + IconManager iconManager, MapService mapService, SettingsManager settingsManager, SourceManager sourceManager, @@ -104,8 +106,7 @@ public class ControllerPanel extends JPanel mChannelController = new ChannelController(channelModel, channelMapModel, sourceManager, aliasModel); - mAliasController = new AliasController(aliasModel, broadcastModel, settingsManager); - + mAliasController = new AliasController(aliasModel, broadcastModel, iconManager); mTunerManagerPanel = new TunerViewPanel(tunerModel); init(); diff --git a/src/gui/SDRTrunk.java b/src/gui/SDRTrunk.java index db8cd501..6e9c5f2e 100644 --- a/src/gui/SDRTrunk.java +++ b/src/gui/SDRTrunk.java @@ -30,7 +30,7 @@ import controller.channel.ChannelModel; import controller.channel.ChannelProcessingManager; import controller.channel.ChannelSelectionManager; import controller.channel.map.ChannelMapModel; -import map.IconManager; +import icon.IconManager; import map.MapService; import module.log.EventLogManager; import net.miginfocom.swing.MigLayout; @@ -66,6 +66,7 @@ public class SDRTrunk implements Listener { private final static Logger mLog = LoggerFactory.getLogger(SDRTrunk.class); + private IconManager mIconManager; private ControllerPanel mControllerPanel; private SettingsManager mSettingsManager; private SpectralDisplayPanel mSpectralPanel; @@ -105,6 +106,8 @@ public class SDRTrunk implements Listener ThreadPoolManager threadPoolManager = new ThreadPoolManager(); + mIconManager = new IconManager(threadPoolManager); + mSettingsManager = new SettingsManager(threadPoolManager, tunerConfigurationModel); AliasModel aliasModel = new AliasModel(); @@ -144,7 +147,7 @@ public class SDRTrunk implements Listener channelProcessingManager.addMessageListener(mapService); mControllerPanel = new ControllerPanel(audioManager, aliasModel, broadcastModel, - channelModel, channelMapModel, channelProcessingManager, + channelModel, channelMapModel, channelProcessingManager, mIconManager, mapService, mSettingsManager, sourceManager, tunerModel); mSpectralPanel = new SpectralDisplayPanel(channelModel, @@ -268,16 +271,7 @@ public class SDRTrunk implements Listener @Override public void actionPerformed(ActionEvent arg0) { - final IconManager iconManager = new IconManager(mSettingsManager, mMainGui); - - EventQueue.invokeLater(new Runnable() - { - @Override - public void run() - { - iconManager.setVisible(true); - } - }); + mIconManager.showEditor(mMainGui); } }); fileMenu.add(settingsMenu); diff --git a/src/icon/Icon.java b/src/icon/Icon.java new file mode 100644 index 00000000..5d133a25 --- /dev/null +++ b/src/icon/Icon.java @@ -0,0 +1,99 @@ +/******************************************************************************* + * 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. + * + * 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 + * + ******************************************************************************/ +package icon; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.swing.*; +import javax.xml.bind.annotation.XmlAttribute; +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.XmlTransient; + +@XmlRootElement(name = "icon") +public class Icon implements Comparable +{ + private final static Logger mLog = LoggerFactory.getLogger(Icon.class); + + protected String mName; + private String mPath; + private ImageIcon mImageIcon; + + public Icon() + { + //No-arg JAXB constructor + } + + public Icon(String name, String path) + { + mName = name; + mPath = path; + } + + @XmlAttribute(name = "name") + public String getName() + { + return mName; + } + + public void setName(String name) + { + mName = name; + } + + public String toString() + { + return mName; + } + + @XmlAttribute(name = "path") + public String getPath() + { + return mPath; + } + + public void setPath(String path) + { + mPath = path; + } + + @XmlTransient + public ImageIcon getIcon() + { + if(mImageIcon == null && mPath != null) + { + try + { + mImageIcon = new ImageIcon(mPath); + } + catch(Exception e) + { + mLog.error("Error loading Icon [" + mPath + "]", e); + } + } + + return mImageIcon; + } + + @Override + public int compareTo(Icon otherIcon) + { + return getName().compareTo(otherIcon.getName()); + } +} diff --git a/src/icon/IconCellRenderer.java b/src/icon/IconCellRenderer.java new file mode 100644 index 00000000..128cffae --- /dev/null +++ b/src/icon/IconCellRenderer.java @@ -0,0 +1,64 @@ +/******************************************************************************* + * 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. + * + * 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 + * + ******************************************************************************/ +package icon; + +import javax.swing.*; +import java.awt.*; + +public class IconCellRenderer extends JLabel implements ListCellRenderer +{ + private static final long serialVersionUID = 1L; + + private IconManager mIconManager; + + public IconCellRenderer(IconManager iconManager) + { + mIconManager = iconManager; + setOpaque(true); + } + + @Override + public Component getListCellRendererComponent(JList list, Icon icon, int index, + boolean isSelected, boolean cellHasFocus) + { + setIcon(mIconManager.getIcon(icon.getName(), 24)); + + if(icon == mIconManager.getModel().getDefaultIcon()) + { + setText(icon.toString() + " (default)"); + } + else + { + setText(icon.toString()); + } + + if(isSelected) + { + setBackground(list.getSelectionBackground()); + setForeground(list.getSelectionForeground()); + } + else + { + setBackground(list.getBackground()); + setForeground(list.getForeground()); + } + + return this; + } +} diff --git a/src/icon/IconManager.java b/src/icon/IconManager.java new file mode 100644 index 00000000..178b2744 --- /dev/null +++ b/src/icon/IconManager.java @@ -0,0 +1,432 @@ +/******************************************************************************* + * 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. + * + * 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 + * + ******************************************************************************/ +package icon; + +import controller.ThreadPoolManager; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import properties.SystemProperties; + +import javax.swing.*; +import javax.swing.event.ListDataEvent; +import javax.swing.event.ListDataListener; +import javax.xml.bind.JAXBContext; +import javax.xml.bind.JAXBException; +import javax.xml.bind.Marshaller; +import javax.xml.bind.Unmarshaller; +import java.awt.*; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.StandardCopyOption; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; + +public class IconManager +{ + private final static Logger mLog = LoggerFactory.getLogger(IconManager.class); + + private Path mIconFolderPath; + private Path mIconFilePath; + private Path mIconBackupFilePath; + private Path mIconLockFilePath; + + private ThreadPoolManager mThreadPoolManager; + private IconViewer mIconViewer; + private IconModel mIconModel; + private AtomicBoolean mSavingIcons = new AtomicBoolean(); + + private Map mResizedIcons = new HashMap<>(); + + public IconManager(ThreadPoolManager threadPoolManager) + { + mThreadPoolManager = threadPoolManager; + } + + /** + * Display the icon editor centered over the specified component. + * + * @param centerOnComponent to center the editor + */ + public void showEditor(Component centerOnComponent) + { + if(mIconViewer == null) + { + mIconViewer = new IconViewer(this); + mIconViewer.setLocationRelativeTo(centerOnComponent); + + if(mIconViewer.isVisible()) + { + mIconViewer.requestFocus(); + } + else + { + EventQueue.invokeLater(new Runnable() + { + @Override + public void run() + { + mIconViewer.setVisible(true); + } + }); + } + } + } + + public IconModel getModel() + { + if(mIconModel == null) + { + IconSet loadedIcons = load(); + + mIconModel = new IconModel(loadedIcons); + mIconModel.addListDataListener(new ListDataListener() + { + @Override + public void intervalAdded(ListDataEvent e) + { + scheduleSave(); + } + + @Override + public void intervalRemoved(ListDataEvent e) + { + scheduleSave(); + } + + @Override + public void contentsChanged(ListDataEvent e) + { + scheduleSave(); + } + }); + } + + return mIconModel; + } + + /** + * All icons in a sorted array + */ + public Icon[] getIcons() + { + return getModel().getIconsAsArray(); + } + + /** + * Returns named icon scaled to the specified height. Utilizes an internal map to retain scaled icons so that they + * are only scaled/generated once. + * + * @param name - name of icon + * @param height - height of icon in pixels + * @return - scaled named icon (if it exists) or a scaled version of the default icon + */ + public ImageIcon getIcon(String name, int height) + { + if(name == null) + { + name = getModel().getDefaultIcon().getName(); + } + + String scaledIconName = name + height; + + if(mResizedIcons.containsKey(scaledIconName)) + { + return mResizedIcons.get(scaledIconName); + } + + Icon icon = getModel().getIcon(name); + + ImageIcon scaledIcon = getScaledIcon(icon.getIcon(), height); + mResizedIcons.put(scaledIconName, scaledIcon); + + return scaledIcon; + } + + /** + * Scales the icon to the new pixel height value + * + * @param original image icon + * @param height new height to scale the image (width will be scaled accordingly) + * @return + */ + private static ImageIcon getScaledIcon(ImageIcon original, int height) + { + double scale = (double) original.getIconHeight() / (double) height; + + int scaledWidth = (int) ((double) original.getIconWidth() / scale); + + Image scaledImage = original.getImage().getScaledInstance(scaledWidth, + height, java.awt.Image.SCALE_SMOOTH); + + return new ImageIcon(scaledImage); + } + + /** + * Folder where icon, backup and lock files are stored + */ + private Path getIconFolderPath() + { + if(mIconFolderPath == null) + { + SystemProperties props = SystemProperties.getInstance(); + + mIconFolderPath = props.getApplicationFolder("settings"); + } + + return mIconFolderPath; + } + + /** + * Path to current icon file + */ + private Path getIconFilePath() + { + if(mIconFilePath == null) + { + mIconFilePath = getIconFolderPath().resolve("icons.xml"); + } + + return mIconFilePath; + } + + /** + * Path to most recent playlist backup + */ + private Path getIconBackupFilePath() + { + if(mIconBackupFilePath == null) + { + mIconBackupFilePath = getIconFolderPath().resolve("icons.backup"); + } + + return mIconBackupFilePath; + } + + /** + * Path to playlist lock file that is created prior to saving a playlist and removed immediately thereafter. + * Presence of a lock file indicates an incomplete or corrupt playlist file on startup. + */ + private Path getIconLockFilePath() + { + if(mIconLockFilePath == null) + { + mIconLockFilePath = getIconFolderPath().resolve("icons.lock"); + } + + return mIconLockFilePath; + } + + /** + * Saves the current playlist + */ + private void save() + { + IconSet iconSet = getModel().getIconSet(); + + JAXBContext context = null; + + //Create a backup copy of the current playlist + if(Files.exists(getIconFilePath())) + { + try + { + Files.copy(getIconFilePath(), getIconBackupFilePath(), StandardCopyOption.REPLACE_EXISTING); + } + catch(Exception e) + { + mLog.error("Error creating backup copy of current icons prior to saving updates [" + + getIconFilePath().toString() + "]", e); + } + } + + //Create a temporary lock file to signify that we're in the process of updating the playlist + if(!Files.exists(getIconLockFilePath())) + { + try + { + Files.createFile(getIconLockFilePath()); + } + catch(IOException e) + { + mLog.error("Error creating temporary lock file prior to saving icons [" + + getIconLockFilePath().toString() + "]", e); + } + } + + try(OutputStream out = Files.newOutputStream(getIconFilePath())) + { + context = JAXBContext.newInstance(IconSet.class); + + Marshaller m = context.createMarshaller(); + + m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true); + + m.marshal(iconSet, out); + + out.flush(); + + //Remove the playlist lock file to indicate that we successfully saved the file + if(Files.exists(getIconLockFilePath())) + { + Files.delete(getIconLockFilePath()); + } + } + catch(JAXBException je) + { + mLog.error("JAXB exception while serializing icons to a file [" + getIconFilePath().toString() + "]", je); + } + catch(IOException ioe) + { + mLog.error("IO error while writing icons to a file [" + getIconFilePath().toString() + "]", ioe); + } + catch(Exception e) + { + mLog.error("Error while saving icons [" + getIconFilePath().toString() + "]", e); + } + } + + /** + * Loads icons from file or creates a default set of icons + */ + public IconSet load() + { + mLog.info("loading icons file [" + getIconFilePath().toString() + "]"); + + IconSet iconSet = null; + + //Check for a lock file that indicates the previous save attempt was incomplete or had an error + if(Files.exists(getIconLockFilePath())) + { + try + { + //Remove the previous icons file + Files.delete(getIconFilePath()); + + //Copy the backup file to restore the previous icons file + if(Files.exists(getIconBackupFilePath())) + { + Files.copy(getIconBackupFilePath(), getIconFilePath()); + } + + //Remove the lock file + Files.delete(getIconLockFilePath()); + } + catch(IOException ioe) + { + mLog.error("Previous icons save attempt was incomplete and there was an error restoring the " + + "icons backup file", ioe); + } + } + + if(Files.exists(getIconFilePath())) + { + JAXBContext context = null; + + try(InputStream in = Files.newInputStream(getIconFilePath())) + { + context = JAXBContext.newInstance(IconSet.class); + + Unmarshaller m = context.createUnmarshaller(); + + iconSet = (IconSet) m.unmarshal(in); + } + catch(JAXBException je) + { + mLog.error("JAXB exception while loading/unmarshalling icons", je); + } + catch(IOException ioe) + { + mLog.error("IO error while reading icons file", ioe); + } + } + else + { + mLog.info("Icons file not found at [" + getIconFilePath().toString() + "]"); + } + + if(iconSet == null) + { + iconSet = getDefaultIconSet(); + } + + return iconSet; + } + + /** + * Creates a default icon set + */ + private IconSet getDefaultIconSet() + { + IconSet iconSet = new IconSet(); + + Icon defaultIcon = new Icon(IconModel.DEFAULT_ICON, "images/no_icon.png"); + iconSet.add(defaultIcon); + iconSet.setDefaultIcon(defaultIcon.getName()); + + iconSet.add(new Icon("Ambulance", "images/ambulance.png")); + iconSet.add(new Icon("Block Truck", "images/concrete_block_truck.png")); + iconSet.add(new Icon("CWID", "images/cwid.png")); + iconSet.add(new Icon("Dispatcher", "images/dispatcher.png")); + iconSet.add(new Icon("Dump Truck", "images/dump_truck_red.png")); + iconSet.add(new Icon("Fire Truck", "images/fire_truck.png")); + iconSet.add(new Icon("Garbage Truck", "images/garbage_truck.png")); + iconSet.add(new Icon("Loader", "images/loader.png")); + iconSet.add(new Icon("Police", "images/police.png")); + iconSet.add(new Icon("Propane Truck", "images/propane_truck.png")); + iconSet.add(new Icon("Rescue Truck", "images/rescue_truck.png")); + iconSet.add(new Icon("School Bus", "images/school_bus.png")); + iconSet.add(new Icon("Taxi", "images/taxi.png")); + iconSet.add(new Icon("Train", "images/train.png")); + iconSet.add(new Icon("Transport Bus", "images/opt_bus.png")); + iconSet.add(new Icon("Van", "images/van.png")); + + return iconSet; + } + + /** + * Schedules an icon file save task. Subsequent calls to this method will be ignored until the save event occurs, + * thus limiting repetitive saving to a minimum. + */ + private void scheduleSave() + { + if(mSavingIcons.compareAndSet(false, true)) + { + mThreadPoolManager.scheduleOnce(new IconSaveTask(), 2, TimeUnit.SECONDS); + } + } + + /** + * Resets the playlist save pending flag to false and proceeds to save the playlist. + */ + public class IconSaveTask implements Runnable + { + @Override + public void run() + { + save(); + + mSavingIcons.set(false); + } + } +} diff --git a/src/icon/IconModel.java b/src/icon/IconModel.java new file mode 100644 index 00000000..4667dd9c --- /dev/null +++ b/src/icon/IconModel.java @@ -0,0 +1,150 @@ +/******************************************************************************* + * 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. + * + * 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 + * + ******************************************************************************/ +package icon; + +import javax.swing.*; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class IconModel extends DefaultListModel +{ + public static final String DEFAULT_ICON = "No Icon"; + + private Icon mDefaultIcon; + private Map mIconMap = new HashMap(); + + public IconModel(IconSet iconSet) + { + for(Icon icon: iconSet.getIcons()) + { + this.addElement(icon); + } + + String defaultIconName = iconSet.getDefaultIcon(); + + if(defaultIconName == null) + { + defaultIconName = DEFAULT_ICON; + } + + Icon defaultIcon = mIconMap.get(defaultIconName); + + if(defaultIcon != null) + { + mDefaultIcon = defaultIcon; + } + else + { + mDefaultIcon = new Icon(DEFAULT_ICON, "images/no_icon.png"); + } + } + + /** + * Icons in an array - suitable for use in a JComboBox + */ + public Icon[] getIconsAsArray() + { + List icons = new ArrayList<>(mIconMap.values()); + + Collections.sort(icons); + + return icons.toArray(new Icon[icons.size()]); + } + + public Icon getDefaultIcon() + { + return mDefaultIcon; + } + + /** + * Indicates if this model contains an icon with the specified name + * @param name to check + * @return true if name is not null and not empty and the internal map contains an icon matching name + */ + public boolean hasIcon(String name) + { + return name != null && !name.isEmpty() && mIconMap.containsKey(name); + } + + public void setDefaultIcon(Icon icon) + { + if(icon != null) + { + mDefaultIcon = icon; + + int index = indexOf(icon); + + fireContentsChanged(icon, index, index); + } + } + + /** + * Returns the named icon or the default icon if the named icon does not exist + */ + public Icon getIcon(String name) + { + if(name == null) + { + return mDefaultIcon; + } + + Icon icon = mIconMap.get(name); + + if(icon == null) + { + icon = mDefaultIcon; + } + + return icon; + } + + @Override + public void addElement(Icon icon) + { + super.addElement(icon); + + mIconMap.put(icon.getName(), icon); + } + + @Override + public boolean removeElement(Object object) + { + if(object instanceof Icon) + { + mIconMap.remove(((Icon)object).getName()); + } + + return super.removeElement(object); + } + + /** + * Creates a new icon set from a snapshot of the icons contained in this model + */ + public IconSet getIconSet() + { + IconSet iconSet = new IconSet(); + iconSet.setIcons(new ArrayList(mIconMap.values())); + iconSet.setDefaultIcon(getDefaultIcon().getName()); + return iconSet; + } +} diff --git a/src/icon/IconSelector.java b/src/icon/IconSelector.java new file mode 100644 index 00000000..6118a0e1 --- /dev/null +++ b/src/icon/IconSelector.java @@ -0,0 +1,175 @@ +/******************************************************************************* + * 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. + * + * 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 + * + ******************************************************************************/ +package icon; + +import net.miginfocom.swing.MigLayout; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.swing.*; +import javax.swing.event.DocumentEvent; +import javax.swing.event.DocumentListener; +import java.awt.*; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.io.File; + +public class IconSelector extends JDialog implements DocumentListener +{ + private IconModel mIconModel; + private JTextField mNameText; + private JTextField mFileLabel; + private JButton mFileChooserButton; + private JButton mOKButton; + private JButton mCancelButton; + + /** + * Window for selecting an Icon to add to the Icon Model + * + * @param owner for this modal dialog + * @param iconModel to receive the added/selected icon + */ + public IconSelector(Frame owner, IconModel iconModel) + { + super(owner, "Add Icon", true); + + mIconModel = iconModel; + + init(); + + setLocationRelativeTo(owner); + } + + private void init() + { + setSize(250, 170); + + JPanel contentPanel = new JPanel(); + contentPanel.setPreferredSize(new Dimension(250, 170)); + contentPanel.setLayout(new MigLayout("insets 1 1 1 1", "[align right][align left,grow,fill]", "[][][][grow,fill][]")); + + contentPanel.add(new JLabel("Name")); + + mNameText = new JTextField(); + mNameText.getDocument().addDocumentListener(this); + contentPanel.add(mNameText, "wrap"); + + contentPanel.add(new JLabel("File")); + + mFileLabel = new JTextField(); + mFileLabel.setEnabled(false); + contentPanel.add(mFileLabel, "wrap"); + + mFileChooserButton = new JButton("Select File ..."); + mFileChooserButton.addActionListener(new ActionListener() + { + @Override + public void actionPerformed(ActionEvent e) + { + JFileChooser fileChooser = new JFileChooser(); + +// fileChooser.setSelectedFile(null); + + //Add a custom file filter and disable the default + fileChooser.addChoosableFileFilter(new ImageFilter()); + fileChooser.setAcceptAllFileFilterUsed(false); + + int returnVal = fileChooser.showDialog(IconSelector.this, "Select"); + + if(returnVal == JFileChooser.APPROVE_OPTION) + { + File file = fileChooser.getSelectedFile(); + + if(file != null) + { + mFileLabel.setText(file.getAbsolutePath()); + checkButtons(); + } + } + } + }); + contentPanel.add(mFileChooserButton, "span,align center"); + + contentPanel.add(new JLabel(), "wrap"); + + JPanel buttonPanel = new JPanel(); + buttonPanel.setLayout(new MigLayout("", "[grow,fill][grow,fill]", "[]")); + + mOKButton = new JButton("OK"); + mOKButton.setEnabled(false); + mOKButton.addActionListener(new ActionListener() + { + @Override + public void actionPerformed(ActionEvent e) + { + String name = mNameText.getText(); + String path = mFileLabel.getText(); + + Icon icon = new Icon(name, path); + mIconModel.addElement(icon); + + IconSelector.this.dispose(); + } + }); + + mCancelButton = new JButton("Cancel"); + mCancelButton.addActionListener(new ActionListener() + { + @Override + public void actionPerformed(ActionEvent e) + { + IconSelector.this.dispose(); + } + }); + + buttonPanel.add(mOKButton); + buttonPanel.add(mCancelButton); + + contentPanel.add(buttonPanel, "span,grow"); + + add(contentPanel); + } + + private void checkButtons() + { + String name = mNameText.getText(); + String path = mFileLabel.getText(); + + mOKButton.setEnabled(name != null && path != null && !path.isEmpty() && !mIconModel.hasIcon(name)); + } + + @Override + public void insertUpdate(DocumentEvent e) + { + checkButtons(); + } + + @Override + public void removeUpdate(DocumentEvent e) + { + checkButtons(); + } + + @Override + public void changedUpdate(DocumentEvent e) + { + checkButtons(); + } +} + diff --git a/src/icon/IconSet.java b/src/icon/IconSet.java new file mode 100644 index 00000000..62ba6c0f --- /dev/null +++ b/src/icon/IconSet.java @@ -0,0 +1,76 @@ +/******************************************************************************* + * 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. + * + * 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 + * + ******************************************************************************/ +package icon; + +import javax.xml.bind.annotation.XmlAttribute; +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlElementWrapper; +import javax.xml.bind.annotation.XmlRootElement; +import java.util.ArrayList; +import java.util.List; + +@XmlRootElement(name="iconset") +public class IconSet +{ + private String mDefaultIcon; + private List mIcons = new ArrayList<>(); + + public IconSet() + { + //No-arg JAXB constructor + } + + public void add(Icon icon) + { + mIcons.add(icon); + } + + public void remove(Icon icon) + { + mIcons.remove(icon); + } + + @XmlElementWrapper(name="icons") + @XmlElement(name = "icon") + public List getIcons() + { + if(mIcons == null) + { + mIcons = new ArrayList<>(); + } + + return mIcons; + } + + public void setIcons(List icons) + { + mIcons = icons; + } + + @XmlAttribute(name = "default") + public String getDefaultIcon() + { + return mDefaultIcon; + } + + public void setDefaultIcon(String name) + { + mDefaultIcon = name; + } +} diff --git a/src/icon/IconViewer.java b/src/icon/IconViewer.java new file mode 100644 index 00000000..d581584d --- /dev/null +++ b/src/icon/IconViewer.java @@ -0,0 +1,137 @@ +/******************************************************************************* + * 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. + * + * 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 + * + ******************************************************************************/ +package icon; + +import net.miginfocom.swing.MigLayout; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.swing.*; +import javax.swing.event.ListSelectionEvent; +import javax.swing.event.ListSelectionListener; +import java.awt.*; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +public class IconViewer extends JFrame implements ActionListener +{ + private static final long serialVersionUID = 1L; + private static final Logger mLog = LoggerFactory.getLogger(IconViewer.class); + private static final String ADD = "Add"; + private static final String DEFAULT = "Set Default"; + private static final String DELETE = "Delete"; + + private JButton mAddButton; + private JButton mDeleteButton; + private JButton mDefaultButton; + private JList mIconList; + private IconManager mIconManager; + + public IconViewer(IconManager model) + { + mIconManager = model; + + init(); + } + + private void init() + { + setTitle("Icon Viewer"); + setSize(400, 400); + setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); + setLayout(new MigLayout("", "[grow,fill]", "[grow,fill]")); + + /** + * Editor Panel + */ + JPanel editorPanel = new JPanel(); + editorPanel.setLayout(new MigLayout("insets 1 1 1 1", "[grow,fill][grow,fill][grow,fill]", "[grow,fill][]")); + + mIconList = new JList<>(mIconManager.getModel()); + IconCellRenderer renderer = new IconCellRenderer(mIconManager); + renderer.setPreferredSize(new Dimension(200, 30)); + mIconList.setCellRenderer(renderer); + mIconList.addListSelectionListener(new ListSelectionListener() + { + @Override + public void valueChanged(ListSelectionEvent e) + { + checkButtons(); + } + }); + editorPanel.add(new JScrollPane(mIconList), "span"); + + mAddButton = new JButton(ADD); + mAddButton.addActionListener(this); + editorPanel.add(mAddButton); + + mDeleteButton = new JButton(DELETE); + mDeleteButton.addActionListener(this); + mDeleteButton.setEnabled(false); + editorPanel.add(mDeleteButton); + + mDefaultButton = new JButton(DEFAULT); + mDefaultButton.addActionListener(this); + mDefaultButton.setEnabled(false); + editorPanel.add(mDefaultButton, "wrap"); + + add(editorPanel); + } + + private void checkButtons() + { + Icon selected = mIconList.getSelectedValue(); + + mDefaultButton.setEnabled(selected != null && selected != mIconManager.getModel().getDefaultIcon()); + mDeleteButton.setEnabled(selected != null && selected != mIconManager.getModel().getDefaultIcon()); + } + + public void actionPerformed(ActionEvent e) + { + Icon selected = mIconList.getSelectedValue(); + + switch(e.getActionCommand()) + { + case ADD: + IconSelector iconSelector = new IconSelector(this, mIconManager.getModel()); + iconSelector.setVisible(true); + break; + case DELETE: + if(selected != null) + { + int choice = JOptionPane.showConfirmDialog(IconViewer.this, + "Are you sure you want to delete this icon?", "Delete Icon?", JOptionPane.YES_NO_OPTION); + + if(choice == JOptionPane.YES_OPTION) + { + mIconManager.getModel().removeElement(selected); + } + } + break; + case DEFAULT: + if(selected != null) + { + mIconManager.getModel().setDefaultIcon(selected); + } + break; + } + + checkButtons(); + } +} diff --git a/src/icon/ImageFilter.java b/src/icon/ImageFilter.java new file mode 100644 index 00000000..2fea9ab8 --- /dev/null +++ b/src/icon/ImageFilter.java @@ -0,0 +1,74 @@ +/******************************************************************************* + * 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. + * + * 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 + * + ******************************************************************************/ +package icon; + +import javax.swing.filechooser.FileFilter; +import java.io.File; + +public class ImageFilter extends FileFilter +{ + //Accept all directories and all gif, jpg, tiff, or png files. + public boolean accept(File f) + { + if(f.isDirectory()) + { + return true; + } + + String extension = getExtension(f); + + if(extension != null) + { + if(extension.equals("tiff") || + extension.equals("tif") || + extension.equals("gif") || + extension.equals("jpeg") || + extension.equals("jpg") || + extension.equals("png")) + { + return true; + } + else + { + return false; + } + } + + return false; + } + + //The description of this filter + public String getDescription() + { + return "Images (*.gif,*.jpg,*.png."; + } + + public String getExtension(File f) + { + String ext = null; + String s = f.getName(); + int i = s.lastIndexOf('.'); + + if(i > 0 && i < s.length() - 1) + { + ext = s.substring(i + 1).toLowerCase(); + } + return ext; + } +} diff --git a/src/map/IconManager.java b/src/map/IconManager.java deleted file mode 100644 index 9cc07f3b..00000000 --- a/src/map/IconManager.java +++ /dev/null @@ -1,495 +0,0 @@ -/******************************************************************************* - * 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 - ******************************************************************************/ -package map; - -import java.awt.Component; -import java.awt.Dimension; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import java.awt.event.MouseEvent; -import java.awt.event.MouseListener; -import java.io.File; -import java.util.Arrays; - -import javax.swing.JButton; -import javax.swing.JFileChooser; -import javax.swing.JFrame; -import javax.swing.JLabel; -import javax.swing.JList; -import javax.swing.JMenuItem; -import javax.swing.JOptionPane; -import javax.swing.JPanel; -import javax.swing.JPopupMenu; -import javax.swing.JScrollPane; -import javax.swing.JSplitPane; -import javax.swing.JTextArea; -import javax.swing.JTextField; -import javax.swing.event.ListSelectionEvent; -import javax.swing.event.ListSelectionListener; -import javax.swing.filechooser.FileFilter; - -import net.miginfocom.swing.MigLayout; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import settings.Setting; -import settings.SettingChangeListener; -import settings.SettingsManager; - -public class IconManager extends JFrame - implements ActionListener, SettingChangeListener - -{ - public enum Mode { NORMAL, NEW, EDIT } - - private static final long serialVersionUID = 1L; - private final static Logger mLog = - LoggerFactory.getLogger( IconManager.class ); - - private static final String sMSG_NEW = "Type a name for the new icon, " - + "select the file, and click Save,\nor Cancel to reset"; - private static final String sMSG_EDIT = "Change the name for the selected \n" - + "icon and/or the file, and click Save\nto update, or Cancel to reset"; - private static final String sMSG_NORMAL = "Select an icon to edit, or \n" - + "click New to add an icon"; - - private JTextField mIconName; - private JTextField mIconPath; - private JButton mButtonLeft = new JButton( "New" ); - private JButton mButtonRight = new JButton( "Edit" ); - private JButton mButtonFile = new JButton( "File" ); - private JTextArea mInstructionLabel = new JTextArea(); - private JList mIconList; - private JFileChooser mFileChooser; - private Mode mMode = Mode.NORMAL; - - private SettingsManager mSettingsManager; - - public IconManager( SettingsManager settingsManager, Component displayOver ) - { - mSettingsManager = settingsManager; - - init( displayOver ); - } - - private void init( Component displayOver ) - { - setTitle( "Icon Manager" ); - setLocationRelativeTo( displayOver ); - setSize( 400, 400 ); - setDefaultCloseOperation( JFrame.DISPOSE_ON_CLOSE ); - setLayout( new MigLayout( "", "[grow,fill]", "[grow,fill]" ) ); - - /** - * Editor Panel - */ - JPanel editorPanel = new JPanel(); - editorPanel.setLayout( new MigLayout( "", "", "[][][][grow,fill]" ) ); - - mButtonFile.addActionListener( this ); - editorPanel.add( mButtonFile, "align right" ); - - mIconPath = new JTextField(); - editorPanel.add( mIconPath, "grow,wrap" ); - - editorPanel.add( new JLabel( "Name:" ), "align right" ); - - mIconName = new JTextField(); - editorPanel.add( mIconName, "grow,wrap" ); - - mButtonLeft.addActionListener( this ); - editorPanel.add( mButtonLeft ); - - mButtonRight.addActionListener( this ); - mButtonRight.setEnabled( false ); - editorPanel.add( mButtonRight, "wrap" ); - - mInstructionLabel.setText( sMSG_NORMAL ); - mInstructionLabel.setRows( 3 ); - mInstructionLabel.setBackground( getBackground() ); - editorPanel.add( mInstructionLabel, "span,grow,wrap" ); - - /** - * MapIcon list - */ - MapIcon[] icons = mSettingsManager.getMapIcons(); - Arrays.sort( icons ); - - mIconList = new JList( icons ); - - MapIconListCellRenderer renderer = new MapIconListCellRenderer(); - renderer.setPreferredSize( new Dimension( 200, 30 ) ); - mIconList.setCellRenderer( renderer ); - - mIconList.addListSelectionListener( new ListSelectionListener() - { - @Override - public void valueChanged( ListSelectionEvent event ) - { - /** - * Refresh the display by setting the mode to normal. If we - * were in editing or new mode, and the user clicks in the list - * throw away the changes and set the mode to normal - */ - setMode( Mode.NORMAL ); - } - } ); - - mIconList.addMouseListener( new MouseListener() - { - @Override - public void mouseClicked( MouseEvent event ) - { - if( event.getButton() == MouseEvent.BUTTON3 ) - { - //Select the item - mIconList.setSelectedIndex( - mIconList.locationToIndex( event.getPoint() ) ); - - //Determine if it can be deleted - final MapIcon icon = mIconList.getSelectedValue(); - - if( icon != null ) - { - JPopupMenu context = new JPopupMenu(); - - JMenuItem setDefault = new JMenuItem( "Set As Default" ); - - setDefault.addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent e ) - { - mSettingsManager.setDefaultIcon( icon ); - - refreshList(); - } - } ); - - context.add( setDefault ); - - if( icon.isEditable() ) - { - JMenuItem deleteItem = new JMenuItem( "Delete Icon" ); - deleteItem.addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent arg0 ) - { - mSettingsManager.deleteMapIcon( icon ); - } - } ); - - context.add( deleteItem ); - } - - context.show( mIconList, event.getX(), event.getY() ); - } - } - } - - //Unused - public void mouseEntered( MouseEvent arg0 ) {} - public void mouseExited( MouseEvent arg0 ) {} - public void mousePressed( MouseEvent arg0 ) {} - public void mouseReleased( MouseEvent arg0 ) {} - } ); - - JScrollPane scroller = new JScrollPane( mIconList ); - - /** - * Splitpane to hold editor and icon list - */ - JSplitPane splitPane = new JSplitPane( JSplitPane.VERTICAL_SPLIT, - scroller, - editorPanel ); - splitPane.setResizeWeight( 0.7D ); - - add( splitPane ); - } - - private void setMode( Mode mode ) - { - MapIcon selected = mIconList.getSelectedValue(); - - switch( mode ) - { - case NORMAL: - setTitle( "Icon Manager" ); - mButtonLeft.setText( "New" ); - mButtonRight.setText( "Edit" ); - - if( selected != null ) - { - if( selected.isEditable() ) - { - mButtonRight.setEnabled( true ); - mIconName.setText( selected.getName() ); - mIconPath.setText( selected.getPath() ); - } - else - { - mButtonRight.setEnabled( false ); - mIconName.setText( selected.getName() + " [NO EDIT]" ); - mIconPath.setText( null ); - } - } - else - { - mButtonRight.setEnabled( false ); - mIconName.setText( null ); - mIconPath.setText( null ); - } - mInstructionLabel.setText( sMSG_NORMAL ); - break; - case EDIT: - if( selected != null ) - { - setTitle( "Icon Manager - editing [" + selected.getName() + "]" ); - } - else - { - setTitle( "Icon Manager - edit [*]" ); - } - mButtonLeft.setText( "Save" ); - mButtonRight.setText( "Cancel" ); - mButtonRight.setEnabled( true ); - mInstructionLabel.setText( sMSG_EDIT ); - break; - case NEW: - setTitle( "Icon Manager - add new icon" ); - mButtonLeft.setText( "Save" ); - mButtonRight.setText( "Cancel" ); - mButtonRight.setEnabled( true ); - mIconName.setText( null ); - mIconPath.setText( null ); - mInstructionLabel.setText( sMSG_NEW ); - break; - } - - mMode = mode; - - repaint(); - } - - public void actionPerformed(ActionEvent e) - { - if( e.getSource() == mButtonFile ) - { - if ( mFileChooser == null ) - { - mFileChooser = new JFileChooser(); - - //Add a custom file filter and disable the default - mFileChooser.addChoosableFileFilter( new ImageFilter() ); - mFileChooser.setAcceptAllFileFilterUsed( false ); - } - - int returnVal = mFileChooser.showDialog( this, "Select" ); - - if ( returnVal == JFileChooser.APPROVE_OPTION ) - { - File file = mFileChooser.getSelectedFile(); - - if( file != null ) - { - mIconPath.setText( file.getAbsolutePath() ); - } - } - - //Reset the file chooser for the next time it's shown. - mFileChooser.setSelectedFile( null ); - } - else if( e.getSource() == mButtonLeft ) - { - switch( mMode ) - { - case EDIT: //Save - save(); - break; - case NEW: //Save - save(); - break; - case NORMAL: - setMode( Mode.NEW ); - break; - } - } - else if( e.getSource() == mButtonRight ) - { - switch( mMode ) - { - case EDIT: //Cancel - case NEW: //Cancel - setMode( Mode.NORMAL ); - break; - case NORMAL: //Edit - setMode( Mode.EDIT ); - break; - } - } - } - - private void refreshList() - { - MapIcon[] icons = mSettingsManager.getMapIcons(); - - Arrays.sort( icons ); - - mIconList.setListData( icons ); - - repaint(); - } - - private void save() - { - String name = mIconName.getText(); - String path = mIconPath.getText(); - - switch( mMode ) - { - case NEW: - if( name != null && !name.isEmpty() ) - { - if( path != null && !path.isEmpty() ) - { - mSettingsManager.addMapIcon( new MapIcon( name, path, true ) ); - } - else - { - JOptionPane.showMessageDialog( this, - "Please select an image file", - "Filename and path missing", - JOptionPane.ERROR_MESSAGE ); - } - } - else - { - JOptionPane.showMessageDialog( this, - "Please type a name for the new icon", - "Name missing", - JOptionPane.ERROR_MESSAGE ); - } - break; - - case EDIT: - MapIcon selected = mIconList.getSelectedValue(); - - if( name != null && !name.isEmpty() ) - { - if( path != null && !path.isEmpty() ) - { - selected.setName( name ); - selected.setPath( path ); - mSettingsManager.updatMapIcon( selected, name, path ); - } - else - { - JOptionPane.showMessageDialog( this, - "Please select an image file", - "Filename and path missing", - JOptionPane.ERROR_MESSAGE ); - } - } - else - { - JOptionPane.showMessageDialog( this, - "Please type a name for the new icon", - "Name missing", - JOptionPane.ERROR_MESSAGE ); - } - - break; - default: - break; - } - - } - - public class ImageFilter extends FileFilter - { - //Accept all directories and all gif, jpg, tiff, or png files. - public boolean accept(File f) - { - if ( f.isDirectory() ) - { - return true; - } - - String extension = getExtension( f ); - - if (extension != null) - { - if (extension.equals( "tiff" ) || - extension.equals( "tif" ) || - extension.equals( "gif" ) || - extension.equals( "jpeg" ) || - extension.equals( "jpg" ) || - extension.equals( "png" ) ) - { - return true; - } - else - { - return false; - } - } - - return false; - } - - //The description of this filter - public String getDescription() - { - return "Images (*.gif,*.jpg,*.png."; - } - - public String getExtension( File f ) - { - String ext = null; - String s = f.getName(); - int i = s.lastIndexOf('.'); - - if (i > 0 && i < s.length() - 1) { - ext = s.substring(i+1).toLowerCase(); - } - return ext; - } - } - - @Override - public void settingChanged( Setting setting ) - { - if( setting instanceof MapIcon ) - { - refreshList(); - setMode( Mode.NORMAL ); - } - } - - @Override - public void settingDeleted( Setting setting ) - { - if( setting instanceof MapIcon ) - { - refreshList(); - setMode( Mode.NORMAL ); - } - } -} diff --git a/src/map/MapIconListCellRenderer.java b/src/map/MapIconListCellRenderer.java deleted file mode 100644 index 2e0508fe..00000000 --- a/src/map/MapIconListCellRenderer.java +++ /dev/null @@ -1,57 +0,0 @@ -/******************************************************************************* - * 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 - ******************************************************************************/ -package map; - -import java.awt.Component; - -import javax.swing.JLabel; -import javax.swing.JList; -import javax.swing.ListCellRenderer; - -public class MapIconListCellRenderer extends JLabel - implements ListCellRenderer -{ - private static final long serialVersionUID = 1L; - - public MapIconListCellRenderer() - { - setOpaque( true ); - } - - @Override - public Component getListCellRendererComponent( - JList list, MapIcon value, int index, - boolean isSelected, boolean cellHasFocus ) - { - setIcon( value.getImageIcon() ); - setText( value.toString() ); - - if ( isSelected ) - { - setBackground( list.getSelectionBackground() ); - setForeground( list.getSelectionForeground() ); - } - else - { - setBackground( list.getBackground() ); - setForeground( list.getForeground() ); - } - - return this; - } -} diff --git a/src/playlist/PlaylistManager.java b/src/playlist/PlaylistManager.java index 8eb5bb49..9163c5ef 100644 --- a/src/playlist/PlaylistManager.java +++ b/src/playlist/PlaylistManager.java @@ -432,9 +432,9 @@ public class PlaylistManager implements ChannelEventListener @Override public void run() { - mPlaylistSavePending.set(false); - save(); + + mPlaylistSavePending.set(false); } } }