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 }