001 /* 002 * $Id: PaintUtils.java 3352 2009-05-25 16:37:52Z kschaefe $ 003 * 004 * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, 005 * Santa Clara, California 95054, U.S.A. All rights reserved. 006 * 007 * This library is free software; you can redistribute it and/or 008 * modify it under the terms of the GNU Lesser General Public 009 * License as published by the Free Software Foundation; either 010 * version 2.1 of the License, or (at your option) any later version. 011 * 012 * This library is distributed in the hope that it will be useful, 013 * but WITHOUT ANY WARRANTY; without even the implied warranty of 014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 015 * Lesser General Public License for more details. 016 * 017 * You should have received a copy of the GNU Lesser General Public 018 * License along with this library; if not, write to the Free Software 019 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 020 */ 021 022 package org.jdesktop.swingx.util; 023 024 import java.awt.Color; 025 import java.awt.FontMetrics; 026 import java.awt.GradientPaint; 027 import java.awt.Graphics; 028 import java.awt.Graphics2D; 029 import java.awt.Paint; 030 import java.awt.Rectangle; 031 import java.awt.geom.Point2D; 032 import java.awt.geom.Rectangle2D; 033 import java.lang.reflect.Constructor; 034 import java.lang.reflect.InvocationTargetException; 035 import java.lang.reflect.Method; 036 037 import javax.swing.JComponent; 038 import javax.swing.JLabel; 039 import javax.swing.SwingConstants; 040 041 /** 042 * A collection of utilties for painting visual effects. 043 * 044 * @author Mark Davidson 045 */ 046 public class PaintUtils { 047 public static final GradientPaint BLUE_EXPERIENCE = new GradientPaint( 048 new Point2D.Double(0, 0), 049 new Color(168, 204, 241), 050 new Point2D.Double(0, 1), 051 new Color(44, 61, 146)); 052 public static final GradientPaint MAC_OSX_SELECTED = new GradientPaint( 053 new Point2D.Double(0, 0), 054 new Color(81, 141, 236), 055 new Point2D.Double(0, 1), 056 new Color(36, 96, 192)); 057 public static final GradientPaint MAC_OSX = new GradientPaint( 058 new Point2D.Double(0, 0), 059 new Color(167, 210, 250), 060 new Point2D.Double(0, 1), 061 new Color(99, 147, 206)); 062 public static final GradientPaint AERITH = new GradientPaint( 063 new Point2D.Double(0, 0), 064 Color.WHITE, 065 new Point2D.Double(0, 1), 066 new Color(64, 110, 161)); 067 public static final GradientPaint GRAY = new GradientPaint( 068 new Point2D.Double(0, 0), 069 new Color(226, 226, 226), 070 new Point2D.Double(0, 1), 071 new Color(250, 248, 248)); 072 public static final GradientPaint RED_XP = new GradientPaint( 073 new Point2D.Double(0, 0), 074 new Color(236, 81, 81), 075 new Point2D.Double(0, 1), 076 new Color(192, 36, 36)); 077 public static final GradientPaint NIGHT_GRAY = new GradientPaint( 078 new Point2D.Double(0, 0), 079 new Color(102, 111, 127), 080 new Point2D.Double(0, 1), 081 new Color(38, 45, 61)); 082 public static final GradientPaint NIGHT_GRAY_LIGHT = new GradientPaint( 083 new Point2D.Double(0, 0), 084 new Color(129, 138, 155), 085 new Point2D.Double(0, 1), 086 new Color(58, 66, 82)); 087 088 089 private PaintUtils() { 090 } 091 092 /** 093 * Returns the bounds that the text of a label will be drawn into. 094 * Takes into account the current font metrics. 095 */ 096 public static Rectangle getTextBounds(Graphics g, JLabel label) { 097 FontMetrics fm = g.getFontMetrics(); 098 Rectangle2D r2d = fm.getStringBounds(label.getText(), g); 099 Rectangle rect = r2d.getBounds(); 100 int xOffset = 0; 101 switch (label.getHorizontalAlignment()) { 102 case SwingConstants.RIGHT: 103 case SwingConstants.TRAILING: 104 xOffset = label.getBounds().width - rect.width; 105 break; 106 case SwingConstants.CENTER: 107 xOffset = (label.getBounds().width - rect.width) / 2; 108 break; 109 default: 110 case SwingConstants.LEFT: 111 case SwingConstants.LEADING: 112 xOffset = 0; 113 break; 114 } 115 int yOffset = 0; 116 switch (label.getVerticalAlignment()) { 117 case SwingConstants.TOP: 118 yOffset = 0; 119 break; 120 case SwingConstants.CENTER: 121 yOffset = (label.getBounds().height - rect.height) / 2; 122 break; 123 case SwingConstants.BOTTOM: 124 yOffset = label.getBounds().height - rect.height; 125 break; 126 } 127 return new Rectangle(xOffset, yOffset, rect.width, rect.height); 128 } 129 130 /** 131 * Paints a top to bottom gradient fill over the component bounds 132 * from color1 to color2. 133 */ 134 public static void paintGradient(Graphics g, JComponent comp, 135 Color color1, Color color2) { 136 GradientPaint paint = new GradientPaint(0, 0, color1, 137 0, comp.getHeight(), color2, 138 true); 139 Graphics2D g2 = (Graphics2D) g; 140 Paint oldPaint = g2.getPaint(); 141 g2.setPaint(paint); 142 g2.fillRect(0, 0, comp.getWidth(), comp.getHeight()); 143 g2.setPaint(oldPaint); 144 } 145 146 /** Resizes a gradient to fill the width and height available. If the 147 * gradient is left to right it will be resized to fill the entire width. 148 * If the gradient is top to bottom it will be resized to fill the entire 149 * height. If the gradient is on an angle it will be resized to go from 150 * one corner to the other of the rectangle formed by (0,0 -> width,height). 151 * 152 * This method can resize java.awt.GradientPaint, java.awt.LinearGradientPaint, 153 * and the LinearGradientPaint implementation from Apache's Batik project. Note, 154 * this method does not require the MultipleGradientPaint.jar from Apache to 155 * compile or to run. MultipleGradientPaint.jar *is* required if you want 156 * to resize the LinearGradientPaint from that jar. 157 * 158 * Any paint passed into this method which is not a kind of gradient paint (like 159 * a Color or TexturePaint) will be returned unmodified. It will not throw 160 * an exception. If the gradient cannot be resized due to other errors the 161 * original paint will be returned unmodified. It will not throw an 162 * exception. 163 * 164 */ 165 public static Paint resizeGradient(Paint p, int width, int height) { 166 if(p == null) return p; 167 168 if(p instanceof GradientPaint) { 169 GradientPaint gp = (GradientPaint)p; 170 Point2D[] pts = new Point2D[2]; 171 pts[0] = gp.getPoint1(); 172 pts[1] = gp.getPoint2(); 173 pts = adjustPoints(pts, width, height); 174 return new GradientPaint(pts[0], gp.getColor1(), pts[1], gp.getColor2(), gp.isCyclic()); 175 } 176 177 if("java.awt.LinearGradientPaint".equals(p.getClass().getName()) || 178 "org.apache.batik.ext.awt.LinearGradientPaint".equals(p.getClass().getName())) { 179 return resizeLinearGradient(p,width,height); 180 } 181 return p; 182 } 183 184 185 private static Paint resizeLinearGradient(Paint p, int width, int height) { 186 try { 187 Point2D[] pts = new Point2D[2]; 188 pts[0] = (Point2D) invokeMethod(p,"getStartPoint"); 189 pts[1] = (Point2D) invokeMethod(p,"getEndPoint"); 190 pts = adjustPoints(pts, width, height); 191 float[] fractions = (float[]) invokeMethod(p,"getFractions"); 192 Color[] colors = (Color[]) invokeMethod(p,"getColors"); 193 194 Constructor<?> con = p.getClass().getDeclaredConstructor( 195 Point2D.class, Point2D.class, 196 new float[0].getClass(), 197 new Color[0].getClass()); 198 return (Paint) con.newInstance(pts[0],pts[1],fractions, colors); 199 } catch (Exception ex) { 200 ex.printStackTrace(); 201 } 202 return p; 203 } 204 205 private static Object invokeMethod(final Object p, final String methodName) 206 throws NoSuchMethodException, InvocationTargetException, IllegalArgumentException, SecurityException, IllegalAccessException { 207 Method meth = p.getClass().getMethod(methodName); 208 return meth.invoke(p); 209 } 210 211 212 private static Point2D[] adjustPoints(Point2D[] pts, int width, int height) { 213 Point2D start = pts[0]; 214 Point2D end = pts[1]; 215 216 double angle = calcAngle(start,end); 217 double a2 = Math.toDegrees(angle); 218 double e = 1; 219 220 // if it is near 0 degrees 221 if(Math.abs(angle) < Math.toRadians(e) || 222 Math.abs(angle) > Math.toRadians(360-e)) { 223 start = new Point2D.Float(0,0); 224 end = new Point2D.Float(width,0); 225 } 226 227 // near 45 228 if(isNear(a2, 45, e)) { 229 start = new Point2D.Float(0,0); 230 end = new Point2D.Float(width,height); 231 } 232 233 // near 90 234 if(isNear(a2, 90, e)) { 235 start = new Point2D.Float(0,0); 236 end = new Point2D.Float(0,height); 237 } 238 239 // near 135 240 if(isNear(a2, 135, e)) { 241 start = new Point2D.Float(width,0); 242 end = new Point2D.Float(0,height); 243 } 244 245 // near 180 246 if(isNear(a2, 180, e)) { 247 start = new Point2D.Float(width,0); 248 end = new Point2D.Float(0,0); 249 } 250 251 // near 225 252 if(isNear(a2, 225, e)) { 253 start = new Point2D.Float(width,height); 254 end = new Point2D.Float(0,0); 255 } 256 257 // near 270 258 if(isNear(a2, 270, e)) { 259 start = new Point2D.Float(0,height); 260 end = new Point2D.Float(0,0); 261 } 262 263 // near 315 264 if(isNear(a2, 315, e)) { 265 start = new Point2D.Float(0,height); 266 end = new Point2D.Float(width,0); 267 } 268 269 return new Point2D[] { start, end }; 270 } 271 272 private static boolean isNear(double angle, double target, double error) { 273 return Math.abs(target - Math.abs(angle)) < error; 274 } 275 276 private static double calcAngle(Point2D p1, Point2D p2) { 277 double x_off = p2.getX() - p1.getX(); 278 double y_off = p2.getY() - p1.getY(); 279 double angle = Math.atan(y_off / x_off); 280 if (x_off < 0) { 281 angle = angle + Math.PI; 282 } 283 284 if(angle < 0) { angle+= 2*Math.PI; } 285 if(angle > 2*Math.PI) { angle -= 2*Math.PI; } 286 return angle; 287 } 288 /* 289 public static void main(String ... args) { 290 LinearGradientPaint in = new LinearGradientPaint( 291 new Point(0,0), new Point(10,0), 292 new float[] {0f, 0.5f, 1f}, 293 new Color[] {Color.RED, Color.GREEN, Color.BLUE}); 294 log.fine("in = " + toString(in)); 295 Paint out = resizeGradient(in,100,100); 296 log.fine(("out = " + toString((MultipleGradientPaint) out)); 297 }*/ 298 /* 299 private static String toString(MultipleGradientPaint paint) { 300 StringBuffer buffer = new StringBuffer(); 301 buffer.append(paint.getClass().getName()); 302 Color[] colors = paint.getColors(); 303 float[] values = paint.getFractions(); 304 buffer.append("["); 305 for(int i=0; i<colors.length; i++) { 306 buffer.append("#").append(Integer.toHexString(colors[i].getRGB())); 307 buffer.append(":"); 308 buffer.append(values[i]); 309 buffer.append(", "); 310 } 311 buffer.append("]"); 312 if(paint instanceof LinearGradientPaint) { 313 LinearGradientPaint lgp = (LinearGradientPaint) paint; 314 buffer.append(", "); 315 buffer.append(""+lgp.getStartPoint().getX() + ", " + lgp.getStartPoint().getY()); 316 buffer.append("->"); 317 buffer.append(""+lgp.getEndPoint().getX() + ", " + lgp.getEndPoint().getY()); 318 } 319 320 return buffer.toString(); 321 }*/ 322 323 // private static void p(String string) { 324 // log.fine((string); 325 // } 326 327 }