// CardLayout.java - Card-based layout engine /* Copyright (C) 2000 Free Software Foundation This file is part of libgcj. This software is copyrighted work licensed under the terms of the Libgcj License. Please consult the file "LIBGCJ_LICENSE" for details. */ package java.awt; import java.util.Enumeration; import java.util.Hashtable; import java.io.Serializable; /** 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. * * @version 0.0 * @author Tom Tromey * @date December 2, 2000 */ public class CardLayout implements LayoutManager2, Serializable { /** Create a new CardLayout object with both gaps zero. */ public CardLayout () { this (0, 0); } /** Create a new CardLayout 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.map = 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 string */ public void addLayoutComponent (Component comp, Object constraints) { if (! (constraints instanceof String)) throw new IllegalArgumentException ("Object " + constraints + " is not a string"); map.put (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 */ public void addLayoutComponent (String name, Component comp) { addLayoutComponent (comp, name); } /** Cause the first component in the container to be displayed. * @param parent The parent container */ public void first (Container parent) { gotoComponent (parent, FIRST, null); } /** Return this layout manager's 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 */ 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 */ public float getLayoutAlignmentY (Container parent) { return Component.CENTER_ALIGNMENT; } /** Return this layout manager's 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 */ public void last (Container parent) { gotoComponent (parent, LAST, null); } /** Lay out the container's components based on the current * settings. * @param parent The parent container */ public void layoutContainer (Container parent) { int width = parent.width; int height = parent.height; Insets ins = parent.getInsets (); int num = parent.ncomponents; Component[] comps = parent.component; for (int i = 0; i < num; ++i) { if (comps[i].isVisible ()) { // Only resize the one we care about. comps[i].setBounds (hgap + ins.left, vgap + ins.top, width - 2 * hgap - ins.left - ins.right, height - 2 * vgap - ins.top - ins.bottom); break; } } } /** Get the maximum layout size of the container. * @param target The parent container */ 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 */ public Dimension minimumLayoutSize (Container target) { return getSize (target, MIN); } /** Cause the next component in the container to be displayed. * @param parent The parent container */ public void next (Container parent) { gotoComponent (parent, NEXT, null); } /** Get the preferred layout size of the container. * @param target The parent container */ public Dimension preferredLayoutSize (Container parent) { return getSize (parent, PREF); } /** Cause the previous component in the container to be displayed. * @param parent The parent container */ public void previous (Container parent) { gotoComponent (parent, PREV, null); } /** Remove the indicated component from this layout manager. * @param comp The component to remove */ public void removeLayoutComponent (Component comp) { Enumeration e = map.keys (); while (e.hasMoreElements ()) { Object key = e.nextElement (); if (map.get (key) == comp) { map.remove (key); 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, this method does nothing. * @param parent The parent container * @param name The name of the component to show */ public void show (Container parent, String name) { Object target = map.get (name); if (target != null) gotoComponent (parent, NONE, (Component) target); } public String toString () { return getClass ().getName () + "[" + hgap + "," + vgap + "]"; } // This implements first(), last(), next(), and previous(). private void gotoComponent (Container parent, int what, Component target) { int num = parent.getComponentCount (); // This is more efficient than calling getComponents(). Component[] comps = parent.component; int choice = -1; if (what == FIRST) choice = 0; else if (what == LAST) choice = num; else if (what >= 0) choice = what; for (int i = 0; i < num; ++i) { // If TARGET is set then we are looking for a specific // component. if (target != null) { if (target == comps[i]) choice = i; } if (comps[i].isVisible ()) { if (what == NEXT) { choice = i + 1; if (choice == num) choice = num - 1; } else if (what == PREV) { choice = i - 1; if (choice < 0) choice = 0; } else { // Do nothing if we're already looking at the right // component. if (choice == i) return; } comps[i].setVisible (false); if (choice >= 0) break; } } comps[choice].setVisible (true); } // Compute the size according to WHAT. private Dimension getSize (Container parent, int what) { 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); } // The gaps. private int hgap; private int vgap; // This hashtable maps a name to a component. private Hashtable map; // These constants are used by the private gotoComponent method. private int FIRST = 0; private int LAST = 1; private int NEXT = 2; private int PREV = 3; private int NONE = 4; // These constants are used by the private getSize method. private int MIN = 0; private int MAX = 1; private int PREF = 2; }