src/share/classes/com/sun/awt/AWTUtilities.java
author art
Wed Feb 11 17:07:06 2009 +0300 (3 years ago)
changeset 1051 d0b6e69791c8
parent 906f36e9200cb85
child 14064cd623432e7d
permissions -rw-r--r--
6633275: Need to support shaped/translucent windows
Summary: forward-port from 6u14, no public API is introduced
Reviewed-by: anthony, dcherepanov
        1 /*
        2  * Copyright 2008-2009 Sun Microsystems, Inc.  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.  Sun designates this
        8  * particular file as subject to the "Classpath" exception as provided
        9  * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
       22  * CA 95054 USA or visit www.sun.com if you need additional information or
       23  * have any 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 AWTAccessor.getWindowAccessor().isOpaque(window);
      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