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