Path: blob/master/src/java.desktop/share/classes/java/awt/CardLayout.java
41152 views
/*1* Copyright (c) 1995, 2021, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425package java.awt;2627import java.io.IOException;28import java.io.ObjectInputStream;29import java.io.ObjectOutputStream;30import java.io.ObjectStreamField;31import java.io.Serial;32import java.io.Serializable;33import java.util.Enumeration;34import java.util.Hashtable;35import java.util.Vector;3637/**38* A {@code CardLayout} object is a layout manager for a39* container. It treats each component in the container as a card.40* Only one card is visible at a time, and the container acts as41* a stack of cards. The first component added to a42* {@code CardLayout} object is the visible component when the43* container is first displayed.44* <p>45* The ordering of cards is determined by the container's own internal46* ordering of its component objects. {@code CardLayout}47* defines a set of methods that allow an application to flip48* through these cards sequentially, or to show a specified card.49* The {@link CardLayout#addLayoutComponent}50* method can be used to associate a string identifier with a given card51* for fast random access.52*53* @author Arthur van Hoff54* @see java.awt.Container55* @since 1.056*/5758public class CardLayout implements LayoutManager2,59Serializable {60/**61* Use serialVersionUID from JDK 1.4 for interoperability.62*/63@Serial64private static final long serialVersionUID = -4328196481005934313L;6566/*67* This creates a Vector to store associated68* pairs of components and their names.69* @see java.util.Vector70*/71Vector<Card> vector = new Vector<>();7273/**74* A pair of component and string that represents its name.75*/76class Card implements Serializable {7778/**79* Use serialVersionUID from JDK 1.4 for interoperability.80*/81@Serial82private static final long serialVersionUID = 6640330810709497518L;83public String name;84public Component comp;85public Card(String cardName, Component cardComponent) {86name = cardName;87comp = cardComponent;88}89}9091/*92* Index of Component currently displayed by CardLayout.93*/94int currentCard = 0;959697/*98* A cards horizontal Layout gap (inset). It specifies99* the space between the left and right edges of a100* container and the current component.101* This should be a non negative Integer.102* @see getHgap()103* @see setHgap()104*/105int hgap;106107/*108* A cards vertical Layout gap (inset). It specifies109* the space between the top and bottom edges of a110* container and the current component.111* This should be a non negative Integer.112* @see getVgap()113* @see setVgap()114*/115int vgap;116117/**118* @serialField tab Hashtable119* deprecated, for forward compatibility only120* @serialField hgap int the horizontal Layout gap121* @serialField vgap int the vertical Layout gap122* @serialField vector Vector the pairs of components and their names123* @serialField currentCard int the index of Component currently displayed124*/125@Serial126private static final ObjectStreamField[] serialPersistentFields = {127new ObjectStreamField("tab", Hashtable.class),128new ObjectStreamField("hgap", Integer.TYPE),129new ObjectStreamField("vgap", Integer.TYPE),130new ObjectStreamField("vector", Vector.class),131new ObjectStreamField("currentCard", Integer.TYPE)132};133134/**135* Creates a new card layout with gaps of size zero.136*/137public CardLayout() {138this(0, 0);139}140141/**142* Creates a new card layout with the specified horizontal and143* vertical gaps. The horizontal gaps are placed at the left and144* right edges. The vertical gaps are placed at the top and bottom145* edges.146* @param hgap the horizontal gap.147* @param vgap the vertical gap.148*/149public CardLayout(int hgap, int vgap) {150this.hgap = hgap;151this.vgap = vgap;152}153154/**155* Gets the horizontal gap between components.156* @return the horizontal gap between components.157* @see java.awt.CardLayout#setHgap(int)158* @see java.awt.CardLayout#getVgap()159* @since 1.1160*/161public int getHgap() {162return hgap;163}164165/**166* Sets the horizontal gap between components.167* @param hgap the horizontal gap between components.168* @see java.awt.CardLayout#getHgap()169* @see java.awt.CardLayout#setVgap(int)170* @since 1.1171*/172public void setHgap(int hgap) {173this.hgap = hgap;174}175176/**177* Gets the vertical gap between components.178* @return the vertical gap between components.179* @see java.awt.CardLayout#setVgap(int)180* @see java.awt.CardLayout#getHgap()181*/182public int getVgap() {183return vgap;184}185186/**187* Sets the vertical gap between components.188* @param vgap the vertical gap between components.189* @see java.awt.CardLayout#getVgap()190* @see java.awt.CardLayout#setHgap(int)191* @since 1.1192*/193public void setVgap(int vgap) {194this.vgap = vgap;195}196197/**198* Adds the specified component to this card layout's internal199* table of names. The object specified by {@code constraints}200* must be a string. The card layout stores this string as a key-value201* pair that can be used for random access to a particular card.202* By calling the {@code show} method, an application can203* display the component with the specified name.204* @param comp the component to be added.205* @param constraints a tag that identifies a particular206* card in the layout.207* @see java.awt.CardLayout#show(java.awt.Container, java.lang.String)208* @exception IllegalArgumentException if the constraint is not a string.209*/210public void addLayoutComponent(Component comp, Object constraints) {211synchronized (comp.getTreeLock()) {212if (constraints == null){213constraints = "";214}215if (constraints instanceof String) {216addLayoutComponent((String)constraints, comp);217} else {218throw new IllegalArgumentException("cannot add to layout: constraint must be a string");219}220}221}222223/**224* @deprecated replaced by225* {@code addLayoutComponent(Component, Object)}.226*/227@Deprecated228public void addLayoutComponent(String name, Component comp) {229synchronized (comp.getTreeLock()) {230if (!vector.isEmpty()) {231comp.setVisible(false);232}233for (int i=0; i < vector.size(); i++) {234if ((vector.get(i)).name.equals(name)) {235(vector.get(i)).comp = comp;236return;237}238}239vector.add(new Card(name, comp));240}241}242243/**244* Removes the specified component from the layout.245* If the card was visible on top, the next card underneath it is shown.246* @param comp the component to be removed.247* @see java.awt.Container#remove(java.awt.Component)248* @see java.awt.Container#removeAll()249*/250public void removeLayoutComponent(Component comp) {251synchronized (comp.getTreeLock()) {252for (int i = 0; i < vector.size(); i++) {253if ((vector.get(i)).comp == comp) {254// if we remove current component we should show next one255if (comp.isVisible() && (comp.getParent() != null)) {256next(comp.getParent());257}258259vector.remove(i);260261// correct currentCard if this is necessary262if (currentCard > i) {263currentCard--;264}265break;266}267}268}269}270271/**272* Determines the preferred size of the container argument using273* this card layout.274* @param parent the parent container in which to do the layout275* @return the preferred dimensions to lay out the subcomponents276* of the specified container277* @see java.awt.Container#getPreferredSize278* @see java.awt.CardLayout#minimumLayoutSize279*/280public Dimension preferredLayoutSize(Container parent) {281synchronized (parent.getTreeLock()) {282Insets insets = parent.getInsets();283int ncomponents = parent.getComponentCount();284int w = 0;285int h = 0;286287for (int i = 0 ; i < ncomponents ; i++) {288Component comp = parent.getComponent(i);289Dimension d = comp.getPreferredSize();290if (d.width > w) {291w = d.width;292}293if (d.height > h) {294h = d.height;295}296}297return new Dimension(insets.left + insets.right + w + hgap*2,298insets.top + insets.bottom + h + vgap*2);299}300}301302/**303* Calculates the minimum size for the specified panel.304* @param parent the parent container in which to do the layout305* @return the minimum dimensions required to lay out the306* subcomponents of the specified container307* @see java.awt.Container#doLayout308* @see java.awt.CardLayout#preferredLayoutSize309*/310public Dimension minimumLayoutSize(Container parent) {311synchronized (parent.getTreeLock()) {312Insets insets = parent.getInsets();313int ncomponents = parent.getComponentCount();314int w = 0;315int h = 0;316317for (int i = 0 ; i < ncomponents ; i++) {318Component comp = parent.getComponent(i);319Dimension d = comp.getMinimumSize();320if (d.width > w) {321w = d.width;322}323if (d.height > h) {324h = d.height;325}326}327return new Dimension(insets.left + insets.right + w + hgap*2,328insets.top + insets.bottom + h + vgap*2);329}330}331332/**333* Returns the maximum dimensions for this layout given the components334* in the specified target container.335* @param target the component which needs to be laid out336* @see Container337* @see #minimumLayoutSize338* @see #preferredLayoutSize339*/340public Dimension maximumLayoutSize(Container target) {341return new Dimension(Integer.MAX_VALUE, Integer.MAX_VALUE);342}343344/**345* Returns the alignment along the x axis. This specifies how346* the component would like to be aligned relative to other347* components. The value should be a number between 0 and 1348* where 0 represents alignment along the origin, 1 is aligned349* the furthest away from the origin, 0.5 is centered, etc.350*/351public float getLayoutAlignmentX(Container parent) {352return 0.5f;353}354355/**356* Returns the alignment along the y axis. This specifies how357* the component would like to be aligned relative to other358* components. The value should be a number between 0 and 1359* where 0 represents alignment along the origin, 1 is aligned360* the furthest away from the origin, 0.5 is centered, etc.361*/362public float getLayoutAlignmentY(Container parent) {363return 0.5f;364}365366/**367* Invalidates the layout, indicating that if the layout manager368* has cached information it should be discarded.369*/370public void invalidateLayout(Container target) {371}372373/**374* Lays out the specified container using this card layout.375* <p>376* Each component in the {@code parent} container is reshaped377* to be the size of the container, minus space for surrounding378* insets, horizontal gaps, and vertical gaps.379*380* @param parent the parent container in which to do the layout381* @see java.awt.Container#doLayout382*/383public void layoutContainer(Container parent) {384synchronized (parent.getTreeLock()) {385Insets insets = parent.getInsets();386int ncomponents = parent.getComponentCount();387Component comp = null;388boolean currentFound = false;389390for (int i = 0 ; i < ncomponents ; i++) {391comp = parent.getComponent(i);392comp.setBounds(hgap + insets.left, vgap + insets.top,393parent.width - (hgap*2 + insets.left + insets.right),394parent.height - (vgap*2 + insets.top + insets.bottom));395if (comp.isVisible()) {396currentFound = true;397}398}399400if (!currentFound && ncomponents > 0) {401parent.getComponent(0).setVisible(true);402}403}404}405406/**407* Make sure that the Container really has a CardLayout installed.408* Otherwise havoc can ensue!409*/410void checkLayout(Container parent) {411if (parent.getLayout() != this) {412throw new IllegalArgumentException("wrong parent for CardLayout");413}414}415416/**417* Flips to the first card of the container.418* @param parent the parent container in which to do the layout419* @see java.awt.CardLayout#last420*/421public void first(Container parent) {422synchronized (parent.getTreeLock()) {423checkLayout(parent);424int ncomponents = parent.getComponentCount();425for (int i = 0 ; i < ncomponents ; i++) {426Component comp = parent.getComponent(i);427if (comp.isVisible()) {428comp.setVisible(false);429break;430}431}432if (ncomponents > 0) {433currentCard = 0;434parent.getComponent(0).setVisible(true);435parent.validate();436}437}438}439440/**441* Flips to the next card of the specified container. If the442* currently visible card is the last one, this method flips to the443* first card in the layout.444* @param parent the parent container in which to do the layout445* @see java.awt.CardLayout#previous446*/447public void next(Container parent) {448synchronized (parent.getTreeLock()) {449checkLayout(parent);450int ncomponents = parent.getComponentCount();451for (int i = 0 ; i < ncomponents ; i++) {452Component comp = parent.getComponent(i);453if (comp.isVisible()) {454comp.setVisible(false);455currentCard = (i + 1) % ncomponents;456comp = parent.getComponent(currentCard);457comp.setVisible(true);458parent.validate();459return;460}461}462showDefaultComponent(parent);463}464}465466/**467* Flips to the previous card of the specified container. If the468* currently visible card is the first one, this method flips to the469* last card in the layout.470* @param parent the parent container in which to do the layout471* @see java.awt.CardLayout#next472*/473public void previous(Container parent) {474synchronized (parent.getTreeLock()) {475checkLayout(parent);476int ncomponents = parent.getComponentCount();477for (int i = 0 ; i < ncomponents ; i++) {478Component comp = parent.getComponent(i);479if (comp.isVisible()) {480comp.setVisible(false);481currentCard = ((i > 0) ? i-1 : ncomponents-1);482comp = parent.getComponent(currentCard);483comp.setVisible(true);484parent.validate();485return;486}487}488showDefaultComponent(parent);489}490}491492void showDefaultComponent(Container parent) {493if (parent.getComponentCount() > 0) {494currentCard = 0;495parent.getComponent(0).setVisible(true);496parent.validate();497}498}499500/**501* Flips to the last card of the container.502* @param parent the parent container in which to do the layout503* @see java.awt.CardLayout#first504*/505public void last(Container parent) {506synchronized (parent.getTreeLock()) {507checkLayout(parent);508int ncomponents = parent.getComponentCount();509for (int i = 0 ; i < ncomponents ; i++) {510Component comp = parent.getComponent(i);511if (comp.isVisible()) {512comp.setVisible(false);513break;514}515}516if (ncomponents > 0) {517currentCard = ncomponents - 1;518parent.getComponent(currentCard).setVisible(true);519parent.validate();520}521}522}523524/**525* Flips to the component that was added to this layout with the526* specified {@code name}, using {@code addLayoutComponent}.527* If no such component exists, then nothing happens.528* @param parent the parent container in which to do the layout529* @param name the component name530* @see java.awt.CardLayout#addLayoutComponent(java.awt.Component, java.lang.Object)531*/532public void show(Container parent, String name) {533synchronized (parent.getTreeLock()) {534checkLayout(parent);535Component next = null;536int ncomponents = vector.size();537for (int i = 0; i < ncomponents; i++) {538Card card = vector.get(i);539if (card.name.equals(name)) {540next = card.comp;541currentCard = i;542break;543}544}545if ((next != null) && !next.isVisible()) {546ncomponents = parent.getComponentCount();547for (int i = 0; i < ncomponents; i++) {548Component comp = parent.getComponent(i);549if (comp.isVisible()) {550comp.setVisible(false);551break;552}553}554next.setVisible(true);555parent.validate();556}557}558}559560/**561* Returns a string representation of the state of this card layout.562* @return a string representation of this card layout.563*/564public String toString() {565return getClass().getName() + "[hgap=" + hgap + ",vgap=" + vgap + "]";566}567568/**569* Reads serializable fields from stream.570*571* @param s the {@code ObjectInputStream} to read572* @throws ClassNotFoundException if the class of a serialized object could573* not be found574* @throws IOException if an I/O error occurs575*/576@Serial577@SuppressWarnings("unchecked")578private void readObject(ObjectInputStream s)579throws ClassNotFoundException, IOException580{581ObjectInputStream.GetField f = s.readFields();582583hgap = f.get("hgap", 0);584vgap = f.get("vgap", 0);585586if (f.defaulted("vector")) {587// pre-1.4 stream588Hashtable<String, Component> tab = (Hashtable)f.get("tab", null);589vector = new Vector<>();590if (tab != null && !tab.isEmpty()) {591for (Enumeration<String> e = tab.keys() ; e.hasMoreElements() ; ) {592String key = e.nextElement();593Component comp = tab.get(key);594vector.add(new Card(key, comp));595if (comp.isVisible()) {596currentCard = vector.size() - 1;597}598}599}600} else {601vector = (Vector)f.get("vector", null);602currentCard = f.get("currentCard", 0);603}604}605606/**607* Writes serializable fields to stream.608*609* @param s the {@code ObjectOutputStream} to write610* @throws IOException if an I/O error occurs611*/612@Serial613private void writeObject(ObjectOutputStream s)614throws IOException615{616Hashtable<String, Component> tab = new Hashtable<>();617int ncomponents = vector.size();618for (int i = 0; i < ncomponents; i++) {619Card card = vector.get(i);620tab.put(card.name, card.comp);621}622623ObjectOutputStream.PutField f = s.putFields();624f.put("hgap", hgap);625f.put("vgap", vgap);626f.put("vector", vector);627f.put("currentCard", currentCard);628f.put("tab", tab);629s.writeFields();630}631}632633634