1 /* 2 * Copyright (c) 1997, 2014, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 package javax.swing.plaf.basic; 26 27 import java.awt.Dimension; 28 import java.awt.Graphics; 29 import java.awt.Color; 30 import java.awt.Graphics2D; 31 import java.awt.geom.AffineTransform; 32 import java.awt.geom.Path2D; 33 34 import javax.swing.*; 35 import javax.swing.plaf.UIResource; 36 import sun.swing.SwingUtilities2; 37 38 /** 39 * JButton object that draws a scaled Arrow in one of the cardinal directions. 40 * <p> 41 * <strong>Warning:</strong> 42 * Serialized objects of this class will not be compatible with 43 * future Swing releases. The current serialization support is 44 * appropriate for short term storage or RMI between applications running 45 * the same version of Swing. As of 1.4, support for long term storage 46 * of all JavaBeans 47 * has been added to the <code>java.beans</code> package. 48 * Please see {@link java.beans.XMLEncoder}. 49 * 50 * @author David Kloba 51 */ 52 @SuppressWarnings("serial") // Same-version serialization only 53 public class BasicArrowButton extends JButton implements SwingConstants 54 { 55 /** 56 * The direction of the arrow. One of 57 * {@code SwingConstants.NORTH}, {@code SwingConstants.SOUTH}, 58 * {@code SwingConstants.EAST} or {@code SwingConstants.WEST}. 59 */ 60 protected int direction; 61 62 private Color shadow; 63 private Color darkShadow; 64 private Color highlight; 65 66 /** 67 * Creates a {@code BasicArrowButton} whose arrow 68 * is drawn in the specified direction and with the specified 69 * colors. 70 * 71 * @param direction the direction of the arrow; one of 72 * {@code SwingConstants.NORTH}, {@code SwingConstants.SOUTH}, 73 * {@code SwingConstants.EAST} or {@code SwingConstants.WEST} 74 * @param background the background color of the button 75 * @param shadow the color of the shadow 76 * @param darkShadow the color of the dark shadow 77 * @param highlight the color of the highlight 78 * @since 1.4 79 */ 80 public BasicArrowButton(int direction, Color background, Color shadow, 81 Color darkShadow, Color highlight) { 82 super(); 83 setRequestFocusEnabled(false); 84 setDirection(direction); 85 setBackground(background); 86 this.shadow = shadow; 87 this.darkShadow = darkShadow; 88 this.highlight = highlight; 89 } 90 91 /** 92 * Creates a {@code BasicArrowButton} whose arrow 93 * is drawn in the specified direction. 94 * 95 * @param direction the direction of the arrow; one of 96 * {@code SwingConstants.NORTH}, {@code SwingConstants.SOUTH}, 97 * {@code SwingConstants.EAST} or {@code SwingConstants.WEST} 98 */ 99 public BasicArrowButton(int direction) { 100 this(direction, UIManager.getColor("control"), UIManager.getColor("controlShadow"), 101 UIManager.getColor("controlDkShadow"), UIManager.getColor("controlLtHighlight")); 102 } 103 104 /** 105 * Returns the direction of the arrow. 106 * 107 * @return the direction of the arrow 108 */ 109 public int getDirection() { 110 return direction; 111 } 112 113 /** 114 * Sets the direction of the arrow. 115 * 116 * @param direction the direction of the arrow; one of 117 * of {@code SwingConstants.NORTH}, 118 * {@code SwingConstants.SOUTH}, 119 * {@code SwingConstants.EAST} or {@code SwingConstants.WEST} 120 */ 121 public void setDirection(int direction) { 122 this.direction = direction; 123 } 124 125 public void paint(Graphics g) { 126 Color origColor; 127 boolean isPressed, isEnabled; 128 int w, h, size; 129 130 w = getSize().width; 131 h = getSize().height; 132 origColor = g.getColor(); 133 isPressed = getModel().isPressed(); 134 isEnabled = isEnabled(); 135 136 g.setColor(getBackground()); 137 g.fillRect(1, 1, w-2, h-2); 138 139 /// Draw the proper Border 140 if (getBorder() != null && !(getBorder() instanceof UIResource)) { 141 paintBorder(g); 142 } else if (isPressed) { 143 g.setColor(shadow); 144 g.drawRect(0, 0, w-1, h-1); 145 } else { 146 // Using the background color set above 147 g.drawLine(0, 0, 0, h-1); 148 g.drawLine(1, 0, w-2, 0); 149 150 g.setColor(highlight); // inner 3D border 151 g.drawLine(1, 1, 1, h-3); 152 g.drawLine(2, 1, w-3, 1); 153 154 g.setColor(shadow); // inner 3D border 155 g.drawLine(1, h-2, w-2, h-2); 156 g.drawLine(w-2, 1, w-2, h-3); 157 158 g.setColor(darkShadow); // black drop shadow __| 159 g.drawLine(0, h-1, w-1, h-1); 160 g.drawLine(w-1, h-1, w-1, 0); 161 } 162 163 // If there's no room to draw arrow, bail 164 if(h < 5 || w < 5) { 165 g.setColor(origColor); 166 return; 167 } 168 169 if (isPressed) { 170 g.translate(1, 1); 171 } 172 173 // Draw the arrow 174 size = Math.min((h - 4) / 3, (w - 4) / 3); 175 size = Math.max(size, 2); 176 paintTriangle(g, (w - size) / 2, (h - size) / 2, 177 size, direction, isEnabled); 178 179 // Reset the Graphics back to it's original settings 180 if (isPressed) { 181 g.translate(-1, -1); 182 } 183 g.setColor(origColor); 184 185 } 186 187 /** 188 * Returns the preferred size of the {@code BasicArrowButton}. 189 * 190 * @return the preferred size 191 */ 192 public Dimension getPreferredSize() { 193 return new Dimension(16, 16); 194 } 195 196 /** 197 * Returns the minimum size of the {@code BasicArrowButton}. 198 * 199 * @return the minimum size 200 */ 201 public Dimension getMinimumSize() { 202 return new Dimension(5, 5); 203 } 204 205 /** 206 * Returns the maximum size of the {@code BasicArrowButton}. 207 * 208 * @return the maximum size 209 */ 210 public Dimension getMaximumSize() { 211 return new Dimension(Integer.MAX_VALUE, Integer.MAX_VALUE); 212 } 213 214 /** 215 * Returns whether the arrow button should get the focus. 216 * {@code BasicArrowButton}s are used as a child component of 217 * composite components such as {@code JScrollBar} and 218 * {@code JComboBox}. Since the composite component typically gets the 219 * focus, this method is overriden to return {@code false}. 220 * 221 * @return {@code false} 222 */ 223 @SuppressWarnings("deprecation") 224 public boolean isFocusTraversable() { 225 return false; 226 } 227 228 /** 229 * Paints a triangle. 230 * 231 * @param g the {@code Graphics} to draw to 232 * @param x the x coordinate 233 * @param y the y coordinate 234 * @param size the size of the triangle to draw 235 * @param direction the direction in which to draw the arrow; 236 * one of {@code SwingConstants.NORTH}, 237 * {@code SwingConstants.SOUTH}, {@code SwingConstants.EAST} or 238 * {@code SwingConstants.WEST} 239 * @param isEnabled whether or not the arrow is drawn enabled 240 */ 241 public void paintTriangle(Graphics g, int x, int y, int size, 242 int direction, boolean isEnabled) { 243 if (SwingUtilities2.isScaledGraphics(g)) { 244 paintScaledTriangle(g, x, y, size, direction, isEnabled); 245 } else { 246 paintUnscaledTriangle(g, x, y, size, direction, isEnabled); 247 } 248 } 249 250 private void paintUnscaledTriangle(Graphics g, int x, int y, int size, 251 int direction, boolean isEnabled) 252 { 253 Color oldColor = g.getColor(); 254 int mid, i, j; 255 256 j = 0; 257 size = Math.max(size, 2); 258 mid = (size / 2) - 1; 259 260 g.translate(x, y); 261 if(isEnabled) 262 g.setColor(darkShadow); 263 else 264 g.setColor(shadow); 265 266 switch(direction) { 267 case NORTH: 268 for(i = 0; i < size; i++) { 269 g.drawLine(mid-i, i, mid+i, i); 270 } 271 if(!isEnabled) { 272 g.setColor(highlight); 273 g.drawLine(mid-i+2, i, mid+i, i); 274 } 275 break; 276 case SOUTH: 277 if(!isEnabled) { 278 g.translate(1, 1); 279 g.setColor(highlight); 280 for(i = size-1; i >= 0; i--) { 281 g.drawLine(mid-i, j, mid+i, j); 282 j++; 283 } 284 g.translate(-1, -1); 285 g.setColor(shadow); 286 } 287 288 j = 0; 289 for(i = size-1; i >= 0; i--) { 290 g.drawLine(mid-i, j, mid+i, j); 291 j++; 292 } 293 break; 294 case WEST: 295 for(i = 0; i < size; i++) { 296 g.drawLine(i, mid-i, i, mid+i); 297 } 298 if(!isEnabled) { 299 g.setColor(highlight); 300 g.drawLine(i, mid-i+2, i, mid+i); 301 } 302 break; 303 case EAST: 304 if(!isEnabled) { 305 g.translate(1, 1); 306 g.setColor(highlight); 307 for(i = size-1; i >= 0; i--) { 308 g.drawLine(j, mid-i, j, mid+i); 309 j++; 310 } 311 g.translate(-1, -1); 312 g.setColor(shadow); 313 } 314 315 j = 0; 316 for(i = size-1; i >= 0; i--) { 317 g.drawLine(j, mid-i, j, mid+i); 318 j++; 319 } 320 break; 321 } 322 g.translate(-x, -y); 323 g.setColor(oldColor); 324 } 325 326 private void paintScaledTriangle(Graphics g, double x, double y, double size, 327 int direction, boolean isEnabled) { 328 size = Math.max(size, 2); 329 Path2D.Double path = new Path2D.Double(); 330 path.moveTo(-size, size / 2); 331 path.lineTo(size, size / 2); 332 path.lineTo(0, -size / 2); 333 path.closePath(); 334 AffineTransform affineTransform = new AffineTransform(); 335 affineTransform.rotate(Math.PI * (direction - 1) / 4); 336 path.transform(affineTransform); 337 338 Graphics2D g2d = (Graphics2D) g; 339 double tx = x + size / 2; 340 double ty = y + size / 2; 341 g2d.translate(tx, ty); 342 Color oldColor = g.getColor(); 343 if (!isEnabled) { 344 g2d.translate(1, 0); 345 g2d.setColor(highlight); 346 g2d.fill(path); 347 g2d.translate(-1, 0); 348 } 349 g2d.setColor(isEnabled ? darkShadow : shadow); 350 g2d.fill(path); 351 g2d.translate(-tx, -ty); 352 g2d.setColor(oldColor); 353 } 354 }