1 /* 2 * Copyright (c) 2008, 2009, 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 com.sun.awt; 27 28 import java.awt.*; 29 30 import sun.awt.AWTAccessor; 31 import sun.awt.SunToolkit; 32 33 /** 34 * A collection of utility methods for AWT. 35 * 36 * The functionality provided by the static methods of the class includes: 37 * <ul> 38 * <li>Setting shapes on top-level windows 39 * <li>Setting a constant alpha value for each pixel of a top-level window 40 * <li>Making a window non-opaque, after that it paints only explicitly 41 * painted pixels on the screen, with arbitrary alpha values for every pixel. 42 * <li>Setting a 'mixing-cutout' shape for a component. 43 * </ul> 44 * <p> 45 * A "top-level window" is an instance of the {@code Window} class (or its 46 * descendant, such as {@code JFrame}). 47 * <p> 48 * Some of the mentioned features may not be supported by the native platform. 49 * To determine whether a particular feature is supported, the user must use 50 * the {@code isTranslucencySupported()} method of the class passing a desired 51 * translucency kind (a member of the {@code Translucency} enum) as an 52 * argument. 53 * <p> 54 * The per-pixel alpha feature also requires the user to create her/his 55 * windows using a translucency-capable graphics configuration. 56 * The {@code isTranslucencyCapable()} method must 57 * be used to verify whether any given GraphicsConfiguration supports 58 * the translucency effects. 59 * <p> 60 * <b>WARNING</b>: This class is an implementation detail and only meant 61 * for limited use outside of the core platform. This API may change 62 * drastically between update release, and it may even be 63 * removed or be moved in some other package(s)/class(es). 64 */ 65 public final class AWTUtilities { 66 67 /** 68 * The AWTUtilities class should not be instantiated 69 */ 70 private AWTUtilities() { 71 } 72 73 /** Kinds of translucency supported by the underlying system. 74 * @see #isTranslucencySupported 75 */ 76 public static enum Translucency { 77 /** 78 * Represents support in the underlying system for windows each pixel 79 * of which is guaranteed to be either completely opaque, with 80 * an alpha value of 1.0, or completely transparent, with an alpha 81 * value of 0.0. 82 */ 83 PERPIXEL_TRANSPARENT, 84 85 /** 86 * Represents support in the underlying system for windows all of 87 * the pixels of which have the same alpha value between or including 88 * 0.0 and 1.0. 89 */ 90 TRANSLUCENT, 91 92 /** 93 * Represents support in the underlying system for windows that 94 * contain or might contain pixels with arbitrary alpha values 95 * between and including 0.0 and 1.0. 96 */ 97 PERPIXEL_TRANSLUCENT; 98 } 99 100 101 /** 102 * Returns whether the given level of translucency is supported by 103 * the underlying system. 104 * 105 * Note that this method may sometimes return the value 106 * indicating that the particular level is supported, but 107 * the native windowing system may still not support the 108 * given level of translucency (due to the bugs in 109 * the windowing system). 110 * 111 * @param translucencyKind a kind of translucency support 112 * (either PERPIXEL_TRANSPARENT, 113 * TRANSLUCENT, or PERPIXEL_TRANSLUCENT) 114 * @return whether the given translucency kind is supported 115 */ 116 public static boolean isTranslucencySupported(Translucency translucencyKind) { 117 switch (translucencyKind) { 118 case PERPIXEL_TRANSPARENT: 119 return isWindowShapingSupported(); 120 case TRANSLUCENT: 121 return isWindowOpacitySupported(); 122 case PERPIXEL_TRANSLUCENT: 123 return isWindowTranslucencySupported(); 124 } 125 return false; 126 } 127 128 129 /** 130 * Returns whether the windowing system supports changing the opacity 131 * value of top-level windows. 132 * Note that this method may sometimes return true, but the native 133 * windowing system may still not support the concept of 134 * translucency (due to the bugs in the windowing system). 135 */ 136 private static boolean isWindowOpacitySupported() { 137 Toolkit curToolkit = Toolkit.getDefaultToolkit(); 138 if (!(curToolkit instanceof SunToolkit)) { 139 return false; 140 } 141 return ((SunToolkit)curToolkit).isWindowOpacitySupported(); 142 } 143 144 /** 145 * Set the opacity of the window. The opacity is at the range [0..1]. 146 * Note that setting the opacity level of 0 may or may not disable 147 * the mouse event handling on this window. This is 148 * a platform-dependent behavior. 149 * 150 * In order for this method to enable the translucency effect, 151 * the isTranslucencySupported() method should indicate that the 152 * TRANSLUCENT level of translucency is supported. 153 * 154 * <p>Also note that the window must not be in the full-screen mode 155 * when setting the opacity value < 1.0f. Otherwise 156 * the IllegalArgumentException is thrown. 157 * 158 * @param window the window to set the opacity level to 159 * @param opacity the opacity level to set to the window 160 * @throws NullPointerException if the window argument is null 161 * @throws IllegalArgumentException if the opacity is out of 162 * the range [0..1] 163 * @throws IllegalArgumentException if the window is in full screen mode, 164 * and the opacity is less than 1.0f 165 * @throws UnsupportedOperationException if the TRANSLUCENT translucency 166 * kind is not supported 167 */ 168 public static void setWindowOpacity(Window window, float opacity) { 169 if (window == null) { 170 throw new NullPointerException( 171 "The window argument should not be null."); 172 } 173 174 AWTAccessor.getWindowAccessor().setOpacity(window, opacity); 175 } 176 177 /** 178 * Get the opacity of the window. If the opacity has not 179 * yet being set, this method returns 1.0. 180 * 181 * @param window the window to get the opacity level from 182 * @throws NullPointerException if the window argument is null 183 */ 184 public static float getWindowOpacity(Window window) { 185 if (window == null) { 186 throw new NullPointerException( 187 "The window argument should not be null."); 188 } 189 190 return AWTAccessor.getWindowAccessor().getOpacity(window); 191 } 192 193 /** 194 * Returns whether the windowing system supports changing the shape 195 * of top-level windows. 196 * Note that this method may sometimes return true, but the native 197 * windowing system may still not support the concept of 198 * shaping (due to the bugs in the windowing system). 199 */ 200 public static boolean isWindowShapingSupported() { 201 Toolkit curToolkit = Toolkit.getDefaultToolkit(); 202 if (!(curToolkit instanceof SunToolkit)) { 203 return false; 204 } 205 return ((SunToolkit)curToolkit).isWindowShapingSupported(); 206 } 207 208 /** 209 * Returns an object that implements the Shape interface and represents 210 * the shape previously set with the call to the setWindowShape() method. 211 * If no shape has been set yet, or the shape has been reset to null, 212 * this method returns null. 213 * 214 * @param window the window to get the shape from 215 * @return the current shape of the window 216 * @throws NullPointerException if the window argument is null 217 */ 218 public static Shape getWindowShape(Window window) { 219 if (window == null) { 220 throw new NullPointerException( 221 "The window argument should not be null."); 222 } 223 return AWTAccessor.getWindowAccessor().getShape(window); 224 } 225 226 /** 227 * Sets a shape for the given window. 228 * If the shape argument is null, this methods restores 229 * the default shape making the window rectangular. 230 * <p>Note that in order to set a shape, the window must be undecorated. 231 * If the window is decorated, this method ignores the {@code shape} 232 * argument and resets the shape to null. 233 * <p>Also note that the window must not be in the full-screen mode 234 * when setting a non-null shape. Otherwise the IllegalArgumentException 235 * is thrown. 236 * <p>Depending on the platform, the method may return without 237 * effecting the shape of the window if the window has a non-null warning 238 * string ({@link Window#getWarningString()}). In this case the passed 239 * shape object is ignored. 240 * 241 * @param window the window to set the shape to 242 * @param shape the shape to set to the window 243 * @throws NullPointerException if the window argument is null 244 * @throws IllegalArgumentException if the window is in full screen mode, 245 * and the shape is not null 246 * @throws UnsupportedOperationException if the PERPIXEL_TRANSPARENT 247 * translucency kind is not supported 248 */ 249 public static void setWindowShape(Window window, Shape shape) { 250 if (window == null) { 251 throw new NullPointerException( 252 "The window argument should not be null."); 253 } 254 AWTAccessor.getWindowAccessor().setShape(window, shape); 255 } 256 257 private static boolean isWindowTranslucencySupported() { 258 /* 259 * Per-pixel alpha is supported if all the conditions are TRUE: 260 * 1. The toolkit is a sort of SunToolkit 261 * 2. The toolkit supports translucency in general 262 * (isWindowTranslucencySupported()) 263 * 3. There's at least one translucency-capable 264 * GraphicsConfiguration 265 */ 266 267 Toolkit curToolkit = Toolkit.getDefaultToolkit(); 268 if (!(curToolkit instanceof SunToolkit)) { 269 return false; 270 } 271 272 if (!((SunToolkit)curToolkit).isWindowTranslucencySupported()) { 273 return false; 274 } 275 276 GraphicsEnvironment env = 277 GraphicsEnvironment.getLocalGraphicsEnvironment(); 278 279 // If the default GC supports translucency return true. 280 // It is important to optimize the verification this way, 281 // see CR 6661196 for more details. 282 if (isTranslucencyCapable(env.getDefaultScreenDevice() 283 .getDefaultConfiguration())) 284 { 285 return true; 286 } 287 288 // ... otherwise iterate through all the GCs. 289 GraphicsDevice[] devices = env.getScreenDevices(); 290 291 for (int i = 0; i < devices.length; i++) { 292 GraphicsConfiguration[] configs = devices[i].getConfigurations(); 293 for (int j = 0; j < configs.length; j++) { 294 if (isTranslucencyCapable(configs[j])) { 295 return true; 296 } 297 } 298 } 299 300 return false; 301 } 302 303 /** 304 * Enables the per-pixel alpha support for the given window. 305 * Once the window becomes non-opaque (the isOpaque is set to false), 306 * the drawing sub-system is starting to respect the alpha value of each 307 * separate pixel. If a pixel gets painted with alpha color component 308 * equal to zero, it becomes visually transparent, if the alpha of the 309 * pixel is equal to 255, the pixel is fully opaque. Interim values 310 * of the alpha color component make the pixel semi-transparent (i.e. 311 * translucent). 312 * <p>Note that in order for the window to support the per-pixel alpha 313 * mode, the window must be created using the GraphicsConfiguration 314 * for which the {@link #isTranslucencyCapable} 315 * method returns true. 316 * <p>Also note that some native systems enable the per-pixel translucency 317 * mode for any window created using the translucency-compatible 318 * graphics configuration. However, it is highly recommended to always 319 * invoke the setWindowOpaque() method for these windows, at least for 320 * the sake of cross-platform compatibility reasons. 321 * <p>Also note that the window must not be in the full-screen mode 322 * when making it non-opaque. Otherwise the IllegalArgumentException 323 * is thrown. 324 * <p>If the window is a {@code Frame} or a {@code Dialog}, the window must 325 * be undecorated prior to enabling the per-pixel translucency effect (see 326 * {@link Frame#setUndecorated()} and/or {@link Dialog#setUndecorated()}). 327 * If the window becomes decorated through a subsequent call to the 328 * corresponding {@code setUndecorated()} method, the per-pixel 329 * translucency effect will be disabled and the opaque property reset to 330 * {@code true}. 331 * <p>Depending on the platform, the method may return without 332 * effecting the opaque property of the window if the window has a non-null 333 * warning string ({@link Window#getWarningString()}). In this case 334 * the passed 'isOpaque' value is ignored. 335 * 336 * @param window the window to set the shape to 337 * @param isOpaque whether the window must be opaque (true), 338 * or translucent (false) 339 * @throws NullPointerException if the window argument is null 340 * @throws IllegalArgumentException if the window uses 341 * a GraphicsConfiguration for which the 342 * {@code isTranslucencyCapable()} 343 * method returns false 344 * @throws IllegalArgumentException if the window is in full screen mode, 345 * and the isOpaque is false 346 * @throws IllegalArgumentException if the window is decorated and the 347 * isOpaque argument is {@code false}. 348 * @throws UnsupportedOperationException if the PERPIXEL_TRANSLUCENT 349 * translucency kind is not supported 350 */ 351 public static void setWindowOpaque(Window window, boolean isOpaque) { 352 if (window == null) { 353 throw new NullPointerException( 354 "The window argument should not be null."); 355 } 356 if (!isOpaque && !isTranslucencySupported(Translucency.PERPIXEL_TRANSLUCENT)) { 357 throw new UnsupportedOperationException( 358 "The PERPIXEL_TRANSLUCENT translucency kind is not supported"); 359 } 360 AWTAccessor.getWindowAccessor().setOpaque(window, isOpaque); 361 } 362 363 /** 364 * Returns whether the window is opaque or translucent. 365 * 366 * @param window the window to set the shape to 367 * @return whether the window is currently opaque (true) 368 * or translucent (false) 369 * @throws NullPointerException if the window argument is null 370 */ 371 public static boolean isWindowOpaque(Window window) { 372 if (window == null) { 373 throw new NullPointerException( 374 "The window argument should not be null."); 375 } 376 377 return window.isOpaque(); 378 } 379 380 /** 381 * Verifies whether a given GraphicsConfiguration supports 382 * the PERPIXEL_TRANSLUCENT kind of translucency. 383 * All windows that are intended to be used with the {@link #setWindowOpaque} 384 * method must be created using a GraphicsConfiguration for which this method 385 * returns true. 386 * <p>Note that some native systems enable the per-pixel translucency 387 * mode for any window created using a translucency-capable 388 * graphics configuration. However, it is highly recommended to always 389 * invoke the setWindowOpaque() method for these windows, at least 390 * for the sake of cross-platform compatibility reasons. 391 * 392 * @param gc GraphicsConfiguration 393 * @throws NullPointerException if the gc argument is null 394 * @return whether the given GraphicsConfiguration supports 395 * the translucency effects. 396 */ 397 public static boolean isTranslucencyCapable(GraphicsConfiguration gc) { 398 if (gc == null) { 399 throw new NullPointerException("The gc argument should not be null"); 400 } 401 /* 402 return gc.isTranslucencyCapable(); 403 */ 404 Toolkit curToolkit = Toolkit.getDefaultToolkit(); 405 if (!(curToolkit instanceof SunToolkit)) { 406 return false; 407 } 408 return ((SunToolkit)curToolkit).isTranslucencyCapable(gc); 409 } 410 411 /** 412 * Sets a 'mixing-cutout' shape for the given component. 413 * 414 * By default a lightweight component is treated as an opaque rectangle for 415 * the purposes of the Heavyweight/Lightweight Components Mixing feature. 416 * This method enables developers to set an arbitrary shape to be cut out 417 * from heavyweight components positioned underneath the lightweight 418 * component in the z-order. 419 * <p> 420 * The {@code shape} argument may have the following values: 421 * <ul> 422 * <li>{@code null} - reverts the default cutout shape (the rectangle equal 423 * to the component's {@code getBounds()}) 424 * <li><i>empty-shape</i> - does not cut out anything from heavyweight 425 * components. This makes the given lightweight component effectively 426 * transparent. Note that descendants of the lightweight component still 427 * affect the shapes of heavyweight components. An example of an 428 * <i>empty-shape</i> is {@code new Rectangle()}. 429 * <li><i>non-empty-shape</i> - the given shape will be cut out from 430 * heavyweight components. 431 * </ul> 432 * <p> 433 * The most common example when the 'mixing-cutout' shape is needed is a 434 * glass pane component. The {@link JRootPane#setGlassPane()} method 435 * automatically sets the <i>empty-shape</i> as the 'mixing-cutout' shape 436 * for the given glass pane component. If a developer needs some other 437 * 'mixing-cutout' shape for the glass pane (which is rare), this must be 438 * changed manually after installing the glass pane to the root pane. 439 * <p> 440 * Note that the 'mixing-cutout' shape neither affects painting, nor the 441 * mouse events handling for the given component. It is used exclusively 442 * for the purposes of the Heavyweight/Lightweight Components Mixing 443 * feature. 444 * 445 * @param component the component that needs non-default 446 * 'mixing-cutout' shape 447 * @param shape the new 'mixing-cutout' shape 448 * @throws NullPointerException if the component argument is {@code null} 449 */ 450 public static void setComponentMixingCutoutShape(Component component, 451 Shape shape) 452 { 453 if (component == null) { 454 throw new NullPointerException( 455 "The component argument should not be null."); 456 } 457 458 AWTAccessor.getComponentAccessor().setMixingCutoutShape(component, 459 shape); 460 } 461 } 462