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