/** * Copyright (C) 2001-2017 by RapidMiner and the contributors * * Complete list of developers available at our web site: * * http://rapidminer.com * * This program is free software: you can redistribute it and/or modify it under the terms of the * GNU Affero 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 * Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License along with this program. * If not, see http://www.gnu.org/licenses/. */ package com.rapidminer.gui.operatortree; import java.awt.event.MouseEvent; import java.awt.event.MouseListener; import java.util.LinkedList; import java.util.List; import javax.swing.Action; import javax.swing.JPopupMenu; import javax.swing.JTree; import javax.swing.event.TreeExpansionEvent; import javax.swing.event.TreeExpansionListener; import javax.swing.event.TreeSelectionEvent; import javax.swing.event.TreeSelectionListener; import javax.swing.tree.TreePath; import javax.swing.tree.TreeSelectionModel; import com.rapidminer.Process; import com.rapidminer.gui.MainFrame; import com.rapidminer.gui.flow.processrendering.view.ProcessRendererView; import com.rapidminer.gui.operatortree.actions.CollapseAllAction; import com.rapidminer.gui.operatortree.actions.ExpandAllAction; import com.rapidminer.gui.processeditor.ProcessEditor; import com.rapidminer.gui.tools.IconSize; import com.rapidminer.gui.tools.PrintingTools; import com.rapidminer.operator.ExecutionUnit; import com.rapidminer.operator.Operator; import com.rapidminer.operator.OperatorChain; /** * Displays the process definition as a JTree. No longer has editing capabilities. * * Since version 5.0 this view was mainly replaced by the process flow view. See * {@link ProcessRendererView}. * * @see com.rapidminer.gui.operatortree.ProcessTreeModel * @author Ingo Mierswa */ public class OperatorTree extends JTree implements TreeSelectionListener, TreeExpansionListener, MouseListener, ProcessEditor { private static final long serialVersionUID = 1L; public transient final Action EXPAND_ALL_ACTION = new ExpandAllAction(this, IconSize.SMALL); public transient final Action COLLAPSE_ALL_ACTION = new CollapseAllAction(this, IconSize.SMALL); /** The main frame. Used for conditional action updates and property table settings. */ private final MainFrame mainFrame; /** The tree model of the operator tree. */ private transient ProcessTreeModel treeModel; private volatile boolean preventEvent = false; /** Creates a new operator tree. */ public OperatorTree(MainFrame mainFrame) { super(); this.mainFrame = mainFrame; setCellRenderer(new OperatorTreeCellRenderer()); addTreeSelectionListener(this); addTreeExpansionListener(this); addMouseListener(this); // forces the tree to ask the nodes for the correct row heights // must also be invoked after LaF changes... setRowHeight(0); getSelectionModel().setSelectionMode(TreeSelectionModel.DISCONTIGUOUS_TREE_SELECTION); } /** Returns the currently selected operator, i.e. the last node in the current selection path. */ public List<Operator> getSelectedOperators() { TreePath[] paths = getSelectionPaths(); if (paths == null) { return null; } else { List<Operator> selection = new LinkedList<Operator>(); for (TreePath path : paths) { Object selected = path.getLastPathComponent(); if (selected instanceof Operator) { selection.add((Operator) selected); } else if (selected instanceof ExecutionUnit) { selection.add(((ExecutionUnit) selected).getEnclosingOperator()); } } return selection; } } /** Expands the complete tree. */ public void expandAll() { int row = 0; while (row < getRowCount()) { expandRow(row); row++; } } /** Collapses the complete tree. */ public void collapseAll() { int row = getRowCount() - 1; while (row >= 0) { collapseRow(row); row--; } } /** * This method will be invoked after a user selection of an operator in the tree. Causes a * property table update and an update of the conditional action container. */ @Override public void valueChanged(TreeSelectionEvent e) { if (preventEvent) { return; } if (mainFrame != null) { List<Operator> selectedOperators = getSelectedOperators(); if (selectedOperators != null && !selectedOperators.isEmpty()) { mainFrame.selectOperators(selectedOperators); } } } @Override public void mouseEntered(MouseEvent e) {} @Override public void mouseExited(MouseEvent e) {} @Override public void mouseClicked(MouseEvent e) { evaluatePopup(e); } @Override public void mousePressed(MouseEvent e) {} @Override public void mouseReleased(MouseEvent e) { evaluatePopup(e); } @Override public void treeCollapsed(TreeExpansionEvent event) { Object last = event.getPath().getLastPathComponent(); if (last instanceof Operator) { ((Operator) last).setExpanded(false); } else if (last instanceof ExecutionUnit) { ((ExecutionUnit) last).setExpanded(false); } } @Override public void treeExpanded(TreeExpansionEvent event) { Object last = event.getPath().getLastPathComponent(); if (last instanceof Operator) { ((Operator) last).setExpanded(true); } else if (last instanceof ExecutionUnit) { ((ExecutionUnit) last).setExpanded(true); } } @Override public void processChanged(Process process) { this.treeModel = new ProcessTreeModel(process.getRootOperator()); setModel(treeModel); setRootVisible(true); applyExpansionState(process.getRootOperator()); } @Override public void processUpdated(Process process) {} @Override public void setSelection(List<Operator> selection) { TreePath[] paths = new TreePath[selection.size()]; int i = 0; for (Operator op : selection) { paths[i++] = treeModel.getPathTo(op); } preventEvent = true; setSelectionPaths(paths); preventEvent = false; } private void applyExpansionState(Operator operator) { if (operator.isExpanded()) { expandPath(treeModel.getPathTo(operator)); if (operator instanceof OperatorChain) { OperatorChain chain = (OperatorChain) operator; if (chain.getNumberOfSubprocesses() == 1) { // subprocesses hidden for (Operator op : chain.getSubprocess(0).getOperators()) { applyExpansionState(op); } } else { for (ExecutionUnit unit : chain.getSubprocesses()) { if (unit.isExpanded()) { if (unit.isExpanded()) { expandPath(treeModel.getPathTo(unit)); for (Operator op : unit.getOperators()) { applyExpansionState(op); } } else { collapsePath(treeModel.getPathTo(unit)); } } } } } } else { collapsePath(treeModel.getPathTo(operator)); } } /** * Checks if the given mouse event is a popup trigger and creates a new popup menu if necessary. */ private void evaluatePopup(MouseEvent e) { if (e.isPopupTrigger()) { createOperatorPopupMenu().show(this, e.getX(), e.getY()); e.consume(); } } /** Creates a new popup menu for the selected operator. */ private JPopupMenu createOperatorPopupMenu() { JPopupMenu menu = new JPopupMenu(); menu.add(EXPAND_ALL_ACTION); menu.add(COLLAPSE_ALL_ACTION); menu.addSeparator(); String name = "Tree"; if (mainFrame.getProcess().getProcessLocation() != null) { name = mainFrame.getProcess().getProcessLocation().getShortName(); } menu.add(PrintingTools.makeExportPrintMenu(this, name)); return menu; } }