/******************************************************************************* * Copyright (c) 1998, 2015 Oracle and/or its affiliates. All rights reserved. * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v1.0 and Eclipse Distribution License v. 1.0 * which accompanies this distribution. * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html * and the Eclipse Distribution License is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * Contributors: * Oracle - initial API and implementation from Oracle TopLink ******************************************************************************/ package org.eclipse.persistence.tools.workbench.uitools.swing; import java.awt.Color; import java.awt.Component; import java.awt.Dimension; import java.awt.Graphics; import java.awt.IllegalComponentStateException; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import java.util.Locale; import javax.accessibility.Accessible; import javax.accessibility.AccessibleContext; import javax.accessibility.AccessibleIcon; import javax.accessibility.AccessibleRole; import javax.accessibility.AccessibleStateSet; import javax.swing.Icon; import javax.swing.SwingConstants; /** * An icon that displays a triangle pointing in one of the four * compass directions. * * @see javax.swing.SwingConstants.NORTH * @see javax.swing.SwingConstants.EAST * @see javax.swing.SwingConstants.SOUTH * @see javax.swing.SwingConstants.WEST */ public class ArrowIcon implements Icon, Accessible { private int width; private int height; /** The arrow's orientation: NORTH, EAST (default), SOUTH, or WEST. */ private int orientation; /** The arrow's dimension, as determined by the icon's dimension. */ private Dimension arrowDimension; /** The color is used only if the icon is opaque. */ private Color color; private boolean opaque; private AccessibleContext accessibleContext; private String description; // ********** constructors ********** /** * Construct a black, 10x10 arrow icon with the specified orientation. */ public ArrowIcon(int orientation) { this(); this.orientation = orientation; } /** * Construct a black, 10x10 arrow icon with an orientation of EAST. */ public ArrowIcon() { this(10, 10); } /** * Construct a black arrow icon with the specified size and * orientation of EAST. */ public ArrowIcon(int width, int height) { this(width, height, SwingConstants.EAST); } /** * Construct a black arrow icon with the specified size and orientation. */ public ArrowIcon(int width, int height, int orientation) { this(width, height, orientation, Color.black); } /** * Construct an arrow icon with the specified size, orientation, and color. */ public ArrowIcon(int width, int height, int orientation, Color color) { super(); this.width = width; this.height = height; this.orientation = orientation; this.color = color; this.opaque = (color != null); } // ********** accessors ********** public int getIconWidth() { return this.width; } public void setIconWidth(int width) { if (width != this.width) { this.width = width; this.arrowDimension = null; } } public int getIconHeight() { return this.height; } public void setIconHeight(int height) { if (height != this.height) { this.height = height; this.arrowDimension = null; } } /** * Return the orientation of icon's arrow. * @see javax.swing.SwingConstants.NORTH * @see javax.swing.SwingConstants.EAST * @see javax.swing.SwingConstants.SOUTH * @see javax.swing.SwingConstants.WEST */ public int getOrientation() { return this.orientation; } /** * Set the orientation of icon's arrow. * @see javax.swing.SwingConstants.NORTH * @see javax.swing.SwingConstants.EAST * @see javax.swing.SwingConstants.SOUTH * @see javax.swing.SwingConstants.WEST */ public void setOrientation(int orientation) { if (orientation != this.orientation) { this.orientation = orientation; this.arrowDimension = null; } } /** * Return the icon's color. The color is used only if the icon is opaque. */ public Color getColor() { return this.color; } /** * Set the icon's color. The color is used only if the icon is opaque. */ public void setColor(Color color) { this.color = color; } /** * Return whether the arrow should be painted with the background color of the * component this arrow is painting on to look pressed. The interior of the * triangle is transparent. */ public boolean isOpaque() { return this.opaque; } /** * Set whether the arrow should be painted with the background color of the * component this arrow is painting on to look pressed. The interior of the * triangle is transparent. */ public void setOpaque(boolean opaque) { this.opaque = opaque; } public AccessibleContext getAccessibleContext() { if (this.accessibleContext == null) { this.accessibleContext = new AccessibleArrowIcon(); } return this.accessibleContext; } /** * Return the icon's description. This is meant to be a brief textual * description of the object. For example, it might be presented to a blind * user to give an indication of the purpose of the icon. The description * may be null. */ public String getDescription() { return this.description; } /** * Set the icon's description. This is meant to be a brief textual * description of the object. For example, it might be presented to a blind * user to give an indication of the purpose of the icon. The description * may be null. */ public void setDescription(String description) { this.description = description; } // ********** Icon implementation ********** public void paintIcon(Component component, Graphics g, int x, int y) { // save the original graphics settings Color oldColor = g.getColor(); if (y < 0) { y = 0; } Dimension arrowDim = this.getArrowDimension(); int deltaX = (this.width - arrowDim.width) / 2; int deltaY = (this.height - arrowDim.height) / 2; g.translate(x + deltaX, y + deltaY); if (this.opaque) { if (component.isEnabled()) { g.setColor((this.color != null) ? this.color : component.getBackground().darker()); } else { Color bgColor = component.getBackground(); g.translate(1, 1); g.setColor(bgColor.brighter()); this.paintOpaqueTriangle(g, arrowDim.width, arrowDim.height); g.translate(-1, -1); g.setColor(bgColor.darker()); } this.paintOpaqueTriangle(g, arrowDim.width, arrowDim.height); } else { this.paintTransparentTriangle(g, arrowDim.width, arrowDim.height, component.getBackground()); } // restore original graphics settings g.translate(-x - deltaX, -y - deltaY); g.setColor(oldColor); } // ********** internal methods ********** /** * Return the arrow's dimension(s), as determined by the size of the icon. */ private Dimension getArrowDimension() { if (this.arrowDimension == null) { this.arrowDimension = this.buildArrowDimension(); } return this.arrowDimension; } /** * Build the arrow's dimension(s), as determined by the size of the icon. */ private Dimension buildArrowDimension() { int w = 0; int h = 0; int iconWidth = this.width; int iconHeight = this.height; int arrowOrientation = this.orientation; if (iconWidth == iconHeight) { if (arrowOrientation == SwingConstants.EAST || arrowOrientation == SwingConstants.WEST) { h = iconHeight; if (h % 2 == 0) { // force 'h' to be an odd number h--; } w = (h + 1) / 2; } else { w = iconWidth; if (w % 2 == 0) { // force 'w' to be an odd number w--; } h = (w + 1) / 2; } } else if (iconWidth < iconHeight) { w = iconWidth; if (arrowOrientation == SwingConstants.NORTH || arrowOrientation == SwingConstants.SOUTH) { if (w % 2 == 0) { // force 'w' to be an odd number w--; } h = (w + 1) / 2; } else { h = (w * 2) - 1; } } else { // (iconWidth > iconHeight) h = iconHeight; if (arrowOrientation == SwingConstants.EAST || arrowOrientation == SwingConstants.WEST) { if (h % 2 == 0) { // force 'h' to be an odd number h--; } w = (h + 1) / 2; } else { w = (h * 2) - 1; } } return new Dimension(w, h); } private void paintTransparentTriangle(Graphics g, int triangleWidth, int triangleHeight, Color triangleColor) { Color brighter = triangleColor.brighter(); Color darker = triangleColor.darker(); int arrowOrientation = this.orientation; triangleWidth--; triangleHeight--; if (arrowOrientation == SwingConstants.EAST) { g.setColor(brighter); g.drawLine(0, triangleHeight, triangleWidth, triangleHeight / 2); // draw / g.setColor(darker); g.drawLine(0, 0, triangleWidth, triangleHeight / 2); // draw \ g.drawLine(0, 0, 0, triangleHeight); // draw | } else if (arrowOrientation == SwingConstants.WEST) { g.setColor(brighter); g.drawLine(0, triangleHeight / 2, triangleWidth, triangleHeight); // draw \ g.drawLine(triangleWidth, 0, triangleWidth, triangleHeight); // draw | g.setColor(darker); g.drawLine(triangleWidth, 0, 0, triangleHeight / 2); // draw / } else if (arrowOrientation == SwingConstants.NORTH) { g.setColor(darker); g.drawLine(0, triangleHeight, triangleWidth / 2, 0); // draw / g.drawLine(0, triangleHeight, triangleWidth, triangleHeight); // draw _ g.setColor(brighter); g.drawLine(triangleWidth / 2, 0, triangleWidth, triangleHeight); // draw \ } else if (arrowOrientation == SwingConstants.SOUTH) { g.setColor(brighter); g.drawLine(triangleWidth / 2, triangleHeight, triangleWidth, 0); // draw / g.setColor(darker); g.drawLine(0, 0, triangleWidth, 0); // draw - g.drawLine(0, 0, triangleWidth / 2, triangleHeight); // draw \ } } private void paintOpaqueTriangle(Graphics g, int triangleWidth, int triangleHeight) { int arrowOrientation = this.orientation; if (arrowOrientation == SwingConstants.EAST) { int maxY = triangleHeight - 1; for (int i = 0; i < triangleWidth; i++) { g.drawLine(i, i, i, maxY - i); } } else if (arrowOrientation == SwingConstants.WEST) { int maxY = triangleWidth - 1; triangleHeight--; for (int i = triangleWidth; i-- > 0; ) { int y = maxY - i; g.drawLine(i, y, i, triangleHeight - y); } } else if (arrowOrientation == SwingConstants.NORTH) { int maxX = triangleHeight - 1; triangleWidth--; for (int i = triangleHeight; i-- > 0; ) { int x = maxX - i; g.drawLine(x, i, triangleWidth - x, i); } } else if (arrowOrientation == SwingConstants.SOUTH) { int maxX = triangleWidth - 1; for (int i = 0; i < triangleHeight; i++) { g.drawLine(i, i, maxX - i, i); } } } // ********** accessibility support ********** private class AccessibleArrowIcon extends AccessibleContext implements AccessibleIcon, Serializable { public AccessibleRole getAccessibleRole() { return AccessibleRole.ICON; } public AccessibleStateSet getAccessibleStateSet() { return null; } public Accessible getAccessibleParent() { return null; } public int getAccessibleIndexInParent() { return -1; } public int getAccessibleChildrenCount() { return 0; } public Accessible getAccessibleChild(int index) { return null; } public Locale getLocale() throws IllegalComponentStateException { return null; } public String getAccessibleIconDescription() { return ArrowIcon.this.getDescription(); } public void setAccessibleIconDescription(String description) { ArrowIcon.this.setDescription(description); } public int getAccessibleIconHeight() { return ArrowIcon.this.getIconHeight(); } public int getAccessibleIconWidth() { return ArrowIcon.this.getIconWidth(); } private void readObject(ObjectInputStream s) throws ClassNotFoundException, IOException { s.defaultReadObject(); } private void writeObject(ObjectOutputStream s) throws IOException { s.defaultWriteObject(); } } }