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