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
26package com.sun.awt;
27
28import java.awt.*;
29
30import javax.swing.JRootPane;
31
32import sun.awt.AWTAccessor;
33import 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 */
67public 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
465