package org.esa.snap.ui.product.spectrum; import com.bc.ceres.swing.TableLayout; import com.jidesoft.swing.TristateCheckBox; import org.esa.snap.core.datamodel.Band; import org.esa.snap.core.util.ArrayUtils; import org.esa.snap.ui.DecimalTableCellRenderer; import org.esa.snap.ui.ModalDialog; import org.esa.snap.ui.UIUtils; import org.esa.snap.ui.product.LoadSaveRasterDataNodesConfigurationsComponent; import org.esa.snap.ui.product.LoadSaveRasterDataNodesConfigurationsProvider; import org.esa.snap.ui.tool.ToolButtonFactory; import javax.swing.AbstractButton; import javax.swing.BorderFactory; import javax.swing.DefaultCellEditor; import javax.swing.ImageIcon; import javax.swing.JCheckBox; import javax.swing.JComboBox; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JTable; import javax.swing.table.DefaultTableModel; import javax.swing.table.TableCellRenderer; import javax.swing.table.TableColumn; import javax.swing.table.TableModel; import javax.swing.table.TableRowSorter; import java.awt.BorderLayout; import java.awt.Color; import java.awt.Component; import java.awt.Dimension; import java.awt.Font; import java.awt.Window; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.text.DecimalFormat; import java.util.ArrayList; import java.util.Arrays; import java.util.List; public class SpectrumChooser extends ModalDialog implements LoadSaveRasterDataNodesConfigurationsComponent { private static final int band_selected_index = 0; private static final int band_name_index = 1; private static final int band_description_index = 2; private static final int band_wavelength_index = 3; private static final int band_bandwidth_ndex = 4; private static final int band_unit_index = 5; private final String[] band_columns = new String[]{"", "Band name", "Band description", "Spectral wavelength (nm)", "Spectral bandwidth (nm)", "Unit"}; private final static ImageIcon[] icons = new ImageIcon[]{ UIUtils.loadImageIcon("icons/PanelDown12.png"), UIUtils.loadImageIcon("icons/PanelUp12.png"), }; private final static ImageIcon[] roll_over_icons = new ImageIcon[]{ ToolButtonFactory.createRolloverIcon(icons[0]), ToolButtonFactory.createRolloverIcon(icons[1]), }; private final DisplayableSpectrum[] originalSpectra; private DisplayableSpectrum[] spectra; private static SpectrumSelectionAdmin selectionAdmin; private static boolean selectionChangeLock; private JPanel spectraPanel; private final JPanel[] bandTablePanels; private final JTable[] bandTables; private final TristateCheckBox[] tristateCheckBoxes; private boolean[] collapsed; private TableLayout spectraPanelLayout; public SpectrumChooser(Window parent, DisplayableSpectrum[] originalSpectra) { super(parent, "Available Spectra", ModalDialog.ID_OK_CANCEL_HELP, "spectrumChooser"); if (originalSpectra != null) { this.originalSpectra = originalSpectra; List<DisplayableSpectrum> spectraWithBands = new ArrayList<>(); for (DisplayableSpectrum spectrum : originalSpectra) { if (spectrum.hasBands()) { spectraWithBands.add(spectrum); } } this.spectra = spectraWithBands.toArray(new DisplayableSpectrum[spectraWithBands.size()]); } else { this.originalSpectra = new DisplayableSpectrum[0]; this.spectra = new DisplayableSpectrum[0]; } selectionAdmin = new SpectrumSelectionAdmin(); selectionChangeLock = false; bandTables = new JTable[spectra.length]; collapsed = new boolean[spectra.length]; Arrays.fill(collapsed, true); bandTablePanels = new JPanel[spectra.length]; tristateCheckBoxes = new TristateCheckBox[spectra.length]; initUI(); } private void initUI() { final JPanel content = new JPanel(new BorderLayout()); initSpectraPanel(); final JScrollPane spectraScrollPane = new JScrollPane(spectraPanel); spectraScrollPane.getHorizontalScrollBar().setUnitIncrement(20); spectraScrollPane.getVerticalScrollBar().setUnitIncrement(20); spectraScrollPane.setPreferredSize(new Dimension(spectraScrollPane.getPreferredSize().width, 180)); content.add(spectraScrollPane, BorderLayout.CENTER); LoadSaveRasterDataNodesConfigurationsProvider provider = new LoadSaveRasterDataNodesConfigurationsProvider(this); AbstractButton loadButton = provider.getLoadButton(); AbstractButton saveButton = provider.getSaveButton(); TableLayout layout = new TableLayout(1); layout.setTablePadding(4, 4); JPanel buttonPanel = new JPanel(layout); buttonPanel.add(loadButton); buttonPanel.add(saveButton); buttonPanel.add(layout.createVerticalSpacer()); content.add(buttonPanel, BorderLayout.EAST); setContent(content); } private void initSpectraPanel() { spectraPanelLayout = new TableLayout(7); spectraPanelLayout.setTableFill(TableLayout.Fill.HORIZONTAL); spectraPanelLayout.setTableWeightY(0.0); spectraPanelLayout.setTableWeightX(1.0); spectraPanelLayout.setColumnWeightX(0, 0.0); spectraPanelLayout.setColumnWeightX(1, 0.0); spectraPanelLayout.setTablePadding(3, 3); spectraPanel = new JPanel(spectraPanelLayout); spectraPanel.add(new JLabel("")); spectraPanel.add(new JLabel("")); spectraPanel.add(new JLabel("Spectrum Name")); spectraPanel.add(new JLabel("Unit")); spectraPanel.add(new JLabel("Line Style")); spectraPanel.add(new JLabel("Symbol")); spectraPanel.add(new JLabel("Symbol Size")); for (int i = 0; i < spectra.length; i++) { selectionAdmin.evaluateSpectrumSelections(spectra[i]); addSpectrumComponentsToSpectraPanel(i); spectraPanelLayout.setCellColspan((i * 2) + 2, 1, 6); spectraPanel.add(new JLabel()); bandTablePanels[i] = new JPanel(new BorderLayout()); bandTablePanels[i].setBorder(BorderFactory.createLineBorder(Color.BLACK, 1)); spectraPanel.add(bandTablePanels[i]); bandTables[i] = createBandsTable(i); } spectraPanel.add(spectraPanelLayout.createVerticalSpacer()); spectraPanel.updateUI(); } private void addSpectrumComponentsToSpectraPanel(int index) { DisplayableSpectrum spectrum = spectra[index]; ImageIcon strokeIcon; if (spectrum.isDefaultOrRemainingBandsSpectrum()) { strokeIcon = new ImageIcon(); } else { strokeIcon = SpectrumStrokeProvider.getStrokeIcon(spectrum.getLineStyle()); } AbstractButton collapseButton = ToolButtonFactory.createButton(icons[0], false); collapseButton.addActionListener(new CollapseListener(index)); final ImageIcon shapeIcon = SpectrumShapeProvider.getShapeIcon(spectrum.getSymbolIndex()); spectraPanel.add(collapseButton); final TristateCheckBox tristateCheckBox = new TristateCheckBox(); tristateCheckBox.setState(selectionAdmin.getState(index)); tristateCheckBox.addActionListener(new TristateCheckboxListener(index)); tristateCheckBoxes[index] = tristateCheckBox; spectraPanel.add(tristateCheckBox); final JLabel spectrumNameLabel = new JLabel(spectrum.getName()); Font font = spectrumNameLabel.getFont(); font = new Font(font.getName(), Font.BOLD, font.getSize()); spectrumNameLabel.setFont(font); spectraPanel.add(spectrumNameLabel); spectraPanel.add(new JLabel(spectrum.getUnit())); JComboBox<ImageIcon> strokeComboBox; if (spectrum.isDefaultOrRemainingBandsSpectrum()) { strokeComboBox = new JComboBox<>(new ImageIcon[]{strokeIcon}); strokeComboBox.setEnabled(false); } else { strokeComboBox = new JComboBox<>(SpectrumStrokeProvider.getStrokeIcons()); strokeComboBox.addActionListener( e -> spectrum.setLineStyle( SpectrumStrokeProvider.getStroke((ImageIcon) strokeComboBox.getSelectedItem()))); } strokeComboBox.setPreferredSize(new Dimension(100, 20)); strokeComboBox.setSelectedItem(strokeIcon); spectraPanel.add(strokeComboBox); JComboBox<ImageIcon> shapeComboBox = new JComboBox<>(SpectrumShapeProvider.getShapeIcons()); JComboBox<Integer> shapeSizeComboBox = new JComboBox<>(SpectrumShapeProvider.getScaleGrades()); shapeComboBox.setPreferredSize(new Dimension(100, 20)); shapeSizeComboBox.setPreferredSize(new Dimension(100, 20)); shapeComboBox.setSelectedItem(shapeIcon); shapeComboBox.addActionListener(e -> { final int shapeIndex = SpectrumShapeProvider.getShape((ImageIcon) shapeComboBox.getSelectedItem()); spectrum.setSymbolIndex(shapeIndex); if (shapeIndex == SpectrumShapeProvider.EMPTY_SHAPE_INDEX) { shapeSizeComboBox.setSelectedItem(""); } else { shapeSizeComboBox.setSelectedItem(spectrum.getSymbolSize()); } }); spectraPanel.add(shapeComboBox); shapeSizeComboBox.setSelectedItem(spectrum.getSymbolSize()); shapeSizeComboBox.addActionListener(e -> { final String selectedItem = shapeSizeComboBox.getSelectedItem().toString(); if (!selectedItem.equals("")) { spectrum.setSymbolSize(Integer.parseInt(selectedItem)); } }); spectraPanel.add(shapeSizeComboBox); } private void toggleCollapsed(int index) { final boolean isCollapsed = !collapsed[index]; collapsed[index] = isCollapsed; int rowIndex = (index * 2) + 2; if (isCollapsed) { spectraPanelLayout.setRowFill(rowIndex, TableLayout.Fill.HORIZONTAL); spectraPanelLayout.setRowWeightY(rowIndex, 0.0); bandTablePanels[index].removeAll(); } else { spectraPanelLayout.setRowFill(rowIndex, TableLayout.Fill.BOTH); spectraPanelLayout.setRowWeightY(rowIndex, 1.0); bandTablePanels[index].add(bandTables[index].getTableHeader(), BorderLayout.NORTH); bandTablePanels[index].add(bandTables[index], BorderLayout.CENTER); } bandTablePanels[index].updateUI(); spectraPanel.updateUI(); } private JTable createBandsTable(int index) { DisplayableSpectrum spectrum = spectra[index]; final Band[] spectralBands = spectrum.getSpectralBands(); Object[][] spectrumData = new Object[spectralBands.length][band_columns.length]; for (int i = 0; i < spectralBands.length; i++) { Band spectralBand = spectralBands[i]; final boolean selected = spectrum.isBandSelected(i) && spectrum.isSelected(); spectrumData[i][band_selected_index] = selected; spectrumData[i][band_name_index] = spectralBand.getName(); spectrumData[i][band_description_index] = spectralBand.getDescription(); spectrumData[i][band_wavelength_index] = spectralBand.getSpectralWavelength(); spectrumData[i][band_bandwidth_ndex] = spectralBand.getSpectralBandwidth(); spectrumData[i][band_unit_index] = spectralBand.getUnit(); } final BandTableModel bandTableModel = new BandTableModel(spectrumData, band_columns); bandTableModel.addTableModelListener(e -> { e.getSource(); if (e.getColumn() == band_selected_index) { final DisplayableSpectrum spectrum1 = spectra[index]; final int bandRow = e.getFirstRow(); final Boolean selected = (Boolean) bandTableModel.getValueAt(bandRow, e.getColumn()); spectrum1.setBandSelected(bandRow, selected); if (!selectionChangeLock) { selectionChangeLock = true; selectionAdmin.setBandSelected(index, bandRow, selected); selectionAdmin.updateSpectrumSelectionState(index, selectionAdmin.getState(index)); tristateCheckBoxes[index].setState(selectionAdmin.getState(index)); spectrum1.setSelected(selectionAdmin.isSpectrumSelected(index)); selectionChangeLock = false; } } }); JTable bandsTable = new JTable(bandTableModel); bandsTable.setRowSorter(new TableRowSorter<>(bandTableModel)); final TableColumn selectionColumn = bandsTable.getColumnModel().getColumn(band_selected_index); final JCheckBox selectionCheckBox = new JCheckBox(); selectionColumn.setCellEditor(new DefaultCellEditor(selectionCheckBox)); selectionColumn.setMinWidth(20); selectionColumn.setMaxWidth(20); BooleanRenderer booleanRenderer = new BooleanRenderer(); selectionColumn.setCellRenderer(booleanRenderer); final TableColumn wavelengthColumn = bandsTable.getColumnModel().getColumn(band_wavelength_index); wavelengthColumn.setCellRenderer(new DecimalTableCellRenderer(new DecimalFormat("###0.0##"))); final TableColumn bandwidthColumn = bandsTable.getColumnModel().getColumn(band_bandwidth_ndex); bandwidthColumn.setCellRenderer(new DecimalTableCellRenderer(new DecimalFormat("###0.0##"))); return bandsTable; } private void updateBandsTable(int index) { final TableModel tableModel = bandTables[index].getModel(); for (int i = 0; i < tableModel.getRowCount(); i++) { tableModel.setValueAt(selectionAdmin.isBandSelected(index, i), i, band_selected_index); } } public DisplayableSpectrum[] getSpectra() { return originalSpectra; } @Override public void setReadRasterDataNodeNames(String[] readRasterDataNodeNames) { for (JTable bandTable : bandTables) { BandTableModel bandTableModel = (BandTableModel) bandTable.getModel(); for (int j = 0; j < bandTableModel.getRowCount(); j++) { String bandName = bandTableModel.getValueAt(j, band_name_index).toString(); boolean selected = ArrayUtils.isMemberOf(bandName, readRasterDataNodeNames); bandTableModel.setValueAt(selected, j, band_selected_index); } } } @Override public String[] getRasterDataNodeNamesToWrite() { List<String> bandNames = new ArrayList<>(); for (JTable bandTable : bandTables) { BandTableModel bandTableModel = (BandTableModel) bandTable.getModel(); for (int j = 0; j < bandTableModel.getRowCount(); j++) { if ((boolean) bandTableModel.getValueAt(j, band_selected_index)) { bandNames.add(bandTableModel.getValueAt(j, band_name_index).toString()); } } } return bandNames.toArray(new String[bandNames.size()]); } private static class BandTableModel extends DefaultTableModel { private BandTableModel(Object[][] spectrumData, String[] bandColumns) { super(spectrumData, bandColumns); } @Override public boolean isCellEditable(int row, int column) { return column == band_selected_index; } } private class BooleanRenderer extends JCheckBox implements TableCellRenderer { private BooleanRenderer() { this.setOpaque(true); } @Override public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { if (isSelected) { setBackground(table.getSelectionBackground()); setForeground(table.getSelectionForeground()); } else { setBackground(table.getBackground()); setForeground(table.getForeground()); } boolean selected = (Boolean) value; setSelected(selected); return this; } } private class CollapseListener implements ActionListener { private final int index; CollapseListener(int index) { this.index = index; } @Override public void actionPerformed(ActionEvent e) { final Object source = e.getSource(); if (source instanceof AbstractButton) { AbstractButton collapseButton = (AbstractButton) source; toggleCollapsed(index); if (collapsed[index]) { collapseButton.setIcon(icons[0]); collapseButton.setRolloverIcon(roll_over_icons[0]); } else { collapseButton.setIcon(icons[1]); collapseButton.setRolloverIcon(roll_over_icons[1]); } } } } private class TristateCheckboxListener implements ActionListener { private final int index; TristateCheckboxListener(int index) { this.index = index; } @Override public void actionPerformed(ActionEvent e) { if (!selectionChangeLock) { selectionChangeLock = true; selectionAdmin.updateSpectrumSelectionState(index, tristateCheckBoxes[index].getState()); tristateCheckBoxes[index].setState(selectionAdmin.getState(index)); updateBandsTable(index); spectra[index].setSelected(selectionAdmin.isSpectrumSelected(index)); selectionChangeLock = false; } } } }