1 /*
2  * Copyright (c) 2009, 2013, 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 javax.swing;
27 
28 import sun.awt.AWTAccessor;
29 
30 import javax.swing.plaf.LayerUI;
31 import javax.swing.border.Border;
32 import javax.accessibility.*;
33 import java.awt.*;
34 import java.awt.event.*;
35 import java.beans.PropertyChangeEvent;
36 import java.beans.PropertyChangeListener;
37 import java.io.IOException;
38 import java.io.ObjectInputStream;
39 import java.util.ArrayList;
40 import java.security.AccessController;
41 import java.security.PrivilegedAction;
42 
43 /**
44  * {@code JLayer} is a universal decorator for Swing components
45  * which enables you to implement various advanced painting effects as well as
46  * receive notifications of all {@code AWTEvent}s generated within its borders.
47  * <p>
48  * {@code JLayer} delegates the handling of painting and input events to a
49  * {@link javax.swing.plaf.LayerUI} object, which performs the actual decoration.
50  * <p>
51  * The custom painting implemented in the {@code LayerUI} and events notification
52  * work for the JLayer itself and all its subcomponents.
53  * This combination enables you to enrich existing components
54  * by adding new advanced functionality such as temporary locking of a hierarchy,
55  * data tips for compound components, enhanced mouse scrolling etc and so on.
56  * <p>
57  * {@code JLayer} is a good solution if you only need to do custom painting
58  * over compound component or catch input events from its subcomponents.
59  * <pre>
60  * import javax.swing.*;
61  * import javax.swing.plaf.LayerUI;
62  * import java.awt.*;
63  *
64  * public class JLayerSample {
65  *
66  *     private static JLayer&lt;JComponent&gt; createLayer() {
67  *         // This custom layerUI will fill the layer with translucent green
68  *         // and print out all mouseMotion events generated within its borders
69  *         LayerUI&lt;JComponent&gt; layerUI = new LayerUI&lt;JComponent&gt;() {
70  *
71  *             public void paint(Graphics g, JComponent c) {
72  *                 // paint the layer as is
73  *                 super.paint(g, c);
74  *                 // fill it with the translucent green
75  *                 g.setColor(new Color(0, 128, 0, 128));
76  *                 g.fillRect(0, 0, c.getWidth(), c.getHeight());
77  *             }
78  *
79  *             public void installUI(JComponent c) {
80  *                 super.installUI(c);
81  *                 // enable mouse motion events for the layer's subcomponents
82  *                 ((JLayer) c).setLayerEventMask(AWTEvent.MOUSE_MOTION_EVENT_MASK);
83  *             }
84  *
85  *             public void uninstallUI(JComponent c) {
86  *                 super.uninstallUI(c);
87  *                 // reset the layer event mask
88  *                 ((JLayer) c).setLayerEventMask(0);
89  *             }
90  *
91  *             // overridden method which catches MouseMotion events
92  *             public void eventDispatched(AWTEvent e, JLayer&lt;? extends JComponent&gt; l) {
93  *                 System.out.println("AWTEvent detected: " + e);
94  *             }
95  *         };
96  *         // create a component to be decorated with the layer
97  *         JPanel panel = new JPanel();
98  *         panel.add(new JButton("JButton"));
99  *
100  *         // create the layer for the panel using our custom layerUI
101  *         return new JLayer&lt;JComponent&gt;(panel, layerUI);
102  *     }
103  *
104  *     private static void createAndShowGUI() {
105  *         final JFrame frame = new JFrame();
106  *         frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
107  *
108  *         // work with the layer as with any other Swing component
109  *         frame.add(createLayer());
110  *
111  *         frame.setSize(200, 200);
112  *         frame.setLocationRelativeTo(null);
113  *         frame.setVisible(true);
114  *     }
115  *
116  *     public static void main(String[] args) throws Exception {
117  *         SwingUtilities.invokeAndWait(new Runnable() {
118  *             public void run() {
119  *                 createAndShowGUI();
120  *             }
121  *         });
122  *     }
123  * }
124  * </pre>
125  *
126  * <b>Note:</b> {@code JLayer} doesn't support the following methods:
127  * <ul>
128  * <li>{@link Container#add(java.awt.Component)}</li>
129  * <li>{@link Container#add(String, java.awt.Component)}</li>
130  * <li>{@link Container#add(java.awt.Component, int)}</li>
131  * <li>{@link Container#add(java.awt.Component, Object)}</li>
132  * <li>{@link Container#add(java.awt.Component, Object, int)}</li>
133  * </ul>
134  * using any of of them will cause {@code UnsupportedOperationException} to be thrown,
135  * to add a component to {@code JLayer}
136  * use {@link #setView(Component)} or {@link #setGlassPane(JPanel)}.
137  *
138  * @param <V> the type of {@code JLayer}'s view component
139  *
140  * @see #JLayer(Component)
141  * @see #setView(Component)
142  * @see #getView()
143  * @see javax.swing.plaf.LayerUI
144  * @see #JLayer(Component, LayerUI)
145  * @see #setUI(javax.swing.plaf.LayerUI)
146  * @see #getUI()
147  * @since 1.7
148  *
149  * @author Alexander Potochkin
150  */
151 public final class JLayer<V extends Component>
152         extends JComponent
153         implements Scrollable, PropertyChangeListener, Accessible {
154     private V view;
155     // this field is necessary because JComponent.ui is transient
156     // when layerUI is serializable
157     private LayerUI<? super V> layerUI;
158     private JPanel glassPane;
159     private long eventMask;
160     private transient boolean isPainting;
161     private transient boolean isPaintingImmediately;
162 
163     private static final LayerEventController eventController =
164             new LayerEventController();
165 
166     /**
167      * Creates a new {@code JLayer} object with a {@code null} view component
168      * and default {@link javax.swing.plaf.LayerUI}.
169      *
170      * @see #setView
171      * @see #setUI
172      */
JLayer()173     public JLayer() {
174         this(null);
175     }
176 
177     /**
178      * Creates a new {@code JLayer} object
179      * with default {@link javax.swing.plaf.LayerUI}.
180      *
181      * @param view the component to be decorated by this {@code JLayer}
182      *
183      * @see #setUI
184      */
JLayer(V view)185     public JLayer(V view) {
186         this(view, new LayerUI<V>());
187     }
188 
189     /**
190      * Creates a new {@code JLayer} object with the specified view component
191      * and {@link javax.swing.plaf.LayerUI} object.
192      *
193      * @param view the component to be decorated
194      * @param ui the {@link javax.swing.plaf.LayerUI} delegate
195      * to be used by this {@code JLayer}
196      */
JLayer(V view, LayerUI<V> ui)197     public JLayer(V view, LayerUI<V> ui) {
198         setGlassPane(createGlassPane());
199         setView(view);
200         setUI(ui);
201     }
202 
203     /**
204      * Returns the {@code JLayer}'s view component or {@code null}.
205      * <br>This is a bound property.
206      *
207      * @return the {@code JLayer}'s view component
208      *         or {@code null} if none exists
209      *
210      * @see #setView(Component)
211      */
getView()212     public V getView() {
213         return view;
214     }
215 
216     /**
217      * Sets the {@code JLayer}'s view component, which can be {@code null}.
218      * <br>This is a bound property.
219      *
220      * @param view the view component for this {@code JLayer}
221      *
222      * @see #getView()
223      */
setView(V view)224     public void setView(V view) {
225         Component oldView = getView();
226         if (oldView != null) {
227             super.remove(oldView);
228         }
229         if (view != null) {
230             super.addImpl(view, null, getComponentCount());
231         }
232         this.view = view;
233         firePropertyChange("view", oldView, view);
234         revalidate();
235         repaint();
236     }
237 
238     /**
239      * Sets the {@link javax.swing.plaf.LayerUI} which will perform painting
240      * and receive input events for this {@code JLayer}.
241      *
242      * @param ui the {@link javax.swing.plaf.LayerUI} for this {@code JLayer}
243      */
setUI(LayerUI<? super V> ui)244     public void setUI(LayerUI<? super V> ui) {
245         this.layerUI = ui;
246         super.setUI(ui);
247     }
248 
249     /**
250      * Returns the {@link javax.swing.plaf.LayerUI} for this {@code JLayer}.
251      *
252      * @return the {@code LayerUI} for this {@code JLayer}
253      */
getUI()254     public LayerUI<? super V> getUI() {
255         return layerUI;
256     }
257 
258     /**
259      * Returns the {@code JLayer}'s glassPane component or {@code null}.
260      * <br>This is a bound property.
261      *
262      * @return the {@code JLayer}'s glassPane component
263      *         or {@code null} if none exists
264      *
265      * @see #setGlassPane(JPanel)
266      */
getGlassPane()267     public JPanel getGlassPane() {
268         return glassPane;
269     }
270 
271     /**
272      * Sets the {@code JLayer}'s glassPane component, which can be {@code null}.
273      * <br>This is a bound property.
274      *
275      * @param glassPane the glassPane component of this {@code JLayer}
276      *
277      * @see #getGlassPane()
278      */
setGlassPane(JPanel glassPane)279     public void setGlassPane(JPanel glassPane) {
280         Component oldGlassPane = getGlassPane();
281         boolean isGlassPaneVisible = false;
282         if (oldGlassPane != null) {
283             isGlassPaneVisible = oldGlassPane.isVisible();
284             super.remove(oldGlassPane);
285         }
286         if (glassPane != null) {
287             AWTAccessor.getComponentAccessor().setMixingCutoutShape(glassPane,
288                     new Rectangle());
289             glassPane.setVisible(isGlassPaneVisible);
290             super.addImpl(glassPane, null, 0);
291         }
292         this.glassPane = glassPane;
293         firePropertyChange("glassPane", oldGlassPane, glassPane);
294         revalidate();
295         repaint();
296     }
297 
298     /**
299      * Called by the constructor methods to create a default {@code glassPane}.
300      * By default this method creates a new JPanel with visibility set to true
301      * and opacity set to false.
302      *
303      * @return the default {@code glassPane}
304      */
createGlassPane()305     public JPanel createGlassPane() {
306         return new DefaultLayerGlassPane();
307     }
308 
309     /**
310      * Sets the layout manager for this container.  This method is
311      * overridden to prevent the layout manager from being set.
312      * <p>Note:  If {@code mgr} is non-{@code null}, this
313      * method will throw an exception as layout managers are not supported on
314      * a {@code JLayer}.
315      *
316      * @param mgr the specified layout manager
317      * @exception IllegalArgumentException this method is not supported
318      */
setLayout(LayoutManager mgr)319     public void setLayout(LayoutManager mgr) {
320         if (mgr != null) {
321             throw new IllegalArgumentException("JLayer.setLayout() not supported");
322         }
323     }
324 
325     /**
326      * A non-{@code null} border, or non-zero insets, isn't supported, to prevent the geometry
327      * of this component from becoming complex enough to inhibit
328      * subclassing of {@code LayerUI} class.  To create a {@code JLayer} with a border,
329      * add it to a {@code JPanel} that has a border.
330      * <p>Note:  If {@code border} is non-{@code null}, this
331      * method will throw an exception as borders are not supported on
332      * a {@code JLayer}.
333      *
334      * @param border the {@code Border} to set
335      * @exception IllegalArgumentException this method is not supported
336      */
setBorder(Border border)337     public void setBorder(Border border) {
338         if (border != null) {
339             throw new IllegalArgumentException("JLayer.setBorder() not supported");
340         }
341     }
342 
343     /**
344      * This method is not supported by {@code JLayer}
345      * and always throws {@code UnsupportedOperationException}
346      *
347      * @throws UnsupportedOperationException this method is not supported
348      *
349      * @see #setView(Component)
350      * @see #setGlassPane(JPanel)
351      */
addImpl(Component comp, Object constraints, int index)352     protected void addImpl(Component comp, Object constraints, int index) {
353         throw new UnsupportedOperationException(
354                 "Adding components to JLayer is not supported, " +
355                         "use setView() or setGlassPane() instead");
356     }
357 
358     /**
359      * {@inheritDoc}
360      */
remove(Component comp)361     public void remove(Component comp) {
362         if (comp == null) {
363             super.remove(comp);
364         } else if (comp == getView()) {
365             setView(null);
366         } else if (comp == getGlassPane()) {
367             setGlassPane(null);
368         } else {
369             super.remove(comp);
370         }
371     }
372 
373     /**
374      * {@inheritDoc}
375      */
removeAll()376     public void removeAll() {
377         if (view != null) {
378             setView(null);
379         }
380         if (glassPane != null) {
381             setGlassPane(null);
382         }
383     }
384 
385     /**
386      * Always returns {@code true} to cause painting to originate from {@code JLayer},
387      * or one of its ancestors.
388      *
389      * @return true
390      * @see JComponent#isPaintingOrigin()
391      */
isPaintingOrigin()392     protected boolean isPaintingOrigin() {
393         return true;
394     }
395 
396     /**
397      * Delegates its functionality to the
398      * {@link javax.swing.plaf.LayerUI#paintImmediately(int, int, int, int, JLayer)} method,
399      * if {@code LayerUI} is set.
400      *
401      * @param x  the x value of the region to be painted
402      * @param y  the y value of the region to be painted
403      * @param w  the width of the region to be painted
404      * @param h  the height of the region to be painted
405      */
paintImmediately(int x, int y, int w, int h)406     public void paintImmediately(int x, int y, int w, int h) {
407         if (!isPaintingImmediately && getUI() != null) {
408             isPaintingImmediately = true;
409             try {
410                 getUI().paintImmediately(x, y, w, h, this);
411             } finally {
412                 isPaintingImmediately = false;
413             }
414         } else {
415             super.paintImmediately(x, y, w, h);
416         }
417     }
418 
419     /**
420      * Delegates all painting to the {@link javax.swing.plaf.LayerUI} object.
421      *
422      * @param g the {@code Graphics} to render to
423      */
paint(Graphics g)424     public void paint(Graphics g) {
425         if (!isPainting) {
426             isPainting = true;
427             try {
428                 super.paintComponent(g);
429             } finally {
430                 isPainting = false;
431             }
432         } else {
433             super.paint(g);
434         }
435     }
436 
437     /**
438      * This method is empty, because all painting is done by
439      * {@link #paint(Graphics)} and
440      * {@link javax.swing.plaf.LayerUI#update(Graphics, JComponent)} methods
441      */
paintComponent(Graphics g)442     protected void paintComponent(Graphics g) {
443     }
444 
445     /**
446      * The {@code JLayer} overrides the default implementation of
447      * this method (in {@code JComponent}) to return {@code false}.
448      * This ensures
449      * that the drawing machinery will call the {@code JLayer}'s
450      * {@code paint}
451      * implementation rather than messaging the {@code JLayer}'s
452      * children directly.
453      *
454      * @return false
455      */
isOptimizedDrawingEnabled()456     public boolean isOptimizedDrawingEnabled() {
457         return false;
458     }
459 
460     /**
461      * {@inheritDoc}
462      */
propertyChange(PropertyChangeEvent evt)463     public void propertyChange(PropertyChangeEvent evt) {
464         if (getUI() != null) {
465             getUI().applyPropertyChange(evt, this);
466         }
467     }
468 
469     /**
470      * Enables the events from JLayer and <b>all its descendants</b>
471      * defined by the specified event mask parameter
472      * to be delivered to the
473      * {@link LayerUI#eventDispatched(AWTEvent, JLayer)} method.
474      * <p>
475      * Events are delivered provided that {@code LayerUI} is set
476      * for this {@code JLayer} and the {@code JLayer}
477      * is displayable.
478      * <p>
479      * The following example shows how to correctly use this method
480      * in the {@code LayerUI} implementations:
481      * <pre>
482      *    public void installUI(JComponent c) {
483      *       super.installUI(c);
484      *       JLayer l = (JLayer) c;
485      *       // this LayerUI will receive only key and focus events
486      *       l.setLayerEventMask(AWTEvent.KEY_EVENT_MASK | AWTEvent.FOCUS_EVENT_MASK);
487      *    }
488      *
489      *    public void uninstallUI(JComponent c) {
490      *       super.uninstallUI(c);
491      *       JLayer l = (JLayer) c;
492      *       // JLayer must be returned to its initial state
493      *       l.setLayerEventMask(0);
494      *    }
495      * </pre>
496      *
497      * By default {@code JLayer} receives no events and its event mask is {@code 0}.
498      *
499      * @param layerEventMask the bitmask of event types to receive
500      *
501      * @see #getLayerEventMask()
502      * @see LayerUI#eventDispatched(AWTEvent, JLayer)
503      * @see Component#isDisplayable()
504      */
setLayerEventMask(long layerEventMask)505     public void setLayerEventMask(long layerEventMask) {
506         long oldEventMask = getLayerEventMask();
507         this.eventMask = layerEventMask;
508         firePropertyChange("layerEventMask", oldEventMask, layerEventMask);
509         if (layerEventMask != oldEventMask) {
510             disableEvents(oldEventMask);
511             enableEvents(eventMask);
512             if (isDisplayable()) {
513                 eventController.updateAWTEventListener(
514                         oldEventMask, layerEventMask);
515             }
516         }
517     }
518 
519     /**
520      * Returns the bitmap of event mask to receive by this {@code JLayer}
521      * and its {@code LayerUI}.
522      * <p>
523      * It means that {@link javax.swing.plaf.LayerUI#eventDispatched(AWTEvent, JLayer)} method
524      * will only receive events that match the event mask.
525      * <p>
526      * By default {@code JLayer} receives no events.
527      *
528      * @return the bitmask of event types to receive for this {@code JLayer}
529      */
getLayerEventMask()530     public long getLayerEventMask() {
531         return eventMask;
532     }
533 
534     /**
535      * Delegates its functionality to the {@link javax.swing.plaf.LayerUI#updateUI(JLayer)} method,
536      * if {@code LayerUI} is set.
537      */
updateUI()538     public void updateUI() {
539         if (getUI() != null) {
540             getUI().updateUI(this);
541         }
542     }
543 
544     /**
545      * Returns the preferred size of the viewport for a view component.
546      * <p>
547      * If the view component of this layer implements {@link Scrollable}, this method delegates its
548      * implementation to the view component.
549      *
550      * @return the preferred size of the viewport for a view component
551      *
552      * @see Scrollable
553      */
getPreferredScrollableViewportSize()554     public Dimension getPreferredScrollableViewportSize() {
555         if (getView() instanceof Scrollable) {
556             return ((Scrollable)getView()).getPreferredScrollableViewportSize();
557         }
558         return getPreferredSize();
559     }
560 
561     /**
562      * Returns a scroll increment, which is required for components
563      * that display logical rows or columns in order to completely expose
564      * one block of rows or columns, depending on the value of orientation.
565      * <p>
566      * If the view component of this layer implements {@link Scrollable}, this method delegates its
567      * implementation to the view component.
568      *
569      * @return the "block" increment for scrolling in the specified direction
570      *
571      * @see Scrollable
572      */
getScrollableBlockIncrement(Rectangle visibleRect, int orientation, int direction)573     public int getScrollableBlockIncrement(Rectangle visibleRect,
574                                            int orientation, int direction) {
575         if (getView() instanceof Scrollable) {
576             return ((Scrollable)getView()).getScrollableBlockIncrement(visibleRect,
577                     orientation, direction);
578         }
579         return (orientation == SwingConstants.VERTICAL) ? visibleRect.height :
580                 visibleRect.width;
581     }
582 
583     /**
584      * Returns {@code false} to indicate that the height of the viewport does not
585      * determine the height of the layer, unless the preferred height
586      * of the layer is smaller than the height of the viewport.
587      * <p>
588      * If the view component of this layer implements {@link Scrollable}, this method delegates its
589      * implementation to the view component.
590      *
591      * @return whether the layer should track the height of the viewport
592      *
593      * @see Scrollable
594      */
getScrollableTracksViewportHeight()595     public boolean getScrollableTracksViewportHeight() {
596         if (getView() instanceof Scrollable) {
597             return ((Scrollable)getView()).getScrollableTracksViewportHeight();
598         }
599         return false;
600     }
601 
602     /**
603      * Returns {@code false} to indicate that the width of the viewport does not
604      * determine the width of the layer, unless the preferred width
605      * of the layer is smaller than the width of the viewport.
606      * <p>
607      * If the view component of this layer implements {@link Scrollable}, this method delegates its
608      * implementation to the view component.
609      *
610      * @return whether the layer should track the width of the viewport
611      *
612      * @see Scrollable
613      */
getScrollableTracksViewportWidth()614     public boolean getScrollableTracksViewportWidth() {
615         if (getView() instanceof Scrollable) {
616             return ((Scrollable)getView()).getScrollableTracksViewportWidth();
617         }
618         return false;
619     }
620 
621     /**
622      * Returns a scroll increment, which is required for components
623      * that display logical rows or columns in order to completely expose
624      * one new row or column, depending on the value of orientation.
625      * Ideally, components should handle a partially exposed row or column
626      * by returning the distance required to completely expose the item.
627      * <p>
628      * Scrolling containers, like {@code JScrollPane}, will use this method
629      * each time the user requests a unit scroll.
630      * <p>
631      * If the view component of this layer implements {@link Scrollable}, this method delegates its
632      * implementation to the view component.
633      *
634      * @return The "unit" increment for scrolling in the specified direction.
635      *         This value should always be positive.
636      *
637      * @see Scrollable
638      */
getScrollableUnitIncrement(Rectangle visibleRect, int orientation, int direction)639     public int getScrollableUnitIncrement(Rectangle visibleRect, int orientation,
640                                           int direction) {
641         if (getView() instanceof Scrollable) {
642             return ((Scrollable) getView()).getScrollableUnitIncrement(
643                     visibleRect, orientation, direction);
644         }
645         return 1;
646     }
647 
readObject(ObjectInputStream s)648     private void readObject(ObjectInputStream s)
649             throws IOException, ClassNotFoundException {
650         s.defaultReadObject();
651         if (layerUI != null) {
652             setUI(layerUI);
653         }
654         if (eventMask != 0) {
655             eventController.updateAWTEventListener(0, eventMask);
656         }
657     }
658 
659     /**
660      * {@inheritDoc}
661      */
addNotify()662     public void addNotify() {
663         super.addNotify();
664         eventController.updateAWTEventListener(0, eventMask);
665     }
666 
667     /**
668      * {@inheritDoc}
669      */
removeNotify()670     public void removeNotify() {
671         super.removeNotify();
672         eventController.updateAWTEventListener(eventMask, 0);
673     }
674 
675     /**
676      * Delegates its functionality to the {@link javax.swing.plaf.LayerUI#doLayout(JLayer)} method,
677      * if {@code LayerUI} is set.
678      */
doLayout()679     public void doLayout() {
680         if (getUI() != null) {
681             getUI().doLayout(this);
682         }
683     }
684 
685     /**
686      * Gets the AccessibleContext associated with this {@code JLayer}.
687      *
688      * @return the AccessibleContext associated with this {@code JLayer}.
689      */
getAccessibleContext()690     public AccessibleContext getAccessibleContext() {
691         if (accessibleContext == null) {
692             accessibleContext = new AccessibleJComponent() {
693                 public AccessibleRole getAccessibleRole() {
694                     return AccessibleRole.PANEL;
695                 }
696             };
697         }
698         return accessibleContext;
699     }
700 
701     /**
702      * static AWTEventListener to be shared with all AbstractLayerUIs
703      */
704     private static class LayerEventController implements AWTEventListener {
705         private ArrayList<Long> layerMaskList =
706                 new ArrayList<Long>();
707 
708         private long currentEventMask;
709 
710         private static final long ACCEPTED_EVENTS =
711                 AWTEvent.COMPONENT_EVENT_MASK |
712                         AWTEvent.CONTAINER_EVENT_MASK |
713                         AWTEvent.FOCUS_EVENT_MASK |
714                         AWTEvent.KEY_EVENT_MASK |
715                         AWTEvent.MOUSE_WHEEL_EVENT_MASK |
716                         AWTEvent.MOUSE_MOTION_EVENT_MASK |
717                         AWTEvent.MOUSE_EVENT_MASK |
718                         AWTEvent.INPUT_METHOD_EVENT_MASK |
719                         AWTEvent.HIERARCHY_EVENT_MASK |
720                         AWTEvent.HIERARCHY_BOUNDS_EVENT_MASK;
721 
722         @SuppressWarnings("unchecked")
eventDispatched(AWTEvent event)723         public void eventDispatched(AWTEvent event) {
724             Object source = event.getSource();
725             if (source instanceof Component) {
726                 Component component = (Component) source;
727                 while (component != null) {
728                     if (component instanceof JLayer) {
729                         JLayer l = (JLayer) component;
730                         LayerUI ui = l.getUI();
731                         if (ui != null &&
732                                 isEventEnabled(l.getLayerEventMask(), event.getID()) &&
733                                 (!(event instanceof InputEvent) || !((InputEvent)event).isConsumed())) {
734                             ui.eventDispatched(event, l);
735                         }
736                     }
737                     component = component.getParent();
738                 }
739             }
740         }
741 
updateAWTEventListener(long oldEventMask, long newEventMask)742         private void updateAWTEventListener(long oldEventMask, long newEventMask) {
743             if (oldEventMask != 0) {
744                 layerMaskList.remove(oldEventMask);
745             }
746             if (newEventMask != 0) {
747                 layerMaskList.add(newEventMask);
748             }
749             long combinedMask = 0;
750             for (Long mask : layerMaskList) {
751                 combinedMask |= mask;
752             }
753             // filter out all unaccepted events
754             combinedMask &= ACCEPTED_EVENTS;
755             if (combinedMask == 0) {
756                 removeAWTEventListener();
757             } else if (getCurrentEventMask() != combinedMask) {
758                 removeAWTEventListener();
759                 addAWTEventListener(combinedMask);
760             }
761             currentEventMask = combinedMask;
762         }
763 
getCurrentEventMask()764         private long getCurrentEventMask() {
765             return currentEventMask;
766         }
767 
addAWTEventListener(final long eventMask)768         private void addAWTEventListener(final long eventMask) {
769             AccessController.doPrivileged(new PrivilegedAction<Void>() {
770                 public Void run() {
771                     Toolkit.getDefaultToolkit().
772                             addAWTEventListener(LayerEventController.this, eventMask);
773                     return null;
774                 }
775             });
776 
777         }
778 
removeAWTEventListener()779         private void removeAWTEventListener() {
780             AccessController.doPrivileged(new PrivilegedAction<Void>() {
781                 public Void run() {
782                     Toolkit.getDefaultToolkit().
783                             removeAWTEventListener(LayerEventController.this);
784                     return null;
785                 }
786             });
787         }
788 
isEventEnabled(long eventMask, int id)789         private boolean isEventEnabled(long eventMask, int id) {
790             return (((eventMask & AWTEvent.COMPONENT_EVENT_MASK) != 0 &&
791                     id >= ComponentEvent.COMPONENT_FIRST &&
792                     id <= ComponentEvent.COMPONENT_LAST)
793                     || ((eventMask & AWTEvent.CONTAINER_EVENT_MASK) != 0 &&
794                     id >= ContainerEvent.CONTAINER_FIRST &&
795                     id <= ContainerEvent.CONTAINER_LAST)
796                     || ((eventMask & AWTEvent.FOCUS_EVENT_MASK) != 0 &&
797                     id >= FocusEvent.FOCUS_FIRST &&
798                     id <= FocusEvent.FOCUS_LAST)
799                     || ((eventMask & AWTEvent.KEY_EVENT_MASK) != 0 &&
800                     id >= KeyEvent.KEY_FIRST &&
801                     id <= KeyEvent.KEY_LAST)
802                     || ((eventMask & AWTEvent.MOUSE_WHEEL_EVENT_MASK) != 0 &&
803                     id == MouseEvent.MOUSE_WHEEL)
804                     || ((eventMask & AWTEvent.MOUSE_MOTION_EVENT_MASK) != 0 &&
805                     (id == MouseEvent.MOUSE_MOVED ||
806                             id == MouseEvent.MOUSE_DRAGGED))
807                     || ((eventMask & AWTEvent.MOUSE_EVENT_MASK) != 0 &&
808                     id != MouseEvent.MOUSE_MOVED &&
809                     id != MouseEvent.MOUSE_DRAGGED &&
810                     id != MouseEvent.MOUSE_WHEEL &&
811                     id >= MouseEvent.MOUSE_FIRST &&
812                     id <= MouseEvent.MOUSE_LAST)
813                     || ((eventMask & AWTEvent.INPUT_METHOD_EVENT_MASK) != 0 &&
814                     id >= InputMethodEvent.INPUT_METHOD_FIRST &&
815                     id <= InputMethodEvent.INPUT_METHOD_LAST)
816                     || ((eventMask & AWTEvent.HIERARCHY_EVENT_MASK) != 0 &&
817                     id == HierarchyEvent.HIERARCHY_CHANGED)
818                     || ((eventMask & AWTEvent.HIERARCHY_BOUNDS_EVENT_MASK) != 0 &&
819                     (id == HierarchyEvent.ANCESTOR_MOVED ||
820                             id == HierarchyEvent.ANCESTOR_RESIZED)));
821         }
822     }
823 
824     /**
825      * The default glassPane for the {@link javax.swing.JLayer}.
826      * It is a subclass of {@code JPanel} which is non opaque by default.
827      */
828     private static class DefaultLayerGlassPane extends JPanel {
829         /**
830          * Creates a new {@link DefaultLayerGlassPane}
831          */
DefaultLayerGlassPane()832         public DefaultLayerGlassPane() {
833             setOpaque(false);
834         }
835 
836         /**
837          * First, implementation of this method iterates through
838          * glassPane's child components and returns {@code true}
839          * if any of them is visible and contains passed x,y point.
840          * After that it checks if no mouseListeners is attached to this component
841          * and no mouse cursor is set, then it returns {@code false},
842          * otherwise calls the super implementation of this method.
843          *
844          * @param x the <i>x</i> coordinate of the point
845          * @param y the <i>y</i> coordinate of the point
846          * @return true if this component logically contains x,y
847          */
contains(int x, int y)848         public boolean contains(int x, int y) {
849             for (int i = 0; i < getComponentCount(); i++) {
850                 Component c = getComponent(i);
851                 Point point = SwingUtilities.convertPoint(this, new Point(x, y), c);
852                 if(c.isVisible() && c.contains(point)){
853                     return true;
854                 }
855             }
856             if (getMouseListeners().length == 0
857                     && getMouseMotionListeners().length == 0
858                     && getMouseWheelListeners().length == 0
859                     && !isCursorSet()) {
860                 return false;
861             }
862             return super.contains(x, y);
863         }
864     }
865 }
866