/*
* RapidMiner
*
* Copyright (C) 2001-2011 by Rapid-I and the contributors
*
* Complete list of developers available at our web site:
*
* http://rapid-i.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.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.Polygon;
import java.util.List;
import javax.swing.BorderFactory;
import javax.swing.Icon;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTree;
import javax.swing.SwingConstants;
import javax.swing.UIManager;
import javax.swing.border.Border;
import javax.swing.tree.DefaultTreeCellRenderer;
import com.rapidminer.BreakpointListener;
import com.rapidminer.gui.dnd.OperatorTreeTransferHandler;
import com.rapidminer.gui.dnd.OperatorTreeTransferHandler.Position;
import com.rapidminer.gui.tools.SwingTools;
import com.rapidminer.operator.ExecutionUnit;
import com.rapidminer.operator.Operator;
import com.rapidminer.operator.OperatorDescription;
import com.rapidminer.operator.ProcessSetupError;
/**
* A renderer for operator tree cells that displays the operator's icon, name,
* class, breakpoints, droplines and error hints.
*
* @author Ingo Mierswa, Helge Homburg
* ingomierswa Exp $
*/
public class OperatorTreeCellRenderer extends DefaultTreeCellRenderer {
/** The panel which will be used for the actual rendering. */
private static class OperatorPanel extends JPanel {
private static final String BREAKPOINT_BEFORE = "16/breakpoint_up.png";
private static final String BREAKPOINT_AFTER = "16/breakpoint_down.png";
private static final String BREAKPOINTS = "16/breakpoints.png";
private static final String WARNINGS = "16/sign_warning.png";
private static final long serialVersionUID = -7680223153786362865L;
private static final Color SELECTED_COLOR = UIManager.getColor("Tree.selectionBackground");
private static final Color BORDER_SELECTED_COLOR = UIManager.getColor("Tree.selectionBorderColor");
private static final Color TEXT_SELECTED_COLOR = UIManager.getColor("Tree.selectionForeground");
private static final Color TEXT_NON_SELECTED_COLOR = UIManager.getColor("Tree.textForeground");
private static Icon breakpointBeforeIcon = null;
private static Icon breakpointAfterIcon = null;
private static Icon breakpointsIcon = null;
private static Icon warningsIcon = null;
static {
// init breakpoint icons
breakpointBeforeIcon = SwingTools.createIcon(BREAKPOINT_BEFORE);
breakpointAfterIcon = SwingTools.createIcon(BREAKPOINT_AFTER);
breakpointsIcon = SwingTools.createIcon(BREAKPOINTS);
// init warnings icon
warningsIcon = SwingTools.createIcon(WARNINGS);
}
private final JLabel iconLabel = new JLabel("");
private final JLabel nameLabel = new JLabel("");
private final JLabel classLabel = new JLabel("");
private final JLabel breakpoint = new JLabel("");
private final JLabel error = new JLabel("");
private boolean isSelected = false;
private boolean hasFocus = false;
private OperatorTreeTransferHandler.Position dndMarker;
private final int[] downArrowXPoints = {
4, 4, 6, 3, 0, 2, 2
};
private final int[] downArrowYPoints = {
0, 4, 4, 7, 4, 4, 0
};
private final int[] upArrowXPoints = {
3, 6, 4, 4, 2, 2, 0
};
private final int[] upArrowYPoints = {
0, 3, 3, 7, 7, 3, 3
};
private final Polygon upArrow = new Polygon(upArrowXPoints, upArrowYPoints, 7);
private final Polygon downArrow = new Polygon(downArrowXPoints, downArrowYPoints, 7);
public OperatorPanel() {
setBackground(new java.awt.Color(0, 0, 0, 0));
setBorder(BorderFactory.createEmptyBorder(2, 2, 2, 2));
GridBagLayout layout = new GridBagLayout();
setLayout(layout);
GridBagConstraints c = new GridBagConstraints();
c.anchor = GridBagConstraints.LINE_START;
c.fill = GridBagConstraints.NONE;
c.weightx = 0;
layout.setConstraints(iconLabel, c);
add(iconLabel);
// name panel
JPanel namePanel = new JPanel();
namePanel.setBackground(new java.awt.Color(0, 0, 0, 0));
GridBagLayout nameLayout = new GridBagLayout();
GridBagConstraints nameC = new GridBagConstraints();
nameC.fill = GridBagConstraints.BOTH;
nameC.insets = new Insets(0, 5, 0, 5); // new Insets(1, 1, 1, 1);
namePanel.setLayout(nameLayout);
nameLabel.setHorizontalAlignment(SwingConstants.LEFT);
nameLabel.setFont(getFont().deriveFont(Font.PLAIN, 12));
// nameLabel.setBorder(BorderFactory.createEmptyBorder(0, 3, 0, 3));
nameC.gridwidth = GridBagConstraints.REMAINDER;
nameLayout.setConstraints(nameLabel, nameC);
namePanel.add(nameLabel);
classLabel.setHorizontalAlignment(SwingConstants.LEFT);
classLabel.setFont(getFont().deriveFont(Font.PLAIN, 10));
nameLayout.setConstraints(classLabel, nameC);
namePanel.add(classLabel);
c.weightx = 1;
add(namePanel, c);
c.gridwidth = GridBagConstraints.RELATIVE;
c.weightx = 0;
layout.setConstraints(breakpoint, c);
add(breakpoint);
c.gridwidth = GridBagConstraints.REMAINDER;
layout.setConstraints(error, c);
add(error);
}
public void updateOperator(JTree tree, Operator operator, boolean selected, boolean focus) {
this.isSelected = selected;
this.hasFocus = focus;
if (selected) {
nameLabel.setForeground(TEXT_SELECTED_COLOR);
classLabel.setForeground(TEXT_SELECTED_COLOR);
} else {
nameLabel.setForeground(TEXT_NON_SELECTED_COLOR);
classLabel.setForeground(TEXT_NON_SELECTED_COLOR);
}
if (tree instanceof OperatorTree)
dndMarker = ((OperatorTree) tree).getOperatorTreeTransferHandler().getMarkerPosition(operator);
else
dndMarker = Position.UNMARKED;
OperatorDescription descr = operator.getOperatorDescription();
Icon icon = descr.getSmallIcon();
if (icon != null) {
iconLabel.setIcon(icon);
} else {
iconLabel.setIcon(null);
}
iconLabel.setEnabled(operator.isEnabled());
nameLabel.setText(operator.getName());
nameLabel.setEnabled(operator.isEnabled());
classLabel.setText(descr.getName());
classLabel.setEnabled(operator.isEnabled());
// ICONS
// breakpoints
if (operator.hasBreakpoint(BreakpointListener.BREAKPOINT_BEFORE)) {
breakpoint.setIcon(breakpointBeforeIcon);
} else if (operator.hasBreakpoint(BreakpointListener.BREAKPOINT_AFTER)) {
breakpoint.setIcon(breakpointAfterIcon);
} else {
breakpoint.setIcon(null);
}
if (operator.hasBreakpoint(BreakpointListener.BREAKPOINT_BEFORE) &&
operator.hasBreakpoint(BreakpointListener.BREAKPOINT_AFTER)) {
breakpoint.setIcon(breakpointsIcon);
}
breakpoint.setEnabled(operator.isEnabled());
// errors
List<ProcessSetupError> errors = operator.getErrorList();
if (errors.size() > 0) {
error.setIcon(warningsIcon);
} else {
error.setIcon(null);
String descriptionText = descr.getLongDescriptionHTML();
if (descriptionText == null) {
descriptionText = descr.getShortDescription();
}
}
error.setEnabled(operator.isEnabled());
setEnabled(operator.isEnabled());
setPreferredSize(new Dimension((int) (Math.max(nameLabel.getPreferredSize().getWidth(), classLabel.getPreferredSize().getWidth()) + 3 * 22),
(int) (nameLabel.getPreferredSize().getHeight() + classLabel.getPreferredSize().getHeight() + 4)));
}
private void paintUpperDropline(Graphics graphics) {
Graphics g = graphics.create();
g.setColor(SwingTools.LIGHT_BLUE);
g.fillRect(0, 0, getWidth() - 1, 2);
g.setColor(SwingTools.DARK_BLUE);
g.drawRect(0, 0, getWidth() - 1, 2);
g.translate(1, 3);
g.setColor(SwingTools.LIGHT_BLUE);
g.fillPolygon(upArrow);
g.setColor(SwingTools.DARK_BLUE);
g.drawPolygon(upArrow);
g.translate(getWidth() - 10, 0);
g.setColor(SwingTools.LIGHT_BLUE);
g.fillPolygon(upArrow);
g.setColor(SwingTools.DARK_BLUE);
g.drawPolygon(upArrow);
g.dispose();
}
private void paintLowerDropline(Graphics graphics) {
Graphics g = graphics.create();
g.setColor(SwingTools.LIGHT_BLUE);
g.fillRect(0, getHeight() - 3, getWidth() - 1, 2);
g.setColor(SwingTools.DARK_BLUE);
g.drawRect(0, getHeight() - 3, getWidth() - 1, 2);
g.translate(1, getHeight() - 11);
g.setColor(SwingTools.LIGHT_BLUE);
g.fillPolygon(downArrow);
g.setColor(SwingTools.DARK_BLUE);
g.drawPolygon(downArrow);
g.translate(getWidth() - 10, 0);
g.setColor(SwingTools.LIGHT_BLUE);
g.fillPolygon(downArrow);
g.setColor(SwingTools.DARK_BLUE);
g.drawPolygon(downArrow);
g.dispose();
}
@Override
public void paint(Graphics g) {
if (isSelected) {
g.setColor(SELECTED_COLOR);
g.fillRect(0, 0, getWidth(), getHeight());
}
if (hasFocus) {
g.setColor(BORDER_SELECTED_COLOR);
g.drawRect(0, 0, getWidth() - 1, getHeight() - 1);
}
switch (dndMarker) {
case ABOVE:
paintUpperDropline(g);
break;
case BELOW:
paintLowerDropline(g);
break;
case INTO:
g.setColor(BORDER_SELECTED_COLOR);
g.drawRect(0, 0, getWidth() - 1, getHeight() - 1);
break;
}
super.paint(g);
}
/**
* This is a workaround to fix a Swing bug with causes the drag cursor to flicker. See
* http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6700748
* TODO: Occasionally check whether this bug was resolved by Sun.
*/
@Override
public boolean isVisible() {
return false;
}
}
private static final long serialVersionUID = -8256080174651447518L;
private static final Icon SUBPROCESS_ICON = SwingTools.createIcon("16/element_selection.png");
private static final Border SUBPROCESS_BORDER = BorderFactory.createEmptyBorder(2, 2, 2, 2);
private static final Border SUBPROCESS_MARKED_BORDER = BorderFactory.createCompoundBorder(BorderFactory.createLineBorder(SwingTools.LIGHT_BLUE),
BorderFactory.createEmptyBorder(2, 2, 2, 2));
private final OperatorPanel operatorPanel = new OperatorPanel();
public OperatorTreeCellRenderer() {
}
@Override
public Component getTreeCellRendererComponent(JTree tree, Object value, boolean selected, boolean expanded, boolean leaf, int row, boolean hasFocus) {
// operatorPanel = new OperatorPanel();
if (value instanceof Operator) {
// operatorPanel = new OperatorPanel();
operatorPanel.updateOperator(tree, (Operator) value, selected, hasFocus);
SwingTools.setEnabledRecursive(operatorPanel, operatorPanel.isEnabled());
return operatorPanel;
} else if (value instanceof ExecutionUnit) {
Component component = super.getTreeCellRendererComponent(tree, ((ExecutionUnit) value).getName(), selected, expanded, leaf, row, hasFocus);
if (tree instanceof OperatorTree) {
OperatorTreeTransferHandler.Position dndMarker = ((OperatorTree) tree).getOperatorTreeTransferHandler().getMarkerPosition((ExecutionUnit) value);
if (dndMarker != OperatorTreeTransferHandler.Position.UNMARKED) {
((JComponent) component).setBorder(SUBPROCESS_MARKED_BORDER);
} else {
((JComponent) component).setBorder(SUBPROCESS_BORDER);
}
} else {
((JComponent) component).setBorder(SUBPROCESS_BORDER);
}
((JLabel) component).setIcon(SUBPROCESS_ICON);
SwingTools.setEnabledRecursive(component, tree.isEnabled());
return component;
} else {
return super.getTreeCellRendererComponent(tree, value, selected, expanded, leaf, row, hasFocus);
}
}
}