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