/* * Copyright 1996-2007 Sun Microsystems, Inc. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Sun designates this * particular file as subject to the "Classpath" exception as provided * by Sun in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, * CA 95054 USA or visit www.sun.com if you need additional information or * have any questions. */ package sun.awt.windows; import java.awt.*; import java.awt.event.*; import java.awt.image.*; import java.awt.peer.*; import java.beans.*; import java.lang.ref.*; import java.lang.reflect.*; import java.security.*; import java.util.*; import java.util.List; import java.util.logging.*; import sun.awt.*; import sun.awt.image.*; public class WWindowPeer extends WPanelPeer implements WindowPeer { private static final Logger log = Logger.getLogger("sun.awt.windows.WWindowPeer"); private static final Logger screenLog = Logger.getLogger("sun.awt.windows.screen.WWindowPeer"); // we can't use WDialogPeer as blocker may be an instance of WPrintDialogPeer that // extends WWindowPeer, not WDialogPeer private WWindowPeer modalBlocker = null; /* * A key used for storing a list of active windows in AppContext. The value * is a list of windows, sorted by the time of activation: later a window is * activated, greater its index is in the list. */ private final static StringBuffer ACTIVE_WINDOWS_KEY = new StringBuffer("active_windows_list"); /* * Listener for 'activeWindow' KFM property changes. It is added to each * AppContext KFM. See ActiveWindowListener inner class below. */ private static PropertyChangeListener activeWindowListener = new ActiveWindowListener(); /* * Contains all the AppContexts where activeWindowListener is added to. */ private static Set<AppContext> trackedAppContexts = new HashSet<AppContext>(); /** * Initialize JNI field IDs */ private static native void initIDs(); static { initIDs(); } // WComponentPeer overrides protected void disposeImpl() { AppContext appContext = SunToolkit.targetToAppContext(target); synchronized (appContext) { List<WWindowPeer> l = (List<WWindowPeer>)appContext.get(ACTIVE_WINDOWS_KEY); if (l != null) { l.remove(this); } } // Remove ourself from the Map of DisplayChangeListeners GraphicsConfiguration gc = getGraphicsConfiguration(); ((Win32GraphicsDevice)gc.getDevice()).removeDisplayChangedListener(this); super.disposeImpl(); } // WindowPeer implementation public void toFront() { updateFocusableWindowState(); _toFront(); } native void _toFront(); public native void toBack(); public native void setAlwaysOnTopNative(boolean value); public void setAlwaysOnTop(boolean value) { if ((value && ((Window)target).isVisible()) || !value) { setAlwaysOnTopNative(value); } } public void updateFocusableWindowState() { setFocusableWindow(((Window)target).isFocusableWindow()); } native void setFocusableWindow(boolean value); // FramePeer & DialogPeer partial shared implementation public void setTitle(String title) { // allow a null title to pass as an empty string. if (title == null) { title = new String(""); } _setTitle(title); } native void _setTitle(String title); public void setResizable(boolean resizable) { _setResizable(resizable); } public native void _setResizable(boolean resizable); // Toolkit & peer internals WWindowPeer(Window target) { super(target); } void initialize() { super.initialize(); updateInsets(insets_); Font f = ((Window)target).getFont(); if (f == null) { f = defaultFont; ((Window)target).setFont(f); setFont(f); } // Express our interest in display changes GraphicsConfiguration gc = getGraphicsConfiguration(); ((Win32GraphicsDevice)gc.getDevice()).addDisplayChangedListener(this); AppContext appContext = AppContext.getAppContext(); synchronized (appContext) { if (!trackedAppContexts.contains(appContext)) { KeyboardFocusManager kfm = KeyboardFocusManager.getCurrentKeyboardFocusManager(); kfm.addPropertyChangeListener("activeWindow", activeWindowListener); trackedAppContexts.add(appContext); } } updateIconImages(); } native void createAwtWindow(WComponentPeer parent); void create(WComponentPeer parent) { createAwtWindow(parent); } // should be overriden in WDialogPeer protected void realShow() { super.show(); } public void show() { updateFocusableWindowState(); boolean alwaysOnTop = ((Window)target).isAlwaysOnTop(); // Fix for 4868278. // If we create a window with a specific GraphicsConfig, and then move it with // setLocation() or setBounds() to another one before its peer has been created, // then calling Window.getGraphicsConfig() returns wrong config. That may lead // to some problems like wrong-placed tooltips. It is caused by calling // super.displayChanged() in WWindowPeer.displayChanged() regardless of whether // GraphicsDevice was really changed, or not. So we need to track it here. updateGC(); resetTargetGC(); realShow(); updateMinimumSize(); if (((Window)target).isAlwaysOnTopSupported() && alwaysOnTop) { setAlwaysOnTop(alwaysOnTop); } } // Synchronize the insets members (here & in helper) with actual window // state. native void updateInsets(Insets i); static native int getSysMinWidth(); static native int getSysMinHeight(); static native int getSysIconWidth(); static native int getSysIconHeight(); static native int getSysSmIconWidth(); static native int getSysSmIconHeight(); /**windows/classes/sun/awt/windows/ * Creates native icon from specified raster data and updates * icon for window and all descendant windows that inherit icon. * Raster data should be passed in the ARGB form. * Note that raster data format was changed to provide support * for XP icons with alpha-channel */ native void setIconImagesData(int[] iconRaster, int w, int h, int[] smallIconRaster, int smw, int smh); synchronized native void reshapeFrame(int x, int y, int width, int height); public boolean requestWindowFocus() { // Win32 window doesn't need this return false; } public boolean focusAllowedFor() { Window target = (Window)this.target; if (!target.isVisible() || !target.isEnabled() || !target.isFocusable()) { return false; } if (isModalBlocked()) { return false; } return true; } public void updateMinimumSize() { Dimension minimumSize = null; if (((Component)target).isMinimumSizeSet()) { minimumSize = ((Component)target).getMinimumSize(); } if (minimumSize != null) { int msw = getSysMinWidth(); int msh = getSysMinHeight(); int w = (minimumSize.width >= msw) ? minimumSize.width : msw; int h = (minimumSize.height >= msh) ? minimumSize.height : msh; setMinSize(w, h); } else { setMinSize(0, 0); } } public void updateIconImages() { java.util.List<Image> imageList = ((Window)target).getIconImages(); if (imageList == null || imageList.size() == 0) { setIconImagesData(null, 0, 0, null, 0, 0); } else { int w = getSysIconWidth(); int h = getSysIconHeight(); int smw = getSysSmIconWidth(); int smh = getSysSmIconHeight(); DataBufferInt iconData = SunToolkit.getScaledIconData(imageList, w, h); DataBufferInt iconSmData = SunToolkit.getScaledIconData(imageList, smw, smh); if (iconData != null && iconSmData != null) { setIconImagesData(iconData.getData(), w, h, iconSmData.getData(), smw, smh); } else { setIconImagesData(null, 0, 0, null, 0, 0); } } } native void setMinSize(int width, int height); /* * ---- MODALITY SUPPORT ---- */ /** * Some modality-related code here because WFileDialogPeer, WPrintDialogPeer and * WPageDialogPeer are descendants of WWindowPeer, not WDialogPeer */ public boolean isModalBlocked() { return modalBlocker != null; } public void setModalBlocked(Dialog dialog, boolean blocked) { synchronized (((Component)getTarget()).getTreeLock()) // State lock should always be after awtLock { // use WWindowPeer instead of WDialogPeer because of FileDialogs and PrintDialogs WWindowPeer blockerPeer = (WWindowPeer)dialog.getPeer(); if (blocked) { modalBlocker = blockerPeer; // handle native dialogs separately, as they may have not // got HWND yet; modalEnable/modalDisable is called from // their setHWnd() methods if (blockerPeer instanceof WFileDialogPeer) { ((WFileDialogPeer)blockerPeer).blockWindow(this); } else if (blockerPeer instanceof WPrintDialogPeer) { ((WPrintDialogPeer)blockerPeer).blockWindow(this); } else { modalDisable(dialog, blockerPeer.getHWnd()); } } else { modalBlocker = null; if (blockerPeer instanceof WFileDialogPeer) { ((WFileDialogPeer)blockerPeer).unblockWindow(this); } else if (blockerPeer instanceof WPrintDialogPeer) { ((WPrintDialogPeer)blockerPeer).unblockWindow(this); } else { modalEnable(dialog); } } } } native void modalDisable(Dialog blocker, long blockerHWnd); native void modalEnable(Dialog blocker); /* * Returns all the ever active windows from the current AppContext. * The list is sorted by the time of activation, so the latest * active window is always at the end. */ public static long[] getActiveWindowHandles() { AppContext appContext = AppContext.getAppContext(); synchronized (appContext) { List<WWindowPeer> l = (List<WWindowPeer>)appContext.get(ACTIVE_WINDOWS_KEY); if (l == null) { return null; } long[] result = new long[l.size()]; for (int j = 0; j < l.size(); j++) { result[j] = l.get(j).getHWnd(); } return result; } } /* * ----DISPLAY CHANGE SUPPORT---- */ /* * Called from native code when we have been dragged onto another screen. */ void draggedToNewScreen() { SunToolkit.executeOnEventHandlerThread((Component)target,new Runnable() { public void run() { displayChanged(); } }); } /* * Called from WCanvasPeer.displayChanged(). * Override to do nothing - Window and WWindowPeer GC must never be set to * null! */ void clearLocalGC() {} public void updateGC() { int scrn = getScreenImOn(); if (screenLog.isLoggable(Level.FINER)) { log.log(Level.FINER, "Screen number: " + scrn); } // get current GD Win32GraphicsDevice oldDev = (Win32GraphicsDevice)winGraphicsConfig .getDevice(); Win32GraphicsDevice newDev; GraphicsDevice devs[] = GraphicsEnvironment .getLocalGraphicsEnvironment() .getScreenDevices(); // Occasionally during device addition/removal getScreenImOn can return // a non-existing screen number. Use the default device in this case. if (scrn >= devs.length) { newDev = (Win32GraphicsDevice)GraphicsEnvironment .getLocalGraphicsEnvironment().getDefaultScreenDevice(); } else { newDev = (Win32GraphicsDevice)devs[scrn]; } // Set winGraphicsConfig to the default GC for the monitor this Window // is now mostly on. winGraphicsConfig = (Win32GraphicsConfig)newDev .getDefaultConfiguration(); if (screenLog.isLoggable(Level.FINE)) { if (winGraphicsConfig == null) { screenLog.log(Level.FINE, "Assertion (winGraphicsConfig != null) failed"); } } // if on a different display, take off old GD and put on new GD if (oldDev != newDev) { oldDev.removeDisplayChangedListener(this); newDev.addDisplayChangedListener(this); } } /* * From the DisplayChangedListener interface * * This method handles a display change - either when the display settings * are changed, or when the window has been dragged onto a different * display. */ public void displayChanged() { updateGC(); super.displayChanged(); } private native int getScreenImOn(); /* * ----END DISPLAY CHANGE SUPPORT---- */ public void grab() { nativeGrab(); } public void ungrab() { nativeUngrab(); } private native void nativeGrab(); private native void nativeUngrab(); private final boolean hasWarningWindow() { return ((Window)target).getWarningString() != null; } boolean isTargetUndecorated() { return true; } // These are the peer bounds. They get updated at: // 1. the WWindowPeer.setBounds() method. // 2. the native code (on WM_SIZE/WM_MOVE) private volatile int sysX = 0; private volatile int sysY = 0; private volatile int sysW = 0; private volatile int sysH = 0; Rectangle constrainBounds(int x, int y, int width, int height) { // We don't restrict the setBounds() operation if the code is trusted. if (!hasWarningWindow()) { return new Rectangle(x, y, width, height); } int newX = x; int newY = y; int newW = width; int newH = height; GraphicsConfiguration gc = ((Window)target).getGraphicsConfiguration(); Rectangle sB = gc.getBounds(); Insets sIn = ((Window)target).getToolkit().getScreenInsets(gc); int screenW = sB.width - sIn.left - sIn.right; int screenH = sB.height - sIn.top - sIn.bottom; // If it's undecorated or is not currently visible if (!((Window)target).isVisible() || isTargetUndecorated()) { // Now check each point is within the visible part of the screen int screenX = sB.x + sIn.left; int screenY = sB.y + sIn.top; // First make sure the size is withing the visible part of the screen if (newW > screenW) { newW = screenW; } if (newH > screenH) { newH = screenH; } // Tweak the location if needed if (newX < screenX) { newX = screenX; } else if (newX + newW > screenX + screenW) { newX = screenX + screenW - newW; } if (newY < screenY) { newY = screenY; } else if (newY + newH > screenY + screenH) { newY = screenY + screenH - newH; } } else { int maxW = Math.max(screenW, sysW); int maxH = Math.max(screenH, sysH); // Make sure the size is withing the visible part of the screen // OR less that the current size of the window. if (newW > maxW) { newW = maxW; } if (newH > maxH) { newH = maxH; } } return new Rectangle(newX, newY, newW, newH); } @Override public void setBounds(int x, int y, int width, int height, int op) { Rectangle newBounds = constrainBounds(x, y, width, height); sysX = newBounds.x; sysY = newBounds.y; sysW = newBounds.width; sysH = newBounds.height; super.setBounds(newBounds.x, newBounds.y, newBounds.width, newBounds.height, op); } /* * Static inner class, listens for 'activeWindow' KFM property changes and * updates the list of active windows per AppContext, so the latest active * window is always at the end of the list. The list is stored in AppContext. */ private static class ActiveWindowListener implements PropertyChangeListener { public void propertyChange(PropertyChangeEvent e) { Window w = (Window)e.getNewValue(); if (w == null) { return; } AppContext appContext = SunToolkit.targetToAppContext(w); synchronized (appContext) { List<WWindowPeer> l = (List<WWindowPeer>)appContext.get(ACTIVE_WINDOWS_KEY); if (l == null) { l = new LinkedList<WWindowPeer>(); appContext.put(ACTIVE_WINDOWS_KEY, l); } WWindowPeer wp = (WWindowPeer)w.getPeer(); // add/move wp to the end of the list l.remove(wp); l.add(wp); } } } }