1 /*
2 * Copyright (c) 2011, 2012, 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
55
56 import sun.java2d.SunGraphics2D;
57 import sun.java2d.opengl.OGLRenderQueue;
58 import sun.java2d.pipe.Region;
59
60 import sun.util.logging.PlatformLogger;
61
62 import javax.swing.JComponent;
63 import javax.swing.SwingUtilities;
64 import javax.swing.RepaintManager;
65
66 import sun.lwawt.macosx.CDropTarget;
67
68 import com.sun.java.swing.SwingUtilities3;
69
70 public abstract class LWComponentPeer<T extends Component, D extends JComponent>
71 implements ComponentPeer, DropTargetPeer
72 {
73 private static final PlatformLogger focusLog = PlatformLogger.getLogger("sun.lwawt.focus.LWComponentPeer");
74
75 // State lock is to be used for modifications to this
76 // peer's fields (e.g. bounds, background, font, etc.)
77 // It should be the last lock in the lock chain
78 private final Object stateLock =
79 new StringBuilder("LWComponentPeer.stateLock");
80
81 // The lock to operate with the peers hierarchy. AWT tree
82 // lock is not used as there are many peers related ops
83 // to be done on the toolkit thread, and we don't want to
84 // depend on a public lock on this thread
85 private static final Object peerTreeLock =
86 new StringBuilder("LWComponentPeer.peerTreeLock");
87
88 private final T target;
89
90 /**
91 * Container peer. It may not be the peer of the target's direct parent, for
92 * example, in the case of hw/lw mixing. However, let's skip this scenario
93 * for the time being. We also assume the container peer is not null, which
94 * might also be false if addNotify() is called for a component outside of
95 * the hierarchy. The exception is LWWindowPeers: their containers are
96 * always null
97 */
98 private final LWContainerPeer containerPeer;
99
100 /**
101 * Handy reference to the top-level window peer. Window peer is borrowed
102 * from the containerPeer in constructor, and should also be updated when
103 * the component is reparented to another container
104 */
105 private final LWWindowPeer windowPeer;
106
107 private final AtomicBoolean disposed = new AtomicBoolean(false);
108
109 // Bounds are relative to parent peer
110 private final Rectangle bounds = new Rectangle();
111 private Region region;
112
113 // Component state. Should be accessed under the state lock
114 private boolean visible = false;
115 private boolean enabled = true;
116
117 private Color background;
118 private Color foreground;
130 private final D delegate;
131 private Container delegateContainer;
132 private Component delegateDropTarget;
133 private final Object dropTargetLock = new Object();
134
135 private int fNumDropTargets = 0;
136 private CDropTarget fDropTarget = null;
137
138 private final PlatformComponent platformComponent;
139
140 /**
141 * Character with reasonable value between the minimum width and maximum.
142 */
143 static final char WIDE_CHAR = '0';
144
145 /**
146 * The back buffer provide user with a BufferStrategy.
147 */
148 private Image backBuffer;
149
150 private final class DelegateContainer extends Container {
151 {
152 enableEvents(0xFFFFFFFF);
153 }
154
155 DelegateContainer() {
156 super();
157 }
158
159 @Override
160 public boolean isLightweight() {
161 return false;
162 }
163
164 @Override
165 public Point getLocation() {
166 return getLocationOnScreen();
167 }
168
169 @Override
170 public Point getLocationOnScreen() {
171 return LWComponentPeer.this.getLocationOnScreen();
172 }
173
174 @Override
175 public int getX() {
176 return getLocation().x;
177 }
178
179 @Override
180 public int getY() {
181 return getLocation().y;
182 }
183 }
184
185 public LWComponentPeer(T target, PlatformComponent platformComponent) {
186 targetPaintArea = new LWRepaintArea();
187 this.target = target;
188 this.platformComponent = platformComponent;
189
190 // Container peer is always null for LWWindowPeers, so
191 // windowPeer is always null for them as well. On the other
192 // hand, LWWindowPeer shouldn't use windowPeer at all
193 final Container container = SunToolkit.getNativeContainer(target);
194 containerPeer = (LWContainerPeer) LWToolkit.targetToPeer(container);
195 windowPeer = containerPeer != null ? containerPeer.getWindowPeerOrSelf()
196 : null;
197 // don't bother about z-order here as updateZOrder()
198 // will be called from addNotify() later anyway
199 if (containerPeer != null) {
200 containerPeer.addChildPeer(this);
201 }
202
203 // the delegate must be created after the target is set
204 AWTEventListener toolkitListener = null;
205 synchronized (Toolkit.getDefaultToolkit()) {
259 protected final void setToolkitAWTEventListener(final AWTEventListener listener) {
260 AccessController.doPrivileged(new PrivilegedAction<Void>() {
261 public Void run() {
262 Toolkit toolkit = Toolkit.getDefaultToolkit();
263 try {
264 Field field = Toolkit.class.getDeclaredField("eventListener");
265 field.setAccessible(true);
266 field.set(toolkit, listener);
267 } catch (Exception e) {
268 throw new InternalError(e.toString());
269 }
270 return null;
271 }
272 });
273 }
274
275 /**
276 * This method is called under getDelegateLock().
277 * Overridden in subclasses.
278 */
279 protected D createDelegate() {
280 return null;
281 }
282
283 protected final D getDelegate() {
284 return delegate;
285 }
286
287 protected Component getDelegateFocusOwner() {
288 return getDelegate();
289 }
290
291 /**
292 * Initializes this peer. The call to initialize() is not placed to
293 * LWComponentPeer ctor to let the subclass ctor to finish completely first.
294 * Instead, it's the LWToolkit object who is responsible for initialization.
295 * Note that we call setVisible() at the end of initialization.
296 */
297 public final void initialize() {
298 platformComponent.initialize(getPlatformWindow());
299 initializeImpl();
300 setVisible(target.isVisible());
301 }
302
303 /**
304 * Fetching general properties from the target. Should be overridden in
305 * subclasses to initialize specific peers properties.
306 */
307 void initializeImpl() {
339 protected static final Object getPeerTreeLock() {
340 return peerTreeLock;
341 }
342
343 public final T getTarget() {
344 return target;
345 }
346
347 // Just a helper method
348 // Returns the window peer or null if this is a window peer
349 protected final LWWindowPeer getWindowPeer() {
350 return windowPeer;
351 }
352
353 // Returns the window peer or 'this' if this is a window peer
354 protected LWWindowPeer getWindowPeerOrSelf() {
355 return getWindowPeer();
356 }
357
358 // Just a helper method
359 protected final LWContainerPeer getContainerPeer() {
360 return containerPeer;
361 }
362
363 public PlatformWindow getPlatformWindow() {
364 LWWindowPeer windowPeer = getWindowPeer();
365 return windowPeer.getPlatformWindow();
366 }
367
368 protected AppContext getAppContext() {
369 return SunToolkit.targetToAppContext(getTarget());
370 }
371
372 // ---- PEER METHODS ---- //
373
374 @Override
375 public Toolkit getToolkit() {
376 return LWToolkit.getLWToolkit();
377 }
378
379 // Just a helper method
380 public LWToolkit getLWToolkit() {
381 return LWToolkit.getLWToolkit();
382 }
383
384 @Override
385 public final void dispose() {
386 if (disposed.compareAndSet(false, true)) {
387 disposeImpl();
388 }
389 }
390
391 protected void disposeImpl() {
392 destroyBuffers();
393 LWContainerPeer cp = getContainerPeer();
394 if (cp != null) {
395 cp.removeChildPeer(this);
396 }
397 platformComponent.dispose();
398 LWToolkit.targetDisposedPeer(getTarget(), this);
399 }
400
401 public final boolean isDisposed() {
402 return disposed.get();
403 }
404
405 /*
406 * GraphicsConfiguration is borrowed from the parent peer. The
407 * return value must not be null.
408 *
409 * Overridden in LWWindowPeer.
410 */
411 @Override
412 public GraphicsConfiguration getGraphicsConfiguration() {
413 // Don't check windowPeer for null as it can only happen
445 }
446
447 /*
448 * Peer Graphics is borrowed from the parent peer, while
449 * foreground and background colors and font are specific to
450 * this peer.
451 */
452 public final Graphics getOnscreenGraphics() {
453 final LWWindowPeer wp = getWindowPeerOrSelf();
454 return wp.getOnscreenGraphics(getForeground(), getBackground(),
455 getFont());
456
457 }
458
459 private void applyConstrain(final Graphics g) {
460 final SunGraphics2D sg2d = (SunGraphics2D) g;
461 final Rectangle size = localToWindow(getSize());
462 sg2d.constrain(size.x, size.y, size.width, size.height, getVisibleRegion());
463 }
464
465 public Region getVisibleRegion() {
466 return computeVisibleRect(this, getRegion());
467 }
468
469 static final Region computeVisibleRect(LWComponentPeer c, Region region) {
470 final LWContainerPeer p = c.getContainerPeer();
471 if (p != null) {
472 final Rectangle r = c.getBounds();
473 region = region.getTranslatedRegion(r.x, r.y);
474 region = region.getIntersection(p.getRegion());
475 region = region.getIntersection(p.getContentSize());
476 region = p.cutChildren(region, c);
477 region = computeVisibleRect(p, region);
478 region = region.getTranslatedRegion(-r.x, -r.y);
479 }
480 return region;
481 }
482
483 @Override
484 public ColorModel getColorModel() {
485 // Is it a correct implementation?
486 return getGraphicsConfiguration().getColorModel();
487 }
488
489 public boolean isTranslucent() {
490 // Translucent windows of the top level are supported only
595 // Return a copy to prevent subsequent modifications
596 return new Rectangle(bounds.width, bounds.height);
597 }
598 }
599
600 @Override
601 public Point getLocationOnScreen() {
602 Point windowLocation = getWindowPeer().getLocationOnScreen();
603 Point locationInWindow = localToWindow(0, 0);
604 return new Point(windowLocation.x + locationInWindow.x,
605 windowLocation.y + locationInWindow.y);
606 }
607
608 /**
609 * Returns the cursor of the peer, which is cursor of the target by default,
610 * but peer can override this behavior.
611 *
612 * @param p Point relative to the peer.
613 * @return Cursor of the peer or null if default cursor should be used.
614 */
615 protected Cursor getCursor(final Point p) {
616 return getTarget().getCursor();
617 }
618
619 @Override
620 public void setBackground(final Color c) {
621 final Color oldBg = getBackground();
622 if (oldBg == c || (oldBg != null && oldBg.equals(c))) {
623 return;
624 }
625 synchronized (getStateLock()) {
626 background = c;
627 }
628 final D delegate = getDelegate();
629 if (delegate != null) {
630 synchronized (getDelegateLock()) {
631 // delegate will repaint the target
632 delegate.setBackground(c);
633 }
634 } else {
635 repaintPeer();
700 // return getWindowPeer().getFontMetrics(f);
701 // Obtain the metrics from the offscreen window where this peer is
702 // mostly drawn to.
703 // TODO: check for "use platform metrics" settings
704 final Graphics g = getOnscreenGraphics();
705 if (g != null) {
706 try {
707 return g.getFontMetrics(f);
708 } finally {
709 g.dispose();
710 }
711 }
712 synchronized (getDelegateLock()) {
713 return delegateContainer.getFontMetrics(f);
714 }
715 }
716
717 @Override
718 public void setEnabled(final boolean e) {
719 boolean status = e;
720 final LWComponentPeer cp = getContainerPeer();
721 if (cp != null) {
722 status &= cp.isEnabled();
723 }
724 synchronized (getStateLock()) {
725 if (enabled == status) {
726 return;
727 }
728 enabled = status;
729 }
730
731 final D delegate = getDelegate();
732
733 if (delegate != null) {
734 synchronized (getDelegateLock()) {
735 delegate.setEnabled(status);
736 }
737 } else {
738 repaintPeer();
739 }
740 }
785 }
786
787 @Override
788 public void print(final Graphics g) {
789 getTarget().print(g);
790 }
791
792 @Override
793 public void reparent(ContainerPeer newContainer) {
794 // TODO: not implemented
795 throw new UnsupportedOperationException("ComponentPeer.reparent()");
796 }
797
798 @Override
799 public boolean isReparentSupported() {
800 // TODO: not implemented
801 return false;
802 }
803
804 @Override
805 public void setZOrder(ComponentPeer above) {
806 LWContainerPeer cp = getContainerPeer();
807 // Don't check containerPeer for null as it can only happen
808 // for windows, but this method is overridden in
809 // LWWindowPeer and doesn't call super()
810 cp.setChildPeerZOrder(this, (LWComponentPeer) above);
811 }
812
813 @Override
814 public void coalescePaintEvent(PaintEvent e) {
815 if (!(e instanceof IgnorePaintEvent)) {
816 Rectangle r = e.getUpdateRect();
817 if ((r != null) && !r.isEmpty()) {
818 targetPaintArea.add(r, e.getID());
819 }
820 }
821 }
822
823 /*
824 * Should be overridden in subclasses which use complex Swing components.
825 */
826 @Override
827 public void layout() {
828 // TODO: not implemented
829 }
830
906 }
907 if (LWKeyboardFocusManagerPeer.processSynchronousLightweightTransfer(
908 getTarget(), lightweightChild, temporary,
909 focusedWindowChangeAllowed, time)) {
910 return true;
911 }
912
913 int result = LWKeyboardFocusManagerPeer.shouldNativelyFocusHeavyweight(
914 getTarget(), lightweightChild, temporary,
915 focusedWindowChangeAllowed, time, cause);
916 switch (result) {
917 case LWKeyboardFocusManagerPeer.SNFH_FAILURE:
918 return false;
919 case LWKeyboardFocusManagerPeer.SNFH_SUCCESS_PROCEED:
920 Window parentWindow = SunToolkit.getContainingWindow(getTarget());
921 if (parentWindow == null) {
922 focusLog.fine("request rejected, parentWindow is null");
923 LWKeyboardFocusManagerPeer.removeLastFocusRequest(getTarget());
924 return false;
925 }
926 LWWindowPeer parentPeer = (LWWindowPeer) parentWindow.getPeer();
927 if (parentPeer == null) {
928 focusLog.fine("request rejected, parentPeer is null");
929 LWKeyboardFocusManagerPeer.removeLastFocusRequest(getTarget());
930 return false;
931 }
932
933 // A fix for 7145768. Ensure the parent window is currently natively focused.
934 // The more evident place to perform this check is in KFM.shouldNativelyFocusHeavyweight,
935 // however that is the shared code and this particular problem's reproducibility has
936 // platform specifics. So, it was decided to narrow down the fix to lwawt (OSX) in
937 // current release. TODO: consider fixing it in the shared code.
938 if (!focusedWindowChangeAllowed) {
939 LWWindowPeer decoratedPeer = parentPeer.isSimpleWindow() ?
940 LWWindowPeer.getOwnerFrameDialog(parentPeer) : parentPeer;
941
942 if (decoratedPeer == null || !decoratedPeer.getPlatformWindow().isActive()) {
943 if (focusLog.isLoggable(PlatformLogger.Level.FINE)) {
944 focusLog.fine("request rejected, focusedWindowChangeAllowed==false, " +
945 "decoratedPeer is inactive: " + decoratedPeer);
946 }
1121 if (backBuffer != null) {
1122 oldBB = backBuffer;
1123 backBuffer = getLWGC().createBackBuffer(this);
1124 }
1125 }
1126 getLWGC().destroyBackBuffer(oldBB);
1127
1128 if (updateTarget) {
1129 AWTAccessor.getComponentAccessor().setSize(getTarget(), w, h);
1130 }
1131 postEvent(new ComponentEvent(getTarget(),
1132 ComponentEvent.COMPONENT_RESIZED));
1133 }
1134
1135 protected final void repaintOldNewBounds(final Rectangle oldB) {
1136 repaintParent(oldB);
1137 repaintPeer(getSize());
1138 }
1139
1140 protected final void repaintParent(final Rectangle oldB) {
1141 final LWContainerPeer cp = getContainerPeer();
1142 if (cp != null) {
1143 // Repaint unobscured part of the parent
1144 cp.repaintPeer(cp.getContentSize().intersection(oldB));
1145 }
1146 }
1147
1148 // ---- EVENTS ---- //
1149
1150 /**
1151 * Post an event to the proper Java EDT.
1152 */
1153 public void postEvent(AWTEvent event) {
1154 SunToolkit.postEvent(getAppContext(), event);
1155 }
1156
1157 protected void postPaintEvent(int x, int y, int w, int h) {
1158 // TODO: call getIgnoreRepaint() directly with the right ACC
1159 if (AWTAccessor.getComponentAccessor().getIgnoreRepaint(target)) {
1160 return;
1161 }
1258 ke.getExtendedKeyCode());
1259 } else if (e instanceof FocusEvent) {
1260 FocusEvent fe = (FocusEvent) e;
1261 delegateEvent = new FocusEvent(getDelegateFocusOwner(), fe.getID(), fe.isTemporary());
1262 }
1263 return delegateEvent;
1264 }
1265
1266 protected void handleJavaMouseEvent(MouseEvent e) {
1267 Component target = getTarget();
1268 assert (e.getSource() == target);
1269
1270 if (!target.isFocusOwner() && LWKeyboardFocusManagerPeer.shouldFocusOnClick(target)) {
1271 LWKeyboardFocusManagerPeer.requestFocusFor(target, CausedFocusEvent.Cause.MOUSE_EVENT);
1272 }
1273 }
1274
1275 /**
1276 * Handler for FocusEvents.
1277 */
1278 protected void handleJavaFocusEvent(FocusEvent e) {
1279 // Note that the peer receives all the FocusEvents from
1280 // its lightweight children as well
1281 KeyboardFocusManagerPeer kfmPeer = LWKeyboardFocusManagerPeer.getInstance();
1282 kfmPeer.setCurrentFocusOwner(e.getID() == FocusEvent.FOCUS_GAINED ? getTarget() : null);
1283 }
1284
1285 /**
1286 * All peers should clear background before paint.
1287 *
1288 * @return false on components that DO NOT require a clearRect() before
1289 * painting.
1290 */
1291 protected final boolean shouldClearRectBeforePaint() {
1292 // TODO: sun.awt.noerasebackground
1293 return true;
1294 }
1295
1296 /**
1297 * Handler for PAINT and UPDATE PaintEvents.
1298 */
1299 private void handleJavaPaintEvent() {
1300 // Skip all painting while layouting and all UPDATEs
1301 // while waiting for native paint
1302 // if (!isLayouting && !paintPending) {
1303 if (!isLayouting()) {
1304 targetPaintArea.paint(getTarget(), shouldClearRectBeforePaint());
1305 }
1306 }
1307
1308 // ---- UTILITY METHODS ---- //
1309
1310 /**
1311 * Finds a top-most visible component for the given point. The location is
1312 * specified relative to the peer's parent.
1313 */
1314 public LWComponentPeer findPeerAt(final int x, final int y) {
1315 final Rectangle r = getBounds();
1316 final Region sh = getRegion();
1317 final boolean found = isVisible() && sh.contains(x - r.x, y - r.y);
1318 return found ? this : null;
1319 }
1320
1321 /*
1322 * Translated the given point in Window coordinates to the point in
1323 * coordinates local to this component. The given window peer must be
1324 * the window where this component is in.
1325 */
1326 public Point windowToLocal(int x, int y, LWWindowPeer wp) {
1327 return windowToLocal(new Point(x, y), wp);
1328 }
1329
1330 public Point windowToLocal(Point p, LWWindowPeer wp) {
1331 LWComponentPeer cp = this;
1332 while (cp != wp) {
1333 Rectangle cpb = cp.getBounds();
1334 p.x -= cpb.x;
1335 p.y -= cpb.y;
1336 cp = cp.getContainerPeer();
1337 }
1338 // Return a copy to prevent subsequent modifications
1339 return new Point(p);
1340 }
1341
1342 public Rectangle windowToLocal(Rectangle r, LWWindowPeer wp) {
1343 Point p = windowToLocal(r.getLocation(), wp);
1344 return new Rectangle(p, r.getSize());
1345 }
1346
1347 public Point localToWindow(int x, int y) {
1348 return localToWindow(new Point(x, y));
1349 }
1350
1351 public Point localToWindow(Point p) {
1352 LWComponentPeer cp = getContainerPeer();
1353 Rectangle r = getBounds();
1354 while (cp != null) {
1355 p.x += r.x;
1356 p.y += r.y;
1357 r = cp.getBounds();
1358 cp = cp.getContainerPeer();
1359 }
1360 // Return a copy to prevent subsequent modifications
1361 return new Point(p);
1362 }
1363
1364 public Rectangle localToWindow(Rectangle r) {
1365 Point p = localToWindow(r.getLocation());
1366 return new Rectangle(p, r.getSize());
1367 }
1368
1369 public final void repaintPeer() {
1370 repaintPeer(getSize());
1371 }
1372
1373 public void repaintPeer(final Rectangle r) {
1374 final Rectangle toPaint = getSize().intersection(r);
1375 if (!isShowing() || toPaint.isEmpty()) {
1376 return;
1377 }
1378
1379 postPaintEvent(toPaint.x, toPaint.y, toPaint.width, toPaint.height);
1380 }
1381
1382 /**
1383 * Determines whether this peer is showing on screen. This means that the
1384 * peer must be visible, and it must be in a container that is visible and
1385 * showing.
1386 *
1387 * @see #isVisible()
1388 */
1389 protected final boolean isShowing() {
1390 synchronized (getPeerTreeLock()) {
1391 if (isVisible()) {
1392 final LWContainerPeer container = getContainerPeer();
1393 return (container == null) || container.isShowing();
1394 }
1395 }
1396 return false;
1397 }
1398
1399 /**
1400 * Paints the peer. Overridden in subclasses to delegate the actual painting
1401 * to Swing components.
1402 */
1403 protected final void paintPeer(final Graphics g) {
1404 final D delegate = getDelegate();
1405 if (delegate != null) {
1406 if (!SwingUtilities.isEventDispatchThread()) {
1407 throw new InternalError("Painting must be done on EDT");
1408 }
1409 synchronized (getDelegateLock()) {
1410 // JComponent.print() is guaranteed to not affect the double buffer
1411 getDelegate().print(g);
1412 }
1413 }
1414 }
1415
1416 protected static final void flushOnscreenGraphics(){
1417 final OGLRenderQueue rq = OGLRenderQueue.getInstance();
1418 rq.lock();
1419 try {
1420 rq.flushNow();
1421 } finally {
|
1 /*
2 * Copyright (c) 2011, 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
55
56 import sun.java2d.SunGraphics2D;
57 import sun.java2d.opengl.OGLRenderQueue;
58 import sun.java2d.pipe.Region;
59
60 import sun.util.logging.PlatformLogger;
61
62 import javax.swing.JComponent;
63 import javax.swing.SwingUtilities;
64 import javax.swing.RepaintManager;
65
66 import sun.lwawt.macosx.CDropTarget;
67
68 import com.sun.java.swing.SwingUtilities3;
69
70 public abstract class LWComponentPeer<T extends Component, D extends JComponent>
71 implements ComponentPeer, DropTargetPeer
72 {
73 private static final PlatformLogger focusLog = PlatformLogger.getLogger("sun.lwawt.focus.LWComponentPeer");
74
75 /**
76 * State lock is to be used for modifications to this peer's fields (e.g.
77 * bounds, background, font, etc.) It should be the last lock in the lock
78 * chain
79 */
80 private final Object stateLock = new Object();
81
82 /**
83 * The lock to operate with the peers hierarchy. AWT tree lock is not used
84 * as there are many peers related ops to be done on the toolkit thread, and
85 * we don't want to depend on a public lock on this thread
86 */
87 private static final Object peerTreeLock = new Object();
88
89 /**
90 * The associated AWT object.
91 */
92 private final T target;
93
94 /**
95 * Container peer. It may not be the peer of the target's direct parent, for
96 * example, in the case of hw/lw mixing. However, let's skip this scenario
97 * for the time being. We also assume the container peer is not null, which
98 * might also be false if addNotify() is called for a component outside of
99 * the hierarchy. The exception is LWWindowPeers: their containers are
100 * always null
101 */
102 private final LWContainerPeer<?, ?> containerPeer;
103
104 /**
105 * Handy reference to the top-level window peer. Window peer is borrowed
106 * from the containerPeer in constructor, and should also be updated when
107 * the component is reparented to another container
108 */
109 private final LWWindowPeer windowPeer;
110
111 private final AtomicBoolean disposed = new AtomicBoolean(false);
112
113 // Bounds are relative to parent peer
114 private final Rectangle bounds = new Rectangle();
115 private Region region;
116
117 // Component state. Should be accessed under the state lock
118 private boolean visible = false;
119 private boolean enabled = true;
120
121 private Color background;
122 private Color foreground;
134 private final D delegate;
135 private Container delegateContainer;
136 private Component delegateDropTarget;
137 private final Object dropTargetLock = new Object();
138
139 private int fNumDropTargets = 0;
140 private CDropTarget fDropTarget = null;
141
142 private final PlatformComponent platformComponent;
143
144 /**
145 * Character with reasonable value between the minimum width and maximum.
146 */
147 static final char WIDE_CHAR = '0';
148
149 /**
150 * The back buffer provide user with a BufferStrategy.
151 */
152 private Image backBuffer;
153
154 /**
155 * All Swing delegates use delegateContainer as a parent. This container
156 * intentionally do not use parent of the peer.
157 */
158 @SuppressWarnings("serial")// Safe: outer class is non-serializable.
159 private final class DelegateContainer extends Container {
160 {
161 enableEvents(0xFFFFFFFF);
162 }
163
164 // Empty non private constructor was added because access to this
165 // class shouldn't be emulated by a synthetic accessor method.
166 DelegateContainer() {
167 super();
168 }
169
170 @Override
171 public boolean isLightweight() {
172 return false;
173 }
174
175 @Override
176 public Point getLocation() {
177 return getLocationOnScreen();
178 }
179
180 @Override
181 public Point getLocationOnScreen() {
182 return LWComponentPeer.this.getLocationOnScreen();
183 }
184
185 @Override
186 public int getX() {
187 return getLocation().x;
188 }
189
190 @Override
191 public int getY() {
192 return getLocation().y;
193 }
194 }
195
196 LWComponentPeer(final T target, final PlatformComponent platformComponent) {
197 targetPaintArea = new LWRepaintArea();
198 this.target = target;
199 this.platformComponent = platformComponent;
200
201 // Container peer is always null for LWWindowPeers, so
202 // windowPeer is always null for them as well. On the other
203 // hand, LWWindowPeer shouldn't use windowPeer at all
204 final Container container = SunToolkit.getNativeContainer(target);
205 containerPeer = (LWContainerPeer) LWToolkit.targetToPeer(container);
206 windowPeer = containerPeer != null ? containerPeer.getWindowPeerOrSelf()
207 : null;
208 // don't bother about z-order here as updateZOrder()
209 // will be called from addNotify() later anyway
210 if (containerPeer != null) {
211 containerPeer.addChildPeer(this);
212 }
213
214 // the delegate must be created after the target is set
215 AWTEventListener toolkitListener = null;
216 synchronized (Toolkit.getDefaultToolkit()) {
270 protected final void setToolkitAWTEventListener(final AWTEventListener listener) {
271 AccessController.doPrivileged(new PrivilegedAction<Void>() {
272 public Void run() {
273 Toolkit toolkit = Toolkit.getDefaultToolkit();
274 try {
275 Field field = Toolkit.class.getDeclaredField("eventListener");
276 field.setAccessible(true);
277 field.set(toolkit, listener);
278 } catch (Exception e) {
279 throw new InternalError(e.toString());
280 }
281 return null;
282 }
283 });
284 }
285
286 /**
287 * This method is called under getDelegateLock().
288 * Overridden in subclasses.
289 */
290 D createDelegate() {
291 return null;
292 }
293
294 final D getDelegate() {
295 return delegate;
296 }
297
298 /**
299 * This method should be called under getDelegateLock().
300 */
301 Component getDelegateFocusOwner() {
302 return getDelegate();
303 }
304
305 /**
306 * Initializes this peer. The call to initialize() is not placed to
307 * LWComponentPeer ctor to let the subclass ctor to finish completely first.
308 * Instead, it's the LWToolkit object who is responsible for initialization.
309 * Note that we call setVisible() at the end of initialization.
310 */
311 public final void initialize() {
312 platformComponent.initialize(getPlatformWindow());
313 initializeImpl();
314 setVisible(target.isVisible());
315 }
316
317 /**
318 * Fetching general properties from the target. Should be overridden in
319 * subclasses to initialize specific peers properties.
320 */
321 void initializeImpl() {
353 protected static final Object getPeerTreeLock() {
354 return peerTreeLock;
355 }
356
357 public final T getTarget() {
358 return target;
359 }
360
361 // Just a helper method
362 // Returns the window peer or null if this is a window peer
363 protected final LWWindowPeer getWindowPeer() {
364 return windowPeer;
365 }
366
367 // Returns the window peer or 'this' if this is a window peer
368 protected LWWindowPeer getWindowPeerOrSelf() {
369 return getWindowPeer();
370 }
371
372 // Just a helper method
373 protected final LWContainerPeer<?, ?> getContainerPeer() {
374 return containerPeer;
375 }
376
377 public PlatformWindow getPlatformWindow() {
378 LWWindowPeer windowPeer = getWindowPeer();
379 return windowPeer.getPlatformWindow();
380 }
381
382 protected AppContext getAppContext() {
383 return SunToolkit.targetToAppContext(getTarget());
384 }
385
386 // ---- PEER METHODS ---- //
387
388 @Override
389 public Toolkit getToolkit() {
390 return LWToolkit.getLWToolkit();
391 }
392
393 // Just a helper method
394 public LWToolkit getLWToolkit() {
395 return LWToolkit.getLWToolkit();
396 }
397
398 @Override
399 public final void dispose() {
400 if (disposed.compareAndSet(false, true)) {
401 disposeImpl();
402 }
403 }
404
405 protected void disposeImpl() {
406 destroyBuffers();
407 LWContainerPeer<?, ?> cp = getContainerPeer();
408 if (cp != null) {
409 cp.removeChildPeer(this);
410 }
411 platformComponent.dispose();
412 LWToolkit.targetDisposedPeer(getTarget(), this);
413 }
414
415 public final boolean isDisposed() {
416 return disposed.get();
417 }
418
419 /*
420 * GraphicsConfiguration is borrowed from the parent peer. The
421 * return value must not be null.
422 *
423 * Overridden in LWWindowPeer.
424 */
425 @Override
426 public GraphicsConfiguration getGraphicsConfiguration() {
427 // Don't check windowPeer for null as it can only happen
459 }
460
461 /*
462 * Peer Graphics is borrowed from the parent peer, while
463 * foreground and background colors and font are specific to
464 * this peer.
465 */
466 public final Graphics getOnscreenGraphics() {
467 final LWWindowPeer wp = getWindowPeerOrSelf();
468 return wp.getOnscreenGraphics(getForeground(), getBackground(),
469 getFont());
470
471 }
472
473 private void applyConstrain(final Graphics g) {
474 final SunGraphics2D sg2d = (SunGraphics2D) g;
475 final Rectangle size = localToWindow(getSize());
476 sg2d.constrain(size.x, size.y, size.width, size.height, getVisibleRegion());
477 }
478
479 Region getVisibleRegion() {
480 return computeVisibleRect(this, getRegion());
481 }
482
483 static final Region computeVisibleRect(final LWComponentPeer<?, ?> c,
484 Region region) {
485 final LWContainerPeer<?, ?> p = c.getContainerPeer();
486 if (p != null) {
487 final Rectangle r = c.getBounds();
488 region = region.getTranslatedRegion(r.x, r.y);
489 region = region.getIntersection(p.getRegion());
490 region = region.getIntersection(p.getContentSize());
491 region = p.cutChildren(region, c);
492 region = computeVisibleRect(p, region);
493 region = region.getTranslatedRegion(-r.x, -r.y);
494 }
495 return region;
496 }
497
498 @Override
499 public ColorModel getColorModel() {
500 // Is it a correct implementation?
501 return getGraphicsConfiguration().getColorModel();
502 }
503
504 public boolean isTranslucent() {
505 // Translucent windows of the top level are supported only
610 // Return a copy to prevent subsequent modifications
611 return new Rectangle(bounds.width, bounds.height);
612 }
613 }
614
615 @Override
616 public Point getLocationOnScreen() {
617 Point windowLocation = getWindowPeer().getLocationOnScreen();
618 Point locationInWindow = localToWindow(0, 0);
619 return new Point(windowLocation.x + locationInWindow.x,
620 windowLocation.y + locationInWindow.y);
621 }
622
623 /**
624 * Returns the cursor of the peer, which is cursor of the target by default,
625 * but peer can override this behavior.
626 *
627 * @param p Point relative to the peer.
628 * @return Cursor of the peer or null if default cursor should be used.
629 */
630 Cursor getCursor(final Point p) {
631 return getTarget().getCursor();
632 }
633
634 @Override
635 public void setBackground(final Color c) {
636 final Color oldBg = getBackground();
637 if (oldBg == c || (oldBg != null && oldBg.equals(c))) {
638 return;
639 }
640 synchronized (getStateLock()) {
641 background = c;
642 }
643 final D delegate = getDelegate();
644 if (delegate != null) {
645 synchronized (getDelegateLock()) {
646 // delegate will repaint the target
647 delegate.setBackground(c);
648 }
649 } else {
650 repaintPeer();
715 // return getWindowPeer().getFontMetrics(f);
716 // Obtain the metrics from the offscreen window where this peer is
717 // mostly drawn to.
718 // TODO: check for "use platform metrics" settings
719 final Graphics g = getOnscreenGraphics();
720 if (g != null) {
721 try {
722 return g.getFontMetrics(f);
723 } finally {
724 g.dispose();
725 }
726 }
727 synchronized (getDelegateLock()) {
728 return delegateContainer.getFontMetrics(f);
729 }
730 }
731
732 @Override
733 public void setEnabled(final boolean e) {
734 boolean status = e;
735 final LWComponentPeer<?, ?> cp = getContainerPeer();
736 if (cp != null) {
737 status &= cp.isEnabled();
738 }
739 synchronized (getStateLock()) {
740 if (enabled == status) {
741 return;
742 }
743 enabled = status;
744 }
745
746 final D delegate = getDelegate();
747
748 if (delegate != null) {
749 synchronized (getDelegateLock()) {
750 delegate.setEnabled(status);
751 }
752 } else {
753 repaintPeer();
754 }
755 }
800 }
801
802 @Override
803 public void print(final Graphics g) {
804 getTarget().print(g);
805 }
806
807 @Override
808 public void reparent(ContainerPeer newContainer) {
809 // TODO: not implemented
810 throw new UnsupportedOperationException("ComponentPeer.reparent()");
811 }
812
813 @Override
814 public boolean isReparentSupported() {
815 // TODO: not implemented
816 return false;
817 }
818
819 @Override
820 public void setZOrder(final ComponentPeer above) {
821 LWContainerPeer<?, ?> cp = getContainerPeer();
822 // Don't check containerPeer for null as it can only happen
823 // for windows, but this method is overridden in
824 // LWWindowPeer and doesn't call super()
825 cp.setChildPeerZOrder(this, (LWComponentPeer<?, ?>) above);
826 }
827
828 @Override
829 public void coalescePaintEvent(PaintEvent e) {
830 if (!(e instanceof IgnorePaintEvent)) {
831 Rectangle r = e.getUpdateRect();
832 if ((r != null) && !r.isEmpty()) {
833 targetPaintArea.add(r, e.getID());
834 }
835 }
836 }
837
838 /*
839 * Should be overridden in subclasses which use complex Swing components.
840 */
841 @Override
842 public void layout() {
843 // TODO: not implemented
844 }
845
921 }
922 if (LWKeyboardFocusManagerPeer.processSynchronousLightweightTransfer(
923 getTarget(), lightweightChild, temporary,
924 focusedWindowChangeAllowed, time)) {
925 return true;
926 }
927
928 int result = LWKeyboardFocusManagerPeer.shouldNativelyFocusHeavyweight(
929 getTarget(), lightweightChild, temporary,
930 focusedWindowChangeAllowed, time, cause);
931 switch (result) {
932 case LWKeyboardFocusManagerPeer.SNFH_FAILURE:
933 return false;
934 case LWKeyboardFocusManagerPeer.SNFH_SUCCESS_PROCEED:
935 Window parentWindow = SunToolkit.getContainingWindow(getTarget());
936 if (parentWindow == null) {
937 focusLog.fine("request rejected, parentWindow is null");
938 LWKeyboardFocusManagerPeer.removeLastFocusRequest(getTarget());
939 return false;
940 }
941 final LWWindowPeer parentPeer =
942 (LWWindowPeer) AWTAccessor.getComponentAccessor()
943 .getPeer(parentWindow);
944 if (parentPeer == null) {
945 focusLog.fine("request rejected, parentPeer is null");
946 LWKeyboardFocusManagerPeer.removeLastFocusRequest(getTarget());
947 return false;
948 }
949
950 // A fix for 7145768. Ensure the parent window is currently natively focused.
951 // The more evident place to perform this check is in KFM.shouldNativelyFocusHeavyweight,
952 // however that is the shared code and this particular problem's reproducibility has
953 // platform specifics. So, it was decided to narrow down the fix to lwawt (OSX) in
954 // current release. TODO: consider fixing it in the shared code.
955 if (!focusedWindowChangeAllowed) {
956 LWWindowPeer decoratedPeer = parentPeer.isSimpleWindow() ?
957 LWWindowPeer.getOwnerFrameDialog(parentPeer) : parentPeer;
958
959 if (decoratedPeer == null || !decoratedPeer.getPlatformWindow().isActive()) {
960 if (focusLog.isLoggable(PlatformLogger.Level.FINE)) {
961 focusLog.fine("request rejected, focusedWindowChangeAllowed==false, " +
962 "decoratedPeer is inactive: " + decoratedPeer);
963 }
1138 if (backBuffer != null) {
1139 oldBB = backBuffer;
1140 backBuffer = getLWGC().createBackBuffer(this);
1141 }
1142 }
1143 getLWGC().destroyBackBuffer(oldBB);
1144
1145 if (updateTarget) {
1146 AWTAccessor.getComponentAccessor().setSize(getTarget(), w, h);
1147 }
1148 postEvent(new ComponentEvent(getTarget(),
1149 ComponentEvent.COMPONENT_RESIZED));
1150 }
1151
1152 protected final void repaintOldNewBounds(final Rectangle oldB) {
1153 repaintParent(oldB);
1154 repaintPeer(getSize());
1155 }
1156
1157 protected final void repaintParent(final Rectangle oldB) {
1158 final LWContainerPeer<?, ?> cp = getContainerPeer();
1159 if (cp != null) {
1160 // Repaint unobscured part of the parent
1161 cp.repaintPeer(cp.getContentSize().intersection(oldB));
1162 }
1163 }
1164
1165 // ---- EVENTS ---- //
1166
1167 /**
1168 * Post an event to the proper Java EDT.
1169 */
1170 public void postEvent(AWTEvent event) {
1171 SunToolkit.postEvent(getAppContext(), event);
1172 }
1173
1174 protected void postPaintEvent(int x, int y, int w, int h) {
1175 // TODO: call getIgnoreRepaint() directly with the right ACC
1176 if (AWTAccessor.getComponentAccessor().getIgnoreRepaint(target)) {
1177 return;
1178 }
1275 ke.getExtendedKeyCode());
1276 } else if (e instanceof FocusEvent) {
1277 FocusEvent fe = (FocusEvent) e;
1278 delegateEvent = new FocusEvent(getDelegateFocusOwner(), fe.getID(), fe.isTemporary());
1279 }
1280 return delegateEvent;
1281 }
1282
1283 protected void handleJavaMouseEvent(MouseEvent e) {
1284 Component target = getTarget();
1285 assert (e.getSource() == target);
1286
1287 if (!target.isFocusOwner() && LWKeyboardFocusManagerPeer.shouldFocusOnClick(target)) {
1288 LWKeyboardFocusManagerPeer.requestFocusFor(target, CausedFocusEvent.Cause.MOUSE_EVENT);
1289 }
1290 }
1291
1292 /**
1293 * Handler for FocusEvents.
1294 */
1295 void handleJavaFocusEvent(final FocusEvent e) {
1296 // Note that the peer receives all the FocusEvents from
1297 // its lightweight children as well
1298 KeyboardFocusManagerPeer kfmPeer = LWKeyboardFocusManagerPeer.getInstance();
1299 kfmPeer.setCurrentFocusOwner(e.getID() == FocusEvent.FOCUS_GAINED ? getTarget() : null);
1300 }
1301
1302 /**
1303 * All peers should clear background before paint.
1304 *
1305 * @return false on components that DO NOT require a clearRect() before
1306 * painting.
1307 */
1308 protected final boolean shouldClearRectBeforePaint() {
1309 // TODO: sun.awt.noerasebackground
1310 return true;
1311 }
1312
1313 /**
1314 * Handler for PAINT and UPDATE PaintEvents.
1315 */
1316 private void handleJavaPaintEvent() {
1317 // Skip all painting while layouting and all UPDATEs
1318 // while waiting for native paint
1319 // if (!isLayouting && !paintPending) {
1320 if (!isLayouting()) {
1321 targetPaintArea.paint(getTarget(), shouldClearRectBeforePaint());
1322 }
1323 }
1324
1325 // ---- UTILITY METHODS ---- //
1326
1327 /**
1328 * Finds a top-most visible component for the given point. The location is
1329 * specified relative to the peer's parent.
1330 */
1331 LWComponentPeer<?, ?> findPeerAt(final int x, final int y) {
1332 final Rectangle r = getBounds();
1333 final Region sh = getRegion();
1334 final boolean found = isVisible() && sh.contains(x - r.x, y - r.y);
1335 return found ? this : null;
1336 }
1337
1338 /*
1339 * Translated the given point in Window coordinates to the point in
1340 * coordinates local to this component. The given window peer must be
1341 * the window where this component is in.
1342 */
1343 public Point windowToLocal(int x, int y, LWWindowPeer wp) {
1344 return windowToLocal(new Point(x, y), wp);
1345 }
1346
1347 public Point windowToLocal(Point p, LWWindowPeer wp) {
1348 LWComponentPeer<?, ?> cp = this;
1349 while (cp != wp) {
1350 Rectangle cpb = cp.getBounds();
1351 p.x -= cpb.x;
1352 p.y -= cpb.y;
1353 cp = cp.getContainerPeer();
1354 }
1355 // Return a copy to prevent subsequent modifications
1356 return new Point(p);
1357 }
1358
1359 public Rectangle windowToLocal(Rectangle r, LWWindowPeer wp) {
1360 Point p = windowToLocal(r.getLocation(), wp);
1361 return new Rectangle(p, r.getSize());
1362 }
1363
1364 public Point localToWindow(int x, int y) {
1365 return localToWindow(new Point(x, y));
1366 }
1367
1368 public Point localToWindow(Point p) {
1369 LWComponentPeer<?, ?> cp = getContainerPeer();
1370 Rectangle r = getBounds();
1371 while (cp != null) {
1372 p.x += r.x;
1373 p.y += r.y;
1374 r = cp.getBounds();
1375 cp = cp.getContainerPeer();
1376 }
1377 // Return a copy to prevent subsequent modifications
1378 return new Point(p);
1379 }
1380
1381 public Rectangle localToWindow(Rectangle r) {
1382 Point p = localToWindow(r.getLocation());
1383 return new Rectangle(p, r.getSize());
1384 }
1385
1386 public final void repaintPeer() {
1387 repaintPeer(getSize());
1388 }
1389
1390 void repaintPeer(final Rectangle r) {
1391 final Rectangle toPaint = getSize().intersection(r);
1392 if (!isShowing() || toPaint.isEmpty()) {
1393 return;
1394 }
1395
1396 postPaintEvent(toPaint.x, toPaint.y, toPaint.width, toPaint.height);
1397 }
1398
1399 /**
1400 * Determines whether this peer is showing on screen. This means that the
1401 * peer must be visible, and it must be in a container that is visible and
1402 * showing.
1403 *
1404 * @see #isVisible()
1405 */
1406 protected final boolean isShowing() {
1407 synchronized (getPeerTreeLock()) {
1408 if (isVisible()) {
1409 final LWContainerPeer<?, ?> container = getContainerPeer();
1410 return (container == null) || container.isShowing();
1411 }
1412 }
1413 return false;
1414 }
1415
1416 /**
1417 * Paints the peer. Delegate the actual painting to Swing components.
1418 */
1419 protected final void paintPeer(final Graphics g) {
1420 final D delegate = getDelegate();
1421 if (delegate != null) {
1422 if (!SwingUtilities.isEventDispatchThread()) {
1423 throw new InternalError("Painting must be done on EDT");
1424 }
1425 synchronized (getDelegateLock()) {
1426 // JComponent.print() is guaranteed to not affect the double buffer
1427 getDelegate().print(g);
1428 }
1429 }
1430 }
1431
1432 protected static final void flushOnscreenGraphics(){
1433 final OGLRenderQueue rq = OGLRenderQueue.getInstance();
1434 rq.lock();
1435 try {
1436 rq.flushNow();
1437 } finally {
|