1 /* 2 * Copyright (c) 2011, 2019, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package sun.lwawt; 27 28 import java.awt.AlphaComposite; 29 import java.awt.Color; 30 import java.awt.Component; 31 import java.awt.Dialog; 32 import java.awt.Dimension; 33 import java.awt.Font; 34 import java.awt.FontMetrics; 35 import java.awt.Frame; 36 import java.awt.Graphics; 37 import java.awt.Graphics2D; 38 import java.awt.GraphicsConfiguration; 39 import java.awt.GraphicsDevice; 40 import java.awt.GraphicsEnvironment; 41 import java.awt.Insets; 42 import java.awt.KeyboardFocusManager; 43 import java.awt.MenuBar; 44 import java.awt.Point; 45 import java.awt.Rectangle; 46 import java.awt.Shape; 47 import java.awt.SystemColor; 48 import java.awt.Toolkit; 49 import java.awt.Window; 50 import java.awt.event.FocusEvent; 51 import java.awt.event.KeyEvent; 52 import java.awt.event.MouseEvent; 53 import java.awt.event.MouseWheelEvent; 54 import java.awt.event.WindowEvent; 55 import java.awt.peer.ComponentPeer; 56 import java.awt.peer.DialogPeer; 57 import java.awt.peer.FramePeer; 58 import java.awt.peer.KeyboardFocusManagerPeer; 59 import java.awt.peer.WindowPeer; 60 import java.util.List; 61 62 import javax.swing.JComponent; 63 64 import sun.awt.AWTAccessor; 65 import sun.awt.AWTAccessor.ComponentAccessor; 66 import sun.awt.AppContext; 67 import sun.awt.CGraphicsDevice; 68 import sun.awt.DisplayChangedListener; 69 import sun.awt.ExtendedKeyCodes; 70 import sun.awt.FullScreenCapable; 71 import sun.awt.SunToolkit; 72 import sun.awt.TimedWindowEvent; 73 import sun.awt.UngrabEvent; 74 import sun.java2d.NullSurfaceData; 75 import sun.java2d.SunGraphics2D; 76 import sun.java2d.SunGraphicsEnvironment; 77 import sun.java2d.SurfaceData; 78 import sun.java2d.loops.Blit; 79 import sun.java2d.loops.CompositeType; 80 import sun.java2d.pipe.Region; 81 import sun.util.logging.PlatformLogger; 82 83 public class LWWindowPeer 84 extends LWContainerPeer<Window, JComponent> 85 implements FramePeer, DialogPeer, FullScreenCapable, DisplayChangedListener, PlatformEventNotifier 86 { 87 public enum PeerType { 88 SIMPLEWINDOW, 89 FRAME, 90 DIALOG, 91 EMBEDDED_FRAME, 92 VIEW_EMBEDDED_FRAME, 93 LW_FRAME 94 } 95 96 private static final PlatformLogger focusLog = PlatformLogger.getLogger("sun.lwawt.focus.LWWindowPeer"); 97 98 private final PlatformWindow platformWindow; 99 100 private static final int MINIMUM_WIDTH = 1; 101 private static final int MINIMUM_HEIGHT = 1; 102 103 private Insets insets = new Insets(0, 0, 0, 0); 104 private Rectangle maximizedBounds; 105 106 private GraphicsDevice graphicsDevice; 107 private GraphicsConfiguration graphicsConfig; 108 109 private SurfaceData surfaceData; 110 private final Object surfaceDataLock = new Object(); 111 112 private volatile int windowState = Frame.NORMAL; 113 114 // check that the mouse is over the window 115 private volatile boolean isMouseOver = false; 116 117 // A peer where the last mouse event came to. Used by cursor manager to 118 // find the component under cursor 119 private static volatile LWComponentPeer<?, ?> lastCommonMouseEventPeer; 120 121 // A peer where the last mouse event came to. Used to generate 122 // MOUSE_ENTERED/EXITED notifications 123 private volatile LWComponentPeer<?, ?> lastMouseEventPeer; 124 125 // Peers where all dragged/released events should come to, 126 // depending on what mouse button is being dragged according to Cocoa 127 private static final LWComponentPeer<?, ?>[] mouseDownTarget = new LWComponentPeer<?, ?>[3]; 128 129 // A bitmask that indicates what mouse buttons produce MOUSE_CLICKED events 130 // on MOUSE_RELEASE. Click events are only generated if there were no drag 131 // events between MOUSE_PRESSED and MOUSE_RELEASED for particular button 132 private static int mouseClickButtons = 0; 133 134 private volatile boolean isOpaque = true; 135 136 private static final Font DEFAULT_FONT = new Font("Lucida Grande", Font.PLAIN, 13); 137 138 private static LWWindowPeer grabbingWindow; 139 140 private volatile boolean skipNextFocusChange; 141 142 private static final Color nonOpaqueBackground = new Color(0, 0, 0, 0); 143 144 private volatile boolean textured; 145 146 private final PeerType peerType; 147 148 private final SecurityWarningWindow warningWindow; 149 150 private volatile boolean targetFocusable; 151 152 /** 153 * Current modal blocker or null. 154 * 155 * Synchronization: peerTreeLock. 156 */ 157 private LWWindowPeer blocker; 158 LWWindowPeer(Window target, PlatformComponent platformComponent, PlatformWindow platformWindow, PeerType peerType)159 public LWWindowPeer(Window target, PlatformComponent platformComponent, 160 PlatformWindow platformWindow, PeerType peerType) 161 { 162 super(target, platformComponent); 163 this.platformWindow = platformWindow; 164 this.peerType = peerType; 165 166 Window owner = target.getOwner(); 167 LWWindowPeer ownerPeer = owner == null ? null : 168 (LWWindowPeer) AWTAccessor.getComponentAccessor().getPeer(owner); 169 PlatformWindow ownerDelegate = (ownerPeer != null) ? ownerPeer.getPlatformWindow() : null; 170 171 // The delegate.initialize() needs a non-null GC on X11. 172 GraphicsConfiguration gc = getTarget().getGraphicsConfiguration(); 173 synchronized (getStateLock()) { 174 // graphicsConfig should be updated according to the real window 175 // bounds when the window is shown, see 4868278 176 this.graphicsConfig = gc; 177 } 178 179 if (!target.isFontSet()) { 180 target.setFont(DEFAULT_FONT); 181 } 182 183 if (!target.isBackgroundSet()) { 184 target.setBackground(SystemColor.window); 185 } else { 186 // first we check if user provided alpha for background. This is 187 // similar to what Apple's Java do. 188 // Since JDK7 we should rely on setOpacity() only. 189 // this.opacity = c.getAlpha(); 190 } 191 192 if (!target.isForegroundSet()) { 193 target.setForeground(SystemColor.windowText); 194 // we should not call setForeground because it will call a repaint 195 // which the peer may not be ready to do yet. 196 } 197 198 platformWindow.initialize(target, this, ownerDelegate); 199 200 // Init warning window(for applets) 201 SecurityWarningWindow warn = null; 202 if (target.getWarningString() != null) { 203 // accessSystemTray permission allows to display TrayIcon, TrayIcon tooltip 204 // and TrayIcon balloon windows without a warning window. 205 if (!AWTAccessor.getWindowAccessor().isTrayIconWindow(target)) { 206 LWToolkit toolkit = (LWToolkit)Toolkit.getDefaultToolkit(); 207 warn = toolkit.createSecurityWarning(target, this); 208 } 209 } 210 211 warningWindow = warn; 212 } 213 214 @Override initializeImpl()215 void initializeImpl() { 216 super.initializeImpl(); 217 218 219 if (getTarget() instanceof Frame) { 220 Frame frame = (Frame) getTarget(); 221 setTitle(frame.getTitle()); 222 setState(frame.getExtendedState()); 223 setMaximizedBounds(frame.getMaximizedBounds()); 224 } else if (getTarget() instanceof Dialog) { 225 setTitle(((Dialog) getTarget()).getTitle()); 226 } 227 228 updateAlwaysOnTopState(); 229 updateMinimumSize(); 230 updateFocusableWindowState(); 231 232 final Shape shape = getTarget().getShape(); 233 if (shape != null) { 234 applyShape(Region.getInstance(shape, null)); 235 } 236 237 final float opacity = getTarget().getOpacity(); 238 if (opacity < 1.0f) { 239 setOpacity(opacity); 240 } 241 242 setOpaque(getTarget().isOpaque()); 243 244 updateInsets(platformWindow.getInsets()); 245 if (getSurfaceData() == null) { 246 replaceSurfaceData(false); 247 } 248 activateDisplayListener(); 249 } 250 251 // Just a helper method 252 @Override getPlatformWindow()253 public PlatformWindow getPlatformWindow() { 254 return platformWindow; 255 } 256 257 @Override getWindowPeerOrSelf()258 protected LWWindowPeer getWindowPeerOrSelf() { 259 return this; 260 } 261 262 // ---- PEER METHODS ---- // 263 264 @Override disposeImpl()265 protected void disposeImpl() { 266 deactivateDisplayListener(); 267 SurfaceData oldData = getSurfaceData(); 268 synchronized (surfaceDataLock){ 269 surfaceData = null; 270 } 271 if (oldData != null) { 272 oldData.invalidate(); 273 } 274 if (isGrabbing()) { 275 ungrab(); 276 } 277 if (warningWindow != null) { 278 warningWindow.dispose(); 279 } 280 281 platformWindow.dispose(); 282 super.disposeImpl(); 283 } 284 285 @Override setVisibleImpl(final boolean visible)286 protected void setVisibleImpl(final boolean visible) { 287 if (!visible && warningWindow != null) { 288 warningWindow.setVisible(false, false); 289 } 290 updateFocusableWindowState(); 291 super.setVisibleImpl(visible); 292 // TODO: update graphicsConfig, see 4868278 293 platformWindow.setVisible(visible); 294 if (isSimpleWindow()) { 295 KeyboardFocusManagerPeer kfmPeer = LWKeyboardFocusManagerPeer.getInstance(); 296 if (visible) { 297 if (!getTarget().isAutoRequestFocus()) { 298 return; 299 } else { 300 requestWindowFocus(FocusEvent.Cause.ACTIVATION); 301 } 302 // Focus the owner in case this window is focused. 303 } else if (kfmPeer.getCurrentFocusedWindow() == getTarget()) { 304 // Transfer focus to the owner. 305 LWWindowPeer owner = getOwnerFrameDialog(LWWindowPeer.this); 306 if (owner != null) { 307 owner.requestWindowFocus(FocusEvent.Cause.ACTIVATION); 308 } 309 } 310 } 311 } 312 313 @Override getGraphicsConfiguration()314 public final GraphicsConfiguration getGraphicsConfiguration() { 315 synchronized (getStateLock()) { 316 return graphicsConfig; 317 } 318 } 319 320 @Override updateGraphicsData(GraphicsConfiguration gc)321 public boolean updateGraphicsData(GraphicsConfiguration gc) { 322 setGraphicsConfig(gc); 323 return false; 324 } 325 getOnscreenGraphics(Color fg, Color bg, Font f)326 protected final Graphics getOnscreenGraphics(Color fg, Color bg, Font f) { 327 SurfaceData surfaceData = getSurfaceData(); 328 if (surfaceData == null) { 329 return null; 330 } 331 if (fg == null) { 332 fg = SystemColor.windowText; 333 } 334 if (bg == null) { 335 bg = SystemColor.window; 336 } 337 if (f == null) { 338 f = DEFAULT_FONT; 339 } 340 return new SunGraphics2D(surfaceData, fg, bg, f); 341 } 342 343 @Override setBounds(int x, int y, int w, int h, int op)344 public void setBounds(int x, int y, int w, int h, int op) { 345 346 if((op & NO_EMBEDDED_CHECK) == 0 && getPeerType() == PeerType.VIEW_EMBEDDED_FRAME) { 347 return; 348 } 349 350 if ((op & SET_CLIENT_SIZE) != 0) { 351 // SET_CLIENT_SIZE is only applicable to window peers, so handle it here 352 // instead of pulling 'insets' field up to LWComponentPeer 353 // no need to add insets since Window's notion of width and height includes insets. 354 op &= ~SET_CLIENT_SIZE; 355 op |= SET_SIZE; 356 } 357 358 // Don't post ComponentMoved/Resized and Paint events 359 // until we've got a notification from the delegate 360 Rectangle cb = constrainBounds(x, y, w, h); 361 362 Rectangle newBounds = new Rectangle(getBounds()); 363 if ((op & (SET_LOCATION | SET_BOUNDS)) != 0) { 364 newBounds.x = cb.x; 365 newBounds.y = cb.y; 366 } 367 if ((op & (SET_SIZE | SET_BOUNDS)) != 0) { 368 newBounds.width = cb.width; 369 newBounds.height = cb.height; 370 } 371 // Native system could constraint bounds, so the peer wold be updated in the callback 372 platformWindow.setBounds(newBounds.x, newBounds.y, newBounds.width, newBounds.height); 373 } 374 constrainBounds(Rectangle bounds)375 public Rectangle constrainBounds(Rectangle bounds) { 376 return constrainBounds(bounds.x, bounds.y, bounds.width, bounds.height); 377 } 378 constrainBounds(int x, int y, int w, int h)379 public Rectangle constrainBounds(int x, int y, int w, int h) { 380 381 if (w < MINIMUM_WIDTH) { 382 w = MINIMUM_WIDTH; 383 } 384 385 if (h < MINIMUM_HEIGHT) { 386 h = MINIMUM_HEIGHT; 387 } 388 389 final int maxW = getLWGC().getMaxTextureWidth(); 390 final int maxH = getLWGC().getMaxTextureHeight(); 391 392 if (w > maxW) { 393 w = maxW; 394 } 395 if (h > maxH) { 396 h = maxH; 397 } 398 399 return new Rectangle(x, y, w, h); 400 } 401 402 @Override getLocationOnScreen()403 public Point getLocationOnScreen() { 404 return platformWindow.getLocationOnScreen(); 405 } 406 407 /** 408 * Overridden from LWContainerPeer to return the correct insets. 409 * Insets are queried from the delegate and are kept up to date by 410 * requiering when needed (i.e. when the window geometry is changed). 411 */ 412 @Override getInsets()413 public Insets getInsets() { 414 synchronized (getStateLock()) { 415 return insets; 416 } 417 } 418 419 @Override getFontMetrics(Font f)420 public FontMetrics getFontMetrics(Font f) { 421 // TODO: check for "use platform metrics" settings 422 return platformWindow.getFontMetrics(f); 423 } 424 425 @Override toFront()426 public void toFront() { 427 platformWindow.toFront(); 428 } 429 430 @Override toBack()431 public void toBack() { 432 platformWindow.toBack(); 433 } 434 435 @Override setZOrder(ComponentPeer above)436 public void setZOrder(ComponentPeer above) { 437 throw new RuntimeException("not implemented"); 438 } 439 440 @Override updateAlwaysOnTopState()441 public void updateAlwaysOnTopState() { 442 platformWindow.setAlwaysOnTop(getTarget().isAlwaysOnTop()); 443 } 444 445 @Override updateFocusableWindowState()446 public void updateFocusableWindowState() { 447 targetFocusable = getTarget().isFocusableWindow(); 448 platformWindow.updateFocusableWindowState(); 449 } 450 451 @Override setModalBlocked(Dialog blocker, boolean blocked)452 public void setModalBlocked(Dialog blocker, boolean blocked) { 453 synchronized (getPeerTreeLock()) { 454 ComponentPeer peer = AWTAccessor.getComponentAccessor().getPeer(blocker); 455 if (blocked && (peer instanceof LWWindowPeer)) { 456 this.blocker = (LWWindowPeer) peer; 457 } else { 458 this.blocker = null; 459 } 460 } 461 462 platformWindow.setModalBlocked(blocked); 463 } 464 465 @Override updateMinimumSize()466 public void updateMinimumSize() { 467 final Dimension min; 468 if (getTarget().isMinimumSizeSet()) { 469 min = getTarget().getMinimumSize(); 470 min.width = Math.max(min.width, MINIMUM_WIDTH); 471 min.height = Math.max(min.height, MINIMUM_HEIGHT); 472 } else { 473 min = new Dimension(MINIMUM_WIDTH, MINIMUM_HEIGHT); 474 } 475 476 final Dimension max; 477 if (getTarget().isMaximumSizeSet()) { 478 max = getTarget().getMaximumSize(); 479 max.width = Math.min(max.width, getLWGC().getMaxTextureWidth()); 480 max.height = Math.min(max.height, getLWGC().getMaxTextureHeight()); 481 } else { 482 max = new Dimension(getLWGC().getMaxTextureWidth(), 483 getLWGC().getMaxTextureHeight()); 484 } 485 486 platformWindow.setSizeConstraints(min.width, min.height, max.width, max.height); 487 } 488 489 @Override updateIconImages()490 public void updateIconImages() { 491 getPlatformWindow().updateIconImages(); 492 } 493 494 @Override setBackground(final Color c)495 public void setBackground(final Color c) { 496 super.setBackground(c); 497 updateOpaque(); 498 } 499 500 @Override setOpacity(float opacity)501 public void setOpacity(float opacity) { 502 getPlatformWindow().setOpacity(opacity); 503 repaintPeer(); 504 } 505 506 @Override setOpaque(final boolean isOpaque)507 public final void setOpaque(final boolean isOpaque) { 508 if (this.isOpaque != isOpaque) { 509 this.isOpaque = isOpaque; 510 updateOpaque(); 511 } 512 } 513 updateOpaque()514 private void updateOpaque() { 515 getPlatformWindow().setOpaque(!isTranslucent()); 516 replaceSurfaceData(false); 517 repaintPeer(); 518 } 519 520 @Override updateWindow()521 public void updateWindow() { 522 } 523 isTextured()524 public final boolean isTextured() { 525 return textured; 526 } 527 setTextured(final boolean isTextured)528 public final void setTextured(final boolean isTextured) { 529 textured = isTextured; 530 } 531 532 @Override isTranslucent()533 public final boolean isTranslucent() { 534 synchronized (getStateLock()) { 535 /* 536 * Textured window is a special case of translucent window. 537 * The difference is only in nswindow background. So when we set 538 * texture property our peer became fully translucent. It doesn't 539 * fill background, create non opaque backbuffers and layer etc. 540 */ 541 return !isOpaque || isShaped() || isTextured(); 542 } 543 } 544 545 @Override applyShapeImpl(final Region shape)546 final void applyShapeImpl(final Region shape) { 547 super.applyShapeImpl(shape); 548 updateOpaque(); 549 } 550 551 @Override repositionSecurityWarning()552 public void repositionSecurityWarning() { 553 if (warningWindow != null) { 554 ComponentAccessor compAccessor = AWTAccessor.getComponentAccessor(); 555 Window target = getTarget(); 556 int x = compAccessor.getX(target); 557 int y = compAccessor.getY(target); 558 int width = compAccessor.getWidth(target); 559 int height = compAccessor.getHeight(target); 560 warningWindow.reposition(x, y, width, height); 561 } 562 } 563 564 // ---- FRAME PEER METHODS ---- // 565 566 @Override // FramePeer and DialogPeer setTitle(String title)567 public void setTitle(String title) { 568 platformWindow.setTitle(title == null ? "" : title); 569 } 570 571 @Override setMenuBar(MenuBar mb)572 public void setMenuBar(MenuBar mb) { 573 platformWindow.setMenuBar(mb); 574 } 575 576 @Override // FramePeer and DialogPeer setResizable(boolean resizable)577 public void setResizable(boolean resizable) { 578 platformWindow.setResizable(resizable); 579 } 580 581 @Override setState(int state)582 public void setState(int state) { 583 platformWindow.setWindowState(state); 584 } 585 586 @Override getState()587 public int getState() { 588 return windowState; 589 } 590 isMaximizedBoundsSet()591 private boolean isMaximizedBoundsSet() { 592 synchronized (getStateLock()) { 593 return maximizedBounds != null; 594 } 595 } 596 getDefaultMaximizedBounds()597 private Rectangle getDefaultMaximizedBounds() { 598 GraphicsConfiguration config = getGraphicsConfiguration(); 599 Insets screenInsets = ((CGraphicsDevice) config.getDevice()) 600 .getScreenInsets(); 601 Rectangle gcBounds = config.getBounds(); 602 return new Rectangle( 603 gcBounds.x + screenInsets.left, 604 gcBounds.y + screenInsets.top, 605 gcBounds.width - screenInsets.left - screenInsets.right, 606 gcBounds.height - screenInsets.top - screenInsets.bottom); 607 } 608 609 @Override setMaximizedBounds(Rectangle bounds)610 public void setMaximizedBounds(Rectangle bounds) { 611 boolean isMaximizedBoundsSet; 612 synchronized (getStateLock()) { 613 this.maximizedBounds = (isMaximizedBoundsSet = (bounds != null)) 614 ? constrainBounds(bounds) : null; 615 } 616 617 setPlatformMaximizedBounds(isMaximizedBoundsSet ? maximizedBounds 618 : getDefaultMaximizedBounds()); 619 } 620 getMaximizedBounds()621 public Rectangle getMaximizedBounds() { 622 synchronized (getStateLock()) { 623 return (maximizedBounds == null) 624 ? getDefaultMaximizedBounds() 625 : maximizedBounds; 626 } 627 } 628 setPlatformMaximizedBounds(Rectangle bounds)629 private void setPlatformMaximizedBounds(Rectangle bounds) { 630 platformWindow.setMaximizedBounds( 631 bounds.x, bounds.y, 632 bounds.width, bounds.height); 633 } 634 635 @Override setBoundsPrivate(int x, int y, int width, int height)636 public void setBoundsPrivate(int x, int y, int width, int height) { 637 setBounds(x, y, width, height, SET_BOUNDS | NO_EMBEDDED_CHECK); 638 } 639 640 @Override getBoundsPrivate()641 public Rectangle getBoundsPrivate() { 642 throw new RuntimeException("not implemented"); 643 } 644 645 // ---- DIALOG PEER METHODS ---- // 646 647 @Override blockWindows(List<Window> windows)648 public void blockWindows(List<Window> windows) { 649 //TODO: LWX will probably need some collectJavaToplevels to speed this up 650 for (Window w : windows) { 651 WindowPeer wp = AWTAccessor.getComponentAccessor().getPeer(w); 652 if (wp != null) { 653 wp.setModalBlocked((Dialog)getTarget(), true); 654 } 655 } 656 } 657 658 // ---- PEER NOTIFICATIONS ---- // 659 660 @Override notifyIconify(boolean iconify)661 public void notifyIconify(boolean iconify) { 662 //The toplevel target is Frame and states are applicable to it. 663 //Otherwise, the target is Window and it don't have state property. 664 //Hopefully, no such events are posted in the queue so consider the 665 //target as Frame in all cases. 666 667 // REMIND: should we send it anyway if the state not changed since last 668 // time? 669 WindowEvent iconifyEvent = new WindowEvent(getTarget(), 670 iconify ? WindowEvent.WINDOW_ICONIFIED 671 : WindowEvent.WINDOW_DEICONIFIED); 672 postEvent(iconifyEvent); 673 674 int newWindowState = iconify ? Frame.ICONIFIED : Frame.NORMAL; 675 postWindowStateChangedEvent(newWindowState); 676 677 // REMIND: RepaintManager doesn't repaint iconified windows and 678 // hence ignores any repaint request during deiconification. 679 // So, we need to repaint window explicitly when it becomes normal. 680 if (!iconify) { 681 repaintPeer(); 682 } 683 } 684 685 @Override notifyZoom(boolean isZoomed)686 public void notifyZoom(boolean isZoomed) { 687 int newWindowState = isZoomed ? Frame.MAXIMIZED_BOTH : Frame.NORMAL; 688 postWindowStateChangedEvent(newWindowState); 689 } 690 691 /** 692 * Called by the {@code PlatformWindow} when any part of the window should 693 * be repainted. 694 */ 695 @Override notifyExpose(final Rectangle r)696 public void notifyExpose(final Rectangle r) { 697 repaintPeer(r); 698 } 699 700 /** 701 * Called by the {@code PlatformWindow} when this window is moved/resized by 702 * user or window insets are changed. There's no notifyReshape() in 703 * LWComponentPeer as the only components which could be resized by user are 704 * top-level windows. 705 * <p> 706 * We need to update the target and post the events, if the peer was moved 707 * or resized, or if the target is out of sync with this peer. 708 */ 709 @Override notifyReshape(int x, int y, int w, int h)710 public void notifyReshape(int x, int y, int w, int h) { 711 final Rectangle pBounds = getBounds(); 712 final boolean invalid = updateInsets(platformWindow.getInsets()); 713 final boolean pMoved = (x != pBounds.x) || (y != pBounds.y); 714 final boolean pResized = (w != pBounds.width) || (h != pBounds.height); 715 716 final ComponentAccessor accessor = AWTAccessor.getComponentAccessor(); 717 final Rectangle tBounds = accessor.getBounds(getTarget()); 718 final boolean tMoved = (x != tBounds.x) || (y != tBounds.y); 719 final boolean tResized = (w != tBounds.width) || (h != tBounds.height); 720 721 // Check if anything changed 722 if (!tMoved && !tResized && !pMoved && !pResized && !invalid) { 723 // Native window(NSWindow)/LWWindowPeer/Target are in sync 724 return; 725 } 726 // First, update peer's bounds 727 setBounds(x, y, w, h, SET_BOUNDS, false, false); 728 729 // Second, update the graphics config and surface data 730 final boolean isNewDevice = updateGraphicsDevice(); 731 if (isNewDevice && !isMaximizedBoundsSet()) { 732 setPlatformMaximizedBounds(getDefaultMaximizedBounds()); 733 } 734 735 if (pResized || isNewDevice) { 736 replaceSurfaceData(); 737 updateMinimumSize(); 738 } 739 740 // Third, COMPONENT_MOVED/COMPONENT_RESIZED/PAINT events 741 if (tMoved || pMoved || invalid) { 742 handleMove(x, y, true); 743 } 744 if (tResized || pResized || invalid || isNewDevice) { 745 handleResize(w, h, true); 746 repaintPeer(); 747 } 748 749 repositionSecurityWarning(); 750 } 751 clearBackground(final int w, final int h)752 private void clearBackground(final int w, final int h) { 753 final Graphics g = getOnscreenGraphics(getForeground(), getBackground(), 754 getFont()); 755 if (g != null) { 756 try { 757 if (g instanceof Graphics2D) { 758 ((Graphics2D) g).setComposite(AlphaComposite.Src); 759 } 760 if (isTranslucent()) { 761 g.setColor(nonOpaqueBackground); 762 g.fillRect(0, 0, w, h); 763 } 764 if (!isTextured()) { 765 if (g instanceof SunGraphics2D) { 766 ((SunGraphics2D) g).constrain(0, 0, w, h, getRegion()); 767 } 768 g.setColor(getBackground()); 769 g.fillRect(0, 0, w, h); 770 } 771 } finally { 772 g.dispose(); 773 } 774 } 775 } 776 777 @Override notifyUpdateCursor()778 public void notifyUpdateCursor() { 779 getLWToolkit().getCursorManager().updateCursorLater(this); 780 } 781 782 @Override notifyActivation(boolean activation, LWWindowPeer opposite)783 public void notifyActivation(boolean activation, LWWindowPeer opposite) { 784 Window oppositeWindow = (opposite == null)? null : opposite.getTarget(); 785 changeFocusedWindow(activation, oppositeWindow); 786 } 787 788 // MouseDown in non-client area 789 @Override notifyNCMouseDown()790 public void notifyNCMouseDown() { 791 // Ungrab except for a click on a Dialog with the grabbing owner 792 if (grabbingWindow != null && 793 !grabbingWindow.isOneOfOwnersOf(this)) 794 { 795 grabbingWindow.ungrab(); 796 } 797 } 798 799 // ---- EVENTS ---- // 800 801 /* 802 * Called by the delegate to dispatch the event to Java. Event 803 * coordinates are relative to non-client window are, i.e. the top-left 804 * point of the client area is (insets.top, insets.left). 805 */ 806 @Override notifyMouseEvent(int id, long when, int button, int x, int y, int absX, int absY, int modifiers, int clickCount, boolean popupTrigger, byte[] bdata)807 public void notifyMouseEvent(int id, long when, int button, 808 int x, int y, int absX, int absY, 809 int modifiers, int clickCount, boolean popupTrigger, 810 byte[] bdata) 811 { 812 // TODO: fill "bdata" member of AWTEvent 813 Rectangle r = getBounds(); 814 // findPeerAt() expects parent coordinates 815 LWComponentPeer<?, ?> targetPeer = findPeerAt(r.x + x, r.y + y); 816 817 if (id == MouseEvent.MOUSE_EXITED) { 818 isMouseOver = false; 819 if (lastMouseEventPeer != null) { 820 if (lastMouseEventPeer.isEnabled()) { 821 Point lp = lastMouseEventPeer.windowToLocal(x, y, 822 this); 823 Component target = lastMouseEventPeer.getTarget(); 824 postMouseExitedEvent(target, when, modifiers, lp, 825 absX, absY, clickCount, popupTrigger, button); 826 } 827 828 // Sometimes we may get MOUSE_EXITED after lastCommonMouseEventPeer is switched 829 // to a peer from another window. So we must first check if this peer is 830 // the same as lastWindowPeer 831 if (lastCommonMouseEventPeer != null && lastCommonMouseEventPeer.getWindowPeerOrSelf() == this) { 832 lastCommonMouseEventPeer = null; 833 } 834 lastMouseEventPeer = null; 835 } 836 } else if(id == MouseEvent.MOUSE_ENTERED) { 837 isMouseOver = true; 838 if (targetPeer != null) { 839 if (targetPeer.isEnabled()) { 840 Point lp = targetPeer.windowToLocal(x, y, this); 841 Component target = targetPeer.getTarget(); 842 postMouseEnteredEvent(target, when, modifiers, lp, 843 absX, absY, clickCount, popupTrigger, button); 844 } 845 lastCommonMouseEventPeer = targetPeer; 846 lastMouseEventPeer = targetPeer; 847 } 848 } else { 849 PlatformWindow topmostPlatformWindow = LWToolkit.getLWToolkit().getPlatformWindowUnderMouse(); 850 851 LWWindowPeer topmostWindowPeer = 852 topmostPlatformWindow != null ? topmostPlatformWindow.getPeer() : null; 853 854 // topmostWindowPeer == null condition is added for the backward 855 // compatibility with applets. It can be removed when the 856 // getTopmostPlatformWindowUnderMouse() method will be properly 857 // implemented in CPlatformEmbeddedFrame class 858 if (topmostWindowPeer == this || topmostWindowPeer == null) { 859 generateMouseEnterExitEventsForComponents(when, button, x, y, 860 absX, absY, modifiers, clickCount, popupTrigger, 861 targetPeer); 862 } else { 863 LWComponentPeer<?, ?> topmostTargetPeer = topmostWindowPeer.findPeerAt(r.x + x, r.y + y); 864 topmostWindowPeer.generateMouseEnterExitEventsForComponents(when, button, x, y, 865 absX, absY, modifiers, clickCount, popupTrigger, 866 topmostTargetPeer); 867 } 868 869 // TODO: fill "bdata" member of AWTEvent 870 871 int eventButtonMask = (button > 0)? MouseEvent.getMaskForButton(button) : 0; 872 int otherButtonsPressed = modifiers & ~eventButtonMask; 873 874 // For pressed/dragged/released events OS X treats other 875 // mouse buttons as if they were BUTTON2, so we do the same 876 int targetIdx = (button > 3) ? MouseEvent.BUTTON2 - 1 : button - 1; 877 878 // MOUSE_ENTERED/EXITED are generated for the components strictly under 879 // mouse even when dragging. That's why we first update lastMouseEventPeer 880 // based on initial targetPeer value and only then recalculate targetPeer 881 // for MOUSE_DRAGGED/RELEASED events 882 if (id == MouseEvent.MOUSE_PRESSED) { 883 884 // Ungrab only if this window is not an owned window of the grabbing one. 885 if (!isGrabbing() && grabbingWindow != null && 886 !grabbingWindow.isOneOfOwnersOf(this)) 887 { 888 grabbingWindow.ungrab(); 889 } 890 if (otherButtonsPressed == 0) { 891 mouseClickButtons = eventButtonMask; 892 } else { 893 mouseClickButtons |= eventButtonMask; 894 } 895 896 // The window should be focused on mouse click. If it gets activated by the native platform, 897 // this request will be no op. It will take effect when: 898 // 1. A simple not focused window is clicked. 899 // 2. An active but not focused owner frame/dialog is clicked. 900 // The mouse event then will trigger a focus request "in window" to the component, so the window 901 // should gain focus before. 902 requestWindowFocus(FocusEvent.Cause.MOUSE_EVENT); 903 904 mouseDownTarget[targetIdx] = targetPeer; 905 } else if (id == MouseEvent.MOUSE_DRAGGED) { 906 // Cocoa dragged event has the information about which mouse 907 // button is being dragged. Use it to determine the peer that 908 // should receive the dragged event. 909 targetPeer = mouseDownTarget[targetIdx]; 910 mouseClickButtons &= ~modifiers; 911 } else if (id == MouseEvent.MOUSE_RELEASED) { 912 // TODO: currently, mouse released event goes to the same component 913 // that received corresponding mouse pressed event. For most cases, 914 // it's OK, however, we need to make sure that our behavior is consistent 915 // with 1.6 for cases where component in question have been 916 // hidden/removed in between of mouse pressed/released events. 917 targetPeer = mouseDownTarget[targetIdx]; 918 919 if ((modifiers & eventButtonMask) == 0) { 920 mouseDownTarget[targetIdx] = null; 921 } 922 923 // mouseClickButtons is updated below, after MOUSE_CLICK is sent 924 } 925 926 if (targetPeer == null) { 927 //TODO This can happen if this window is invisible. this is correct behavior in this case? 928 targetPeer = this; 929 } 930 931 932 Point lp = targetPeer.windowToLocal(x, y, this); 933 if (targetPeer.isEnabled()) { 934 MouseEvent event = new MouseEvent(targetPeer.getTarget(), id, 935 when, modifiers, lp.x, lp.y, 936 absX, absY, clickCount, 937 popupTrigger, button); 938 postEvent(event); 939 } 940 941 if (id == MouseEvent.MOUSE_RELEASED) { 942 if ((mouseClickButtons & eventButtonMask) != 0 943 && targetPeer.isEnabled()) { 944 postEvent(new MouseEvent(targetPeer.getTarget(), 945 MouseEvent.MOUSE_CLICKED, 946 when, modifiers, 947 lp.x, lp.y, absX, absY, 948 clickCount, popupTrigger, button)); 949 } 950 mouseClickButtons &= ~eventButtonMask; 951 } 952 } 953 notifyUpdateCursor(); 954 } 955 generateMouseEnterExitEventsForComponents(long when, int button, int x, int y, int screenX, int screenY, int modifiers, int clickCount, boolean popupTrigger, final LWComponentPeer<?, ?> targetPeer)956 private void generateMouseEnterExitEventsForComponents(long when, 957 int button, int x, int y, int screenX, int screenY, 958 int modifiers, int clickCount, boolean popupTrigger, 959 final LWComponentPeer<?, ?> targetPeer) { 960 961 if (!isMouseOver || targetPeer == lastMouseEventPeer) { 962 return; 963 } 964 965 // Generate Mouse Exit for components 966 if (lastMouseEventPeer != null && lastMouseEventPeer.isEnabled()) { 967 Point oldp = lastMouseEventPeer.windowToLocal(x, y, this); 968 Component target = lastMouseEventPeer.getTarget(); 969 postMouseExitedEvent(target, when, modifiers, oldp, screenX, screenY, 970 clickCount, popupTrigger, button); 971 } 972 lastCommonMouseEventPeer = targetPeer; 973 lastMouseEventPeer = targetPeer; 974 975 // Generate Mouse Enter for components 976 if (targetPeer != null && targetPeer.isEnabled()) { 977 Point newp = targetPeer.windowToLocal(x, y, this); 978 Component target = targetPeer.getTarget(); 979 postMouseEnteredEvent(target, when, modifiers, newp, screenX, screenY, clickCount, popupTrigger, button); 980 } 981 } 982 postMouseEnteredEvent(Component target, long when, int modifiers, Point loc, int xAbs, int yAbs, int clickCount, boolean popupTrigger, int button)983 private void postMouseEnteredEvent(Component target, long when, int modifiers, 984 Point loc, int xAbs, int yAbs, 985 int clickCount, boolean popupTrigger, int button) { 986 987 updateSecurityWarningVisibility(); 988 989 postEvent(new MouseEvent(target, 990 MouseEvent.MOUSE_ENTERED, 991 when, modifiers, 992 loc.x, loc.y, xAbs, yAbs, 993 clickCount, popupTrigger, button)); 994 } 995 postMouseExitedEvent(Component target, long when, int modifiers, Point loc, int xAbs, int yAbs, int clickCount, boolean popupTrigger, int button)996 private void postMouseExitedEvent(Component target, long when, int modifiers, 997 Point loc, int xAbs, int yAbs, 998 int clickCount, boolean popupTrigger, int button) { 999 1000 updateSecurityWarningVisibility(); 1001 1002 postEvent(new MouseEvent(target, 1003 MouseEvent.MOUSE_EXITED, 1004 when, modifiers, 1005 loc.x, loc.y, xAbs, yAbs, 1006 clickCount, popupTrigger, button)); 1007 } 1008 1009 @Override notifyMouseWheelEvent(long when, int x, int y, int absX, int absY, int modifiers, int scrollType, int scrollAmount, int wheelRotation, double preciseWheelRotation, byte[] bdata)1010 public void notifyMouseWheelEvent(long when, int x, int y, int absX, 1011 int absY, int modifiers, int scrollType, 1012 int scrollAmount, int wheelRotation, 1013 double preciseWheelRotation, byte[] bdata) 1014 { 1015 // TODO: could we just use the last mouse event target here? 1016 Rectangle r = getBounds(); 1017 // findPeerAt() expects parent coordinates 1018 final LWComponentPeer<?, ?> targetPeer = findPeerAt(r.x + x, r.y + y); 1019 if (targetPeer == null || !targetPeer.isEnabled()) { 1020 return; 1021 } 1022 1023 Point lp = targetPeer.windowToLocal(x, y, this); 1024 // TODO: fill "bdata" member of AWTEvent 1025 postEvent(new MouseWheelEvent(targetPeer.getTarget(), 1026 MouseEvent.MOUSE_WHEEL, 1027 when, modifiers, 1028 lp.x, lp.y, 1029 absX, absY, /* absX, absY */ 1030 0 /* clickCount */, false /* popupTrigger */, 1031 scrollType, scrollAmount, 1032 wheelRotation, preciseWheelRotation)); 1033 } 1034 1035 /* 1036 * Called by the delegate when a key is pressed. 1037 */ 1038 @Override notifyKeyEvent(int id, long when, int modifiers, int keyCode, char keyChar, int keyLocation)1039 public void notifyKeyEvent(int id, long when, int modifiers, 1040 int keyCode, char keyChar, int keyLocation) 1041 { 1042 LWKeyboardFocusManagerPeer kfmPeer = LWKeyboardFocusManagerPeer.getInstance(); 1043 Component focusOwner = kfmPeer.getCurrentFocusOwner(); 1044 1045 if (focusOwner == null) { 1046 focusOwner = kfmPeer.getCurrentFocusedWindow(); 1047 if (focusOwner == null) { 1048 focusOwner = this.getTarget(); 1049 } 1050 } 1051 1052 KeyEvent keyEvent = new KeyEvent(focusOwner, id, when, modifiers, 1053 keyCode, keyChar, keyLocation); 1054 AWTAccessor.getKeyEventAccessor().setExtendedKeyCode(keyEvent, 1055 (keyChar == KeyEvent.CHAR_UNDEFINED) ? keyCode 1056 : ExtendedKeyCodes.getExtendedKeyCodeForChar(keyChar)); 1057 postEvent(keyEvent); 1058 } 1059 1060 // ---- UTILITY METHODS ---- // 1061 activateDisplayListener()1062 private void activateDisplayListener() { 1063 final GraphicsEnvironment ge = 1064 GraphicsEnvironment.getLocalGraphicsEnvironment(); 1065 ((SunGraphicsEnvironment) ge).addDisplayChangedListener(this); 1066 } 1067 deactivateDisplayListener()1068 private void deactivateDisplayListener() { 1069 final GraphicsEnvironment ge = 1070 GraphicsEnvironment.getLocalGraphicsEnvironment(); 1071 ((SunGraphicsEnvironment) ge).removeDisplayChangedListener(this); 1072 } 1073 postWindowStateChangedEvent(int newWindowState)1074 private void postWindowStateChangedEvent(int newWindowState) { 1075 if (getTarget() instanceof Frame) { 1076 AWTAccessor.getFrameAccessor().setExtendedState( 1077 (Frame)getTarget(), newWindowState); 1078 } 1079 1080 WindowEvent stateChangedEvent = new WindowEvent(getTarget(), 1081 WindowEvent.WINDOW_STATE_CHANGED, 1082 windowState, newWindowState); 1083 postEvent(stateChangedEvent); 1084 windowState = newWindowState; 1085 1086 updateSecurityWarningVisibility(); 1087 } 1088 getGraphicsConfigScreen(GraphicsConfiguration gc)1089 private static int getGraphicsConfigScreen(GraphicsConfiguration gc) { 1090 // TODO: this method can be implemented in a more 1091 // efficient way by forwarding to the delegate 1092 GraphicsDevice gd = gc.getDevice(); 1093 GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment(); 1094 GraphicsDevice[] gds = ge.getScreenDevices(); 1095 for (int i = 0; i < gds.length; i++) { 1096 if (gds[i] == gd) { 1097 return i; 1098 } 1099 } 1100 // Should never happen if gc is a screen device config 1101 return 0; 1102 } 1103 1104 /* 1105 * This method is called when window's graphics config is changed from 1106 * the app code (e.g. when the window is made non-opaque) or when 1107 * the window is moved to another screen by user. 1108 * 1109 * Returns true if the graphics config has been changed, false otherwise. 1110 */ setGraphicsConfig(GraphicsConfiguration gc)1111 private boolean setGraphicsConfig(GraphicsConfiguration gc) { 1112 synchronized (getStateLock()) { 1113 if (graphicsConfig == gc) { 1114 return false; 1115 } 1116 // If window's graphics config is changed from the app code, the 1117 // config correspond to the same device as before; when the window 1118 // is moved by user, graphicsDevice is updated in notifyReshape(). 1119 // In either case, there's nothing to do with screenOn here 1120 graphicsConfig = gc; 1121 } 1122 // SurfaceData is replaced later in updateGraphicsData() 1123 return true; 1124 } 1125 1126 /** 1127 * Returns true if the GraphicsDevice has been changed, false otherwise. 1128 */ updateGraphicsDevice()1129 public boolean updateGraphicsDevice() { 1130 GraphicsDevice newGraphicsDevice = platformWindow.getGraphicsDevice(); 1131 synchronized (getStateLock()) { 1132 if (graphicsDevice == newGraphicsDevice) { 1133 return false; 1134 } 1135 graphicsDevice = newGraphicsDevice; 1136 } 1137 1138 final GraphicsConfiguration newGC = newGraphicsDevice.getDefaultConfiguration(); 1139 1140 if (!setGraphicsConfig(newGC)) return false; 1141 1142 SunToolkit.executeOnEventHandlerThread(getTarget(), new Runnable() { 1143 public void run() { 1144 AWTAccessor.getComponentAccessor().setGraphicsConfiguration(getTarget(), newGC); 1145 } 1146 }); 1147 return true; 1148 } 1149 1150 @Override displayChanged()1151 public final void displayChanged() { 1152 if (updateGraphicsDevice()) { 1153 updateMinimumSize(); 1154 if (!isMaximizedBoundsSet()) { 1155 setPlatformMaximizedBounds(getDefaultMaximizedBounds()); 1156 } 1157 } 1158 // Replace surface unconditionally, because internal state of the 1159 // GraphicsDevice could be changed. 1160 replaceSurfaceData(); 1161 repaintPeer(); 1162 } 1163 1164 @Override paletteChanged()1165 public final void paletteChanged() { 1166 // components do not need to react to this event. 1167 } 1168 1169 /* 1170 * May be called by delegate to provide SD to Java2D code. 1171 */ getSurfaceData()1172 public SurfaceData getSurfaceData() { 1173 synchronized (surfaceDataLock) { 1174 return surfaceData; 1175 } 1176 } 1177 replaceSurfaceData()1178 private void replaceSurfaceData() { 1179 replaceSurfaceData(true); 1180 } 1181 replaceSurfaceData(final boolean blit)1182 private void replaceSurfaceData(final boolean blit) { 1183 synchronized (surfaceDataLock) { 1184 final SurfaceData oldData = getSurfaceData(); 1185 surfaceData = platformWindow.replaceSurfaceData(); 1186 final Rectangle size = getSize(); 1187 if (getSurfaceData() != null && oldData != getSurfaceData()) { 1188 clearBackground(size.width, size.height); 1189 } 1190 1191 if (blit) { 1192 blitSurfaceData(oldData, getSurfaceData()); 1193 } 1194 1195 if (oldData != null && oldData != getSurfaceData()) { 1196 // TODO: drop oldData for D3D/WGL pipelines 1197 // This can only happen when this peer is being created 1198 oldData.flush(); 1199 } 1200 } 1201 flushOnscreenGraphics(); 1202 } 1203 blitSurfaceData(final SurfaceData src, final SurfaceData dst)1204 private void blitSurfaceData(final SurfaceData src, final SurfaceData dst) { 1205 //TODO blit. proof-of-concept 1206 if (src != dst && src != null && dst != null 1207 && !(dst instanceof NullSurfaceData) 1208 && !(src instanceof NullSurfaceData) 1209 && src.getSurfaceType().equals(dst.getSurfaceType()) 1210 && src.getDefaultScaleX() == dst.getDefaultScaleX() 1211 && src.getDefaultScaleY() == dst.getDefaultScaleY()) 1212 { 1213 final Rectangle size = src.getBounds(); 1214 final Blit blit = Blit.locate(src.getSurfaceType(), 1215 CompositeType.Src, 1216 dst.getSurfaceType()); 1217 if (blit != null) { 1218 blit.Blit(src, dst, AlphaComposite.Src, null, 0, 0, 0, 0, 1219 size.width, size.height); 1220 } 1221 } 1222 } 1223 1224 /** 1225 * Request the window insets from the delegate and compares it with the 1226 * current one. This method is mostly called by the delegate, e.g. when the 1227 * window state is changed and insets should be recalculated. 1228 * <p/> 1229 * This method may be called on the toolkit thread. 1230 */ updateInsets(final Insets newInsets)1231 public final boolean updateInsets(final Insets newInsets) { 1232 synchronized (getStateLock()) { 1233 if (insets.equals(newInsets)) { 1234 return false; 1235 } 1236 insets = newInsets; 1237 } 1238 return true; 1239 } 1240 getWindowUnderCursor()1241 public static LWWindowPeer getWindowUnderCursor() { 1242 return lastCommonMouseEventPeer != null ? lastCommonMouseEventPeer.getWindowPeerOrSelf() : null; 1243 } 1244 getPeerUnderCursor()1245 public static LWComponentPeer<?, ?> getPeerUnderCursor() { 1246 return lastCommonMouseEventPeer; 1247 } 1248 1249 /* 1250 * Requests platform to set native focus on a frame/dialog. 1251 * In case of a simple window, triggers appropriate java focus change. 1252 */ requestWindowFocus(FocusEvent.Cause cause)1253 public boolean requestWindowFocus(FocusEvent.Cause cause) { 1254 if (focusLog.isLoggable(PlatformLogger.Level.FINE)) { 1255 focusLog.fine("requesting native focus to " + this); 1256 } 1257 1258 if (!focusAllowedFor()) { 1259 focusLog.fine("focus is not allowed"); 1260 return false; 1261 } 1262 1263 if (platformWindow.rejectFocusRequest(cause)) { 1264 return false; 1265 } 1266 1267 AppContext targetAppContext = AWTAccessor.getComponentAccessor().getAppContext(getTarget()); 1268 KeyboardFocusManager kfm = AWTAccessor.getKeyboardFocusManagerAccessor() 1269 .getCurrentKeyboardFocusManager(targetAppContext); 1270 Window currentActive = kfm.getActiveWindow(); 1271 1272 1273 Window opposite = LWKeyboardFocusManagerPeer.getInstance(). 1274 getCurrentFocusedWindow(); 1275 1276 // Make the owner active window. 1277 if (isSimpleWindow()) { 1278 LWWindowPeer owner = getOwnerFrameDialog(this); 1279 1280 // If owner is not natively active, request native 1281 // activation on it w/o sending events up to java. 1282 if (owner != null && !owner.platformWindow.isActive()) { 1283 if (focusLog.isLoggable(PlatformLogger.Level.FINE)) { 1284 focusLog.fine("requesting native focus to the owner " + owner); 1285 } 1286 LWWindowPeer currentActivePeer = currentActive == null ? null : 1287 (LWWindowPeer) AWTAccessor.getComponentAccessor().getPeer( 1288 currentActive); 1289 1290 // Ensure the opposite is natively active and suppress sending events. 1291 if (currentActivePeer != null && currentActivePeer.platformWindow.isActive()) { 1292 if (focusLog.isLoggable(PlatformLogger.Level.FINE)) { 1293 focusLog.fine("the opposite is " + currentActivePeer); 1294 } 1295 currentActivePeer.skipNextFocusChange = true; 1296 } 1297 owner.skipNextFocusChange = true; 1298 1299 owner.platformWindow.requestWindowFocus(); 1300 } 1301 1302 // DKFM will synthesize all the focus/activation events correctly. 1303 changeFocusedWindow(true, opposite); 1304 return true; 1305 1306 // In case the toplevel is active but not focused, change focus directly, 1307 // as requesting native focus on it will not have effect. 1308 } else if (getTarget() == currentActive && !getTarget().hasFocus()) { 1309 1310 changeFocusedWindow(true, opposite); 1311 return true; 1312 } 1313 1314 return platformWindow.requestWindowFocus(); 1315 } 1316 focusAllowedFor()1317 protected boolean focusAllowedFor() { 1318 Window window = getTarget(); 1319 // TODO: check if modal blocked 1320 return window.isVisible() && window.isEnabled() && isFocusableWindow(); 1321 } 1322 isFocusableWindow()1323 private boolean isFocusableWindow() { 1324 boolean focusable = targetFocusable; 1325 if (isSimpleWindow()) { 1326 LWWindowPeer ownerPeer = getOwnerFrameDialog(this); 1327 if (ownerPeer == null) { 1328 return false; 1329 } 1330 return focusable && ownerPeer.targetFocusable; 1331 } 1332 return focusable; 1333 } 1334 isSimpleWindow()1335 public boolean isSimpleWindow() { 1336 Window window = getTarget(); 1337 return !(window instanceof Dialog || window instanceof Frame); 1338 } 1339 1340 @Override emulateActivation(boolean activate)1341 public void emulateActivation(boolean activate) { 1342 changeFocusedWindow(activate, null); 1343 } 1344 1345 @SuppressWarnings("deprecation") isOneOfOwnersOf(LWWindowPeer peer)1346 private boolean isOneOfOwnersOf(LWWindowPeer peer) { 1347 Window owner = (peer != null ? peer.getTarget().getOwner() : null); 1348 while (owner != null) { 1349 final ComponentAccessor acc = AWTAccessor.getComponentAccessor(); 1350 if (acc.getPeer(owner) == this) { 1351 return true; 1352 } 1353 owner = owner.getOwner(); 1354 } 1355 return false; 1356 } 1357 1358 /* 1359 * Changes focused window on java level. 1360 */ changeFocusedWindow(boolean becomesFocused, Window opposite)1361 protected void changeFocusedWindow(boolean becomesFocused, Window opposite) { 1362 if (focusLog.isLoggable(PlatformLogger.Level.FINE)) { 1363 focusLog.fine((becomesFocused?"gaining":"loosing") + " focus window: " + this); 1364 } 1365 if (skipNextFocusChange) { 1366 focusLog.fine("skipping focus change"); 1367 skipNextFocusChange = false; 1368 return; 1369 } 1370 if (!isFocusableWindow() && becomesFocused) { 1371 focusLog.fine("the window is not focusable"); 1372 return; 1373 } 1374 if (becomesFocused) { 1375 synchronized (getPeerTreeLock()) { 1376 if (blocker != null) { 1377 if (focusLog.isLoggable(PlatformLogger.Level.FINEST)) { 1378 focusLog.finest("the window is blocked by " + blocker); 1379 } 1380 return; 1381 } 1382 } 1383 } 1384 1385 // Note, the method is not called: 1386 // - when the opposite (gaining focus) window is an owned/owner window. 1387 // - for a simple window in any case. 1388 if (!becomesFocused && 1389 (isGrabbing() || this.isOneOfOwnersOf(grabbingWindow))) 1390 { 1391 if (focusLog.isLoggable(PlatformLogger.Level.FINE)) { 1392 focusLog.fine("ungrabbing on " + grabbingWindow); 1393 } 1394 // ungrab a simple window if its owner looses activation. 1395 grabbingWindow.ungrab(); 1396 } 1397 1398 KeyboardFocusManagerPeer kfmPeer = LWKeyboardFocusManagerPeer.getInstance(); 1399 1400 if (!becomesFocused && kfmPeer.getCurrentFocusedWindow() != getTarget()) { 1401 // late window focus lost event - ingoring 1402 return; 1403 } 1404 1405 kfmPeer.setCurrentFocusedWindow(becomesFocused ? getTarget() : null); 1406 1407 int eventID = becomesFocused ? WindowEvent.WINDOW_GAINED_FOCUS : WindowEvent.WINDOW_LOST_FOCUS; 1408 WindowEvent windowEvent = new TimedWindowEvent(getTarget(), eventID, opposite, System.currentTimeMillis()); 1409 1410 // TODO: wrap in SequencedEvent 1411 postEvent(windowEvent); 1412 } 1413 1414 /* 1415 * Retrieves the owner of the peer. 1416 * Note: this method returns the owner which can be activated, (i.e. the instance 1417 * of Frame or Dialog may be returned). 1418 */ getOwnerFrameDialog(LWWindowPeer peer)1419 static LWWindowPeer getOwnerFrameDialog(LWWindowPeer peer) { 1420 Window owner = (peer != null ? peer.getTarget().getOwner() : null); 1421 while (owner != null && !(owner instanceof Frame || owner instanceof Dialog)) { 1422 owner = owner.getOwner(); 1423 } 1424 return owner == null ? null : AWTAccessor.getComponentAccessor() 1425 .getPeer(owner); 1426 } 1427 1428 /** 1429 * Returns the foremost modal blocker of this window, or null. 1430 */ getBlocker()1431 public LWWindowPeer getBlocker() { 1432 synchronized (getPeerTreeLock()) { 1433 LWWindowPeer blocker = this.blocker; 1434 if (blocker == null) { 1435 return null; 1436 } 1437 while (blocker.blocker != null) { 1438 blocker = blocker.blocker; 1439 } 1440 return blocker; 1441 } 1442 } 1443 1444 @Override enterFullScreenMode()1445 public void enterFullScreenMode() { 1446 platformWindow.enterFullScreenMode(); 1447 updateSecurityWarningVisibility(); 1448 } 1449 1450 @Override exitFullScreenMode()1451 public void exitFullScreenMode() { 1452 platformWindow.exitFullScreenMode(); 1453 updateSecurityWarningVisibility(); 1454 } 1455 getLayerPtr()1456 public long getLayerPtr() { 1457 return getPlatformWindow().getLayerPtr(); 1458 } 1459 grab()1460 void grab() { 1461 if (grabbingWindow != null && !isGrabbing()) { 1462 grabbingWindow.ungrab(); 1463 } 1464 grabbingWindow = this; 1465 } 1466 ungrab(boolean doPost)1467 final void ungrab(boolean doPost) { 1468 if (isGrabbing()) { 1469 grabbingWindow = null; 1470 if (doPost) { 1471 postEvent(new UngrabEvent(getTarget())); 1472 } 1473 } 1474 } 1475 ungrab()1476 void ungrab() { 1477 ungrab(true); 1478 } 1479 isGrabbing()1480 private boolean isGrabbing() { 1481 return this == grabbingWindow; 1482 } 1483 getPeerType()1484 public PeerType getPeerType() { 1485 return peerType; 1486 } 1487 updateSecurityWarningVisibility()1488 public void updateSecurityWarningVisibility() { 1489 if (warningWindow == null) { 1490 return; 1491 } 1492 1493 if (!isVisible()) { 1494 return; // The warning window should already be hidden. 1495 } 1496 1497 boolean show = false; 1498 1499 if (!platformWindow.isFullScreenMode()) { 1500 if (isVisible()) { 1501 if (LWKeyboardFocusManagerPeer.getInstance().getCurrentFocusedWindow() == 1502 getTarget()) { 1503 show = true; 1504 } 1505 1506 if (platformWindow.isUnderMouse() || warningWindow.isUnderMouse()) { 1507 show = true; 1508 } 1509 } 1510 } 1511 1512 warningWindow.setVisible(show, true); 1513 } 1514 1515 @Override toString()1516 public String toString() { 1517 return super.toString() + " [target is " + getTarget() + "]"; 1518 } 1519 } 1520