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