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