package org.esa.snap.rcp.imgfilter; import org.esa.snap.rcp.imgfilter.model.Filter; import org.esa.snap.rcp.imgfilter.model.FilterSet; import org.esa.snap.rcp.imgfilter.model.FilterSetStore; import org.esa.snap.rcp.util.Dialogs; import org.esa.snap.tango.TangoIcons; import javax.swing.DropMode; import javax.swing.Icon; import javax.swing.ImageIcon; import javax.swing.JButton; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JToolBar; import javax.swing.JTree; import javax.swing.SwingConstants; import javax.swing.border.EmptyBorder; import javax.swing.event.TreeModelEvent; import javax.swing.event.TreeModelListener; import javax.swing.event.TreeSelectionEvent; import javax.swing.event.TreeSelectionListener; import javax.swing.tree.DefaultTreeCellRenderer; import javax.swing.tree.TreePath; import javax.swing.tree.TreeSelectionModel; import java.awt.BorderLayout; import java.awt.Component; import java.awt.Font; import java.awt.dnd.DnDConstants; import java.awt.dnd.DropTarget; import java.awt.dnd.DropTargetAdapter; import java.awt.dnd.DropTargetDragEvent; import java.awt.dnd.DropTargetDropEvent; import java.awt.dnd.DropTargetEvent; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.io.IOException; import java.util.ArrayList; import java.util.List; /** * A form used too edit and display a single {@link FilterSet} in a {@code JTree}. * * @author Norman */ public class FilterSetForm extends JPanel { private FilterSet filterSet; private JButton addButton; private JButton removeButton; private JButton editButton; private JTree filterTree; private FilterSetStore filterSetStore; private FilterEditor filterEditor; private transient List<Listener> listeners; private JButton saveButton; private boolean modified; public FilterSetForm(FilterSet filterSet, FilterSetStore filterSetStore, FilterEditor filterEditor) { super(new BorderLayout(4, 4)); this.filterSetStore = filterSetStore; this.filterEditor = filterEditor; setBorder(new EmptyBorder(4, 4, 4, 4)); listeners = new ArrayList<>(); this.filterSet = filterSet; this.filterSet.addListener(new FilterSet.Listener() { @Override public void filterAdded(FilterSet filterSet, Filter filter) { setModified(true); } @Override public void filterRemoved(FilterSet filterSet, Filter filter) { setModified(true); } @Override public void filterChanged(FilterSet filterSet, Filter filter, String propertyName) { setModified(true); } }); initUI(); } public boolean isModified() { return modified; } public void setModified(boolean modified) { if (this.modified != modified) { this.modified = modified; updateState(); } } public void addListener(Listener listener) { listeners.add(listener); filterSet.addListener(listener); } public void removeListener(Listener listener) { listeners.remove(listener); filterSet.removeListener(listener); } public Filter getSelectedFilterModel() { TreePath selectionPath = filterTree.getSelectionPath(); if (selectionPath != null) { return getFilterForSelectionPath(selectionPath); } return null; } private void initUI() { addButton = new JButton(TangoIcons.actions_list_add(TangoIcons.Res.R22)); addButton.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { Filter filter = Filter.create(5); filter.setEditable(true); FilterTreeModel model = (FilterTreeModel) filterTree.getModel(); model.addFilter(filter, filterTree.getSelectionPath()); TreePath filterPath = model.getFilterPath(filter); filterTree.setSelectionPath(filterPath); } }); addButton.setToolTipText("Add user-defined filter"); removeButton = new JButton(TangoIcons.actions_list_remove(TangoIcons.Res.R22)); removeButton.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { ((FilterTreeModel) filterTree.getModel()).removeFilter((Filter) filterTree.getSelectionPath().getLastPathComponent()); } }); removeButton.setToolTipText("Remove user-defined filter"); editButton = new JButton(TangoIcons.actions_document_properties(TangoIcons.Res.R22)); editButton.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { Filter filter = (Filter) filterTree.getSelectionPath().getLastPathComponent(); filterEditor.setFilter(filter); filterEditor.show(); } }); editButton.setToolTipText("Show or edit properties of selected filter"); saveButton = new JButton(TangoIcons.actions_document_save(TangoIcons.Res.R22)); saveButton.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { try { filterSetStore.storeFilterSetModel(filterSet); setModified(false); } catch (IOException ioe) { Dialogs.showError("Save","Failed to save:\n" + ioe.getMessage()); } } }); saveButton.setToolTipText("Store the selected user-defined filter"); JToolBar toolBar = new JToolBar(SwingConstants.VERTICAL); toolBar.setFloatable(false); toolBar.setBorderPainted(false); toolBar.add(addButton); toolBar.add(removeButton); toolBar.add(editButton); toolBar.add(saveButton); filterTree = new JTree(filterSet != null ? new FilterTreeModel(filterSet) : null); filterTree.setRootVisible(false); filterTree.setShowsRootHandles(true); filterTree.getSelectionModel().setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION); filterTree.addTreeSelectionListener(new TreeSelectionListener() { @Override public void valueChanged(TreeSelectionEvent e) { updateState(); TreePath selectionPath = filterTree.getSelectionPath(); System.out.println("TreeSelectionListener.valueChanged: selectionPath = " + selectionPath); Filter selectedFilter = getFilterForSelectionPath(selectionPath); if (selectedFilter != null) { filterEditor.setFilter(selectedFilter); fireFilterSelected(selectedFilter); } else { // selectedFilter == null: We will clear editor only, if the currently displayed filter // is still contained in our filter set (otherwise the selection change may have occurred due to a removal). Filter displayedFilter = filterEditor.getFilter(); if (!filterSet.containsFilter(displayedFilter)) { filterEditor.setFilter(null); } } } }); filterTree.setCellRenderer(new MyDefaultTreeCellRenderer()); filterTree.putClientProperty("JTree.lineStyle", "Angled"); filterTree.getModel().addTreeModelListener(new TreeModelListener() { @Override public void treeNodesChanged(TreeModelEvent e) { } @Override public void treeNodesInserted(TreeModelEvent e) { } @Override public void treeNodesRemoved(TreeModelEvent e) { } @Override public void treeStructureChanged(TreeModelEvent e) { Filter filter = filterEditor.getFilter(); if (filter != null) { FilterTreeModel model = (FilterTreeModel) filterTree.getModel(); TreePath filterPath = model.getFilterPath(filter); filterTree.expandPath(filterPath); filterTree.setSelectionPath(filterPath); } } }); //installTreeDragAndDrop(); expandAllTreeNodes(); add(new JScrollPane(filterTree), BorderLayout.CENTER); add(toolBar, BorderLayout.EAST); updateState(); } private Filter getFilterForSelectionPath(TreePath selectionPath) { if (selectionPath != null) { Object lastPathComponent = selectionPath.getLastPathComponent(); if (lastPathComponent instanceof Filter) { return (Filter) lastPathComponent; } } return null; } @SuppressWarnings("UnusedDeclaration") private void installTreeDragAndDrop() { filterTree.setDragEnabled(true); filterTree.setDropMode(DropMode.INSERT); filterTree.setDropTarget(new DropTarget(filterTree, DnDConstants.ACTION_MOVE, new DropTargetAdapter() { @Override public void dragEnter(DropTargetDragEvent dtde) { System.out.println("dragEnter: dtde = " + dtde); } @Override public void dragOver(DropTargetDragEvent dtde) { System.out.println("dragOver: dtde = " + dtde); } @Override public void dropActionChanged(DropTargetDragEvent dtde) { System.out.println("dropActionChanged: dtde = " + dtde); } @Override public void dragExit(DropTargetEvent dte) { System.out.println("dragExit: dte = " + dte); } @Override public void drop(DropTargetDropEvent dtde) { System.out.println("drop: dtde = " + dtde); } })); } private void updateState() { TreePath selectionPath = filterTree.getSelectionPath(); boolean filterModelSelected = selectionPath != null && selectionPath.getLastPathComponent() instanceof Filter; addButton.setEnabled(filterSet.isEditable()); removeButton.setEnabled(filterModelSelected && filterSet.isEditable()); editButton.setEnabled(filterModelSelected); saveButton.setEnabled(filterSet.isEditable() && isModified()); } private void expandAllTreeNodes() { FilterTreeModel model = (FilterTreeModel) filterTree.getModel(); int childCount = model.getChildCount(model.getRoot()); for (int i = 0; i < childCount; i++) { Object child = model.getChild(model.getRoot(), i); TreePath treePath = new TreePath(new Object[]{model.getRoot(), child}); filterTree.expandRow(filterTree.getRowForPath(treePath)); } } private static class MyDefaultTreeCellRenderer extends DefaultTreeCellRenderer { private final static Icon seIcon = new ImageIcon(FilterSetForm.class.getResource("se.png")); private final static Icon kernelIcon = new ImageIcon(FilterSetForm.class.getResource("kernel.png")); private Font plainFont; private Font boldFont; @Override public Component getTreeCellRendererComponent(JTree tree, Object value, boolean sel, boolean expanded, boolean leaf, int row, boolean hasFocus) { final JLabel c = (JLabel) super.getTreeCellRendererComponent(tree, value, sel, expanded, leaf, row, hasFocus); if (plainFont == null) { plainFont = c.getFont().deriveFont(Font.PLAIN); boldFont = c.getFont().deriveFont(Font.BOLD); } c.setFont(leaf ? plainFont : boldFont); if (value instanceof Filter) { Filter filter = (Filter) value; c.setIcon(filter.getOperation() == Filter.Operation.CONVOLVE ? kernelIcon : seIcon); } else { c.setIcon(null); } return c; } } void fireFilterSelected(Filter filter) { for (Listener listener : listeners) { listener.filterSelected(filterSet, filter); } } public interface Listener extends FilterSet.Listener { void filterSelected(FilterSet filterSet, Filter filter); } }