diff -r fd16c54261b3 -r 90ce3da70b43 jdk/src/share/classes/javax/swing/JDesktopPane.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/src/share/classes/javax/swing/JDesktopPane.java Sat Dec 01 00:00:00 2007 +0000 @@ -0,0 +1,636 @@ +/* + * Copyright 1997-2006 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 javax.swing; + +import java.util.List; +import java.util.ArrayList; +import java.util.Vector; +import javax.swing.plaf.*; +import javax.accessibility.*; + +import java.awt.Component; +import java.awt.Container; +import java.awt.DefaultFocusTraversalPolicy; +import java.awt.FocusTraversalPolicy; +import java.awt.Window; +import java.io.ObjectOutputStream; +import java.io.ObjectInputStream; +import java.io.IOException; +import java.beans.PropertyVetoException; +import java.util.Set; +import java.util.TreeSet; + +/** + * A container used to create a multiple-document interface or a virtual desktop. + * You create JInternalFrame objects and add them to the + * JDesktopPane. JDesktopPane extends + * JLayeredPane to manage the potentially overlapping internal + * frames. It also maintains a reference to an instance of + * DesktopManager that is set by the UI + * class for the current look and feel (L&F). Note that JDesktopPane + * does not support borders. + *

+ * This class is normally used as the parent of JInternalFrames + * to provide a pluggable DesktopManager object to the + * JInternalFrames. The installUI of the + * L&F specific implementation is responsible for setting the + * desktopManager variable appropriately. + * When the parent of a JInternalFrame is a JDesktopPane, + * it should delegate most of its behavior to the desktopManager + * (closing, resizing, etc). + *

+ * For further documentation and examples see + * How to Use Internal Frames, + * a section in The Java Tutorial. + *

+ * Warning: Swing is not thread safe. For more + * information see Swing's Threading + * Policy. + *

+ * Warning: + * Serialized objects of this class will not be compatible with + * future Swing releases. The current serialization support is + * appropriate for short term storage or RMI between applications running + * the same version of Swing. As of 1.4, support for long term storage + * of all JavaBeansTM + * has been added to the java.beans package. + * Please see {@link java.beans.XMLEncoder}. + * + * @see JInternalFrame + * @see JInternalFrame.JDesktopIcon + * @see DesktopManager + * + * @author David Kloba + */ +public class JDesktopPane extends JLayeredPane implements Accessible +{ + /** + * @see #getUIClassID + * @see #readObject + */ + private static final String uiClassID = "DesktopPaneUI"; + + transient DesktopManager desktopManager; + + private transient JInternalFrame selectedFrame = null; + + /** + * Indicates that the entire contents of the item being dragged + * should appear inside the desktop pane. + * + * @see #OUTLINE_DRAG_MODE + * @see #setDragMode + */ + public static final int LIVE_DRAG_MODE = 0; + + /** + * Indicates that an outline only of the item being dragged + * should appear inside the desktop pane. + * + * @see #LIVE_DRAG_MODE + * @see #setDragMode + */ + public static final int OUTLINE_DRAG_MODE = 1; + + private int dragMode = LIVE_DRAG_MODE; + private boolean dragModeSet = false; + private transient List framesCache; + private boolean componentOrderCheckingEnabled = true; + private boolean componentOrderChanged = false; + + /** + * Creates a new JDesktopPane. + */ + public JDesktopPane() { + setUIProperty("opaque", Boolean.TRUE); + setFocusCycleRoot(true); + + setFocusTraversalPolicy(new LayoutFocusTraversalPolicy() { + public Component getDefaultComponent(Container c) { + JInternalFrame jifArray[] = getAllFrames(); + Component comp = null; + for (int i = 0; i < jifArray.length; i++) { + comp = jifArray[i].getFocusTraversalPolicy().getDefaultComponent(jifArray[i]); + if (comp != null) { + break; + } + } + return comp; + } + }); + updateUI(); + } + + /** + * Returns the L&F object that renders this component. + * + * @return the DesktopPaneUI object that + * renders this component + */ + public DesktopPaneUI getUI() { + return (DesktopPaneUI)ui; + } + + /** + * Sets the L&F object that renders this component. + * + * @param ui the DesktopPaneUI L&F object + * @see UIDefaults#getUI + * @beaninfo + * bound: true + * hidden: true + * attribute: visualUpdate true + * description: The UI object that implements the Component's LookAndFeel. + */ + public void setUI(DesktopPaneUI ui) { + super.setUI(ui); + } + + /** + * Sets the "dragging style" used by the desktop pane. + * You may want to change to one mode or another for + * performance or aesthetic reasons. + * + * @param dragMode the style of drag to use for items in the Desktop + * + * @see #LIVE_DRAG_MODE + * @see #OUTLINE_DRAG_MODE + * + * @beaninfo + * bound: true + * description: Dragging style for internal frame children. + * enum: LIVE_DRAG_MODE JDesktopPane.LIVE_DRAG_MODE + * OUTLINE_DRAG_MODE JDesktopPane.OUTLINE_DRAG_MODE + * @since 1.3 + */ + public void setDragMode(int dragMode) { + int oldDragMode = this.dragMode; + this.dragMode = dragMode; + firePropertyChange("dragMode", oldDragMode, this.dragMode); + dragModeSet = true; + } + + /** + * Gets the current "dragging style" used by the desktop pane. + * @return either Live_DRAG_MODE or + * OUTLINE_DRAG_MODE + * @see #setDragMode + * @since 1.3 + */ + public int getDragMode() { + return dragMode; + } + + /** + * Returns the DesktopManger that handles + * desktop-specific UI actions. + */ + public DesktopManager getDesktopManager() { + return desktopManager; + } + + /** + * Sets the DesktopManger that will handle + * desktop-specific UI actions. + * + * @param d the DesktopManager to use + * + * @beaninfo + * bound: true + * description: Desktop manager to handle the internal frames in the + * desktop pane. + */ + public void setDesktopManager(DesktopManager d) { + DesktopManager oldValue = desktopManager; + desktopManager = d; + firePropertyChange("desktopManager", oldValue, desktopManager); + } + + /** + * Notification from the UIManager that the L&F has changed. + * Replaces the current UI object with the latest version from the + * UIManager. + * + * @see JComponent#updateUI + */ + public void updateUI() { + setUI((DesktopPaneUI)UIManager.getUI(this)); + } + + + /** + * Returns the name of the L&F class that renders this component. + * + * @return the string "DesktopPaneUI" + * @see JComponent#getUIClassID + * @see UIDefaults#getUI + */ + public String getUIClassID() { + return uiClassID; + } + + /** + * Returns all JInternalFrames currently displayed in the + * desktop. Returns iconified frames as well as expanded frames. + * + * @return an array of JInternalFrame objects + */ + public JInternalFrame[] getAllFrames() { + int i, count; + JInternalFrame[] results; + Vector vResults = new Vector(10); + Object next, tmp; + + count = getComponentCount(); + for(i = 0; i < count; i++) { + next = getComponent(i); + if(next instanceof JInternalFrame) + vResults.addElement(next); + else if(next instanceof JInternalFrame.JDesktopIcon) { + tmp = ((JInternalFrame.JDesktopIcon)next).getInternalFrame(); + if(tmp != null) + vResults.addElement(tmp); + } + } + + results = new JInternalFrame[vResults.size()]; + vResults.copyInto(results); + + return results; + } + + /** Returns the currently active JInternalFrame + * in this JDesktopPane, or null + * if no JInternalFrame is currently active. + * + * @return the currently active JInternalFrame or + * null + * @since 1.3 + */ + + public JInternalFrame getSelectedFrame() { + return selectedFrame; + } + + /** Sets the currently active JInternalFrame + * in this JDesktopPane. This method is used to bridge + * the package gap between JDesktopPane and the platform implementation + * code and should not be called directly. To visually select the frame + * the client must call JInternalFrame.setSelected(true) to activate + * the frame. + * @see JInternalFrame#setSelected(boolean) + * + * @param f the internal frame that's currently selected + * @since 1.3 + */ + + public void setSelectedFrame(JInternalFrame f) { + selectedFrame = f; + } + + /** + * Returns all JInternalFrames currently displayed in the + * specified layer of the desktop. Returns iconified frames as well + * expanded frames. + * + * @param layer an int specifying the desktop layer + * @return an array of JInternalFrame objects + * @see JLayeredPane + */ + public JInternalFrame[] getAllFramesInLayer(int layer) { + int i, count; + JInternalFrame[] results; + Vector vResults = new Vector(10); + Object next, tmp; + + count = getComponentCount(); + for(i = 0; i < count; i++) { + next = getComponent(i); + if(next instanceof JInternalFrame) { + if(((JInternalFrame)next).getLayer() == layer) + vResults.addElement(next); + } else if(next instanceof JInternalFrame.JDesktopIcon) { + tmp = ((JInternalFrame.JDesktopIcon)next).getInternalFrame(); + if(tmp != null && ((JInternalFrame)tmp).getLayer() == layer) + vResults.addElement(tmp); + } + } + + results = new JInternalFrame[vResults.size()]; + vResults.copyInto(results); + + return results; + } + + private List getFrames() { + Component c; + Set set = new TreeSet(); + for (int i = 0; i < getComponentCount(); i++) { + c = getComponent(i); + if (c instanceof JInternalFrame) { + set.add(new ComponentPosition((JInternalFrame)c, getLayer(c), + i)); + } + else if (c instanceof JInternalFrame.JDesktopIcon) { + c = ((JInternalFrame.JDesktopIcon)c).getInternalFrame(); + set.add(new ComponentPosition((JInternalFrame)c, getLayer(c), + i)); + } + } + List frames = new ArrayList( + set.size()); + for (ComponentPosition position : set) { + frames.add(position.component); + } + return frames; + } + + private static class ComponentPosition implements + Comparable { + private final JInternalFrame component; + private final int layer; + private final int zOrder; + + ComponentPosition(JInternalFrame component, int layer, int zOrder) { + this.component = component; + this.layer = layer; + this.zOrder = zOrder; + } + + public int compareTo(ComponentPosition o) { + int delta = o.layer - layer; + if (delta == 0) { + return zOrder - o.zOrder; + } + return delta; + } + } + + private JInternalFrame getNextFrame(JInternalFrame f, boolean forward) { + verifyFramesCache(); + if (f == null) { + return getTopInternalFrame(); + } + int i = framesCache.indexOf(f); + if (i == -1 || framesCache.size() == 1) { + /* error */ + return null; + } + if (forward) { + // navigate to the next frame + if (++i == framesCache.size()) { + /* wrap */ + i = 0; + } + } + else { + // navigate to the previous frame + if (--i == -1) { + /* wrap */ + i = framesCache.size() - 1; + } + } + return framesCache.get(i); + } + + JInternalFrame getNextFrame(JInternalFrame f) { + return getNextFrame(f, true); + } + + private JInternalFrame getTopInternalFrame() { + if (framesCache.size() == 0) { + return null; + } + return framesCache.get(0); + } + + private void updateFramesCache() { + framesCache = getFrames(); + } + + private void verifyFramesCache() { + // If framesCache is dirty, then recreate it. + if (componentOrderChanged) { + componentOrderChanged = false; + updateFramesCache(); + } + } + + /** + * Selects the next JInternalFrame in this desktop pane. + * + * @param forward a boolean indicating which direction to select in; + * true for forward, false for + * backward + * @return the JInternalFrame that was selected or null + * if nothing was selected + * @since 1.6 + */ + public JInternalFrame selectFrame(boolean forward) { + JInternalFrame selectedFrame = getSelectedFrame(); + JInternalFrame frameToSelect = getNextFrame(selectedFrame, forward); + if (frameToSelect == null) { + return null; + } + // Maintain navigation traversal order until an + // external stack change, such as a click on a frame. + setComponentOrderCheckingEnabled(false); + if (forward && selectedFrame != null) { + selectedFrame.moveToBack(); // For Windows MDI fidelity. + } + try { frameToSelect.setSelected(true); + } catch (PropertyVetoException pve) {} + setComponentOrderCheckingEnabled(true); + return frameToSelect; + } + + /* + * Sets whether component order checking is enabled. + * @param enable a boolean value, where true means + * a change in component order will cause a change in the keyboard + * navigation order. + * @since 1.6 + */ + void setComponentOrderCheckingEnabled(boolean enable) { + componentOrderCheckingEnabled = enable; + } + + /** + * {@inheritDoc} + * @since 1.6 + */ + protected void addImpl(Component comp, Object constraints, int index) { + super.addImpl(comp, constraints, index); + if (componentOrderCheckingEnabled) { + if (comp instanceof JInternalFrame || + comp instanceof JInternalFrame.JDesktopIcon) { + componentOrderChanged = true; + } + } + } + + /** + * {@inheritDoc} + * @since 1.6 + */ + public void remove(int index) { + if (componentOrderCheckingEnabled) { + Component comp = getComponent(index); + if (comp instanceof JInternalFrame || + comp instanceof JInternalFrame.JDesktopIcon) { + componentOrderChanged = true; + } + } + super.remove(index); + } + + /** + * {@inheritDoc} + * @since 1.6 + */ + public void removeAll() { + if (componentOrderCheckingEnabled) { + int count = getComponentCount(); + for (int i = 0; i < count; i++) { + Component comp = getComponent(i); + if (comp instanceof JInternalFrame || + comp instanceof JInternalFrame.JDesktopIcon) { + componentOrderChanged = true; + break; + } + } + } + super.removeAll(); + } + + /** + * {@inheritDoc} + * @since 1.6 + */ + public void setComponentZOrder(Component comp, int index) { + super.setComponentZOrder(comp, index); + if (componentOrderCheckingEnabled) { + if (comp instanceof JInternalFrame || + comp instanceof JInternalFrame.JDesktopIcon) { + componentOrderChanged = true; + } + } + } + + /** + * See readObject() and writeObject() in JComponent for more + * information about serialization in Swing. + */ + private void writeObject(ObjectOutputStream s) throws IOException { + s.defaultWriteObject(); + if (getUIClassID().equals(uiClassID)) { + byte count = JComponent.getWriteObjCounter(this); + JComponent.setWriteObjCounter(this, --count); + if (count == 0 && ui != null) { + ui.installUI(this); + } + } + } + + void setUIProperty(String propertyName, Object value) { + if (propertyName == "dragMode") { + if (!dragModeSet) { + setDragMode(((Integer)value).intValue()); + dragModeSet = false; + } + } else { + super.setUIProperty(propertyName, value); + } + } + + /** + * Returns a string representation of this JDesktopPane. + * This method is intended to be used only for debugging purposes, and the + * content and format of the returned string may vary between + * implementations. The returned string may be empty but may not + * be null. + * + * @return a string representation of this JDesktopPane + */ + protected String paramString() { + String desktopManagerString = (desktopManager != null ? + desktopManager.toString() : ""); + + return super.paramString() + + ",desktopManager=" + desktopManagerString; + } + +///////////////// +// Accessibility support +//////////////// + + /** + * Gets the AccessibleContext associated with this + * JDesktopPane. For desktop panes, the + * AccessibleContext takes the form of an + * AccessibleJDesktopPane. + * A new AccessibleJDesktopPane instance is created if necessary. + * + * @return an AccessibleJDesktopPane that serves as the + * AccessibleContext of this JDesktopPane + */ + public AccessibleContext getAccessibleContext() { + if (accessibleContext == null) { + accessibleContext = new AccessibleJDesktopPane(); + } + return accessibleContext; + } + + /** + * This class implements accessibility support for the + * JDesktopPane class. It provides an implementation of the + * Java Accessibility API appropriate to desktop pane user-interface + * elements. + *

+ * Warning: + * Serialized objects of this class will not be compatible with + * future Swing releases. The current serialization support is + * appropriate for short term storage or RMI between applications running + * the same version of Swing. As of 1.4, support for long term storage + * of all JavaBeansTM + * has been added to the java.beans package. + * Please see {@link java.beans.XMLEncoder}. + */ + protected class AccessibleJDesktopPane extends AccessibleJComponent { + + /** + * Get the role of this object. + * + * @return an instance of AccessibleRole describing the role of the + * object + * @see AccessibleRole + */ + public AccessibleRole getAccessibleRole() { + return AccessibleRole.DESKTOP_PANE; + } + } +}