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