/* * Copyright (C) 2010 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.rcp.statistics; import com.jidesoft.swing.CheckBoxList; import org.esa.snap.core.datamodel.Mask; import org.esa.snap.core.datamodel.Product; import org.esa.snap.core.datamodel.ProductNode; import org.esa.snap.core.datamodel.ProductNodeEvent; import org.esa.snap.core.datamodel.ProductNodeGroup; import org.esa.snap.core.datamodel.ProductNodeListener; import org.esa.snap.core.datamodel.RasterDataNode; import org.esa.snap.core.util.StringUtils; import org.esa.snap.ui.GridBagUtils; import org.esa.snap.ui.UIUtils; import org.esa.snap.ui.tool.ToolButtonFactory; import org.openide.util.ImageUtilities; import org.openide.windows.TopComponent; import org.openide.windows.WindowManager; import javax.swing.AbstractButton; import javax.swing.DefaultListModel; import javax.swing.JCheckBox; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JSeparator; import javax.swing.JTextField; import javax.swing.ListSelectionModel; import javax.swing.SwingUtilities; import javax.swing.event.DocumentEvent; import javax.swing.event.DocumentListener; import javax.swing.event.ListSelectionEvent; import javax.swing.event.ListSelectionListener; import java.awt.BorderLayout; import java.awt.GridBagConstraints; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.ItemEvent; import java.awt.event.ItemListener; import java.util.ArrayList; import java.util.List; /** * A panel which performs the 'compute' action. * * @author Marco Zuehlke */ class MultipleRoiComputePanel extends JPanel { private DefaultListModel<String> maskNameListModel; private final JTextField maskNameSearchField; private int[] indexesInMaskNameList; interface ComputeMasks { void compute(Mask[] selectedMasks); } private final ProductNodeListener productNodeListener; private final AbstractButton refreshButton; private final JCheckBox useRoiCheckBox; private final CheckBoxList maskNameList; private final JCheckBox selectAllCheckBox; private final JCheckBox selectNoneCheckBox; private RasterDataNode raster; private Product product; MultipleRoiComputePanel(final ComputeMasks method, final RasterDataNode rasterDataNode) { productNodeListener = new PNL(); maskNameSearchField = new JTextField(); maskNameSearchField.setEnabled(false); maskNameSearchField.getDocument().addDocumentListener(new DocumentListener() { @Override public void insertUpdate(DocumentEvent e) { updateMaskListState(); } @Override public void removeUpdate(DocumentEvent e) { updateMaskListState(); } @Override public void changedUpdate(DocumentEvent e) { updateMaskListState(); } }); maskNameList = new CheckBoxList(new DefaultListModel()); maskNameList.getCheckBoxListSelectionModel().setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION); maskNameList.getCheckBoxListSelectionModel().addListSelectionListener(new ListSelectionListener() { @Override public void valueChanged(ListSelectionEvent e) { updateEnablement(); refreshButton.setEnabled(true); if (!e.getValueIsAdjusting()) { selectAndEnableCheckBoxes(); } } }); useRoiCheckBox = new JCheckBox("Use ROI mask(s):"); useRoiCheckBox.setMnemonic('R'); useRoiCheckBox.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { updateEnablement(); } }); final JPanel topPanel = new JPanel(new BorderLayout()); refreshButton = ToolButtonFactory.createButton( UIUtils.loadImageIcon("icons/ViewRefresh22.png"), false); refreshButton.setEnabled(rasterDataNode != null); refreshButton.setToolTipText("Refresh View"); refreshButton.setName("refreshButton"); refreshButton.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { boolean useRoi = useRoiCheckBox.isSelected(); Mask[] selectedMasks; if (useRoi) { int[] listIndexes = maskNameList.getCheckBoxListSelectedIndices(); if (listIndexes.length > 0) { selectedMasks = new Mask[listIndexes.length]; for (int i = 0; i < listIndexes.length; i++) { int listIndex = listIndexes[i]; String maskName = maskNameList.getModel().getElementAt(listIndex).toString(); selectedMasks[i] = raster.getProduct().getMaskGroup().get(maskName); } } else { selectedMasks = new Mask[]{null}; } } else { selectedMasks = new Mask[]{null}; } method.compute(selectedMasks); refreshButton.setEnabled(false); } }); topPanel.add(refreshButton, BorderLayout.WEST); //todo enable showMaskManagerButton AbstractButton showMaskManagerButton = createShowMaskManagerButton(); selectAllCheckBox = new JCheckBox("Select all"); selectAllCheckBox.addItemListener(new ItemListener() { @Override public void itemStateChanged(ItemEvent e) { if (selectAllCheckBox.isSelected()) { maskNameList.selectAll(); } selectAndEnableCheckBoxes(); } }); selectNoneCheckBox = new JCheckBox("Select none"); selectNoneCheckBox.addItemListener(new ItemListener() { @Override public void itemStateChanged(ItemEvent e) { if (selectNoneCheckBox.isSelected()) { maskNameList.selectNone(); } selectAndEnableCheckBoxes(); } }); JPanel checkBoxPanel = new JPanel(); checkBoxPanel.add(selectAllCheckBox); checkBoxPanel.add(selectNoneCheckBox); final JPanel multiRoiComputePanel = GridBagUtils.createPanel(); GridBagConstraints multiRoiComputePanelConstraints = GridBagUtils.createConstraints("anchor=NORTHWEST,weightx=1"); GridBagUtils.addToPanel(multiRoiComputePanel, topPanel, multiRoiComputePanelConstraints, "gridx=0,gridy=0,gridwidth=3"); GridBagUtils.addToPanel(multiRoiComputePanel, new JSeparator(), multiRoiComputePanelConstraints, "gridy=1,fill=HORIZONTAL"); GridBagUtils.addToPanel(multiRoiComputePanel, useRoiCheckBox, multiRoiComputePanelConstraints, "gridy=2,weightx=0"); GridBagUtils.addToPanel(multiRoiComputePanel, new JLabel(("Filter: ")), multiRoiComputePanelConstraints, "gridy=3,gridx=0,gridwidth=1,anchor=WEST"); GridBagUtils.addToPanel(multiRoiComputePanel, maskNameSearchField, multiRoiComputePanelConstraints, "gridx=1,weightx=1"); GridBagUtils.addToPanel(multiRoiComputePanel, showMaskManagerButton, multiRoiComputePanelConstraints, "gridy=3,gridx=2,weightx=0"); GridBagUtils.addToPanel(multiRoiComputePanel, new JScrollPane(maskNameList), multiRoiComputePanelConstraints, "gridy=4,gridx=0,fill=HORIZONTAL,gridwidth=3,anchor=NORTHWEST"); GridBagUtils.addToPanel(multiRoiComputePanel, checkBoxPanel, multiRoiComputePanelConstraints, "gridy=5,weighty=1,gridwidth=3"); add(multiRoiComputePanel); setRaster(rasterDataNode); } private AbstractButton createShowMaskManagerButton() { final AbstractButton showMaskManagerButton = ToolButtonFactory.createButton(ImageUtilities.loadImageIcon("org/esa/snap/rcp/icons/MaskManager24.png", false), false); showMaskManagerButton.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { SwingUtilities.invokeLater(new Runnable() { public void run() { final TopComponent maskManagerTopComponent = WindowManager.getDefault().findTopComponent("MaskManagerTopComponent"); maskManagerTopComponent.open(); maskManagerTopComponent.requestActive(); } }); } }); return showMaskManagerButton; } void setRaster(final RasterDataNode newRaster) { if (this.raster != newRaster) { this.raster = newRaster; if (newRaster == null) { if (product != null) { product.removeProductNodeListener(productNodeListener); } product = null; } else if (product != newRaster.getProduct()) { if (product != null) { product.removeProductNodeListener(productNodeListener); } product = newRaster.getProduct(); if (product != null) { product.addProductNodeListener(productNodeListener); } } resetMaskListState(); refreshButton.setEnabled(raster != null); } } private void selectAndEnableCheckBoxes() { final int numEntries = maskNameList.getModel().getSize(); final int numSelected = maskNameList.getCheckBoxListSelectedIndices().length; selectNoneCheckBox.setEnabled(numSelected > 0); selectAllCheckBox.setEnabled(numSelected < numEntries); selectNoneCheckBox.setSelected(numSelected == 0); selectAllCheckBox.setSelected(numSelected == numEntries); } private String[] getSelectedMaskNames() { final Object[] selectedValues = maskNameList.getCheckBoxListSelectedValues(); return StringUtils.toStringArray(selectedValues); } private void resetMaskListState() { maskNameListModel = new DefaultListModel<>(); final String[] currentSelectedMaskNames = getSelectedMaskNames(); if (product != null && raster != null) { //todo [multisize_products] compare scenerastertransform (or its successor) rather than size (tf) final ProductNodeGroup<Mask> maskGroup = product.getMaskGroup(); for (int i = 0; i < maskGroup.getNodeCount(); i++) { final Mask mask = maskGroup.get(i); if (mask.getRasterSize().equals(raster.getRasterSize())) { maskNameListModel.addElement(mask.getName()); } } maskNameList.setModel(maskNameListModel); } final String[] allNames = StringUtils.toStringArray(maskNameListModel.toArray()); indexesInMaskNameList = new int[allNames.length]; for (int i = 0; i < allNames.length; i++) { String name = allNames[i]; if (StringUtils.contains(currentSelectedMaskNames, name)) { maskNameList.getCheckBoxListSelectionModel().addSelectionInterval(i, i); } indexesInMaskNameList[i] = i; } updateEnablement(); } void updateEnablement() { boolean hasMasks = (product != null && product.getMaskGroup().getNodeCount() > 0); boolean canSelectMasks = hasMasks && useRoiCheckBox.isSelected(); useRoiCheckBox.setEnabled(hasMasks); maskNameSearchField.setEnabled(canSelectMasks); maskNameList.setEnabled(canSelectMasks); selectAllCheckBox.setEnabled(canSelectMasks && maskNameList.getCheckBoxListSelectedIndices().length < maskNameList.getModel().getSize()); selectNoneCheckBox.setEnabled(canSelectMasks && maskNameList.getCheckBoxListSelectedIndices().length > 0); refreshButton.setEnabled(raster != null); } private void updateMaskListState() { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { final String text = maskNameSearchField.getText(); final DefaultListModel<Object> updatedListModel = new DefaultListModel<>(); List<Boolean> selected = new ArrayList<>(); int[] newIndexesInMaskNameList = new int[maskNameListModel.getSize()]; int counter = 0; for (int i = 0; i < maskNameListModel.getSize(); i++) { if (maskNameListModel.get(i).toLowerCase().contains(text.toLowerCase())) { updatedListModel.addElement(maskNameListModel.get(i)); if (indexesInMaskNameList[i] >= 0) { selected.add(maskNameList.getCheckBoxListSelectionModel().isSelectedIndex(indexesInMaskNameList[i])); } else { selected.add(false); } newIndexesInMaskNameList[i] = counter++; } else { newIndexesInMaskNameList[i] = -1; } } indexesInMaskNameList = newIndexesInMaskNameList; maskNameList.setModel(updatedListModel); for (int i = 0; i < selected.size(); i++) { if (selected.get(i)) { maskNameList.getCheckBoxListSelectionModel().addSelectionInterval(i, i); } } } }); } private class PNL implements ProductNodeListener { @Override public void nodeAdded(ProductNodeEvent event) { handleEvent(event); } @Override public void nodeChanged(ProductNodeEvent event) { // handleEvent(event); } @Override public void nodeDataChanged(ProductNodeEvent event) { if (!useRoiCheckBox.isSelected()) { return; } final ProductNode sourceNode = event.getSourceNode(); if (!(sourceNode instanceof Mask)) { return; } final String maskName = ((Mask) sourceNode).getName(); final String[] selectedNames = getSelectedMaskNames(); if (StringUtils.contains(selectedNames, maskName)) { updateEnablement(); } } @Override public void nodeRemoved(ProductNodeEvent event) { handleEvent(event); } private void handleEvent(ProductNodeEvent event) { ProductNode sourceNode = event.getSourceNode(); if (sourceNode instanceof Mask) { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { resetMaskListState(); } }); } } } }