package org.esa.snap.rcp.imgfilter; import org.esa.snap.rcp.imgfilter.model.Filter; import javax.swing.Box; import javax.swing.DefaultComboBoxModel; import javax.swing.JComboBox; import javax.swing.JLabel; import javax.swing.JMenuItem; import javax.swing.JPanel; import javax.swing.JPopupMenu; import javax.swing.JSpinner; import javax.swing.JTextField; import javax.swing.JToolBar; import javax.swing.SpinnerNumberModel; import javax.swing.border.EmptyBorder; import java.awt.BorderLayout; import java.awt.Dimension; import java.awt.Toolkit; import java.awt.datatransfer.Clipboard; import java.awt.datatransfer.DataFlavor; import java.awt.datatransfer.StringSelection; import java.awt.datatransfer.Transferable; import java.awt.datatransfer.UnsupportedFlavorException; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.io.IOException; /** * A form containing a {link @FilterKernelCanvas} and components to change size and fill value of the kernel. * * @author Norman */ public class FilterKernelForm extends JPanel implements Filter.Listener { private Filter filter; private CanvasMouseListener canvasMouseListener; private FilterKernelCanvas kernelCanvas; private JSpinner kernelWidthSpinner; private JSpinner kernelHeightSpinner; private JComboBox<Number> fillValueCombo; private double fillValue; private final DefaultComboBoxModel<Number> structuringFillValueModel = new DefaultComboBoxModel<>(new Number[]{0, 1}); private final DefaultComboBoxModel<Number> kernelFillValueModel = new DefaultComboBoxModel<>(new Number[]{-5., -4., -3., -2., -1., 0., 1., 2., 3., 4., 5.}); public FilterKernelForm(Filter filter) { super(new BorderLayout(4, 4)); setBorder(new EmptyBorder(4, 4, 4, 4)); this.filter = null; this.fillValue = 0.0; setFilter(filter); } public double getFillValue() { return fillValue; } public void setFillValue(double fillValue) { double fillValueOld = this.fillValue; this.fillValue = fillValue; firePropertyChange("fillValue", fillValueOld, fillValue); } @Override public void filterChanged(Filter filter, String propertyName) { if (this.filter != filter) { return; } boolean structureElement = filter.getOperation() != Filter.Operation.CONVOLVE; fillValueCombo.setModel(structureElement ? structuringFillValueModel : kernelFillValueModel); int kernelWidth = filter.getKernelWidth(); int kernelHeight = filter.getKernelHeight(); if (kernelWidthSpinner != null && ((Number) kernelWidthSpinner.getValue()).intValue() != kernelWidth) { kernelWidthSpinner.setValue(kernelWidth); } if (kernelHeightSpinner != null && ((Number) kernelHeightSpinner.getValue()).intValue() != kernelHeight) { kernelHeightSpinner.setValue(kernelHeight); } } @Override public Dimension getPreferredSize() { return new Dimension(320, 320); } public Filter getFilter() { return filter; } public void setFilter(Filter filter) { Filter filterOld = this.filter; if (filterOld != filter) { if (this.filter != null) { this.filter.removeListener(this); } this.filter = filter; initUI(); if (this.filter != null) { this.filter.addListener(this); } firePropertyChange("filter", filterOld, this.filter); } } private void initUI() { removeAll(); if (kernelCanvas != null && canvasMouseListener != null) { kernelCanvas.removeMouseListener(canvasMouseListener); kernelCanvas.removeMouseMotionListener(canvasMouseListener); kernelCanvas = null; } fillValueCombo = null; kernelHeightSpinner = null; kernelWidthSpinner = null; if (filter == null) { invalidate(); revalidate(); repaint(); return; } kernelCanvas = new FilterKernelCanvas(filter); if (canvasMouseListener == null) { canvasMouseListener = new CanvasMouseListener(); } kernelCanvas.addMouseListener(canvasMouseListener); if (filter.isEditable()) { kernelCanvas.addMouseMotionListener(canvasMouseListener); } if (filter.isEditable()) { boolean structureElement = filter.getOperation() != Filter.Operation.CONVOLVE; if (structureElement) { fillValueCombo = new JComboBox<>(structuringFillValueModel); ((JTextField) fillValueCombo.getEditor().getEditorComponent()).setColumns(1); fillValueCombo.setEditable(false); fillValueCombo.setSelectedItem((int) getFillValue()); } else { fillValueCombo = new JComboBox<>(kernelFillValueModel); ((JTextField) fillValueCombo.getEditor().getEditorComponent()).setColumns(3); fillValueCombo.setEditable(true); fillValueCombo.setSelectedItem(getFillValue()); } fillValueCombo.addItemListener(e -> setFillValue(((Number) fillValueCombo.getSelectedItem()).doubleValue())); fillValueCombo.setToolTipText("Value that will be used to set a kernel element when clicking into the matrix"); kernelWidthSpinner = new JSpinner(new SpinnerNumberModel(1, 1, 100, 1)); kernelWidthSpinner.setValue(filter.getKernelHeight()); kernelWidthSpinner.addChangeListener(e -> { Integer kernelWidth = (Integer) kernelWidthSpinner.getValue(); filter.setKernelSize(kernelWidth, filter.getKernelHeight()); }); kernelWidthSpinner.setToolTipText("Width of the kernel (number of matrix columns)"); kernelHeightSpinner = new JSpinner(new SpinnerNumberModel(1, 1, 100, 1)); kernelHeightSpinner.setValue(filter.getKernelWidth()); kernelHeightSpinner.addChangeListener(e -> { Integer kernelHeight = (Integer) kernelHeightSpinner.getValue(); filter.setKernelSize(filter.getKernelWidth(), kernelHeight); }); kernelHeightSpinner.setToolTipText("Height of the kernel (number of matrix rows)"); JToolBar toolBar = new JToolBar(JToolBar.HORIZONTAL); toolBar.setFloatable(false); toolBar.setRollover(true); toolBar.add(new JLabel("Fill:")); toolBar.add(fillValueCombo); toolBar.add(Box.createHorizontalStrut(32)); toolBar.add(new JLabel(" W:")); toolBar.add(kernelWidthSpinner); toolBar.add(new JLabel(" H:")); toolBar.add(kernelHeightSpinner); add(kernelCanvas, BorderLayout.CENTER); add(toolBar, BorderLayout.SOUTH); } else { add(kernelCanvas, BorderLayout.CENTER); } invalidate(); revalidate(); repaint(); } private class CanvasMouseListener extends MouseAdapter { @Override public void mouseReleased(MouseEvent e) { FilterKernelCanvas kernelCanvas = (FilterKernelCanvas) e.getComponent(); if (e.isPopupTrigger()) { showPopup(e, kernelCanvas); } else { kernelCanvas.getFilter().adjustKernelQuotient(); } } @Override public void mousePressed(MouseEvent e) { FilterKernelCanvas kernelCanvas = (FilterKernelCanvas) e.getComponent(); if (e.isPopupTrigger()) { showPopup(e, kernelCanvas); } else if (e.getButton() == 1 && kernelCanvas.getFilter().isEditable()) { setElement(kernelCanvas, e); } } @Override public void mouseDragged(MouseEvent e) { boolean button1 = (e.getModifiersEx() & MouseEvent.BUTTON1_DOWN_MASK) != 0; if (button1 && kernelCanvas.getFilter().isEditable()) { FilterKernelCanvas kernelCanvas = (FilterKernelCanvas) e.getComponent(); setElement(kernelCanvas, e); } } private void showPopup(MouseEvent e, FilterKernelCanvas kernelCanvas) { e.consume(); JPopupMenu popupMenu = createPopupMenu(kernelCanvas); popupMenu.show(kernelCanvas, e.getX(), e.getY()); } private void setElement(FilterKernelCanvas kernelCanvas, MouseEvent e) { int index = kernelCanvas.getKernelElementIndex(e.getX(), e.getY()); if (index >= 0) { kernelCanvas.getFilter().setKernelElement(index, getFillValue()); } } } protected JPopupMenu createPopupMenu(final FilterKernelCanvas kernelCanvas) { JPopupMenu popupMenu = new JPopupMenu(); JMenuItem copyItem = new JMenuItem("Copy"); copyItem.addActionListener(e -> { Clipboard systemClip = Toolkit.getDefaultToolkit().getSystemClipboard(); systemClip.setContents(new StringSelection(kernelCanvas.getFilter().getKernelElementsAsText()), null); }); popupMenu.add(copyItem); if (!filter.isEditable()) { return popupMenu; } JMenuItem pasteItem = new JMenuItem("Paste"); pasteItem.setEnabled(isPastePossible()); pasteItem.addActionListener(e -> { Clipboard systemClip = Toolkit.getDefaultToolkit().getSystemClipboard(); Transferable transfer = systemClip.getContents(null); try { String data = (String) transfer.getTransferData(DataFlavor.stringFlavor); kernelCanvas.getFilter().setKernelElementsFromText(data); kernelCanvas.getFilter().adjustKernelQuotient(); } catch (Error | RuntimeException e1) { e1.printStackTrace(); throw e1; } catch (Throwable e1) { e1.printStackTrace(); } }); popupMenu.add(pasteItem); JMenuItem clearItem = new JMenuItem("Clear"); clearItem.addActionListener(e -> kernelCanvas.getFilter().fillRectangle(0.0)); popupMenu.add(clearItem); popupMenu.addSeparator(); final double fillValue = getFillValue(); String fillValueText = fillValue == (int) fillValue ? String.valueOf((int) fillValue) : String.valueOf(fillValue); JMenuItem fillRectangleItem = new JMenuItem(String.format("Fill Rectangle by <%s>", fillValueText)); fillRectangleItem.addActionListener(e -> { kernelCanvas.getFilter().fillRectangle(fillValue); kernelCanvas.getFilter().adjustKernelQuotient(); }); popupMenu.add(fillRectangleItem); JMenuItem fillEllipseItem = new JMenuItem(String.format("Fill Ellipse by <%s>", fillValueText)); fillEllipseItem.addActionListener(e -> { kernelCanvas.getFilter().fillEllipse(fillValue); kernelCanvas.getFilter().adjustKernelQuotient(); }); popupMenu.add(fillEllipseItem); JMenuItem fillGaussItem = new JMenuItem("Fill Gaussian"); fillGaussItem.addActionListener(e -> { kernelCanvas.getFilter().fillGaussian(); kernelCanvas.getFilter().adjustKernelQuotient(); }); popupMenu.add(fillGaussItem); JMenuItem fillLaplaceItem = new JMenuItem("Fill Laplacian"); fillLaplaceItem.addActionListener(e -> { kernelCanvas.getFilter().fillLaplacian(); kernelCanvas.getFilter().adjustKernelQuotient(); }); popupMenu.add(fillLaplaceItem); JMenuItem fillRandomItem = new JMenuItem("Fill Random"); fillRandomItem.addActionListener(e -> { kernelCanvas.getFilter().fillRandom(); kernelCanvas.getFilter().adjustKernelQuotient(); }); popupMenu.add(fillRandomItem); return popupMenu; } private boolean isPastePossible() { boolean enabled = false; Clipboard systemClip = Toolkit.getDefaultToolkit().getSystemClipboard(); Transferable transfer = systemClip.getContents(null); if (transfer.isDataFlavorSupported(DataFlavor.stringFlavor)) { try { String data = (String) transfer.getTransferData(DataFlavor.stringFlavor); enabled = Filter.isKernelDataText(data); } catch (UnsupportedFlavorException | IOException ignored) { } } return enabled; } }