1 /*
2  * Copyright (c) 1998, 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 
26 package javax.swing.plaf.metal;
27 
28 import javax.swing.plaf.*;
29 import javax.swing.*;
30 import java.awt.*;
31 import java.awt.image.*;
32 import java.lang.ref.*;
33 import java.util.*;
34 import sun.swing.CachedPainter;
35 import sun.swing.ImageIconUIResource;
36 
37 /**
38  * This is a dumping ground for random stuff we want to use in several places.
39  *
40  * @author Steve Wilson
41  */
42 
43 class MetalUtils {
44 
drawFlush3DBorder(Graphics g, Rectangle r)45     static void drawFlush3DBorder(Graphics g, Rectangle r) {
46         drawFlush3DBorder(g, r.x, r.y, r.width, r.height);
47     }
48 
49     /**
50       * This draws the "Flush 3D Border" which is used throughout the Metal L&F
51       */
drawFlush3DBorder(Graphics g, int x, int y, int w, int h)52     static void drawFlush3DBorder(Graphics g, int x, int y, int w, int h) {
53         g.translate( x, y);
54         g.setColor( MetalLookAndFeel.getControlDarkShadow() );
55         g.drawRect( 0, 0, w-2, h-2 );
56         g.setColor( MetalLookAndFeel.getControlHighlight() );
57         g.drawRect( 1, 1, w-2, h-2 );
58         g.setColor( MetalLookAndFeel.getControl() );
59         g.drawLine( 0, h-1, 1, h-2 );
60         g.drawLine( w-1, 0, w-2, 1 );
61         g.translate( -x, -y);
62     }
63 
64     /**
65       * This draws a variant "Flush 3D Border"
66       * It is used for things like pressed buttons.
67       */
drawPressed3DBorder(Graphics g, Rectangle r)68     static void drawPressed3DBorder(Graphics g, Rectangle r) {
69         drawPressed3DBorder( g, r.x, r.y, r.width, r.height );
70     }
71 
drawDisabledBorder(Graphics g, int x, int y, int w, int h)72     static void drawDisabledBorder(Graphics g, int x, int y, int w, int h) {
73         g.translate( x, y);
74         g.setColor( MetalLookAndFeel.getControlShadow() );
75         g.drawRect( 0, 0, w-1, h-1 );
76         g.translate(-x, -y);
77     }
78 
79     /**
80       * This draws a variant "Flush 3D Border"
81       * It is used for things like pressed buttons.
82       */
drawPressed3DBorder(Graphics g, int x, int y, int w, int h)83     static void drawPressed3DBorder(Graphics g, int x, int y, int w, int h) {
84         g.translate( x, y);
85 
86         drawFlush3DBorder(g, 0, 0, w, h);
87 
88         g.setColor( MetalLookAndFeel.getControlShadow() );
89         g.drawLine( 1, 1, 1, h-2 );
90         g.drawLine( 1, 1, w-2, 1 );
91         g.translate( -x, -y);
92     }
93 
94     /**
95       * This draws a variant "Flush 3D Border"
96       * It is used for things like active toggle buttons.
97       * This is used rarely.
98       */
drawDark3DBorder(Graphics g, Rectangle r)99     static void drawDark3DBorder(Graphics g, Rectangle r) {
100         drawDark3DBorder(g, r.x, r.y, r.width, r.height);
101     }
102 
103     /**
104       * This draws a variant "Flush 3D Border"
105       * It is used for things like active toggle buttons.
106       * This is used rarely.
107       */
drawDark3DBorder(Graphics g, int x, int y, int w, int h)108     static void drawDark3DBorder(Graphics g, int x, int y, int w, int h) {
109         g.translate( x, y);
110 
111         drawFlush3DBorder(g, 0, 0, w, h);
112 
113         g.setColor( MetalLookAndFeel.getControl() );
114         g.drawLine( 1, 1, 1, h-2 );
115         g.drawLine( 1, 1, w-2, 1 );
116         g.setColor( MetalLookAndFeel.getControlShadow() );
117         g.drawLine( 1, h-2, 1, h-2 );
118         g.drawLine( w-2, 1, w-2, 1 );
119         g.translate( -x, -y);
120     }
121 
drawButtonBorder(Graphics g, int x, int y, int w, int h, boolean active)122     static void drawButtonBorder(Graphics g, int x, int y, int w, int h, boolean active) {
123         if (active) {
124             drawActiveButtonBorder(g, x, y, w, h);
125         } else {
126             drawFlush3DBorder(g, x, y, w, h);
127         }
128     }
129 
drawActiveButtonBorder(Graphics g, int x, int y, int w, int h)130     static void drawActiveButtonBorder(Graphics g, int x, int y, int w, int h) {
131         drawFlush3DBorder(g, x, y, w, h);
132         g.setColor( MetalLookAndFeel.getPrimaryControl() );
133         g.drawLine( x+1, y+1, x+1, h-3 );
134         g.drawLine( x+1, y+1, w-3, x+1 );
135         g.setColor( MetalLookAndFeel.getPrimaryControlDarkShadow() );
136         g.drawLine( x+2, h-2, w-2, h-2 );
137         g.drawLine( w-2, y+2, w-2, h-2 );
138     }
139 
drawDefaultButtonBorder(Graphics g, int x, int y, int w, int h, boolean active)140     static void drawDefaultButtonBorder(Graphics g, int x, int y, int w, int h, boolean active) {
141         drawButtonBorder(g, x+1, y+1, w-1, h-1, active);
142         g.translate(x, y);
143         g.setColor( MetalLookAndFeel.getControlDarkShadow() );
144         g.drawRect( 0, 0, w-3, h-3 );
145         g.drawLine( w-2, 0, w-2, 0);
146         g.drawLine( 0, h-2, 0, h-2);
147         g.translate(-x, -y);
148     }
149 
drawDefaultButtonPressedBorder(Graphics g, int x, int y, int w, int h)150     static void drawDefaultButtonPressedBorder(Graphics g, int x, int y, int w, int h) {
151         drawPressed3DBorder(g, x + 1, y + 1, w - 1, h - 1);
152         g.translate(x, y);
153         g.setColor(MetalLookAndFeel.getControlDarkShadow());
154         g.drawRect(0, 0, w - 3, h - 3);
155         g.drawLine(w - 2, 0, w - 2, 0);
156         g.drawLine(0, h - 2, 0, h - 2);
157         g.setColor(MetalLookAndFeel.getControl());
158         g.drawLine(w - 1, 0, w - 1, 0);
159         g.drawLine(0, h - 1, 0, h - 1);
160         g.translate(-x, -y);
161     }
162 
163     /*
164      * Convenience function for determining ComponentOrientation.  Helps us
165      * avoid having Munge directives throughout the code.
166      */
isLeftToRight( Component c )167     static boolean isLeftToRight( Component c ) {
168         return c.getComponentOrientation().isLeftToRight();
169     }
170 
getInt(Object key, int defaultValue)171     static int getInt(Object key, int defaultValue) {
172         Object value = UIManager.get(key);
173 
174         if (value instanceof Integer) {
175             return ((Integer)value).intValue();
176         }
177         if (value instanceof String) {
178             try {
179                 return Integer.parseInt((String)value);
180             } catch (NumberFormatException nfe) {}
181         }
182         return defaultValue;
183     }
184 
185     //
186     // Ocean specific stuff.
187     //
188     /**
189      * Draws a radial type gradient. The gradient will be drawn vertically if
190      * <code>vertical</code> is true, otherwise horizontally.
191      * The UIManager key consists of five values:
192      * r1 r2 c1 c2 c3. The gradient is broken down into four chunks drawn
193      * in order from the origin.
194      * <ol>
195      * <li>Gradient r1 % of the size from c1 to c2
196      * <li>Rectangle r2 % of the size in c2.
197      * <li>Gradient r1 % of the size from c2 to c1
198      * <li>The remaining size will be filled with a gradient from c1 to c3.
199      * </ol>
200      *
201      * @param c Component rendering to
202      * @param g Graphics to draw to.
203      * @param key UIManager key used to look up gradient values.
204      * @param x X coordinate to draw from
205      * @param y Y coordinate to draw from
206      * @param w Width to draw to
207      * @param h Height to draw to
208      * @param vertical Direction of the gradient
209      * @return true if <code>key</code> exists, otherwise false.
210      */
drawGradient(Component c, Graphics g, String key, int x, int y, int w, int h, boolean vertical)211     static boolean drawGradient(Component c, Graphics g, String key,
212                                 int x, int y, int w, int h, boolean vertical) {
213         @SuppressWarnings("unchecked")
214         java.util.List<?> gradient = (java.util.List)UIManager.get(key);
215         if (gradient == null || !(g instanceof Graphics2D)) {
216             return false;
217         }
218 
219         if (w <= 0 || h <= 0) {
220             return true;
221         }
222 
223         GradientPainter.INSTANCE.paint(
224                 c, (Graphics2D)g, gradient, x, y, w, h, vertical);
225         return true;
226     }
227 
228 
229     private static class GradientPainter extends CachedPainter {
230         /**
231          * Instance used for painting.  This is the only instance that is
232          * ever created.
233          */
234         public static final GradientPainter INSTANCE = new GradientPainter(8);
235 
236         // Size of images to create. For vertical gradients this is the width,
237         // otherwise it's the height.
238         private static final int IMAGE_SIZE = 64;
239 
240         /**
241          * This is the actual width we're painting in, or last painted to.
242          */
243         private int w;
244         /**
245          * This is the actual height we're painting in, or last painted to
246          */
247         private int h;
248 
249 
GradientPainter(int count)250         GradientPainter(int count) {
251             super(count);
252         }
253 
paint(Component c, Graphics2D g, java.util.List<?> gradient, int x, int y, int w, int h, boolean isVertical)254         public void paint(Component c, Graphics2D g,
255                           java.util.List<?> gradient, int x, int y, int w,
256                           int h, boolean isVertical) {
257             int imageWidth;
258             int imageHeight;
259             if (isVertical) {
260                 imageWidth = IMAGE_SIZE;
261                 imageHeight = h;
262             }
263             else {
264                 imageWidth = w;
265                 imageHeight = IMAGE_SIZE;
266             }
267             synchronized(c.getTreeLock()) {
268                 this.w = w;
269                 this.h = h;
270                 paint(c, g, x, y, imageWidth, imageHeight,
271                       gradient, isVertical);
272             }
273         }
274 
paintToImage(Component c, Image image, Graphics g, int w, int h, Object[] args)275         protected void paintToImage(Component c, Image image, Graphics g,
276                                     int w, int h, Object[] args) {
277             Graphics2D g2 = (Graphics2D)g;
278             @SuppressWarnings("unchecked")
279             java.util.List<?> gradient = (java.util.List)args[0];
280             boolean isVertical = ((Boolean)args[1]).booleanValue();
281             // Render to the VolatileImage
282             if (isVertical) {
283                 drawVerticalGradient(g2,
284                                      ((Number)gradient.get(0)).floatValue(),
285                                      ((Number)gradient.get(1)).floatValue(),
286                                      (Color)gradient.get(2),
287                                      (Color)gradient.get(3),
288                                      (Color)gradient.get(4), w, h);
289             }
290             else {
291                 drawHorizontalGradient(g2,
292                                       ((Number)gradient.get(0)).floatValue(),
293                                       ((Number)gradient.get(1)).floatValue(),
294                                       (Color)gradient.get(2),
295                                       (Color)gradient.get(3),
296                                       (Color)gradient.get(4), w, h);
297             }
298         }
299 
paintImage(Component c, Graphics g, int x, int y, int imageW, int imageH, Image image, Object[] args)300         protected void paintImage(Component c, Graphics g,
301                                   int x, int y, int imageW, int imageH,
302                                   Image image, Object[] args) {
303             boolean isVertical = ((Boolean)args[1]).booleanValue();
304             // Render to the screen
305             g.translate(x, y);
306             if (isVertical) {
307                 for (int counter = 0; counter < w; counter += IMAGE_SIZE) {
308                     int tileSize = Math.min(IMAGE_SIZE, w - counter);
309                     g.drawImage(image, counter, 0, counter + tileSize, h,
310                                 0, 0, tileSize, h, null);
311                 }
312             }
313             else {
314                 for (int counter = 0; counter < h; counter += IMAGE_SIZE) {
315                     int tileSize = Math.min(IMAGE_SIZE, h - counter);
316                     g.drawImage(image, 0, counter, w, counter + tileSize,
317                                 0, 0, w, tileSize, null);
318                 }
319             }
320             g.translate(-x, -y);
321         }
322 
drawVerticalGradient(Graphics2D g, float ratio1, float ratio2, Color c1,Color c2, Color c3, int w, int h)323         private void drawVerticalGradient(Graphics2D g, float ratio1,
324                                           float ratio2, Color c1,Color c2,
325                                           Color c3, int w, int h) {
326             int mid = (int)(ratio1 * h);
327             int mid2 = (int)(ratio2 * h);
328             if (mid > 0) {
329                 g.setPaint(getGradient((float)0, (float)0, c1, (float)0,
330                                        (float)mid, c2));
331                 g.fillRect(0, 0, w, mid);
332             }
333             if (mid2 > 0) {
334                 g.setColor(c2);
335                 g.fillRect(0, mid, w, mid2);
336             }
337             if (mid > 0) {
338                 g.setPaint(getGradient((float)0, (float)mid + mid2, c2,
339                                        (float)0, (float)mid * 2 + mid2, c1));
340                 g.fillRect(0, mid + mid2, w, mid);
341             }
342             if (h - mid * 2 - mid2 > 0) {
343                 g.setPaint(getGradient((float)0, (float)mid * 2 + mid2, c1,
344                                        (float)0, (float)h, c3));
345                 g.fillRect(0, mid * 2 + mid2, w, h - mid * 2 - mid2);
346             }
347         }
348 
drawHorizontalGradient(Graphics2D g, float ratio1, float ratio2, Color c1,Color c2, Color c3, int w, int h)349         private void drawHorizontalGradient(Graphics2D g, float ratio1,
350                                             float ratio2, Color c1,Color c2,
351                                             Color c3, int w, int h) {
352             int mid = (int)(ratio1 * w);
353             int mid2 = (int)(ratio2 * w);
354             if (mid > 0) {
355                 g.setPaint(getGradient((float)0, (float)0, c1,
356                                        (float)mid, (float)0, c2));
357                 g.fillRect(0, 0, mid, h);
358             }
359             if (mid2 > 0) {
360                 g.setColor(c2);
361                 g.fillRect(mid, 0, mid2, h);
362             }
363             if (mid > 0) {
364                 g.setPaint(getGradient((float)mid + mid2, (float)0, c2,
365                                        (float)mid * 2 + mid2, (float)0, c1));
366                 g.fillRect(mid + mid2, 0, mid, h);
367             }
368             if (w - mid * 2 - mid2 > 0) {
369                 g.setPaint(getGradient((float)mid * 2 + mid2, (float)0, c1,
370                                        w, (float)0, c3));
371                 g.fillRect(mid * 2 + mid2, 0, w - mid * 2 - mid2, h);
372             }
373         }
374 
getGradient(float x1, float y1, Color c1, float x2, float y2, Color c2)375         private GradientPaint getGradient(float x1, float y1,
376                                                  Color c1, float x2, float y2,
377                                                  Color c2) {
378             return new GradientPaint(x1, y1, c1, x2, y2, c2, true);
379         }
380     }
381 
382 
383     /**
384      * Returns true if the specified widget is in a toolbar.
385      */
isToolBarButton(JComponent c)386     static boolean isToolBarButton(JComponent c) {
387         return (c.getParent() instanceof JToolBar);
388     }
389 
getOceanToolBarIcon(Image i)390     static Icon getOceanToolBarIcon(Image i) {
391         ImageProducer prod = new FilteredImageSource(i.getSource(),
392                              new OceanToolBarImageFilter());
393         return new ImageIconUIResource(Toolkit.getDefaultToolkit().createImage(prod));
394     }
395 
getOceanDisabledButtonIcon(Image image)396     static Icon getOceanDisabledButtonIcon(Image image) {
397         Object[] range = (Object[])UIManager.get("Button.disabledGrayRange");
398         int min = 180;
399         int max = 215;
400         if (range != null) {
401             min = ((Integer)range[0]).intValue();
402             max = ((Integer)range[1]).intValue();
403         }
404         ImageProducer prod = new FilteredImageSource(image.getSource(),
405                       new OceanDisabledButtonImageFilter(min , max));
406         return new ImageIconUIResource(Toolkit.getDefaultToolkit().createImage(prod));
407     }
408 
409 
410 
411 
412     /**
413      * Used to create a disabled Icon with the ocean look.
414      */
415     private static class OceanDisabledButtonImageFilter extends RGBImageFilter{
416         private float min;
417         private float factor;
418 
OceanDisabledButtonImageFilter(int min, int max)419         OceanDisabledButtonImageFilter(int min, int max) {
420             canFilterIndexColorModel = true;
421             this.min = (float)min;
422             this.factor = (max - min) / 255f;
423         }
424 
filterRGB(int x, int y, int rgb)425         public int filterRGB(int x, int y, int rgb) {
426             // Coefficients are from the sRGB color space:
427             int gray = Math.min(255, (int)(((0.2125f * ((rgb >> 16) & 0xFF)) +
428                     (0.7154f * ((rgb >> 8) & 0xFF)) +
429                     (0.0721f * (rgb & 0xFF)) + .5f) * factor + min));
430 
431             return (rgb & 0xff000000) | (gray << 16) | (gray << 8) |
432                 (gray << 0);
433         }
434     }
435 
436 
437     /**
438      * Used to create the rollover icons with the ocean look.
439      */
440     private static class OceanToolBarImageFilter extends RGBImageFilter {
OceanToolBarImageFilter()441         OceanToolBarImageFilter() {
442             canFilterIndexColorModel = true;
443         }
444 
filterRGB(int x, int y, int rgb)445         public int filterRGB(int x, int y, int rgb) {
446             int r = ((rgb >> 16) & 0xff);
447             int g = ((rgb >> 8) & 0xff);
448             int b = (rgb & 0xff);
449             int gray = Math.max(Math.max(r, g), b);
450             return (rgb & 0xff000000) | (gray << 16) | (gray << 8) |
451                 (gray << 0);
452         }
453     }
454 }
455