| /* CardLayout.java -- Card-based layout engine | |
| Copyright (C) 1999, 2000, 2002, 2003, 2004 Free Software Foundation | |
| This file is part of GNU Classpath. | |
| GNU Classpath is free software; you can redistribute it and/or modify | |
| it under the terms of the GNU General Public License as published by | |
| the Free Software Foundation; either version 2, or (at your option) | |
| any later version. | |
| GNU Classpath 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 for more details. | |
| You should have received a copy of the GNU General Public License | |
| along with GNU Classpath; see the file COPYING. If not, write to the | |
| Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA | |
| 02110-1301 USA. | |
| Linking this library statically or dynamically with other modules is | |
| making a combined work based on this library. Thus, the terms and | |
| conditions of the GNU General Public License cover the whole | |
| combination. | |
| As a special exception, the copyright holders of this library give you | |
| permission to link this library with independent modules to produce an | |
| executable, regardless of the license terms of these independent | |
| modules, and to copy and distribute the resulting executable under | |
| terms of your choice, provided that you also meet, for each linked | |
| independent module, the terms and conditions of the license of that | |
| module. An independent module is a module which is not derived from | |
| or based on this library. If you modify this library, you may extend | |
| this exception to your version of the library, but you are not | |
| obligated to do so. If you do not wish to do so, delete this | |
| exception statement from your version. */ | |
| package java.awt; | |
| import java.io.Serializable; | |
| import java.util.Enumeration; | |
| import java.util.Hashtable; | |
| /** | |
| * This class implements a card-based layout scheme. Each included | |
| * component is treated as a card. Only one card can be shown at a | |
| * time. This class includes methods for changing which card is | |
| * shown. | |
| * | |
| * @author Tom Tromey (tromey@redhat.com) | |
| * @author Aaron M. Renn (arenn@urbanophile.com) | |
| */ | |
| public class CardLayout implements LayoutManager2, Serializable | |
| { | |
| private static final long serialVersionUID = -4328196481005934313L; | |
| /** | |
| * Initializes a new instance of <code>CardLayout</code> with horizontal | |
| * and vertical gaps of 0. | |
| */ | |
| public CardLayout () | |
| { | |
| this (0, 0); | |
| } | |
| /** | |
| * Create a new <code>CardLayout</code> object with the specified | |
| * horizontal and vertical gaps. | |
| * | |
| * @param hgap The horizontal gap | |
| * @param vgap The vertical gap | |
| */ | |
| public CardLayout (int hgap, int vgap) | |
| { | |
| this.hgap = hgap; | |
| this.vgap = vgap; | |
| this.tab = new Hashtable (); | |
| } | |
| /** | |
| * Add a new component to the layout. The constraint must be a | |
| * string which is used to name the component. This string can | |
| * later be used to refer to the particular component. | |
| * | |
| * @param comp The component to add | |
| * @param constraints The name by which the component can later be called | |
| * | |
| * @exception IllegalArgumentException If `constraints' is not a | |
| * <code>String</code> | |
| */ | |
| public void addLayoutComponent (Component comp, Object constraints) | |
| { | |
| if (! (constraints instanceof String)) | |
| throw new IllegalArgumentException ("Object " + constraints | |
| + " is not a string"); | |
| addLayoutComponent ((String) constraints, comp); | |
| } | |
| /** | |
| * Add a new component to the layout. The name can be used later | |
| * to refer to the component. | |
| * | |
| * @param name The name by which the component can later be called | |
| * @param comp The component to add | |
| * | |
| * @deprecated This method is deprecated in favor of | |
| * <code>addLayoutComponent(Component, Object)</code>. | |
| */ | |
| public void addLayoutComponent (String name, Component comp) | |
| { | |
| tab.put (name, comp); | |
| // First component added is the default component. | |
| comp.setVisible(tab.size() == 1); | |
| } | |
| /** | |
| * Cause the first component in the container to be displayed. | |
| * | |
| * @param parent The parent container, not <code>null</code>. | |
| */ | |
| public void first (Container parent) | |
| { | |
| gotoComponent (parent, FIRST); | |
| } | |
| /** | |
| * Return this layout manager's horizontal gap. | |
| * | |
| * @return the horizontal gap | |
| */ | |
| public int getHgap () | |
| { | |
| return hgap; | |
| } | |
| /** | |
| * Return this layout manager's x alignment. This method always | |
| * returns Component.CENTER_ALIGNMENT. | |
| * | |
| * @param parent Container using this layout manager instance | |
| * | |
| * @return the x-axis alignment | |
| */ | |
| public float getLayoutAlignmentX (Container parent) | |
| { | |
| return Component.CENTER_ALIGNMENT; | |
| } | |
| /** | |
| * Returns this layout manager's y alignment. This method always | |
| * returns Component.CENTER_ALIGNMENT. | |
| * | |
| * @param parent Container using this layout manager instance | |
| * | |
| * @return the y-axis alignment | |
| */ | |
| public float getLayoutAlignmentY (Container parent) | |
| { | |
| return Component.CENTER_ALIGNMENT; | |
| } | |
| /** | |
| * Return this layout manager's vertical gap. | |
| * | |
| * @return the vertical gap | |
| */ | |
| public int getVgap () | |
| { | |
| return vgap; | |
| } | |
| /** | |
| * Invalidate this layout manager's state. | |
| */ | |
| public void invalidateLayout (Container target) | |
| { | |
| // Do nothing. | |
| } | |
| /** | |
| * Cause the last component in the container to be displayed. | |
| * | |
| * @param parent The parent container, not <code>null</code>. | |
| */ | |
| public void last (Container parent) | |
| { | |
| gotoComponent (parent, LAST); | |
| } | |
| /** | |
| * Lays out the container. This is done by resizing the child components | |
| * to be the same size as the parent, less insets and gaps. | |
| * | |
| * @param parent The parent container. | |
| */ | |
| public void layoutContainer (Container parent) | |
| { | |
| synchronized (parent.getTreeLock ()) | |
| { | |
| int width = parent.width; | |
| int height = parent.height; | |
| Insets ins = parent.getInsets (); | |
| int num = parent.ncomponents; | |
| Component[] comps = parent.component; | |
| int x = ins.left + hgap; | |
| int y = ins.top + vgap; | |
| width = width - 2 * hgap - ins.left - ins.right; | |
| height = height - 2 * vgap - ins.top - ins.bottom; | |
| for (int i = 0; i < num; ++i) | |
| comps[i].setBounds (x, y, width, height); | |
| } | |
| } | |
| /** | |
| * Get the maximum layout size of the container. | |
| * | |
| * @param target The parent container | |
| * | |
| * @return the maximum layout size | |
| */ | |
| public Dimension maximumLayoutSize (Container target) | |
| { | |
| // The JCL says that this returns Integer.MAX_VALUE for both | |
| // dimensions. But that just seems wrong to me. | |
| return getSize (target, MAX); | |
| } | |
| /** | |
| * Get the minimum layout size of the container. | |
| * | |
| * @param target The parent container | |
| * | |
| * @return the minimum layout size | |
| */ | |
| public Dimension minimumLayoutSize (Container target) | |
| { | |
| return getSize (target, MIN); | |
| } | |
| /** | |
| * Cause the next component in the container to be displayed. If | |
| * this current card is the last one in the deck, the first | |
| * component is displayed. | |
| * | |
| * @param parent The parent container, not <code>null</code>. | |
| */ | |
| public void next (Container parent) | |
| { | |
| gotoComponent (parent, NEXT); | |
| } | |
| /** | |
| * Get the preferred layout size of the container. | |
| * | |
| * @param parent The parent container | |
| * | |
| * @return the preferred layout size | |
| */ | |
| public Dimension preferredLayoutSize (Container parent) | |
| { | |
| return getSize (parent, PREF); | |
| } | |
| /** | |
| * Cause the previous component in the container to be displayed. | |
| * If this current card is the first one in the deck, the last | |
| * component is displayed. | |
| * | |
| * @param parent The parent container, not <code>null</code>. | |
| */ | |
| public void previous (Container parent) | |
| { | |
| gotoComponent (parent, PREV); | |
| } | |
| /** | |
| * Remove the indicated component from this layout manager. | |
| * | |
| * @param comp The component to remove | |
| */ | |
| public void removeLayoutComponent (Component comp) | |
| { | |
| Enumeration e = tab.keys (); | |
| while (e.hasMoreElements ()) | |
| { | |
| Object key = e.nextElement (); | |
| if (tab.get (key) == comp) | |
| { | |
| tab.remove (key); | |
| Container parent = comp.getParent(); | |
| next(parent); | |
| break; | |
| } | |
| } | |
| } | |
| /** | |
| * Set this layout manager's horizontal gap. | |
| * | |
| * @param hgap The new gap | |
| */ | |
| public void setHgap (int hgap) | |
| { | |
| this.hgap = hgap; | |
| } | |
| /** | |
| * Set this layout manager's vertical gap. | |
| * | |
| * @param vgap The new gap | |
| */ | |
| public void setVgap (int vgap) | |
| { | |
| this.vgap = vgap; | |
| } | |
| /** | |
| * Cause the named component to be shown. If the component name is | |
| * unknown or <code>null</code>, this method does nothing. | |
| * | |
| * @param parent The parent container, not <code>null</code>. | |
| * @param name The name of the component to show | |
| */ | |
| public void show (Container parent, String name) | |
| { | |
| if (name == null) | |
| return; | |
| if (parent.getLayout() != this) | |
| throw new IllegalArgumentException("parent's layout is not this CardLayout"); | |
| Object target = tab.get (name); | |
| if (target != null) | |
| { | |
| int num = parent.ncomponents; | |
| // This is more efficient than calling getComponents(). | |
| Component[] comps = parent.component; | |
| for (int i = 0; i < num; ++i) | |
| { | |
| if (comps[i].isVisible()) | |
| { | |
| if (target == comps[i]) | |
| return; | |
| comps[i].setVisible (false); | |
| } | |
| } | |
| ((Component) target).setVisible (true); | |
| parent.validate(); | |
| } | |
| } | |
| /** | |
| * Returns a string representation of this layout manager. | |
| * | |
| * @return A string representation of this object. | |
| */ | |
| public String toString () | |
| { | |
| return getClass ().getName () + "[" + hgap + "," + vgap + "]"; | |
| } | |
| /** | |
| * This implements first(), last(), next(), and previous(). | |
| * | |
| * @param parent The parent container | |
| * @param what The type of goto: FIRST, LAST, NEXT or PREV | |
| * | |
| * @throws IllegalArgumentException if parent has not this | |
| * CardLayout set as its layout. | |
| */ | |
| private void gotoComponent (Container parent, int what) | |
| { | |
| if (parent.getLayout() != this) | |
| throw new IllegalArgumentException("parent's layout is not this CardLayout"); | |
| synchronized (parent.getTreeLock ()) | |
| { | |
| int num = parent.ncomponents; | |
| // This is more efficient than calling getComponents(). | |
| Component[] comps = parent.component; | |
| if (num == 1) | |
| { | |
| comps[0].setVisible(true); | |
| return; | |
| } | |
| int choice = -1; | |
| if (what == FIRST) | |
| choice = 0; | |
| else if (what == LAST) | |
| choice = num - 1; | |
| for (int i = 0; i < num; ++i) | |
| { | |
| if (comps[i].isVisible ()) | |
| { | |
| if (what == NEXT) | |
| { | |
| choice = i + 1; | |
| if (choice == num) | |
| choice = 0; | |
| } | |
| else if (what == PREV) | |
| { | |
| choice = i - 1; | |
| if (choice < 0) | |
| choice = num - 1; | |
| } | |
| else if (choice == i) | |
| { | |
| // Do nothing if we're already looking at the right | |
| // component. | |
| return; | |
| } | |
| comps[i].setVisible (false); | |
| if (choice >= 0) | |
| break; | |
| } | |
| } | |
| if (choice >= 0 && choice < num) | |
| comps[choice].setVisible (true); | |
| } | |
| } | |
| // Compute the size according to WHAT. | |
| private Dimension getSize (Container parent, int what) | |
| { | |
| synchronized (parent.getTreeLock ()) | |
| { | |
| int w = 0, h = 0, num = parent.ncomponents; | |
| Component[] comps = parent.component; | |
| for (int i = 0; i < num; ++i) | |
| { | |
| Dimension d; | |
| if (what == MIN) | |
| d = comps[i].getMinimumSize (); | |
| else if (what == MAX) | |
| d = comps[i].getMaximumSize (); | |
| else | |
| d = comps[i].getPreferredSize (); | |
| w = Math.max (d.width, w); | |
| h = Math.max (d.height, h); | |
| } | |
| Insets i = parent.getInsets (); | |
| w += 2 * hgap + i.right + i.left; | |
| h += 2 * vgap + i.bottom + i.top; | |
| // Handle overflow. | |
| if (w < 0) | |
| w = Integer.MAX_VALUE; | |
| if (h < 0) | |
| h = Integer.MAX_VALUE; | |
| return new Dimension (w, h); | |
| } | |
| } | |
| /** | |
| * @serial Horizontal gap value. | |
| */ | |
| private int hgap; | |
| /** | |
| * @serial Vertical gap value. | |
| */ | |
| private int vgap; | |
| /** | |
| * @serial Table of named components. | |
| */ | |
| private Hashtable tab; | |
| // These constants are used by the private gotoComponent method. | |
| private static final int FIRST = 0; | |
| private static final int LAST = 1; | |
| private static final int NEXT = 2; | |
| private static final int PREV = 3; | |
| // These constants are used by the private getSize method. | |
| private static final int MIN = 0; | |
| private static final int MAX = 1; | |
| private static final int PREF = 2; | |
| } |