package colorMap; import java.awt.Color; import java.awt.Component; import java.awt.Graphics; import java.awt.Point; import java.awt.Polygon; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.MouseEvent; import java.awt.event.MouseListener; import java.awt.event.MouseMotionListener; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import javax.swing.JColorChooser; import javax.swing.JMenuItem; import javax.swing.JPanel; import javax.swing.JPopupMenu; /** * A panel which displays a ColorMap * * @author Curran Kelleher * */ public class ColorMapEditorPanel extends JPanel implements MouseListener, MouseMotionListener { private static final long serialVersionUID = 5598720624549427817L; /** * The ColorMap which this panel will display. */ ColorMap associatedColorMap; /** * The editors for the individual color nodes. */ List<ColorNodeEditor> nodeEditors = new ArrayList<ColorNodeEditor>(); /** * The editor which is currently "grabbed" by the user. */ ColorNodeEditor grabbedNodeEditor = null; /** * The popup menu which appears when right clicked */ final ColorNodeEditorPopupMenu popupMenu = new ColorNodeEditorPopupMenu(); /** * Construct a ColorMapEditorPanel which will display and edit the specified * ColorMap * * @param associatedColorMap */ public ColorMapEditorPanel(ColorMap associatedColorMap) { setColorMap(associatedColorMap); addMouseListener(this); addMouseMotionListener(this); } /** * Sets the color map associated with this color map editor * @param associatedColorMap */ public void setColorMap(ColorMap associatedColorMap) { this.associatedColorMap = associatedColorMap; rebuildEditorPanels(); } /** * Clears all existing editor panels, and creates new ones which mirror the * nodes of the associatedColorMap. * */ private void rebuildEditorPanels() { nodeEditors.clear(); for (Iterator<ColorNode> it = associatedColorMap.colorNodes.iterator(); it .hasNext();) nodeEditors.add(new ColorNodeEditor(it.next())); repaint(); } public void paint(Graphics g) { associatedColorMap.paintOnThis(g, 0, 0, getWidth(), getHeight()); // paint the editors for (Iterator<ColorNodeEditor> it = nodeEditors.iterator(); it .hasNext();) it.next().paintOnThis(g, getWidth(), getHeight()); } @SuppressWarnings("serial") private class ColorNodeEditor extends Polygon { ColorNode node; int xLowerBound = 0, xUpperBound = 0; int halfEditorWidth = 4; int editorHeight = 3; int pointHeight = 4; int offsetFromTop = 0; public ColorNodeEditor(ColorNode node) { super(new int[5], new int[5], 5); this.node = node; } public void paintOnThis(Graphics g, int width, int height) { //if reset is not called, then contains() will not work this.reset(); int thisNodesXCenter = (int) (node.value * width); xLowerBound = thisNodesXCenter - halfEditorWidth; xUpperBound = thisNodesXCenter + halfEditorWidth; xpoints[0] = xLowerBound; xpoints[1] = xUpperBound; xpoints[2] = xUpperBound; xpoints[3] = thisNodesXCenter; xpoints[4] = xLowerBound; ypoints[0] = offsetFromTop; ypoints[1] = offsetFromTop; ypoints[2] = offsetFromTop + editorHeight; ypoints[3] = offsetFromTop + editorHeight + pointHeight; ypoints[4] = offsetFromTop + editorHeight; npoints = 5; g.setColor(node.color); g.fillPolygon(this); g.setColor((double)(node.color.getRed()+node.color.getGreen()+node.color.getBlue())/(255.0*3)>.3?Color.black:Color.white); g.drawPolygon(this); } } public void mouseClicked(MouseEvent e) { } /** * Displays a color chooser for a node. * * @param current */ private void displayColorChooserForNode(ColorNodeEditor current) { if (current == null) return; Color newColor = JColorChooser.showDialog(this, "Choose Color", current.node.color); if (newColor != null) { current.node.color = newColor; associatedColorMap.calculateColors(); repaint(); } } public void mouseEntered(MouseEvent e) { } public void mouseExited(MouseEvent e) { } public void mousePressed(MouseEvent e) { grabbedNodeEditor = getEditorUnderPoint(e.getPoint()); } /** * Figures out if the specified point is over a color node editor. * * @param p * the point to check * @return the ColorNodeEditor that the point p is inside, or null if p is * not inside a ColorNodeEditor */ private ColorNodeEditor getEditorUnderPoint(Point p) { for (Iterator<ColorNodeEditor> it = nodeEditors.iterator(); it .hasNext();) { ColorNodeEditor current = it.next(); if (current.contains(p)) return current; } return null; } public void mouseReleased(MouseEvent e) { grabbedNodeEditor = null; if (e.isPopupTrigger()) popupMenu.showForNodeEditor(getEditorUnderPoint(e.getPoint()), getWidth(), e.getComponent(), e.getX(), e.getY()); else displayColorChooserForNode(getEditorUnderPoint(e.getPoint())); } public void mouseDragged(MouseEvent e) { // if the user is dragging a node editor if (grabbedNodeEditor != null) { double v = (double) e.getX() / getWidth(); // move the node editor grabbedNodeEditor.node.value = v<0?0:v>1?1:v; // recalculate the color map associatedColorMap.calculateColors(); // repaint repaint(); } } public void mouseMoved(MouseEvent e) { } @SuppressWarnings("serial") private class ColorNodeEditorPopupMenu extends JPopupMenu { ColorNodeEditor associatedEditor = null; JMenuItem createNodeMenuItem, deleteNodeMenuItem, changeColorMenuItem; int currentX = 0, currentWidth = 1; public ColorNodeEditorPopupMenu() { deleteNodeMenuItem = new JMenuItem("Delete Node"); deleteNodeMenuItem.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { if (associatedEditor != null) { associatedColorMap.colorNodes .remove(associatedEditor.node); associatedColorMap.calculateColors(); rebuildEditorPanels(); } } }); changeColorMenuItem = new JMenuItem("Change Color"); changeColorMenuItem.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { if (associatedEditor != null) displayColorChooserForNode(associatedEditor); } }); createNodeMenuItem = new JMenuItem("Create Node"); createNodeMenuItem.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { double value = (double) currentX / currentWidth; associatedColorMap.colorNodes.add(new ColorNode(associatedColorMap.getColorAtValue(value), value)); associatedColorMap.calculateColors(); rebuildEditorPanels(); } }); } /** * Shows the popup menu * * @param editorUnderPoint * the ColorNodeEditor that the menu should interact with, or * null if the menu should not interact with any node editor */ public void showForNodeEditor(ColorNodeEditor editorUnderPoint, int width, Component invoker, int x, int y) { currentX = x; currentWidth = width; associatedEditor = editorUnderPoint; removeAll(); if (associatedEditor != null) { add(changeColorMenuItem); add(deleteNodeMenuItem); } else add(createNodeMenuItem); super.show(invoker, x, y); } } }