/*
* Copyright (C) 2014 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.pixex.visat;
import com.bc.ceres.binding.Property;
import com.bc.ceres.binding.PropertyContainer;
import com.bc.ceres.binding.PropertyDescriptor;
import com.bc.ceres.binding.ValidationException;
import com.bc.ceres.binding.ValueRange;
import com.bc.ceres.swing.TableLayout;
import com.bc.ceres.swing.binding.BindingContext;
import com.bc.ceres.swing.binding.Enablement;
import com.vividsolutions.jts.geom.Point;
import org.esa.snap.core.datamodel.GeoPos;
import org.esa.snap.core.datamodel.Placemark;
import org.esa.snap.core.datamodel.PlacemarkDescriptor;
import org.esa.snap.core.datamodel.PlacemarkGroup;
import org.esa.snap.core.datamodel.Product;
import org.esa.snap.core.datamodel.ProductData;
import org.esa.snap.pixex.Coordinate;
import org.esa.snap.pixex.PixExOp;
import org.esa.snap.rcp.util.DateCellRenderer;
import org.esa.snap.rcp.util.DateTimePickerCellEditor;
import org.esa.snap.ui.AppContext;
import org.esa.snap.ui.DecimalCellEditor;
import org.esa.snap.ui.DecimalTableCellRenderer;
import org.esa.snap.ui.ModalDialog;
import org.esa.snap.ui.UIUtils;
import org.esa.snap.ui.product.ProductExpressionPane;
import org.esa.snap.ui.tool.ToolButtonFactory;
import org.geotools.feature.NameImpl;
import org.geotools.feature.simple.SimpleFeatureBuilder;
import org.geotools.feature.type.AttributeDescriptorImpl;
import org.geotools.feature.type.AttributeTypeImpl;
import org.opengis.feature.simple.SimpleFeature;
import org.opengis.feature.simple.SimpleFeatureType;
import org.opengis.feature.type.AttributeDescriptor;
import org.opengis.feature.type.AttributeType;
import javax.swing.AbstractButton;
import javax.swing.BoxLayout;
import javax.swing.ButtonGroup;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JComboBox;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.JRadioButton;
import javax.swing.JScrollPane;
import javax.swing.JSpinner;
import javax.swing.JTable;
import javax.swing.JTextArea;
import javax.swing.JTextField;
import javax.swing.ListSelectionModel;
import javax.swing.SpinnerNumberModel;
import javax.swing.SwingConstants;
import javax.swing.SwingUtilities;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Insets;
import java.awt.Rectangle;
import java.awt.Window;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.text.DateFormat;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
class PixelExtractionParametersForm {
private static final ImageIcon ADD_ICON = UIUtils.loadImageIcon("icons/Plus24.gif");
private static final ImageIcon REMOVE_ICON = UIUtils.loadImageIcon("icons/Minus24.gif");
private JPanel mainPanel;
private JLabel windowLabel;
private JSpinner windowSpinner;
private final AppContext appContext;
private final CoordinateTableModel coordinateTableModel;
private JButton editExpressionButton;
private JCheckBox useExpressionCheckBox;
private JTextArea expressionArea;
private JRadioButton expressionAsFilterButton;
private JRadioButton exportExpressionResultButton;
private Product activeProduct;
private JLabel expressionNoteLabel;
private JSpinner timeSpinner;
private JComboBox<String> timeUnitComboBox;
private JCheckBox includeOriginalInputBox;
private Enablement aggregationEneblement;
private JCheckBox timeBox;
PixelExtractionParametersForm(AppContext appContext, PropertyContainer container) {
this.appContext = appContext;
coordinateTableModel = new CoordinateTableModel();
createUi(container);
updateUi();
}
public JPanel getPanel() {
return mainPanel;
}
public Coordinate[] getCoordinates() {
Coordinate[] coordinates = new Coordinate[coordinateTableModel.getRowCount()];
for (int i = 0; i < coordinateTableModel.getRowCount(); i++) {
final Placemark placemark = coordinateTableModel.getPlacemarkAt(i);
SimpleFeature feature = placemark.getFeature();
final Date dateTime = (Date) feature.getAttribute(Placemark.PROPERTY_NAME_DATETIME);
final Coordinate.OriginalValue[] originalValues = PixExOp.getOriginalValues(feature);
if (placemark.getGeoPos() == null) {
final Point point = (Point) feature.getDefaultGeometry();
coordinates[i] = new Coordinate(placemark.getName(), point.getY(), point.getX(),
dateTime, originalValues);
} else {
coordinates[i] = new Coordinate(placemark.getName(), placemark.getGeoPos().getLat(),
placemark.getGeoPos().getLon(), dateTime, originalValues);
}
}
return coordinates;
}
void setCoordinates(Coordinate[] coordinates) {
Placemark[] toDelete = coordinateTableModel.getPlacemarks();
for (Placemark placemark : toDelete) {
coordinateTableModel.removePlacemark(placemark);
}
PlacemarkDescriptor placemarkDescriptor = coordinateTableModel.getPlacemarkDescriptor();
final SimpleFeatureType placemarkFT = placemarkDescriptor.getBaseFeatureType();
SimpleFeatureBuilder fb = new SimpleFeatureBuilder(placemarkFT);
AttributeType at = new AttributeTypeImpl(new NameImpl("label"), String.class,
false, false, null, null, null);
for (Coordinate coordinate : coordinates) {
List<AttributeDescriptor> attributeDescriptors = new ArrayList<>();
List<String> attributeValues = new ArrayList<>();
for (Coordinate.OriginalValue originalValue : coordinate.getOriginalValues()) {
attributeDescriptors.add(new AttributeDescriptorImpl(at,
new NameImpl(originalValue.getVariableName()),
0, 1,
false,
null));
attributeValues.add(originalValue.getValue());
}
placemarkFT.getUserData().put("originalAttributeDescriptors", attributeDescriptors);
int attributeCount = placemarkFT.getAttributeCount();
final SimpleFeature f = fb.buildFeature(coordinate.getName(), new Object[attributeCount]);
f.getUserData().put("originalAttributes", attributeValues);
f.setAttribute(Placemark.PROPERTY_NAME_DATETIME, coordinate.getDateTime());
final Placemark placemark = placemarkDescriptor.createPlacemark(f);
placemark.setGeoPos(new GeoPos(coordinate.getLat(), coordinate.getLon()));
placemark.setName(coordinate.getName());
coordinateTableModel.addPlacemark(placemark);
}
}
public String getExpression() {
if (useExpressionCheckBox.isSelected()) {
return expressionArea.getText();
} else {
return null;
}
}
void setExpression(String expression) {
useExpressionCheckBox.setSelected(expression != null);
expressionArea.setText(expression);
}
public String getAllowedTimeDifference() {
return createAllowedTimeDifferenceString();
}
void setAllowedTimeDifference(String timeDifference) {
if (timeDifference != null && !timeDifference.isEmpty()) {
timeBox.setSelected(true);
String timePart = timeDifference.substring(0, timeDifference.length() - 1);
timeSpinner.setValue(Integer.parseInt(timePart));
char lastChar = timeDifference.charAt(timeDifference.length() - 1);
int index = 0;
switch (lastChar) {
case 'D':
index = 0;
break;
case 'H':
index = 1;
break;
case 'M':
index = 2;
break;
}
timeUnitComboBox.setSelectedIndex(index);
} else {
timeBox.setSelected(false);
}
}
public boolean isExportExpressionResultSelected() {
return exportExpressionResultButton.isSelected();
}
void setExportExpressionResultSelected(boolean isSelected) {
exportExpressionResultButton.setSelected(isSelected);
}
private void createUi(PropertyContainer container) {
final TableLayout tableLayout = new TableLayout(3);
tableLayout.setTableAnchor(TableLayout.Anchor.NORTHWEST);
tableLayout.setTableFill(TableLayout.Fill.HORIZONTAL);
tableLayout.setTablePadding(4, 4);
tableLayout.setTableWeightX(0.0);
tableLayout.setTableWeightY(0.0);
tableLayout.setColumnWeightX(1, 1.0);
tableLayout.setCellFill(0, 1, TableLayout.Fill.BOTH); // coordinate table
tableLayout.setCellWeightY(0, 1, 7.0);
tableLayout.setCellPadding(6, 0, new Insets(8, 4, 4, 4)); // expression label
tableLayout.setCellPadding(6, 1, new Insets(0, 0, 0, 0)); // expression panel
tableLayout.setCellWeightX(6, 1, 1.0);
tableLayout.setCellWeightY(6, 1, 3.0);
tableLayout.setCellFill(6, 1, TableLayout.Fill.BOTH);
tableLayout.setCellPadding(7, 0, new Insets(8, 4, 4, 4)); // Sub-scene label
tableLayout.setCellPadding(7, 1, new Insets(0, 0, 0, 0));
tableLayout.setCellPadding(8, 0, new Insets(8, 4, 4, 4)); // kmz export label
tableLayout.setCellPadding(8, 1, new Insets(0, 0, 0, 0));
tableLayout.setCellPadding(9, 0, new Insets(8, 4, 4, 4)); // output match label
tableLayout.setCellPadding(9, 1, new Insets(0, 0, 0, 0));
mainPanel = new JPanel(tableLayout);
mainPanel.add(new JLabel("Coordinates:"));
final JComponent[] coordinatesComponents = createCoordinatesComponents();
mainPanel.add(coordinatesComponents[0]);
mainPanel.add(coordinatesComponents[1]);
final Component[] timeDeltaComponents = createTimeDeltaComponents(tableLayout);
for (Component timeDeltaComponent : timeDeltaComponents) {
mainPanel.add(timeDeltaComponent);
}
coordinateTableModel.addTableModelListener(e -> updateIncludeOriginalInputBox());
final BindingContext bindingContext = new BindingContext(container);
mainPanel.add(new JLabel("Export:"));
mainPanel.add(createExportPanel(bindingContext));
mainPanel.add(tableLayout.createHorizontalSpacer());
mainPanel.add(new JLabel("Window size:"));
windowSpinner = createWindowSizeEditor(bindingContext);
windowLabel = new JLabel();
windowLabel.setHorizontalAlignment(SwingConstants.CENTER);
windowSpinner.addChangeListener(e -> handleWindowSpinnerChange());
mainPanel.add(windowSpinner);
mainPanel.add(windowLabel);
mainPanel.add(new JLabel("Pixel value aggregation method:"));
JComboBox aggregationStrategyChooser = new JComboBox();
bindingContext.bind("aggregatorStrategyType", aggregationStrategyChooser);
aggregationEneblement = bindingContext.bindEnabledState("aggregatorStrategyType", true, new Enablement.Condition() {
@Override
public boolean evaluate(BindingContext bindingContext) {
final Integer windowSize = (Integer) windowSpinner.getValue();
return windowSize > 1;
}
});
mainPanel.add(aggregationStrategyChooser);
mainPanel.add(tableLayout.createVerticalSpacer());
mainPanel.add(new JLabel("Expression:"));
mainPanel.add(createExpressionPanel(bindingContext));
mainPanel.add(tableLayout.createHorizontalSpacer());
mainPanel.add(new JLabel("Sub-scenes:"));
mainPanel.add(createSubSceneExportPanel(bindingContext));
mainPanel.add(tableLayout.createHorizontalSpacer());
mainPanel.add(new JLabel("Google Earth export:"));
mainPanel.add(createKmzExportPanel(bindingContext));
mainPanel.add(tableLayout.createHorizontalSpacer());
mainPanel.add(new JLabel("Match with original input:"));
mainPanel.add(createIncludeOriginalInputBox(bindingContext));
mainPanel.add(tableLayout.createHorizontalSpacer());
}
private JComponent createIncludeOriginalInputBox(BindingContext bindingContext) {
final TableLayout tableLayout = new TableLayout(1);
tableLayout.setTablePadding(4, 4);
tableLayout.setTableWeightX(1.0);
tableLayout.setTableWeightY(0.0);
tableLayout.setTableFill(TableLayout.Fill.BOTH);
tableLayout.setTableAnchor(TableLayout.Anchor.NORTHWEST);
final JPanel panel = new JPanel(tableLayout);
includeOriginalInputBox = new JCheckBox("Include original input");
bindingContext.bind("includeOriginalInput", includeOriginalInputBox);
panel.add(includeOriginalInputBox);
updateIncludeOriginalInputBox();
return panel;
}
private Component createKmzExportPanel(BindingContext bindingContext) {
final TableLayout tableLayout = new TableLayout(1);
tableLayout.setTablePadding(4, 4);
tableLayout.setTableWeightX(1.0);
tableLayout.setTableWeightY(0.0);
tableLayout.setTableFill(TableLayout.Fill.BOTH);
tableLayout.setTableAnchor(TableLayout.Anchor.NORTHWEST);
final JPanel panel = new JPanel(tableLayout);
final JCheckBox exportKmzBox = new JCheckBox("Export output coordinates to Google Earth (KMZ)");
bindingContext.bind("exportKmz", exportKmzBox);
panel.add(exportKmzBox);
return panel;
}
private JPanel createSubSceneExportPanel(BindingContext bindingContext) {
final TableLayout tableLayout = new TableLayout(4);
tableLayout.setTablePadding(4, 4);
tableLayout.setTableFill(TableLayout.Fill.BOTH);
tableLayout.setTableWeightX(0.0);
tableLayout.setTableWeightY(0.0);
tableLayout.setColumnWeightX(1, 0.3);
tableLayout.setColumnWeightX(3, 1.0);
tableLayout.setTableAnchor(TableLayout.Anchor.NORTHWEST);
final JPanel exportPanel = new JPanel(tableLayout);
final JCheckBox exportSubScenesCheckBox = new JCheckBox("Enable export");
final JLabel borderSizeLabel = new JLabel("Border size:");
final JTextField borderSizeTextField = new JTextField();
borderSizeTextField.setHorizontalAlignment(JTextField.RIGHT);
bindingContext.bind("exportSubScenes", exportSubScenesCheckBox);
bindingContext.bind("subSceneBorderSize", borderSizeTextField);
bindingContext.bindEnabledState("subSceneBorderSize", false, "exportSubScenes", false);
exportPanel.add(exportSubScenesCheckBox);
exportPanel.add(new JLabel());
exportPanel.add(borderSizeLabel);
exportPanel.add(borderSizeTextField);
return exportPanel;
}
private Component[] createTimeDeltaComponents(TableLayout tableLayout) {
final JLabel boxLabel = new JLabel("Allowed time difference:");
timeBox = new JCheckBox("Use time difference constraint");
final Component horizontalSpacer = tableLayout.createHorizontalSpacer();
final Component horizontalSpacer2 = tableLayout.createHorizontalSpacer();
timeSpinner = new JSpinner(new SpinnerNumberModel(1, 1, null, 1));
timeSpinner.setEnabled(false);
timeUnitComboBox = new JComboBox<>(new String[]{"Day(s)", "Hour(s)", "Minute(s)"});
timeUnitComboBox.setEnabled(false);
timeBox.addActionListener(e -> {
timeSpinner.setEnabled(timeBox.isSelected());
timeUnitComboBox.setEnabled(timeBox.isSelected());
});
return new Component[]{boxLabel, timeBox, horizontalSpacer, horizontalSpacer2, timeSpinner, timeUnitComboBox};
}
private String createAllowedTimeDifferenceString() {
return timeBox.isSelected() ? String.valueOf(
timeSpinner.getValue()) + timeUnitComboBox.getSelectedItem().toString().charAt(0) : "";
}
void updateUi() {
handleWindowSpinnerChange();
updateExpressionComponents();
updateIncludeOriginalInputBox();
}
private void updateIncludeOriginalInputBox() {
includeOriginalInputBox.setEnabled(false);
final Coordinate[] coordinates = getCoordinates();
for (Coordinate coordinate : coordinates) {
if (coordinate.getOriginalValues().length > 0) {
includeOriginalInputBox.setEnabled(true);
return;
}
}
}
private void updateExpressionComponents() {
final boolean useExpressionSelected = useExpressionCheckBox.isSelected();
editExpressionButton.setEnabled(useExpressionSelected && activeProduct != null);
String toolTip = null;
if (activeProduct == null) {
toolTip = String.format("Editor can only be used with a product opened in %s.",
appContext.getApplicationName());
}
editExpressionButton.setToolTipText(toolTip);
expressionArea.setEnabled(useExpressionSelected);
expressionNoteLabel.setEnabled(useExpressionSelected);
expressionAsFilterButton.setEnabled(useExpressionSelected);
exportExpressionResultButton.setEnabled(useExpressionSelected);
}
private void handleWindowSpinnerChange() {
final Integer windowSize = (Integer) windowSpinner.getValue();
windowLabel.setText(String.format("%1$d x %1$d", windowSize));
aggregationEneblement.apply();
}
private JPanel createExportPanel(BindingContext bindingContext) {
final TableLayout tableLayout = new TableLayout(4);
tableLayout.setTablePadding(4, 0);
tableLayout.setTableFill(TableLayout.Fill.VERTICAL);
tableLayout.setTableAnchor(TableLayout.Anchor.NORTHWEST);
tableLayout.setColumnWeightX(3, 1.0);
tableLayout.setTableWeightY(1.0);
final JPanel exportPanel = new JPanel(tableLayout);
exportPanel.add(createIncludeCheckbox(bindingContext, "Bands", "exportBands"));
exportPanel.add(createIncludeCheckbox(bindingContext, "Tie-point grids", "exportTiePoints"));
exportPanel.add(createIncludeCheckbox(bindingContext, "Masks", "exportMasks"));
exportPanel.add(tableLayout.createHorizontalSpacer());
return exportPanel;
}
private JCheckBox createIncludeCheckbox(BindingContext bindingContext, String labelText, String propertyName) {
final Property windowProperty = bindingContext.getPropertySet().getProperty(propertyName);
final Boolean defaultValue = (Boolean) windowProperty.getDescriptor().getDefaultValue();
final JCheckBox checkbox = new JCheckBox(labelText, defaultValue);
bindingContext.bind(propertyName, checkbox);
return checkbox;
}
private JPanel createExpressionPanel(BindingContext bindingContext) {
final TableLayout tableLayout = new TableLayout(2);
tableLayout.setTablePadding(4, 4);
tableLayout.setTableFill(TableLayout.Fill.BOTH);
tableLayout.setTableWeightX(1.0);
tableLayout.setTableWeightY(0.0);
tableLayout.setTableAnchor(TableLayout.Anchor.NORTHWEST);
tableLayout.setCellAnchor(0, 1, TableLayout.Anchor.NORTHEAST); // edit expression button
tableLayout.setRowFill(0, TableLayout.Fill.VERTICAL);
tableLayout.setCellFill(1, 0, TableLayout.Fill.BOTH); // expression text area
tableLayout.setCellWeightY(1, 0, 1.0);
tableLayout.setCellColspan(1, 0, 2);
tableLayout.setCellColspan(2, 0, 2); // expression note line 1
tableLayout.setCellColspan(3, 0, 2); // radio button group
tableLayout.setCellFill(3, 0, TableLayout.Fill.BOTH);
final JPanel panel = new JPanel(tableLayout);
useExpressionCheckBox = new JCheckBox("Use expression");
useExpressionCheckBox.addActionListener(e -> updateExpressionComponents());
editExpressionButton = new JButton("Edit Expression...");
final Window parentWindow = SwingUtilities.getWindowAncestor(panel);
editExpressionButton.addActionListener(new EditExpressionActionListener(parentWindow));
panel.add(useExpressionCheckBox);
panel.add(editExpressionButton);
expressionArea = new JTextArea(3, 40);
expressionArea.setLineWrap(true);
panel.add(new JScrollPane(expressionArea));
expressionNoteLabel = new JLabel("Note: The expression might not be applicable to all products.");
panel.add(expressionNoteLabel);
final ButtonGroup buttonGroup = new ButtonGroup();
expressionAsFilterButton = new JRadioButton("Use expression as filter", true);
buttonGroup.add(expressionAsFilterButton);
exportExpressionResultButton = new JRadioButton("Export expression result");
buttonGroup.add(exportExpressionResultButton);
final Property exportResultProperty = bindingContext.getPropertySet().getProperty("exportExpressionResult");
final Boolean defaultValue = (Boolean) exportResultProperty.getDescriptor().getDefaultValue();
exportExpressionResultButton.setSelected(defaultValue);
exportExpressionResultButton.setToolTipText(
"Expression result is exported to the output file for each exported pixel.");
expressionAsFilterButton.setSelected(!defaultValue);
expressionAsFilterButton.setToolTipText(
"Expression is used as filter (all pixels in given window must be valid).");
final JPanel expressionButtonPanel = new JPanel(new FlowLayout(FlowLayout.LEFT));
expressionButtonPanel.add(expressionAsFilterButton);
expressionButtonPanel.add(exportExpressionResultButton);
panel.add(expressionButtonPanel);
return panel;
}
private JComponent[] createCoordinatesComponents() {
Product selectedProduct = appContext.getSelectedProduct();
if (selectedProduct != null) {
final PlacemarkGroup pinGroup = selectedProduct.getPinGroup();
for (int i = 0; i < pinGroup.getNodeCount(); i++) {
coordinateTableModel.addPlacemark(pinGroup.get(i));
}
}
JTable coordinateTable = new JTable(coordinateTableModel);
coordinateTable.setName("coordinateTable");
coordinateTable.setAutoResizeMode(JTable.AUTO_RESIZE_NEXT_COLUMN);
coordinateTable.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
coordinateTable.setRowSelectionAllowed(true);
coordinateTable.getTableHeader().setReorderingAllowed(false);
coordinateTable.setDefaultRenderer(Double.class, new DecimalTableCellRenderer(new DecimalFormat("0.0000")));
coordinateTable.setPreferredScrollableViewportSize(new Dimension(250, 100));
coordinateTable.getColumnModel().getColumn(1).setCellEditor(new DecimalCellEditor(-90, 90));
coordinateTable.getColumnModel().getColumn(2).setCellEditor(new DecimalCellEditor(-180, 180));
final DateFormat dateFormat = ProductData.UTC.createDateFormat("yyyy-MM-dd'T'HH:mm:ss"); // ISO 8601
final DateFormat timeFormat = ProductData.UTC.createDateFormat("HH:mm:ss"); // ISO 8601
DateTimePickerCellEditor cellEditor = new DateTimePickerCellEditor(dateFormat, timeFormat);
cellEditor.setClickCountToStart(1);
coordinateTable.getColumnModel().getColumn(3).setCellEditor(cellEditor);
coordinateTable.getColumnModel().getColumn(3).setPreferredWidth(200);
final DateCellRenderer dateCellRenderer = new DateCellRenderer(dateFormat);
dateCellRenderer.setHorizontalAlignment(SwingConstants.RIGHT);
coordinateTable.getColumnModel().getColumn(3).setCellRenderer(dateCellRenderer);
final JScrollPane rasterScrollPane = new JScrollPane(coordinateTable);
final AbstractButton addButton = ToolButtonFactory.createButton(ADD_ICON, false);
addButton.addActionListener(new AddPopupListener());
final AbstractButton removeButton = ToolButtonFactory.createButton(REMOVE_ICON, false);
removeButton.addActionListener(new RemovePlacemarksListener(coordinateTable, coordinateTableModel));
final JPanel buttonPanel = new JPanel();
final BoxLayout layout = new BoxLayout(buttonPanel, BoxLayout.Y_AXIS);
buttonPanel.setLayout(layout);
buttonPanel.add(addButton);
buttonPanel.add(removeButton);
return new JComponent[]{rasterScrollPane, buttonPanel};
}
private JSpinner createWindowSizeEditor(BindingContext bindingContext) {
final PropertyDescriptor windowSizeDescriptor = bindingContext.getPropertySet().getProperty("windowSize").getDescriptor();
windowSizeDescriptor.setValueRange(new ValueRange(1, Double.POSITIVE_INFINITY));
windowSizeDescriptor.setAttribute("stepSize", 2);
windowSizeDescriptor.setValidator((property, value) -> {
if (((Number) value).intValue() % 2 == 0) {
throw new ValidationException("Only odd values allowed as window size.");
}
});
final JSpinner spinner = new JSpinner();
bindingContext.bind("windowSize", spinner);
return spinner;
}
public void setActiveProduct(Product product) {
activeProduct = product;
updateExpressionComponents();
}
private class AddPopupListener implements ActionListener {
@Override
public void actionPerformed(ActionEvent e) {
final JPopupMenu popup = new JPopupMenu("Add");
final Object source = e.getSource();
if (source instanceof Component) {
final Component component = (Component) source;
final Rectangle buttonBounds = component.getBounds();
popup.add(new AddCoordinateAction(coordinateTableModel));
popup.add(new AddPlacemarkFileAction(appContext, coordinateTableModel, mainPanel));
popup.add(new AddCsvFileAction(appContext, coordinateTableModel, mainPanel));
popup.show(component, 1, buttonBounds.height + 1);
}
}
}
private static class RemovePlacemarksListener implements ActionListener {
private final JTable coordinateTable;
private final CoordinateTableModel tableModel;
private RemovePlacemarksListener(JTable coordinateTable, CoordinateTableModel tableModel) {
this.coordinateTable = coordinateTable;
this.tableModel = tableModel;
}
@Override
public void actionPerformed(ActionEvent e) {
int[] selectedRows = coordinateTable.getSelectedRows();
Placemark[] toRemove = new Placemark[selectedRows.length];
for (int i = 0; i < selectedRows.length; i++) {
toRemove[i] = tableModel.getPlacemarkAt(selectedRows[i]);
}
for (Placemark placemark : toRemove) {
tableModel.removePlacemark(placemark);
}
}
}
private class EditExpressionActionListener implements ActionListener {
private final Window parentWindow;
private EditExpressionActionListener(Window parentWindow) {
this.parentWindow = parentWindow;
}
@Override
public void actionPerformed(ActionEvent e) {
ProductExpressionPane pep = ProductExpressionPane.createBooleanExpressionPane(new Product[]{activeProduct},
activeProduct,
appContext.getPreferences());
pep.setCode(expressionArea.getText());
final int i = pep.showModalDialog(parentWindow, "Expression Editor");
if (i == ModalDialog.ID_OK) {
expressionArea.setText(pep.getCode());
}
}
}
}