/* * Copyright (C) 2011 Brockmann Consult GmbH (info@brockmann-consult.de) * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free * Software Foundation; either version 3 of the License, or (at your option) * any later version. * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, see http://www.gnu.org/licenses/ */ package org.esa.snap.ui; import com.bc.ceres.swing.TableLayout; import org.esa.snap.core.ui.ModalDialog; import org.esa.snap.core.ui.UIUtils; import org.esa.snap.core.ui.tool.ToolButtonFactory; import org.esa.snap.rcp.SnapApp; import javax.swing.AbstractAction; import javax.swing.AbstractButton; import javax.swing.AbstractListModel; import javax.swing.BoxLayout; import javax.swing.DefaultCellEditor; import javax.swing.JComponent; import javax.swing.JDialog; import javax.swing.JLabel; import javax.swing.JList; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JTable; import javax.swing.JTextField; import javax.swing.ListModel; import javax.swing.ListSelectionModel; import javax.swing.event.ListSelectionEvent; import javax.swing.event.ListSelectionListener; import javax.swing.table.AbstractTableModel; import java.awt.BorderLayout; import java.awt.Dimension; import java.awt.Insets; import java.awt.event.ActionEvent; import java.util.ArrayList; import java.util.List; import java.util.Set; /** * UI component for names association. * * @author Sabine Embacher * @author Thomas Storm */ public class NamesAssociationDialog extends ModalDialog { private final AssociationModel associationModel; private final NameProvider nameProvider; private JTable aliasNames; private JScrollPane aliasNameScrollPane; private JList<String> centerNames; private JList<String> rightNames; private AbstractButton removeButton; private boolean shown = false; public static void show(AssociationModel associationModel, NameProvider nameProvider, String helpId) { new NamesAssociationDialog(associationModel, nameProvider, helpId).show(); } private NamesAssociationDialog(AssociationModel associationModel, NameProvider nameProvider, String helpId) { super(SnapApp.getDefault().getMainFrame(), nameProvider.windowTitle, ModalDialog.ID_OK, helpId); this.associationModel = associationModel; this.nameProvider = nameProvider; init(); } @Override public int show() { setButtonID(0); final JDialog dialog = getJDialog(); if (!shown) { dialog.pack(); center(); } dialog.setMinimumSize(dialog.getSize()); dialog.setVisible(true); shown = true; return getButtonID(); } public static interface AssociationModel { List<String> getRightListNames(String alias); List<String> getCenterListNames(String alias); void addFromCenterList(String alias, String name); void addFromRightList(String alias, String name); void removeAlias(String alias); void addAlias(String alias); void removeFromRightList(String alias, String name); void removeFromCenterList(String alias, String name); Set<String> getAliasNames(); void replaceAlias(String beforeName, String changedName); } public static abstract class NameProvider { final String windowTitle; final String aliasHeaderName; final String centerHeaderName; final String rightHeaderName; public NameProvider(String windowTitle, String aliasHeaderName, String centerHeaderName, String rightHeaderName) { this.aliasHeaderName = aliasHeaderName; this.centerHeaderName = centerHeaderName; this.rightHeaderName = rightHeaderName; this.windowTitle = windowTitle; } public abstract String[] getCenterNames(); public abstract String[] getRightNames(); } private void init() { final TableLayout layout = createLayout(); final JPanel mainPanel = new JPanel(layout); mainPanel.add(new JLabel(nameProvider.aliasHeaderName)); mainPanel.add(new JLabel("")); mainPanel.add(new JLabel(nameProvider.centerHeaderName)); mainPanel.add(new JLabel(nameProvider.rightHeaderName)); mainPanel.add(createAliasList()); mainPanel.add(createButtonsPanel()); mainPanel.add(createCenterList()); mainPanel.add(createRightList()); setContent(mainPanel); } private JPanel createButtonsPanel() { final JPanel buttonsPanel = new JPanel(); buttonsPanel.setLayout(new BoxLayout(buttonsPanel, BoxLayout.Y_AXIS)); final AbstractButton addButton = ToolButtonFactory.createButton(new AddAliasAction(), false); removeButton = ToolButtonFactory.createButton(new RemoveAliasAction(), false); removeButton.setEnabled(false); buttonsPanel.add(addButton, BorderLayout.NORTH); buttonsPanel.add(removeButton, BorderLayout.SOUTH); return buttonsPanel; } private TableLayout createLayout() { final TableLayout layout = new TableLayout(4); layout.setTableAnchor(TableLayout.Anchor.NORTHWEST); layout.setTablePadding(new Insets(4, 4, 4, 4)); layout.setColumnFill(0, TableLayout.Fill.BOTH); layout.setColumnFill(1, TableLayout.Fill.VERTICAL); layout.setColumnFill(2, TableLayout.Fill.BOTH); layout.setColumnFill(3, TableLayout.Fill.BOTH); layout.setRowFill(0, TableLayout.Fill.NONE); layout.setRowAnchor(0, TableLayout.Anchor.SOUTHWEST); layout.setRowWeightY(1, 100.0); layout.setColumnWeightX(0, 100.0); layout.setColumnWeightX(2, 100.0); layout.setColumnWeightX(3, 100.0); layout.setCellFill(0, 0, TableLayout.Fill.NONE); layout.setCellFill(0, 1, TableLayout.Fill.NONE); layout.setCellFill(0, 2, TableLayout.Fill.NONE); layout.setCellFill(0, 3, TableLayout.Fill.NONE); return layout; } private JComponent createAliasList() { aliasNames = new JTable(); final AbstractTableModel tableModel = new AbstractTableModel() { @Override public boolean isCellEditable(int rowIndex, int columnIndex) { return true; } @Override public int getRowCount() { return associationModel.getAliasNames().size(); } @Override public int getColumnCount() { return 1; } @Override public void setValueAt(Object aValue, int rowIndex, int columnIndex) { final String beforeName = getAliasNameAt(rowIndex); final String changedName = aValue.toString(); associationModel.replaceAlias(beforeName, changedName); aliasNameScrollPane.repaint(); } @Override public Object getValueAt(int rowIndex, int columnIndex) { return getAliasNameAt(rowIndex); } }; aliasNames.setModel(tableModel); aliasNames.setSelectionMode(ListSelectionModel.SINGLE_INTERVAL_SELECTION); aliasNames.setColumnSelectionAllowed(true); aliasNames.setRowSelectionAllowed(true); aliasNames.setTableHeader(null); aliasNames.getSelectionModel().addListSelectionListener(new AliasNamesSelectionListener()); aliasNameScrollPane = new JScrollPane(aliasNames); aliasNameScrollPane.setPreferredSize(new Dimension(160, 200)); aliasNameScrollPane.setMinimumSize(new Dimension(160, 200)); return aliasNameScrollPane; } private String getAliasNameAt(int rowIndex) { final Set<String> names = associationModel.getAliasNames(); return names.toArray(new String[names.size()])[rowIndex]; } private JComponent createCenterList() { centerNames = new JList<>(new AbstractListModel<String>() { @Override public int getSize() { return nameProvider.getCenterNames().length; } @Override public String getElementAt(int index) { return nameProvider.getCenterNames()[index]; } }); centerNames.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION); centerNames.addListSelectionListener(new CenterListSelectionListener(centerNames)); centerNames.setEnabled(false); final JScrollPane scrollPane = new JScrollPane(centerNames); scrollPane.setPreferredSize(new Dimension(160, 200)); return scrollPane; } private JComponent createRightList() { rightNames = new JList<>(new AbstractListModel<String>() { @Override public int getSize() { return nameProvider.getRightNames().length; } @Override public String getElementAt(int index) { return nameProvider.getRightNames()[index]; } }); rightNames.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION); rightNames.addListSelectionListener(new RightListSelectionListener(rightNames)); rightNames.setEnabled(false); final JScrollPane scrollPane = new JScrollPane(rightNames); scrollPane.setPreferredSize(new Dimension(160, 200)); return scrollPane; } private class AddAliasAction extends AbstractAction { private AddAliasAction() { super("Add alias", UIUtils.loadImageIcon("icons/Plus16.gif")); } @Override public void actionPerformed(ActionEvent e) { associationModel.addAlias("..."); removeButton.setEnabled(true); aliasNameScrollPane.repaint(); int rowIndex = 0; for (String aliasName : associationModel.getAliasNames()) { if (aliasName.equals("...")) { break; } rowIndex++; } DefaultCellEditor editor = (DefaultCellEditor) aliasNames.getCellEditor(rowIndex, 0); aliasNames.editCellAt(rowIndex, 0); final JTextField textField = (JTextField) editor.getComponent(); textField.requestFocus(); textField.selectAll(); } } private class RemoveAliasAction extends AbstractAction { private RemoveAliasAction() { super("Remove alias", UIUtils.loadImageIcon("icons/Minus16.gif")); } @Override public void actionPerformed(ActionEvent e) { final ListSelectionModel selectionModel = aliasNames.getSelectionModel(); final int minSelectionIndex = selectionModel.getMinSelectionIndex(); final int maxSelectionIndex = selectionModel.getMaxSelectionIndex(); selectionModel.clearSelection(); if (minSelectionIndex != -1) { for (int i = maxSelectionIndex; i >= minSelectionIndex; i--) { associationModel.removeAlias(getAliasNameAt(i)); } } removeButton.setEnabled(associationModel.getAliasNames().size() > 0); aliasNameScrollPane.repaint(); } } private abstract class VariableNamesSelectionListener implements ListSelectionListener { private final JList variableNames; private VariableNamesSelectionListener(JList variableNames) { this.variableNames = variableNames; } @Override public void valueChanged(ListSelectionEvent e) { // todo - don't allow the the user to select center/right names which are already selected for another alias final ArrayList<Integer> selectedIndices = new ArrayList<>(); for (int index : variableNames.getSelectedIndices()) { selectedIndices.add(index); } final int selectedAliasRow = aliasNames.getSelectedRow(); if (selectedAliasRow != -1) { final String currentAlias = aliasNames.getModel().getValueAt(selectedAliasRow, 0).toString(); for (int i = 0; i < variableNames.getModel().getSize(); i++) { final String variableName = variableNames.getModel().getElementAt(i).toString(); if (selectedIndices.contains(i)) { addVariableName(currentAlias, variableName); } else { removeVariableName(currentAlias, variableName); } } } } abstract void addVariableName(String currentAlias, String name); abstract void removeVariableName(String currentAlias, String name); } private class CenterListSelectionListener extends VariableNamesSelectionListener { private CenterListSelectionListener(JList variableNames) { super(variableNames); } @Override void addVariableName(String currentAlias, String variableName) { associationModel.addFromCenterList(currentAlias, variableName); } @Override void removeVariableName(String currentAlias, String variableName) { associationModel.removeFromCenterList(currentAlias, variableName); } } private class RightListSelectionListener extends VariableNamesSelectionListener { private RightListSelectionListener(JList variableNames) { super(variableNames); } @Override void addVariableName(String currentAlias, String variableName) { associationModel.addFromRightList(currentAlias, variableName); } @Override void removeVariableName(String currentAlias, String variableName) { associationModel.removeFromRightList(currentAlias, variableName); } } private class AliasNamesSelectionListener implements ListSelectionListener { @Override public void valueChanged(ListSelectionEvent e) { final boolean isSomeAliasSelected = aliasNames.getSelectionModel().getMinSelectionIndex() != -1; centerNames.setEnabled(isSomeAliasSelected); rightNames.setEnabled(isSomeAliasSelected); removeButton.setEnabled(isSomeAliasSelected); if(isSomeAliasSelected) { final int[] selectedCenterIndices = getSelectedCenterIndices(); final int[] selectedRightIndices = getSelectedRightIndices(); centerNames.setSelectedIndices(selectedCenterIndices); rightNames.setSelectedIndices(selectedRightIndices); } else { centerNames.clearSelection(); rightNames.clearSelection(); } } private int[] getSelectedCenterIndices() { final String currentAlias = getCurrentAlias(); final List<String> selectedCenterNames = associationModel.getCenterListNames(currentAlias); return getSelectedIndices(selectedCenterNames, centerNames); } private int[] getSelectedRightIndices() { final String currentAlias = getCurrentAlias(); final List<String> selectedRightNames = associationModel.getRightListNames(currentAlias); return getSelectedIndices(selectedRightNames, rightNames); } private String getCurrentAlias() { final int minSelectionIndex = aliasNames.getSelectionModel().getMinSelectionIndex(); return aliasNames.getModel().getValueAt(minSelectionIndex, 0).toString(); } private int[] getSelectedIndices(List<String> selectedVariableNames, JList variableNames) { final List<Integer> selectedIndices = new ArrayList<>(selectedVariableNames.size()); final ListModel variableNamesModel = variableNames.getModel(); for(int i = 0; i < variableNamesModel.getSize(); i++) { final String name = variableNamesModel.getElementAt(i).toString(); if (selectedVariableNames.contains(name)) { selectedIndices.add(i); } } final int[] selectedIndicesArray = new int[selectedIndices.size()]; int i = 0; for (Integer index : selectedIndices) { selectedIndicesArray[i] = index; i++; } return selectedIndicesArray; } } }