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