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