/*
* Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved.
* ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*/
package javax.swing;
import java.util.
List;
import java.util.
ArrayList;
import java.util.
Collection;
import java.util.
Iterator;
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;
import java.util.
LinkedHashSet;
/**
* A container used to create a multiple-document interface or a virtual desktop.
* You create <code>JInternalFrame</code> objects and add them to the
* <code>JDesktopPane</code>. <code>JDesktopPane</code> extends
* <code>JLayeredPane</code> to manage the potentially overlapping internal
* frames. It also maintains a reference to an instance of
* <code>DesktopManager</code> that is set by the UI
* class for the current look and feel (L&F). Note that <code>JDesktopPane</code>
* does not support borders.
* <p>
* This class is normally used as the parent of <code>JInternalFrames</code>
* to provide a pluggable <code>DesktopManager</code> object to the
* <code>JInternalFrames</code>. The <code>installUI</code> of the
* L&F specific implementation is responsible for setting the
* <code>desktopManager</code> variable appropriately.
* When the parent of a <code>JInternalFrame</code> is a <code>JDesktopPane</code>,
* it should delegate most of its behavior to the <code>desktopManager</code>
* (closing, resizing, etc).
* <p>
* For further documentation and examples see
* <a href="https://docs.oracle.com/javase/tutorial/uiswing/components/internalframe.html">How to Use Internal Frames</a>,
* a section in <em>The Java Tutorial</em>.
* <p>
* <strong>Warning:</strong> Swing is not thread safe. For more
* information see <a
* href="package-summary.html#threading">Swing's Threading
* Policy</a>.
* <p>
* <strong>Warning:</strong>
* 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 JavaBeans™
* has been added to the <code>java.beans</code> 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<
JInternalFrame>
framesCache;
private boolean
componentOrderCheckingEnabled = true;
private boolean
componentOrderChanged = false;
/**
* Creates a new <code>JDesktopPane</code>.
*/
public
JDesktopPane() {
setUIProperty("opaque",
Boolean.
TRUE);
setFocusCycleRoot(true);
setFocusTraversalPolicy(new
LayoutFocusTraversalPolicy() {
public
Component getDefaultComponent(
Container c) {
JInternalFrame jifArray[] =
getAllFrames();
Component comp = null;
for (
JInternalFrame jif :
jifArray) {
comp =
jif.
getFocusTraversalPolicy().
getDefaultComponent(
jif);
if (
comp != null) {
break;
}
}
return
comp;
}
});
updateUI();
}
/**
* Returns the L&F object that renders this component.
*
* @return the <code>DesktopPaneUI</code> 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 <code>Live_DRAG_MODE</code> or
* <code>OUTLINE_DRAG_MODE</code>
* @see #setDragMode
* @since 1.3
*/
public int
getDragMode() {
return
dragMode;
}
/**
* Returns the <code>DesktopManger</code> that handles
* desktop-specific UI actions.
*/
public
DesktopManager getDesktopManager() {
return
desktopManager;
}
/**
* Sets the <code>DesktopManger</code> that will handle
* desktop-specific UI actions. This may be overridden by
* {@code LookAndFeel}.
*
* @param d the <code>DesktopManager</code> 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 <code>UIManager</code> that the L&F has changed.
* Replaces the current UI object with the latest version from the
* <code>UIManager</code>.
*
* @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 <code>JInternalFrames</code> currently displayed in the
* desktop. Returns iconified frames as well as expanded frames.
*
* @return an array of <code>JInternalFrame</code> objects
*/
public
JInternalFrame[]
getAllFrames() {
return
getAllFrames(this).
toArray(new
JInternalFrame[0]);
}
private static
Collection<
JInternalFrame>
getAllFrames(
Container parent) {
int
i,
count;
Collection<
JInternalFrame>
results = new
LinkedHashSet<>();
count =
parent.
getComponentCount();
for (
i = 0;
i <
count;
i++) {
Component next =
parent.
getComponent(
i);
if (
next instanceof
JInternalFrame) {
results.
add((
JInternalFrame)
next);
} else if (
next instanceof
JInternalFrame.
JDesktopIcon) {
JInternalFrame tmp = ((
JInternalFrame.
JDesktopIcon)
next).
getInternalFrame();
if (
tmp != null) {
results.
add(
tmp);
}
} else if (
next instanceof
Container) {
results.
addAll(
getAllFrames((
Container)
next));
}
}
return
results;
}
/** Returns the currently active <code>JInternalFrame</code>
* in this <code>JDesktopPane</code>, or <code>null</code>
* if no <code>JInternalFrame</code> is currently active.
*
* @return the currently active <code>JInternalFrame</code> or
* <code>null</code>
* @since 1.3
*/
public
JInternalFrame getSelectedFrame() {
return
selectedFrame;
}
/** Sets the currently active <code>JInternalFrame</code>
* in this <code>JDesktopPane</code>. 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 <code>JInternalFrames</code> 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 <code>JInternalFrame</code> objects
* @see JLayeredPane
*/
public
JInternalFrame[]
getAllFramesInLayer(int
layer) {
Collection<
JInternalFrame>
allFrames =
getAllFrames(this);
Iterator<
JInternalFrame>
iterator =
allFrames.
iterator();
while (
iterator.
hasNext()) {
if (
iterator.
next().
getLayer() !=
layer) {
iterator.
remove();
}
}
return
allFrames.
toArray(new
JInternalFrame[0]);
}
private
List<
JInternalFrame>
getFrames() {
Component c;
Set<
ComponentPosition>
set = new
TreeSet<
ComponentPosition>();
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<
JInternalFrame>
frames = new
ArrayList<
JInternalFrame>(
set.
size());
for (
ComponentPosition position :
set) {
frames.
add(
position.
component);
}
return
frames;
}
private static class
ComponentPosition implements
Comparable<
ComponentPosition> {
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();
}
}
/**
* {@inheritDoc}
*/
@
Override
public void
remove(
Component comp) {
super.remove(
comp);
updateFramesCache();
}
/**
* Selects the next <code>JInternalFrame</code> in this desktop pane.
*
* @param forward a boolean indicating which direction to select in;
* <code>true</code> for forward, <code>false</code> for
* backward
* @return the JInternalFrame that was selected or <code>null</code>
* 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 <code>true</code> 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 <code>JDesktopPane</code>.
* 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 <code>null</code>.
*
* @return a string representation of this <code>JDesktopPane</code>
*/
protected
String paramString() {
String desktopManagerString = (
desktopManager != null ?
desktopManager.
toString() : "");
return super.paramString() +
",desktopManager=" +
desktopManagerString;
}
/////////////////
// Accessibility support
////////////////
/**
* Gets the <code>AccessibleContext</code> associated with this
* <code>JDesktopPane</code>. For desktop panes, the
* <code>AccessibleContext</code> takes the form of an
* <code>AccessibleJDesktopPane</code>.
* A new <code>AccessibleJDesktopPane</code> instance is created if necessary.
*
* @return an <code>AccessibleJDesktopPane</code> that serves as the
* <code>AccessibleContext</code> of this <code>JDesktopPane</code>
*/
public
AccessibleContext getAccessibleContext() {
if (
accessibleContext == null) {
accessibleContext = new
AccessibleJDesktopPane();
}
return
accessibleContext;
}
/**
* This class implements accessibility support for the
* <code>JDesktopPane</code> class. It provides an implementation of the
* Java Accessibility API appropriate to desktop pane user-interface
* elements.
* <p>
* <strong>Warning:</strong>
* 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 JavaBeans™
* has been added to the <code>java.beans</code> 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;
}
}
}