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