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