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