1 /* 2 * Copyright (c) 1996, 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 package sun.awt.windows; 26 27 import java.awt.*; 28 import java.awt.event.*; 29 import java.awt.image.*; 30 import java.awt.peer.*; 31 32 import java.beans.*; 33 34 import java.lang.reflect.*; 35 36 import java.util.*; 37 import java.util.List; 38 import java.util.logging.*; 39 40 import sun.awt.*; 41 42 import sun.java2d.pipe.Region; 43 44 public class WWindowPeer extends WPanelPeer implements WindowPeer { 45 46 private static final Logger log = Logger.getLogger("sun.awt.windows.WWindowPeer"); 47 private static final Logger screenLog = Logger.getLogger("sun.awt.windows.screen.WWindowPeer"); 48 49 // we can't use WDialogPeer as blocker may be an instance of WPrintDialogPeer that 50 // extends WWindowPeer, not WDialogPeer 51 private WWindowPeer modalBlocker = null; 52 53 private boolean isOpaque; 54 55 private TranslucentWindowPainter painter; 56 57 /* 58 * A key used for storing a list of active windows in AppContext. The value 59 * is a list of windows, sorted by the time of activation: later a window is 60 * activated, greater its index is in the list. 61 */ 62 private final static StringBuffer ACTIVE_WINDOWS_KEY = 63 new StringBuffer("active_windows_list"); 64 65 /* 66 * Listener for 'activeWindow' KFM property changes. It is added to each 67 * AppContext KFM. See ActiveWindowListener inner class below. 68 */ 69 private static PropertyChangeListener activeWindowListener = 70 new ActiveWindowListener(); 71 72 /* 73 * Contains all the AppContexts where activeWindowListener is added to. 74 */ 75 private static Set<AppContext> trackedAppContexts = new HashSet<AppContext>(); 76 77 /** 78 * Initialize JNI field IDs 79 */ 80 private static native void initIDs(); 81 static { 82 initIDs(); 83 } 84 85 // WComponentPeer overrides 86 87 protected void disposeImpl() { 88 AppContext appContext = SunToolkit.targetToAppContext(target); 89 synchronized (appContext) { 90 List<WWindowPeer> l = (List<WWindowPeer>)appContext.get(ACTIVE_WINDOWS_KEY); 91 if (l != null) { 92 l.remove(this); 93 } 94 } 95 96 // Remove ourself from the Map of DisplayChangeListeners 97 GraphicsConfiguration gc = getGraphicsConfiguration(); 98 ((Win32GraphicsDevice)gc.getDevice()).removeDisplayChangedListener(this); 99 100 synchronized (getStateLock()) { 101 TranslucentWindowPainter currentPainter = painter; 102 if (currentPainter != null) { 103 currentPainter.flush(); 104 // don't set the current one to null here; reduces the chances of 105 // MT issues (like NPEs) 106 } 107 } 108 109 super.disposeImpl(); 110 } 111 112 // WindowPeer implementation 113 114 public void toFront() { 115 updateFocusableWindowState(); 116 _toFront(); 117 } 118 native void _toFront(); 119 public native void toBack(); 120 121 public native void setAlwaysOnTopNative(boolean value); 122 public void setAlwaysOnTop(boolean value) { 123 if ((value && ((Window)target).isVisible()) || !value) { 124 setAlwaysOnTopNative(value); 125 } 126 } 127 128 public void updateAlwaysOnTopState() { 129 setAlwaysOnTop(((Window)target).isAlwaysOnTop()); 130 } 131 132 public void updateFocusableWindowState() { 133 setFocusableWindow(((Window)target).isFocusableWindow()); 134 } 135 native void setFocusableWindow(boolean value); 136 137 // FramePeer & DialogPeer partial shared implementation 138 139 public void setTitle(String title) { 140 // allow a null title to pass as an empty string. 141 if (title == null) { 142 title = new String(""); 143 } 144 _setTitle(title); 145 } 146 native void _setTitle(String title); 147 148 public void setResizable(boolean resizable) { 149 _setResizable(resizable); 150 } 151 public native void _setResizable(boolean resizable); 152 153 // Toolkit & peer internals 154 155 WWindowPeer(Window target) { 156 super(target); 157 } 158 159 void initialize() { 160 super.initialize(); 161 162 updateInsets(insets_); 163 164 Font f = ((Window)target).getFont(); 165 if (f == null) { 166 f = defaultFont; 167 ((Window)target).setFont(f); 168 setFont(f); 169 } 170 // Express our interest in display changes 171 GraphicsConfiguration gc = getGraphicsConfiguration(); 172 ((Win32GraphicsDevice)gc.getDevice()).addDisplayChangedListener(this); 173 174 AppContext appContext = AppContext.getAppContext(); 175 synchronized (appContext) { 176 if (!trackedAppContexts.contains(appContext)) { 177 KeyboardFocusManager kfm = KeyboardFocusManager.getCurrentKeyboardFocusManager(); 178 kfm.addPropertyChangeListener("activeWindow", activeWindowListener); 179 trackedAppContexts.add(appContext); 180 } 181 } 182 183 updateIconImages(); 184 185 Shape shape = AWTAccessor.getWindowAccessor().getShape((Window)target); 186 if (shape != null) { 187 applyShape(Region.getInstance(shape, null)); 188 } 189 190 float opacity = AWTAccessor.getWindowAccessor().getOpacity((Window)target); 191 if (opacity < 1.0f) { 192 setOpacity(opacity); 193 } 194 195 synchronized (getStateLock()) { 196 // default value of a boolean field is 'false', so set isOpaque to 197 // true here explicitly 198 this.isOpaque = true; 199 Color bgColor = ((Window)target).getBackground(); 200 setOpaque((bgColor == null) || (bgColor.getAlpha() == 255)); 201 } 202 } 203 204 native void createAwtWindow(WComponentPeer parent); 205 void create(WComponentPeer parent) { 206 createAwtWindow(parent); 207 } 208 209 // should be overriden in WDialogPeer 210 protected void realShow() { 211 super.show(); 212 } 213 214 public void show() { 215 updateFocusableWindowState(); 216 217 boolean alwaysOnTop = ((Window)target).isAlwaysOnTop(); 218 219 // Fix for 4868278. 220 // If we create a window with a specific GraphicsConfig, and then move it with 221 // setLocation() or setBounds() to another one before its peer has been created, 222 // then calling Window.getGraphicsConfig() returns wrong config. That may lead 223 // to some problems like wrong-placed tooltips. It is caused by calling 224 // super.displayChanged() in WWindowPeer.displayChanged() regardless of whether 225 // GraphicsDevice was really changed, or not. So we need to track it here. 226 updateGC(); 227 resetTargetGC(); 228 229 realShow(); 230 updateMinimumSize(); 231 232 if (((Window)target).isAlwaysOnTopSupported() && alwaysOnTop) { 233 setAlwaysOnTop(alwaysOnTop); 234 } 235 236 synchronized (getStateLock()) { 237 if (!isOpaque) { 238 updateWindow(true); 239 } 240 } 241 } 242 243 // Synchronize the insets members (here & in helper) with actual window 244 // state. 245 native void updateInsets(Insets i); 246 247 static native int getSysMinWidth(); 248 static native int getSysMinHeight(); 249 static native int getSysIconWidth(); 250 static native int getSysIconHeight(); 251 static native int getSysSmIconWidth(); 252 static native int getSysSmIconHeight(); 253 /**windows/classes/sun/awt/windows/ 254 * Creates native icon from specified raster data and updates 255 * icon for window and all descendant windows that inherit icon. 256 * Raster data should be passed in the ARGB form. 257 * Note that raster data format was changed to provide support 258 * for XP icons with alpha-channel 259 */ 260 native void setIconImagesData(int[] iconRaster, int w, int h, 261 int[] smallIconRaster, int smw, int smh); 262 263 synchronized native void reshapeFrame(int x, int y, int width, int height); 264 public boolean requestWindowFocus() { 265 // Win32 window doesn't need this 266 return false; 267 } 268 269 public boolean focusAllowedFor() { 270 Window target = (Window)this.target; 271 if (!target.isVisible() || 272 !target.isEnabled() || 273 !target.isFocusable()) 274 { 275 return false; 276 } 277 278 if (isModalBlocked()) { 279 return false; 280 } 281 282 return true; 283 } 284 285 public void updateMinimumSize() { 286 Dimension minimumSize = null; 287 if (((Component)target).isMinimumSizeSet()) { 288 minimumSize = ((Component)target).getMinimumSize(); 289 } 290 if (minimumSize != null) { 291 int msw = getSysMinWidth(); 292 int msh = getSysMinHeight(); 293 int w = (minimumSize.width >= msw) ? minimumSize.width : msw; 294 int h = (minimumSize.height >= msh) ? minimumSize.height : msh; 295 setMinSize(w, h); 296 } else { 297 setMinSize(0, 0); 298 } 299 } 300 301 public void updateIconImages() { 302 java.util.List<Image> imageList = ((Window)target).getIconImages(); 303 if (imageList == null || imageList.size() == 0) { 304 setIconImagesData(null, 0, 0, null, 0, 0); 305 } else { 306 int w = getSysIconWidth(); 307 int h = getSysIconHeight(); 308 int smw = getSysSmIconWidth(); 309 int smh = getSysSmIconHeight(); 310 DataBufferInt iconData = SunToolkit.getScaledIconData(imageList, 311 w, h); 312 DataBufferInt iconSmData = SunToolkit.getScaledIconData(imageList, 313 smw, smh); 314 if (iconData != null && iconSmData != null) { 315 setIconImagesData(iconData.getData(), w, h, 316 iconSmData.getData(), smw, smh); 317 } else { 318 setIconImagesData(null, 0, 0, null, 0, 0); 319 } 320 } 321 } 322 323 native void setMinSize(int width, int height); 324 325 /* 326 * ---- MODALITY SUPPORT ---- 327 */ 328 329 /** 330 * Some modality-related code here because WFileDialogPeer, WPrintDialogPeer and 331 * WPageDialogPeer are descendants of WWindowPeer, not WDialogPeer 332 */ 333 334 public boolean isModalBlocked() { 335 return modalBlocker != null; 336 } 337 338 public void setModalBlocked(Dialog dialog, boolean blocked) { 339 synchronized (((Component)getTarget()).getTreeLock()) // State lock should always be after awtLock 340 { 341 // use WWindowPeer instead of WDialogPeer because of FileDialogs and PrintDialogs 342 WWindowPeer blockerPeer = (WWindowPeer)dialog.getPeer(); 343 if (blocked) 344 { 345 modalBlocker = blockerPeer; 346 // handle native dialogs separately, as they may have not 347 // got HWND yet; modalEnable/modalDisable is called from 348 // their setHWnd() methods 349 if (blockerPeer instanceof WFileDialogPeer) { 350 ((WFileDialogPeer)blockerPeer).blockWindow(this); 351 } else if (blockerPeer instanceof WPrintDialogPeer) { 352 ((WPrintDialogPeer)blockerPeer).blockWindow(this); 353 } else { 354 modalDisable(dialog, blockerPeer.getHWnd()); 355 } 356 } else { 357 modalBlocker = null; 358 if (blockerPeer instanceof WFileDialogPeer) { 359 ((WFileDialogPeer)blockerPeer).unblockWindow(this); 360 } else if (blockerPeer instanceof WPrintDialogPeer) { 361 ((WPrintDialogPeer)blockerPeer).unblockWindow(this); 362 } else { 363 modalEnable(dialog); 364 } 365 } 366 } 367 } 368 369 native void modalDisable(Dialog blocker, long blockerHWnd); 370 native void modalEnable(Dialog blocker); 371 372 /* 373 * Returns all the ever active windows from the current AppContext. 374 * The list is sorted by the time of activation, so the latest 375 * active window is always at the end. 376 */ 377 public static long[] getActiveWindowHandles() { 378 AppContext appContext = AppContext.getAppContext(); 379 if (appContext == null) return null; 380 synchronized (appContext) { 381 List<WWindowPeer> l = (List<WWindowPeer>)appContext.get(ACTIVE_WINDOWS_KEY); 382 if (l == null) { 383 return null; 384 } 385 long[] result = new long[l.size()]; 386 for (int j = 0; j < l.size(); j++) { 387 result[j] = l.get(j).getHWnd(); 388 } 389 return result; 390 } 391 } 392 393 /* 394 * ----DISPLAY CHANGE SUPPORT---- 395 */ 396 397 /* 398 * Called from native code when we have been dragged onto another screen. 399 */ 400 void draggedToNewScreen() { 401 SunToolkit.executeOnEventHandlerThread((Component)target,new Runnable() 402 { 403 public void run() { 404 displayChanged(); 405 } 406 }); 407 } 408 409 410 /* 411 * Called from WCanvasPeer.displayChanged(). 412 * Override to do nothing - Window and WWindowPeer GC must never be set to 413 * null! 414 */ 415 void clearLocalGC() {} 416 417 public void updateGC() { 418 int scrn = getScreenImOn(); 419 if (screenLog.isLoggable(Level.FINER)) { 420 log.log(Level.FINER, "Screen number: " + scrn); 421 } 422 423 // get current GD 424 Win32GraphicsDevice oldDev = (Win32GraphicsDevice)winGraphicsConfig 425 .getDevice(); 426 427 Win32GraphicsDevice newDev; 428 GraphicsDevice devs[] = GraphicsEnvironment 429 .getLocalGraphicsEnvironment() 430 .getScreenDevices(); 431 // Occasionally during device addition/removal getScreenImOn can return 432 // a non-existing screen number. Use the default device in this case. 433 if (scrn >= devs.length) { 434 newDev = (Win32GraphicsDevice)GraphicsEnvironment 435 .getLocalGraphicsEnvironment().getDefaultScreenDevice(); 436 } else { 437 newDev = (Win32GraphicsDevice)devs[scrn]; 438 } 439 440 // Set winGraphicsConfig to the default GC for the monitor this Window 441 // is now mostly on. 442 winGraphicsConfig = (Win32GraphicsConfig)newDev 443 .getDefaultConfiguration(); 444 if (screenLog.isLoggable(Level.FINE)) { 445 if (winGraphicsConfig == null) { 446 screenLog.log(Level.FINE, "Assertion (winGraphicsConfig != null) failed"); 447 } 448 } 449 450 // if on a different display, take off old GD and put on new GD 451 if (oldDev != newDev) { 452 oldDev.removeDisplayChangedListener(this); 453 newDev.addDisplayChangedListener(this); 454 } 455 } 456 457 /* 458 * From the DisplayChangedListener interface 459 * 460 * This method handles a display change - either when the display settings 461 * are changed, or when the window has been dragged onto a different 462 * display. 463 */ 464 public void displayChanged() { 465 updateGC(); 466 super.displayChanged(); 467 } 468 469 private native int getScreenImOn(); 470 471 /* 472 * ----END DISPLAY CHANGE SUPPORT---- 473 */ 474 475 public void grab() { 476 nativeGrab(); 477 } 478 479 public void ungrab() { 480 nativeUngrab(); 481 } 482 private native void nativeGrab(); 483 private native void nativeUngrab(); 484 485 private final boolean hasWarningWindow() { 486 return ((Window)target).getWarningString() != null; 487 } 488 489 boolean isTargetUndecorated() { 490 return true; 491 } 492 493 // These are the peer bounds. They get updated at: 494 // 1. the WWindowPeer.setBounds() method. 495 // 2. the native code (on WM_SIZE/WM_MOVE) 496 private volatile int sysX = 0; 497 private volatile int sysY = 0; 498 private volatile int sysW = 0; 499 private volatile int sysH = 0; 500 501 Rectangle constrainBounds(int x, int y, int width, int height) { 502 GraphicsConfiguration gc = this.winGraphicsConfig; 503 504 // We don't restrict the setBounds() operation if the code is trusted. 505 if (!hasWarningWindow() || gc == null) { 506 return new Rectangle(x, y, width, height); 507 } 508 509 int newX = x; 510 int newY = y; 511 int newW = width; 512 int newH = height; 513 514 Rectangle sB = gc.getBounds(); 515 Insets sIn = Toolkit.getDefaultToolkit().getScreenInsets(gc); 516 517 int screenW = sB.width - sIn.left - sIn.right; 518 int screenH = sB.height - sIn.top - sIn.bottom; 519 520 // If it's undecorated or is not currently visible 521 if (!AWTAccessor.getComponentAccessor().isVisible_NoClientCode( 522 (Component)target) || isTargetUndecorated()) 523 { 524 // Now check each point is within the visible part of the screen 525 int screenX = sB.x + sIn.left; 526 int screenY = sB.y + sIn.top; 527 528 // First make sure the size is within the visible part of the screen 529 if (newW > screenW) { 530 newW = screenW; 531 } 532 if (newH > screenH) { 533 newH = screenH; 534 } 535 536 // Tweak the location if needed 537 if (newX < screenX) { 538 newX = screenX; 539 } else if (newX + newW > screenX + screenW) { 540 newX = screenX + screenW - newW; 541 } 542 if (newY < screenY) { 543 newY = screenY; 544 } else if (newY + newH > screenY + screenH) { 545 newY = screenY + screenH - newH; 546 } 547 } else { 548 int maxW = Math.max(screenW, sysW); 549 int maxH = Math.max(screenH, sysH); 550 551 // Make sure the size is withing the visible part of the screen 552 // OR less that the current size of the window. 553 if (newW > maxW) { 554 newW = maxW; 555 } 556 if (newH > maxH) { 557 newH = maxH; 558 } 559 } 560 561 return new Rectangle(newX, newY, newW, newH); 562 } 563 564 public native void repositionSecurityWarning(); 565 566 @Override 567 public void setBounds(int x, int y, int width, int height, int op) { 568 Rectangle newBounds = constrainBounds(x, y, width, height); 569 570 sysX = newBounds.x; 571 sysY = newBounds.y; 572 sysW = newBounds.width; 573 sysH = newBounds.height; 574 575 super.setBounds(newBounds.x, newBounds.y, newBounds.width, newBounds.height, op); 576 } 577 578 @Override 579 public void print(Graphics g) { 580 // We assume we print the whole frame, 581 // so we expect no clip was set previously 582 Shape shape = AWTAccessor.getWindowAccessor().getShape((Window)target); 583 if (shape != null) { 584 g.setClip(shape); 585 } 586 super.print(g); 587 } 588 589 private void replaceSurfaceDataRecursively(Component c) { 590 if (c instanceof Container) { 591 for (Component child : ((Container)c).getComponents()) { 592 replaceSurfaceDataRecursively(child); 593 } 594 } 595 ComponentPeer cp = c.getPeer(); 596 if (cp instanceof WComponentPeer) { 597 ((WComponentPeer)cp).replaceSurfaceDataLater(); 598 } 599 } 600 601 @Override 602 public Graphics getGraphics() { 603 synchronized (getStateLock()) { 604 if (!isOpaque) { 605 return painter.getBackBuffer(false).getGraphics(); 606 } 607 } 608 return super.getGraphics(); 609 } 610 611 @Override 612 public void setBackground(Color c) { 613 super.setBackground(c); 614 synchronized (getStateLock()) { 615 if (!isOpaque && ((Window)target).isVisible()) { 616 updateWindow(true); 617 } 618 } 619 } 620 621 private native void setOpacity(int iOpacity); 622 623 public void setOpacity(float opacity) { 624 if (!((SunToolkit)((Window)target).getToolkit()). 625 isWindowOpacitySupported()) 626 { 627 return; 628 } 629 630 replaceSurfaceDataRecursively((Component)getTarget()); 631 632 final int maxOpacity = 0xff; 633 int iOpacity = (int)(opacity * maxOpacity); 634 if (iOpacity < 0) { 635 iOpacity = 0; 636 } 637 if (iOpacity > maxOpacity) { 638 iOpacity = maxOpacity; 639 } 640 641 setOpacity(iOpacity); 642 643 synchronized (getStateLock()) { 644 if (!isOpaque && ((Window)target).isVisible()) { 645 updateWindow(true); 646 } 647 } 648 } 649 650 private native void setOpaqueImpl(boolean isOpaque); 651 652 public void setOpaque(boolean isOpaque) { 653 synchronized (getStateLock()) { 654 if (this.isOpaque == isOpaque) { 655 return; 656 } 657 } 658 659 Window target = (Window)getTarget(); 660 661 SunToolkit sunToolkit = (SunToolkit)target.getToolkit(); 662 if (!sunToolkit.isWindowTranslucencySupported() || 663 !sunToolkit.isTranslucencyCapable(target.getGraphicsConfiguration())) 664 { 665 return; 666 } 667 668 boolean isVistaOS = Win32GraphicsEnvironment.isVistaOS(); 669 670 if (!isVistaOS) { 671 // non-Vista OS: only replace the surface data if the opacity 672 // status changed (see WComponentPeer.isAccelCapable() for more) 673 replaceSurfaceDataRecursively(target); 674 } 675 676 synchronized (getStateLock()) { 677 this.isOpaque = isOpaque; 678 setOpaqueImpl(isOpaque); 679 if (isOpaque) { 680 TranslucentWindowPainter currentPainter = painter; 681 if (currentPainter != null) { 682 currentPainter.flush(); 683 painter = null; 684 } 685 } else { 686 painter = TranslucentWindowPainter.createInstance(this); 687 } 688 } 689 690 if (isVistaOS) { 691 // On Vista: setting the window non-opaque makes the window look 692 // rectangular, though still catching the mouse clicks within 693 // its shape only. To restore the correct visual appearance 694 // of the window (i.e. w/ the correct shape) we have to reset 695 // the shape. 696 Shape shape = AWTAccessor.getWindowAccessor().getShape(target); 697 if (shape != null) { 698 AWTAccessor.getWindowAccessor().setShape(target, shape); 699 } 700 } 701 702 if (((Window)target).isVisible()) { 703 updateWindow(true); 704 } 705 } 706 707 public native void updateWindowImpl(int[] data, int width, int height); 708 709 public void updateWindow() { 710 updateWindow(false); 711 } 712 713 private void updateWindow(boolean repaint) { 714 Window w = (Window)target; 715 synchronized (getStateLock()) { 716 if (isOpaque || !w.isVisible() || 717 (w.getWidth() <= 0) || (w.getHeight() <= 0)) 718 { 719 return; 720 } 721 TranslucentWindowPainter currentPainter = painter; 722 if (currentPainter != null) { 723 currentPainter.updateWindow(repaint); 724 } else if (log.isLoggable(Level.FINER)) { 725 log.log(Level.FINER, 726 "Translucent window painter is null in updateWindow"); 727 } 728 } 729 } 730 731 /* 732 * Static inner class, listens for 'activeWindow' KFM property changes and 733 * updates the list of active windows per AppContext, so the latest active 734 * window is always at the end of the list. The list is stored in AppContext. 735 */ 736 private static class ActiveWindowListener implements PropertyChangeListener { 737 public void propertyChange(PropertyChangeEvent e) { 738 Window w = (Window)e.getNewValue(); 739 if (w == null) { 740 return; 741 } 742 AppContext appContext = SunToolkit.targetToAppContext(w); 743 synchronized (appContext) { 744 List<WWindowPeer> l = (List<WWindowPeer>)appContext.get(ACTIVE_WINDOWS_KEY); 745 if (l == null) { 746 l = new LinkedList<WWindowPeer>(); 747 appContext.put(ACTIVE_WINDOWS_KEY, l); 748 } 749 WWindowPeer wp = (WWindowPeer)w.getPeer(); 750 // add/move wp to the end of the list 751 l.remove(wp); 752 l.add(wp); 753 } 754 } 755 } 756 }