/******************************************************************************* * Breakout Cave Survey Visualizer * * Copyright (C) 2014 James Edwards * * jedwards8 at fastmail dot fm * * This program 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 of the License, or (at your option) any later * version. * * This program 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 * this program; if not, write to the Free Software Foundation, Inc., 51 * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. *******************************************************************************/ package org.andork.awt.layout; import java.awt.Component; import java.awt.Container; import java.awt.Dimension; import java.awt.Insets; import java.awt.LayoutManager2; import java.util.Vector; /** * A <code>BetterCardLayout</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>BetterCardLayout</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>BetterCardLayout</code> defines a set of * methods that allow an application to flip through these cards sequentially, * or to show a specified card. The {@link BetterCardLayout#addLayoutComponent} * method can be used to associate a string identifier with a given card for * fast random access. * * @version %I% %G% * @author Arthur van Hoff * @see java.awt.Container * @since JDK1.0 */ public class BetterCardLayout implements LayoutManager2 { /* * A pair of Component and String that represents its name. */ class Card { public Object key; public Component comp; public Card(Object cardKey, Component cardComponent) { key = cardKey; comp = cardComponent; } } /* * This creates a Vector to store associated pairs of components and their * names. * * @see java.util.Vector */ Vector<Card> vector = new Vector<Card>(); private boolean sizeHidden = true; /* * Index of Component currently displayed by BetterCardLayout. */ 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; /** * Creates a new card layout with gaps of size zero. */ public BetterCardLayout() { this(0, 0); } public BetterCardLayout(boolean sizeHidden) { super(); this.sizeHidden = sizeHidden; } /** * 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 BetterCardLayout(int hgap, int vgap) { this.hgap = hgap; 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.BetterCardLayout#show(java.awt.Container, java.lang.String) * @exception IllegalArgumentException * if the constraint is not a string. */ @Override public void addLayoutComponent(Component comp, Object key) { synchronized (comp.getTreeLock()) { if (!vector.isEmpty()) { comp.setVisible(false); } for (int i = 0; i < vector.size(); i++) { if (vector.get(i).key.equals(key)) { vector.get(i).comp = comp; return; } } vector.add(new Card(key, comp)); } } /** * @deprecated replaced by * <code>addLayoutComponent(Component, Object)</code>. */ @Override @Deprecated public void addLayoutComponent(String name, Component comp) { addLayoutComponent(comp, name); } /** * Make sure that the Container really has a BetterCardLayout installed. * Otherwise havoc can ensue! */ void checkLayout(Container parent) { if (parent.getLayout() != this) { throw new IllegalArgumentException("wrong parent for BetterCardLayout"); } } /** * Flips to the first card of the container. * * @param parent * the parent container in which to do the layout * @see java.awt.BetterCardLayout#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(); } } } public Object getCurrentCardKey() { return currentCard < 0 || currentCard >= vector.size() ? null : vector.get(currentCard).key; } /** * Gets the horizontal gap between components. * * @return the horizontal gap between components. * @see java.awt.BetterCardLayout#setHgap(int) * @see java.awt.BetterCardLayout#getVgap() * @since JDK1.1 */ public int getHgap() { return hgap; } /** * 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. */ @Override 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. */ @Override public float getLayoutAlignmentY(Container parent) { return 0.5f; } /** * Gets the vertical gap between components. * * @return the vertical gap between components. * @see java.awt.BetterCardLayout#setVgap(int) * @see java.awt.BetterCardLayout#getHgap() */ public int getVgap() { return vgap; } /** * Invalidates the layout, indicating that if the layout manager has cached * information it should be discarded. */ @Override public void invalidateLayout(Container target) { } public boolean isSizeHidden() { return sizeHidden; } /** * Flips to the last card of the container. * * @param parent * the parent container in which to do the layout * @see java.awt.BetterCardLayout#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(); } } } /** * 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 */ @Override public void layoutContainer(Container parent) { synchronized (parent.getTreeLock()) { Insets insets = parent.getInsets(); int ncomponents = parent.getComponentCount(); Component comp = null; for (int i = 0; i < ncomponents; i++) { comp = parent.getComponent(i); comp.setBounds(hgap + insets.left, vgap + insets.top, parent.getWidth() - (hgap * 2 + insets.left + insets.right), parent.getHeight() - (vgap * 2 + insets.top + insets.bottom)); } } } /** * 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 */ @Override public Dimension maximumLayoutSize(Container target) { return new Dimension(Integer.MAX_VALUE, Integer.MAX_VALUE); } /** * 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.BetterCardLayout#preferredLayoutSize */ @Override public Dimension minimumLayoutSize(Container parent) { synchronized (parent.getTreeLock()) { Insets insets = parent.getInsets(); int ncomponents = parent.getComponentCount(); int w = 0; int h = 0; boolean anyVisible = false; for (int i = 0; i < ncomponents; i++) { Component comp = parent.getComponent(i); anyVisible |= comp.isVisible(); if (comp.isVisible() || sizeHidden) { Dimension d = comp.getMinimumSize(); if (d.width > w) { w = d.width; } if (d.height > h) { h = d.height; } } } if (!anyVisible) { return new Dimension(0, 0); } return new Dimension(insets.left + insets.right + w + hgap * 2, insets.top + insets.bottom + h + vgap * 2); } } /** * 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.BetterCardLayout#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); } } /** * 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.BetterCardLayout#minimumLayoutSize */ @Override public Dimension preferredLayoutSize(Container parent) { synchronized (parent.getTreeLock()) { Insets insets = parent.getInsets(); int ncomponents = parent.getComponentCount(); int w = 0; int h = 0; boolean anyVisible = false; for (int i = 0; i < ncomponents; i++) { Component comp = parent.getComponent(i); anyVisible |= comp.isVisible(); if (comp.isVisible() || sizeHidden) { Dimension d = comp.getPreferredSize(); if (d.width > w) { w = d.width; } if (d.height > h) { h = d.height; } } } if (!anyVisible) { return new Dimension(0, 0); } return new Dimension(insets.left + insets.right + w + hgap * 2, insets.top + insets.bottom + h + vgap * 2); } } /** * 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.BetterCardLayout#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); } } /** * 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() */ @Override public void removeLayoutComponent(Component comp) { synchronized (comp.getTreeLock()) { for (int i = 0; i < vector.size(); i++) { if (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; } } } } /** * Sets the horizontal gap between components. * * @param hgap * the horizontal gap between components. * @see java.awt.BetterCardLayout#getHgap() * @see java.awt.BetterCardLayout#setVgap(int) * @since JDK1.1 */ public void setHgap(int hgap) { this.hgap = hgap; } public void setSizeHidden(boolean sizeHidden) { this.sizeHidden = sizeHidden; } /** * Sets the vertical gap between components. * * @param vgap * the vertical gap between components. * @see java.awt.BetterCardLayout#getVgap() * @see java.awt.BetterCardLayout#setHgap(int) * @since JDK1.1 */ public void setVgap(int vgap) { this.vgap = vgap; } /** * 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 key * the component name * @see java.awt.BetterCardLayout#addLayoutComponent(java.awt.Component, * java.lang.Object) */ public void show(Container parent, Object key) { synchronized (parent.getTreeLock()) { checkLayout(parent); Component next = null; int ncomponents = vector.size(); for (int i = 0; i < ncomponents; i++) { Card card = vector.get(i); if (card.key.equals(key)) { next = card.comp; currentCard = i; break; } } ncomponents = parent.getComponentCount(); for (int i = 0; i < ncomponents; i++) { Component comp = parent.getComponent(i); if (comp.isVisible()) { comp.setVisible(false); break; } } if (next != null && !next.isVisible()) { next.setVisible(true); } parent.invalidate(); } } void showDefaultComponent(Container parent) { if (parent.getComponentCount() > 0) { currentCard = 0; parent.getComponent(0).setVisible(true); parent.validate(); } } /** * Returns a string representation of the state of this card layout. * * @return a string representation of this card layout. */ @Override public String toString() { return getClass().getName() + "[hgap=" + hgap + ",vgap=" + vgap + "]"; } }