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 trasnlcency 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 &lt; 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