/*
* Copyright (c) 1995, 2013, Oracle and/or its affiliates. All rights reserved.
* ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*/
package java.awt;
import java.util.
Hashtable;
import java.util.
Vector;
import java.util.
Enumeration;
import java.io.
Serializable;
import java.io.
ObjectInputStream;
import java.io.
ObjectOutputStream;
import java.io.
ObjectStreamField;
import java.io.
IOException;
/**
* A <code>CardLayout</code> object is a layout manager for a
* container. It treats each component in the container as a card.
* Only one card is visible at a time, and the container acts as
* a stack of cards. The first component added to a
* <code>CardLayout</code> object is the visible component when the
* container is first displayed.
* <p>
* The ordering of cards is determined by the container's own internal
* ordering of its component objects. <code>CardLayout</code>
* defines a set of methods that allow an application to flip
* through these cards sequentially, or to show a specified card.
* The {@link CardLayout#addLayoutComponent}
* method can be used to associate a string identifier with a given card
* for fast random access.
*
* @author Arthur van Hoff
* @see java.awt.Container
* @since JDK1.0
*/
public class
CardLayout implements
LayoutManager2,
Serializable {
private static final long
serialVersionUID = -4328196481005934313L;
/*
* This creates a Vector to store associated
* pairs of components and their names.
* @see java.util.Vector
*/
Vector<
Card>
vector = new
Vector<>();
/*
* A pair of Component and String that represents its name.
*/
class
Card implements
Serializable {
static final long
serialVersionUID = 6640330810709497518L;
public
String name;
public
Component comp;
public
Card(
String cardName,
Component cardComponent) {
name =
cardName;
comp =
cardComponent;
}
}
/*
* Index of Component currently displayed by CardLayout.
*/
int
currentCard = 0;
/*
* A cards horizontal Layout gap (inset). It specifies
* the space between the left and right edges of a
* container and the current component.
* This should be a non negative Integer.
* @see getHgap()
* @see setHgap()
*/
int
hgap;
/*
* A cards vertical Layout gap (inset). It specifies
* the space between the top and bottom edges of a
* container and the current component.
* This should be a non negative Integer.
* @see getVgap()
* @see setVgap()
*/
int
vgap;
/**
* @serialField tab Hashtable
* deprectated, for forward compatibility only
* @serialField hgap int
* @serialField vgap int
* @serialField vector Vector
* @serialField currentCard int
*/
private static final
ObjectStreamField[]
serialPersistentFields = {
new
ObjectStreamField("tab",
Hashtable.class),
new
ObjectStreamField("hgap",
Integer.
TYPE),
new
ObjectStreamField("vgap",
Integer.
TYPE),
new
ObjectStreamField("vector",
Vector.class),
new
ObjectStreamField("currentCard",
Integer.
TYPE)
};
/**
* Creates a new card layout with gaps of size zero.
*/
public
CardLayout() {
this(0, 0);
}
/**
* Creates a new card layout with the specified horizontal and
* vertical gaps. The horizontal gaps are placed at the left and
* right edges. The vertical gaps are placed at the top and bottom
* edges.
* @param hgap the horizontal gap.
* @param vgap the vertical gap.
*/
public
CardLayout(int
hgap, int
vgap) {
this.
hgap =
hgap;
this.
vgap =
vgap;
}
/**
* Gets the horizontal gap between components.
* @return the horizontal gap between components.
* @see java.awt.CardLayout#setHgap(int)
* @see java.awt.CardLayout#getVgap()
* @since JDK1.1
*/
public int
getHgap() {
return
hgap;
}
/**
* Sets the horizontal gap between components.
* @param hgap the horizontal gap between components.
* @see java.awt.CardLayout#getHgap()
* @see java.awt.CardLayout#setVgap(int)
* @since JDK1.1
*/
public void
setHgap(int
hgap) {
this.
hgap =
hgap;
}
/**
* Gets the vertical gap between components.
* @return the vertical gap between components.
* @see java.awt.CardLayout#setVgap(int)
* @see java.awt.CardLayout#getHgap()
*/
public int
getVgap() {
return
vgap;
}
/**
* Sets the vertical gap between components.
* @param vgap the vertical gap between components.
* @see java.awt.CardLayout#getVgap()
* @see java.awt.CardLayout#setHgap(int)
* @since JDK1.1
*/
public void
setVgap(int
vgap) {
this.
vgap =
vgap;
}
/**
* Adds the specified component to this card layout's internal
* table of names. The object specified by <code>constraints</code>
* must be a string. The card layout stores this string as a key-value
* pair that can be used for random access to a particular card.
* By calling the <code>show</code> method, an application can
* display the component with the specified name.
* @param comp the component to be added.
* @param constraints a tag that identifies a particular
* card in the layout.
* @see java.awt.CardLayout#show(java.awt.Container, java.lang.String)
* @exception IllegalArgumentException if the constraint is not a string.
*/
public void
addLayoutComponent(
Component comp,
Object constraints) {
synchronized (
comp.
getTreeLock()) {
if (
constraints == null){
constraints = "";
}
if (
constraints instanceof
String) {
addLayoutComponent((
String)
constraints,
comp);
} else {
throw new
IllegalArgumentException("cannot add to layout: constraint must be a string");
}
}
}
/**
* @deprecated replaced by
* <code>addLayoutComponent(Component, Object)</code>.
*/
@
Deprecated
public void
addLayoutComponent(
String name,
Component comp) {
synchronized (
comp.
getTreeLock()) {
if (!
vector.
isEmpty()) {
comp.
setVisible(false);
}
for (int
i=0;
i <
vector.
size();
i++) {
if (((
Card)
vector.
get(
i)).
name.
equals(
name)) {
((
Card)
vector.
get(
i)).
comp =
comp;
return;
}
}
vector.
add(new
Card(
name,
comp));
}
}
/**
* Removes the specified component from the layout.
* If the card was visible on top, the next card underneath it is shown.
* @param comp the component to be removed.
* @see java.awt.Container#remove(java.awt.Component)
* @see java.awt.Container#removeAll()
*/
public void
removeLayoutComponent(
Component comp) {
synchronized (
comp.
getTreeLock()) {
for (int
i = 0;
i <
vector.
size();
i++) {
if (((
Card)
vector.
get(
i)).
comp ==
comp) {
// if we remove current component we should show next one
if (
comp.
isVisible() && (
comp.
getParent() != null)) {
next(
comp.
getParent());
}
vector.
remove(
i);
// correct currentCard if this is necessary
if (
currentCard >
i) {
currentCard--;
}
break;
}
}
}
}
/**
* Determines the preferred size of the container argument using
* this card layout.
* @param parent the parent container in which to do the layout
* @return the preferred dimensions to lay out the subcomponents
* of the specified container
* @see java.awt.Container#getPreferredSize
* @see java.awt.CardLayout#minimumLayoutSize
*/
public
Dimension preferredLayoutSize(
Container parent) {
synchronized (
parent.
getTreeLock()) {
Insets insets =
parent.
getInsets();
int
ncomponents =
parent.
getComponentCount();
int
w = 0;
int
h = 0;
for (int
i = 0 ;
i <
ncomponents ;
i++) {
Component comp =
parent.
getComponent(
i);
Dimension d =
comp.
getPreferredSize();
if (
d.
width >
w) {
w =
d.
width;
}
if (
d.
height >
h) {
h =
d.
height;
}
}
return new
Dimension(
insets.
left +
insets.
right +
w +
hgap*2,
insets.
top +
insets.
bottom +
h +
vgap*2);
}
}
/**
* Calculates the minimum size for the specified panel.
* @param parent the parent container in which to do the layout
* @return the minimum dimensions required to lay out the
* subcomponents of the specified container
* @see java.awt.Container#doLayout
* @see java.awt.CardLayout#preferredLayoutSize
*/
public
Dimension minimumLayoutSize(
Container parent) {
synchronized (
parent.
getTreeLock()) {
Insets insets =
parent.
getInsets();
int
ncomponents =
parent.
getComponentCount();
int
w = 0;
int
h = 0;
for (int
i = 0 ;
i <
ncomponents ;
i++) {
Component comp =
parent.
getComponent(
i);
Dimension d =
comp.
getMinimumSize();
if (
d.
width >
w) {
w =
d.
width;
}
if (
d.
height >
h) {
h =
d.
height;
}
}
return new
Dimension(
insets.
left +
insets.
right +
w +
hgap*2,
insets.
top +
insets.
bottom +
h +
vgap*2);
}
}
/**
* Returns the maximum dimensions for this layout given the components
* in the specified target container.
* @param target the component which needs to be laid out
* @see Container
* @see #minimumLayoutSize
* @see #preferredLayoutSize
*/
public
Dimension maximumLayoutSize(
Container target) {
return new
Dimension(
Integer.
MAX_VALUE,
Integer.
MAX_VALUE);
}
/**
* Returns the alignment along the x axis. This specifies how
* the component would like to be aligned relative to other
* components. The value should be a number between 0 and 1
* where 0 represents alignment along the origin, 1 is aligned
* the furthest away from the origin, 0.5 is centered, etc.
*/
public float
getLayoutAlignmentX(
Container parent) {
return 0.5f;
}
/**
* Returns the alignment along the y axis. This specifies how
* the component would like to be aligned relative to other
* components. The value should be a number between 0 and 1
* where 0 represents alignment along the origin, 1 is aligned
* the furthest away from the origin, 0.5 is centered, etc.
*/
public float
getLayoutAlignmentY(
Container parent) {
return 0.5f;
}
/**
* Invalidates the layout, indicating that if the layout manager
* has cached information it should be discarded.
*/
public void
invalidateLayout(
Container target) {
}
/**
* Lays out the specified container using this card layout.
* <p>
* Each component in the <code>parent</code> container is reshaped
* to be the size of the container, minus space for surrounding
* insets, horizontal gaps, and vertical gaps.
*
* @param parent the parent container in which to do the layout
* @see java.awt.Container#doLayout
*/
public void
layoutContainer(
Container parent) {
synchronized (
parent.
getTreeLock()) {
Insets insets =
parent.
getInsets();
int
ncomponents =
parent.
getComponentCount();
Component comp = null;
boolean
currentFound = false;
for (int
i = 0 ;
i <
ncomponents ;
i++) {
comp =
parent.
getComponent(
i);
comp.
setBounds(
hgap +
insets.
left,
vgap +
insets.
top,
parent.
width - (
hgap*2 +
insets.
left +
insets.
right),
parent.
height - (
vgap*2 +
insets.
top +
insets.
bottom));
if (
comp.
isVisible()) {
currentFound = true;
}
}
if (!
currentFound &&
ncomponents > 0) {
parent.
getComponent(0).
setVisible(true);
}
}
}
/**
* Make sure that the Container really has a CardLayout installed.
* Otherwise havoc can ensue!
*/
void
checkLayout(
Container parent) {
if (
parent.
getLayout() != this) {
throw new
IllegalArgumentException("wrong parent for CardLayout");
}
}
/**
* Flips to the first card of the container.
* @param parent the parent container in which to do the layout
* @see java.awt.CardLayout#last
*/
public void
first(
Container parent) {
synchronized (
parent.
getTreeLock()) {
checkLayout(
parent);
int
ncomponents =
parent.
getComponentCount();
for (int
i = 0 ;
i <
ncomponents ;
i++) {
Component comp =
parent.
getComponent(
i);
if (
comp.
isVisible()) {
comp.
setVisible(false);
break;
}
}
if (
ncomponents > 0) {
currentCard = 0;
parent.
getComponent(0).
setVisible(true);
parent.
validate();
}
}
}
/**
* Flips to the next card of the specified container. If the
* currently visible card is the last one, this method flips to the
* first card in the layout.
* @param parent the parent container in which to do the layout
* @see java.awt.CardLayout#previous
*/
public void
next(
Container parent) {
synchronized (
parent.
getTreeLock()) {
checkLayout(
parent);
int
ncomponents =
parent.
getComponentCount();
for (int
i = 0 ;
i <
ncomponents ;
i++) {
Component comp =
parent.
getComponent(
i);
if (
comp.
isVisible()) {
comp.
setVisible(false);
currentCard = (
i + 1) %
ncomponents;
comp =
parent.
getComponent(
currentCard);
comp.
setVisible(true);
parent.
validate();
return;
}
}
showDefaultComponent(
parent);
}
}
/**
* Flips to the previous card of the specified container. If the
* currently visible card is the first one, this method flips to the
* last card in the layout.
* @param parent the parent container in which to do the layout
* @see java.awt.CardLayout#next
*/
public void
previous(
Container parent) {
synchronized (
parent.
getTreeLock()) {
checkLayout(
parent);
int
ncomponents =
parent.
getComponentCount();
for (int
i = 0 ;
i <
ncomponents ;
i++) {
Component comp =
parent.
getComponent(
i);
if (
comp.
isVisible()) {
comp.
setVisible(false);
currentCard = ((
i > 0) ?
i-1 :
ncomponents-1);
comp =
parent.
getComponent(
currentCard);
comp.
setVisible(true);
parent.
validate();
return;
}
}
showDefaultComponent(
parent);
}
}
void
showDefaultComponent(
Container parent) {
if (
parent.
getComponentCount() > 0) {
currentCard = 0;
parent.
getComponent(0).
setVisible(true);
parent.
validate();
}
}
/**
* Flips to the last card of the container.
* @param parent the parent container in which to do the layout
* @see java.awt.CardLayout#first
*/
public void
last(
Container parent) {
synchronized (
parent.
getTreeLock()) {
checkLayout(
parent);
int
ncomponents =
parent.
getComponentCount();
for (int
i = 0 ;
i <
ncomponents ;
i++) {
Component comp =
parent.
getComponent(
i);
if (
comp.
isVisible()) {
comp.
setVisible(false);
break;
}
}
if (
ncomponents > 0) {
currentCard =
ncomponents - 1;
parent.
getComponent(
currentCard).
setVisible(true);
parent.
validate();
}
}
}
/**
* Flips to the component that was added to this layout with the
* specified <code>name</code>, using <code>addLayoutComponent</code>.
* If no such component exists, then nothing happens.
* @param parent the parent container in which to do the layout
* @param name the component name
* @see java.awt.CardLayout#addLayoutComponent(java.awt.Component, java.lang.Object)
*/
public void
show(
Container parent,
String name) {
synchronized (
parent.
getTreeLock()) {
checkLayout(
parent);
Component next = null;
int
ncomponents =
vector.
size();
for (int
i = 0;
i <
ncomponents;
i++) {
Card card = (
Card)
vector.
get(
i);
if (
card.
name.
equals(
name)) {
next =
card.
comp;
currentCard =
i;
break;
}
}
if ((
next != null) && !
next.
isVisible()) {
ncomponents =
parent.
getComponentCount();
for (int
i = 0;
i <
ncomponents;
i++) {
Component comp =
parent.
getComponent(
i);
if (
comp.
isVisible()) {
comp.
setVisible(false);
break;
}
}
next.
setVisible(true);
parent.
validate();
}
}
}
/**
* Returns a string representation of the state of this card layout.
* @return a string representation of this card layout.
*/
public
String toString() {
return
getClass().
getName() + "[hgap=" +
hgap + ",vgap=" +
vgap + "]";
}
/**
* Reads serializable fields from stream.
*/
private void
readObject(
ObjectInputStream s)
throws
ClassNotFoundException,
IOException
{
ObjectInputStream.
GetField f =
s.
readFields();
hgap =
f.
get("hgap", 0);
vgap =
f.
get("vgap", 0);
if (
f.
defaulted("vector")) {
// pre-1.4 stream
Hashtable<
String,
Component>
tab = (
Hashtable)
f.
get("tab", null);
vector = new
Vector<>();
if (
tab != null && !
tab.
isEmpty()) {
for (
Enumeration<
String>
e =
tab.
keys() ;
e.
hasMoreElements() ; ) {
String key = (
String)
e.
nextElement();
Component comp = (
Component)
tab.
get(
key);
vector.
add(new
Card(
key,
comp));
if (
comp.
isVisible()) {
currentCard =
vector.
size() - 1;
}
}
}
} else {
vector = (
Vector)
f.
get("vector", null);
currentCard =
f.
get("currentCard", 0);
}
}
/**
* Writes serializable fields to stream.
*/
private void
writeObject(
ObjectOutputStream s)
throws
IOException
{
Hashtable<
String,
Component>
tab = new
Hashtable<>();
int
ncomponents =
vector.
size();
for (int
i = 0;
i <
ncomponents;
i++) {
Card card = (
Card)
vector.
get(
i);
tab.
put(
card.
name,
card.
comp);
}
ObjectOutputStream.
PutField f =
s.
putFields();
f.
put("hgap",
hgap);
f.
put("vgap",
vgap);
f.
put("vector",
vector);
f.
put("currentCard",
currentCard);
f.
put("tab",
tab);
s.
writeFields();
}
}