/******************************************************************************* * 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.swing.table; import java.awt.Component; import java.awt.Container; import java.awt.Dimension; import java.awt.Insets; import java.awt.LayoutManager2; /** * A border layout lays out a container, arranging and resizing its components * to fit in five regions: north, south, east, west, and center. Each region may * contain no more than one component, and is identified by a corresponding * constant: <code>NORTH</code>, <code>SOUTH</code>, <code>EAST</code>, * <code>WEST</code>, and <code>CENTER</code>. When adding a component to a * container with a border layout, use one of these five constants, for example: * * <pre> * Panel p = new Panel(); * p.setLayout(new BorderLayout()); * p.add(new Button("Okay"), BorderLayout.SOUTH); * </pre> * * As a convenience, <code>BorderLayout</code> interprets the absence of a * string specification the same as the constant <code>CENTER</code>: * * <pre> * Panel p2 = new Panel(); * p2.setLayout(new BorderLayout()); * p2.add(new TextArea()); // Same as p.add(new TextArea(), * // BorderLayout.CENTER); * </pre> * <p> * In addition, <code>BorderLayout</code> supports the relative positioning * constants, <code>PAGE_START</code>, <code>PAGE_END</code>, * <code>LINE_START</code>, and <code>LINE_END</code>. In a container whose * <code>ComponentOrientation</code> is set to * <code>ComponentOrientation.LEFT_TO_RIGHT</code>, these constants map to * <code>NORTH</code>, <code>SOUTH</code>, <code>WEST</code>, and * <code>EAST</code>, respectively. * <p> * For compatibility with previous releases, <code>BorderLayout</code> also * includes the relative positioning constants <code>BEFORE_FIRST_LINE</code>, * <code>AFTER_LAST_LINE</code>, <code>BEFORE_LINE_BEGINS</code> and * <code>AFTER_LINE_ENDS</code>. These are equivalent to <code>PAGE_START</code> * , <code>PAGE_END</code>, <code>LINE_START</code> and <code>LINE_END</code> * respectively. For consistency with the relative positioning constants used by * other components, the latter constants are preferred. * <p> * Mixing both absolute and relative positioning constants can lead to * unpredictable results. If you use both types, the relative constants will * take precedence. For example, if you add components using both the * <code>NORTH</code> and <code>PAGE_START</code> constants in a container whose * orientation is <code>LEFT_TO_RIGHT</code>, only the <code>PAGE_START</code> * will be layed out. * <p> * NOTE: Currently (in the Java 2 platform v1.2), <code>BorderLayout</code> does * not support vertical orientations. The <code>isVertical</code> setting on the * container's <code>ComponentOrientation</code> is not respected. * <p> * The components are laid out according to their preferred sizes and the * constraints of the container's size. The <code>NORTH</code> and * <code>SOUTH</code> components may be stretched horizontally; the * <code>EAST</code> and <code>WEST</code> components may be stretched * vertically; the <code>CENTER</code> component may stretch both horizontally * and vertically to fill any space left over. * <p> * Here is an example of five buttons in an applet laid out using the * <code>BorderLayout</code> layout manager: * <p> * <img src="doc-files/BorderLayout-1.gif" alt="Diagram of an applet * demonstrating BorderLayout. Each section of the BorderLayout contains a * Button corresponding to its position in the layout, one of: North, West, * Center, East, or South." style="float:center; margin: 7px 10px;"> * <p> * The code for this applet is as follows: * * <hr> * <blockquote> * * <pre> * import java.awt.*; * import java.applet.Applet; * * public class buttonDir extends Applet { * public void init() { * setLayout(new BorderLayout()); * add(new Button("North"), BorderLayout.NORTH); * add(new Button("South"), BorderLayout.SOUTH); * add(new Button("East"), BorderLayout.EAST); * add(new Button("West"), BorderLayout.WEST); * add(new Button("Center"), BorderLayout.CENTER); * } * } * </pre> * * </blockquote> * <hr> * <p> * * @author Arthur van Hoff * @see java.awt.Container#add(String, Component) * @see java.awt.ComponentOrientation * @since JDK1.0 */ public class OverrideInsetsBorderLayout implements LayoutManager2, java.io.Serializable { /** * The north layout constraint (top of container). */ public static final String NORTH = "North"; /** * The south layout constraint (bottom of container). */ public static final String SOUTH = "South"; /** * The east layout constraint (right side of container). */ public static final String EAST = "East"; /** * The west layout constraint (left side of container). */ public static final String WEST = "West"; /** * The center layout constraint (middle of container). */ public static final String CENTER = "Center"; /** * Synonym for PAGE_START. Exists for compatibility with previous versions. * PAGE_START is preferred. * * @see #PAGE_START * @since 1.2 */ public static final String BEFORE_FIRST_LINE = "First"; /** * Synonym for PAGE_END. Exists for compatibility with previous versions. * PAGE_END is preferred. * * @see #PAGE_END * @since 1.2 */ public static final String AFTER_LAST_LINE = "Last"; /** * Synonym for LINE_START. Exists for compatibility with previous versions. * LINE_START is preferred. * * @see #LINE_START * @since 1.2 */ public static final String BEFORE_LINE_BEGINS = "Before"; /** * Synonym for LINE_END. Exists for compatibility with previous versions. * LINE_END is preferred. * * @see #LINE_END * @since 1.2 */ public static final String AFTER_LINE_ENDS = "After"; /** * The component comes before the first line of the layout's content. For * Western, left-to-right and top-to-bottom orientations, this is equivalent * to NORTH. * * @see java.awt.Component#getComponentOrientation * @since 1.4 */ public static final String PAGE_START = BEFORE_FIRST_LINE; /** * The component comes after the last line of the layout's content. For * Western, left-to-right and top-to-bottom orientations, this is equivalent * to SOUTH. * * @see java.awt.Component#getComponentOrientation * @since 1.4 */ public static final String PAGE_END = AFTER_LAST_LINE; /** * The component goes at the beginning of the line direction for the layout. * For Western, left-to-right and top-to-bottom orientations, this is * equivalent to WEST. * * @see java.awt.Component#getComponentOrientation * @since 1.4 */ public static final String LINE_START = BEFORE_LINE_BEGINS; /** * The component goes at the end of the line direction for the layout. For * Western, left-to-right and top-to-bottom orientations, this is equivalent * to EAST. * * @see java.awt.Component#getComponentOrientation * @since 1.4 */ public static final String LINE_END = AFTER_LINE_ENDS; /* * JDK 1.1 serialVersionUID */ private static final long serialVersionUID = -8658291919501921765L; /** * Constructs a border layout with the horizontal gaps between components. * The horizontal gap is specified by <code>hgap</code>. * * @see #getHgap() * @see #setHgap(int) * * @serial */ int hgap; /** * Constructs a border layout with the vertical gaps between components. The * vertical gap is specified by <code>vgap</code>. * * @see #getVgap() * @see #setVgap(int) * @serial */ int vgap; /** * Constant to specify components location to be the north portion of the * border layout. * * @serial * @see #getChild(String, boolean) * @see #addLayoutComponent * @see #getLayoutAlignmentX * @see #getLayoutAlignmentY * @see #removeLayoutComponent */ Component north; /** * Constant to specify components location to be the west portion of the * border layout. * * @serial * @see #getChild(String, boolean) * @see #addLayoutComponent * @see #getLayoutAlignmentX * @see #getLayoutAlignmentY * @see #removeLayoutComponent */ Component west; /** * Constant to specify components location to be the east portion of the * border layout. * * @serial * @see #getChild(String, boolean) * @see #addLayoutComponent * @see #getLayoutAlignmentX * @see #getLayoutAlignmentY * @see #removeLayoutComponent */ Component east; /** * Constant to specify components location to be the south portion of the * border layout. * * @serial * @see #getChild(String, boolean) * @see #addLayoutComponent * @see #getLayoutAlignmentX * @see #getLayoutAlignmentY * @see #removeLayoutComponent */ Component south; /** * Constant to specify components location to be the center portion of the * border layout. * * @serial * @see #getChild(String, boolean) * @see #addLayoutComponent * @see #getLayoutAlignmentX * @see #getLayoutAlignmentY * @see #removeLayoutComponent */ Component center; /** * * A relative positioning constant, that can be used instead of north, * south, east, west or center. mixing the two types of constants can lead * to unpredictable results. If you use both types, the relative constants * will take precedence. For example, if you add components using both the * <code>NORTH</code> and <code>BEFORE_FIRST_LINE</code> constants in a * container whose orientation is <code>LEFT_TO_RIGHT</code>, only the * <code>BEFORE_FIRST_LINE</code> will be layed out. This will be the same * for lastLine, firstItem, lastItem. * * @serial */ Component firstLine; /** * A relative positioning constant, that can be used instead of north, * south, east, west or center. Please read Description for firstLine. * * @serial */ Component lastLine; /** * A relative positioning constant, that can be used instead of north, * south, east, west or center. Please read Description for firstLine. * * @serial */ Component firstItem; /** * A relative positioning constant, that can be used instead of north, * south, east, west or center. Please read Description for firstLine. * * @serial */ Component lastItem; Insets insets = new Insets(0, 0, 0, 0); /** * Constructs a new border layout with no gaps between components. */ public OverrideInsetsBorderLayout() { this(0, 0); } /** * Constructs a border layout with the specified gaps between components. * The horizontal gap is specified by <code>hgap</code> and the vertical gap * is specified by <code>vgap</code>. * * @param hgap * the horizontal gap. * @param vgap * the vertical gap. */ public OverrideInsetsBorderLayout(int hgap, int vgap) { this.hgap = hgap; this.vgap = vgap; } /** * Adds the specified component to the layout, using the specified * constraint object. For border layouts, the constraint must be one of the * following constants: <code>NORTH</code>, <code>SOUTH</code>, * <code>EAST</code>, <code>WEST</code>, or <code>CENTER</code>. * <p> * Most applications do not call this method directly. This method is called * when a component is added to a container using the * <code>Container.add</code> method with the same argument types. * * @param comp * the component to be added. * @param constraints * an object that specifies how and where the component is added * to the layout. * @see java.awt.Container#add(java.awt.Component, java.lang.Object) * @exception IllegalArgumentException * if the constraint object is not a string, or if it not one * of the five specified constants. * @since JDK1.1 */ @Override public void addLayoutComponent(Component comp, Object constraints) { synchronized (comp.getTreeLock()) { if (constraints == null || constraints instanceof String) { addLayoutComponent((String) constraints, comp); } else { throw new IllegalArgumentException("cannot add to layout: constraint must be a string (or null)"); } } } /** * @deprecated replaced by * <code>addLayoutComponent(Component, Object)</code>. */ @Override @Deprecated public void addLayoutComponent(String name, Component comp) { synchronized (comp.getTreeLock()) { /* Special case: treat null the same as "Center". */ if (name == null) { name = "Center"; } /* * Assign the component to one of the known regions of the layout. */ if ("Center".equals(name)) { center = comp; } else if ("North".equals(name)) { north = comp; } else if ("South".equals(name)) { south = comp; } else if ("East".equals(name)) { east = comp; } else if ("West".equals(name)) { west = comp; } else if (BEFORE_FIRST_LINE.equals(name)) { firstLine = comp; } else if (AFTER_LAST_LINE.equals(name)) { lastLine = comp; } else if (BEFORE_LINE_BEGINS.equals(name)) { firstItem = comp; } else if (AFTER_LINE_ENDS.equals(name)) { lastItem = comp; } else { throw new IllegalArgumentException("cannot add to layout: unknown constraint: " + name); } } } /** * Get the component that corresponds to the given constraint location * * @param key * The desired absolute position, either NORTH, SOUTH, EAST, or * WEST. * @param ltr * Is the component line direction left-to-right? */ private Component getChild(String key, boolean ltr) { Component result = null; if (key == NORTH) { result = firstLine != null ? firstLine : north; } else if (key == SOUTH) { result = lastLine != null ? lastLine : south; } else if (key == WEST) { result = ltr ? firstItem : lastItem; if (result == null) { result = west; } } else if (key == EAST) { result = ltr ? lastItem : firstItem; if (result == null) { result = east; } } else if (key == CENTER) { result = center; } if (result != null && !result.isVisible()) { result = null; } return result; } /** * Gets the constraints for the specified component * * @param comp * the component to be queried * @return the constraint for the specified component, or null if component * is null or is not present in this layout * @see #addLayoutComponent(java.awt.Component, java.lang.Object) * @since 1.5 */ public Object getConstraints(Component comp) { // fix for 6242148 : API method // java.awt.BorderLayout.getConstraints(null) should return null if (comp == null) { return null; } if (comp == center) { return CENTER; } else if (comp == north) { return NORTH; } else if (comp == south) { return SOUTH; } else if (comp == west) { return WEST; } else if (comp == east) { return EAST; } else if (comp == firstLine) { return PAGE_START; } else if (comp == lastLine) { return PAGE_END; } else if (comp == firstItem) { return LINE_START; } else if (comp == lastItem) { return LINE_END; } return null; } /** * Returns the horizontal gap between components. * * @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; } /** * Returns the component that corresponds to the given constraint location * based on the target <code>Container</code>'s component orientation. * Components added with the relative constraints <code>PAGE_START</code>, * <code>PAGE_END</code>, <code>LINE_START</code>, and <code>LINE_END</code> * take precedence over components added with the explicit constraints * <code>NORTH</code>, <code>SOUTH</code>, <code>WEST</code>, and * <code>EAST</code>. The <code>Container</code>'s component orientation is * used to determine the location of components added with * <code>LINE_START</code> and <code>LINE_END</code>. * * @param constraints * the desired absolute position, one of <code>CENTER</code>, * <code>NORTH</code>, <code>SOUTH</code>, <code>EAST</code>, * <code>WEST</code> * @param target * the {@code Container} used to obtain the constraint location * based on the target {@code Container}'s component orientation. * @return the component at the given location, or <code>null</code> if the * location is empty * @exception IllegalArgumentException * if the constraint object is not one of the five specified * constants * @exception NullPointerException * if the target parameter is null * @see #addLayoutComponent(java.awt.Component, java.lang.Object) * @since 1.5 */ public Component getLayoutComponent(Container target, Object constraints) { boolean ltr = target.getComponentOrientation().isLeftToRight(); Component result = null; if (NORTH.equals(constraints)) { result = firstLine != null ? firstLine : north; } else if (SOUTH.equals(constraints)) { result = lastLine != null ? lastLine : south; } else if (WEST.equals(constraints)) { result = ltr ? firstItem : lastItem; if (result == null) { result = west; } } else if (EAST.equals(constraints)) { result = ltr ? lastItem : firstItem; if (result == null) { result = east; } } else if (CENTER.equals(constraints)) { result = center; } else { throw new IllegalArgumentException("cannot get component: invalid constraint: " + constraints); } return result; } /** * Gets the component that was added using the given constraint * * @param constraints * the desired constraint, one of <code>CENTER</code>, * <code>NORTH</code>, <code>SOUTH</code>, <code>WEST</code>, * <code>EAST</code>, <code>PAGE_START</code>, * <code>PAGE_END</code>, <code>LINE_START</code>, * <code>LINE_END</code> * @return the component at the given location, or <code>null</code> if the * location is empty * @exception IllegalArgumentException * if the constraint object is not one of the nine specified * constants * @see #addLayoutComponent(java.awt.Component, java.lang.Object) * @since 1.5 */ public Component getLayoutComponent(Object constraints) { if (CENTER.equals(constraints)) { return center; } else if (NORTH.equals(constraints)) { return north; } else if (SOUTH.equals(constraints)) { return south; } else if (WEST.equals(constraints)) { return west; } else if (EAST.equals(constraints)) { return east; } else if (PAGE_START.equals(constraints)) { return firstLine; } else if (PAGE_END.equals(constraints)) { return lastLine; } else if (LINE_START.equals(constraints)) { return firstItem; } else if (LINE_END.equals(constraints)) { return lastItem; } else { throw new IllegalArgumentException("cannot get component: unknown constraint: " + constraints); } } /** * Returns the vertical gap between components. * * @since JDK1.1 */ 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) { } /** * Lays out the container argument using this border layout. * <p> * This method actually reshapes the components in the specified container * in order to satisfy the constraints of this <code>BorderLayout</code> * object. The <code>NORTH</code> and <code>SOUTH</code> components, if any, * are placed at the top and bottom of the container, respectively. The * <code>WEST</code> and <code>EAST</code> components are then placed on the * left and right, respectively. Finally, the <code>CENTER</code> object is * placed in any remaining space in the middle. * <p> * Most applications do not call this method directly. This method is called * when a container calls its <code>doLayout</code> method. * * @param target * the container in which to do the layout. * @see java.awt.Container * @see java.awt.Container#doLayout() */ @Override public void layoutContainer(Container target) { synchronized (target.getTreeLock()) { Insets insets = target.getInsets(); int top = insets.top; int bottom = target.getHeight() - insets.bottom; int left = insets.left; int right = target.getWidth() - insets.right; boolean ltr = target.getComponentOrientation().isLeftToRight(); Component c = null; if ((c = getChild(NORTH, ltr)) != null) { c.setSize(right - left, c.getHeight()); Dimension d = c.getPreferredSize(); c.setBounds(left, top, right - left, d.height); top += d.height + vgap; } if ((c = getChild(SOUTH, ltr)) != null) { c.setSize(right - left, c.getHeight()); Dimension d = c.getPreferredSize(); c.setBounds(left, bottom - d.height, right - left, d.height); bottom -= d.height + vgap; } if ((c = getChild(EAST, ltr)) != null) { c.setSize(c.getWidth(), bottom - top); Dimension d = c.getPreferredSize(); c.setBounds(right - d.width, top, d.width, bottom - top); right -= d.width + hgap; } if ((c = getChild(WEST, ltr)) != null) { c.setSize(c.getWidth(), bottom - top); Dimension d = c.getPreferredSize(); c.setBounds(left, top, d.width, bottom - top); left += d.width + hgap; } if ((c = getChild(CENTER, ltr)) != null) { c.setBounds(left, top, right - left, bottom - top); } } } /** * 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); } /** * Determines the minimum size of the <code>target</code> container using * this layout manager. * <p> * This method is called when a container calls its * <code>getMinimumSize</code> method. Most applications do not call this * method directly. * * @param target * the container in which to do the layout. * @return the minimum dimensions needed to lay out the subcomponents of the * specified container. * @see java.awt.Container * @see java.awt.BorderLayout#preferredLayoutSize * @see java.awt.Container#getMinimumSize() */ @Override public Dimension minimumLayoutSize(Container target) { synchronized (target.getTreeLock()) { Dimension dim = new Dimension(0, 0); boolean ltr = target.getComponentOrientation().isLeftToRight(); Component c = null; if ((c = getChild(EAST, ltr)) != null) { Dimension d = c.getMinimumSize(); dim.width += d.width + hgap; dim.height = Math.max(d.height, dim.height); } if ((c = getChild(WEST, ltr)) != null) { Dimension d = c.getMinimumSize(); dim.width += d.width + hgap; dim.height = Math.max(d.height, dim.height); } if ((c = getChild(CENTER, ltr)) != null) { Dimension d = c.getMinimumSize(); dim.width += d.width; dim.height = Math.max(d.height, dim.height); } if ((c = getChild(NORTH, ltr)) != null) { Dimension d = c.getMinimumSize(); dim.width = Math.max(d.width, dim.width); dim.height += d.height + vgap; } if ((c = getChild(SOUTH, ltr)) != null) { Dimension d = c.getMinimumSize(); dim.width = Math.max(d.width, dim.width); dim.height += d.height + vgap; } Insets insets = target.getInsets(); dim.width += insets.left + insets.right; dim.height += insets.top + insets.bottom; return dim; } } /** * Determines the preferred size of the <code>target</code> container using * this layout manager, based on the components in the container. * <p> * Most applications do not call this method directly. This method is called * when a container calls its <code>getPreferredSize</code> method. * * @param target * the container in which to do the layout. * @return the preferred dimensions to lay out the subcomponents of the * specified container. * @see java.awt.Container * @see java.awt.BorderLayout#minimumLayoutSize * @see java.awt.Container#getPreferredSize() */ @Override public Dimension preferredLayoutSize(Container target) { synchronized (target.getTreeLock()) { Dimension dim = new Dimension(0, 0); boolean ltr = target.getComponentOrientation().isLeftToRight(); Component c = null; if ((c = getChild(EAST, ltr)) != null) { Dimension d = c.getPreferredSize(); dim.width += d.width + hgap; dim.height = Math.max(d.height, dim.height); } if ((c = getChild(WEST, ltr)) != null) { Dimension d = c.getPreferredSize(); dim.width += d.width + hgap; dim.height = Math.max(d.height, dim.height); } if ((c = getChild(CENTER, ltr)) != null) { Dimension d = c.getPreferredSize(); dim.width += d.width; dim.height = Math.max(d.height, dim.height); } if ((c = getChild(NORTH, ltr)) != null) { Dimension d = c.getPreferredSize(); dim.width = Math.max(d.width, dim.width); dim.height += d.height + vgap; } if ((c = getChild(SOUTH, ltr)) != null) { Dimension d = c.getPreferredSize(); dim.width = Math.max(d.width, dim.width); dim.height += d.height + vgap; } Insets insets = target.getInsets(); dim.width += insets.left + insets.right; dim.height += insets.top + insets.bottom; return dim; } } /** * Removes the specified component from this border layout. This method is * called when a container calls its <code>remove</code> or * <code>removeAll</code> methods. Most applications do not call this method * directly. * * @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()) { if (comp == center) { center = null; } else if (comp == north) { north = null; } else if (comp == south) { south = null; } else if (comp == east) { east = null; } else if (comp == west) { west = null; } if (comp == firstLine) { firstLine = null; } else if (comp == lastLine) { lastLine = null; } else if (comp == firstItem) { firstItem = null; } else if (comp == lastItem) { lastItem = null; } } } /** * Sets the horizontal gap between components. * * @param hgap * the horizontal gap between components * @since JDK1.1 */ public void setHgap(int hgap) { this.hgap = hgap; } /** * Sets the vertical gap between components. * * @param vgap * the vertical gap between components * @since JDK1.1 */ public void setVgap(int vgap) { this.vgap = vgap; } /** * Returns a string representation of the state of this border layout. * * @return a string representation of this border layout. */ @Override public String toString() { return getClass().getName() + "[hgap=" + hgap + ",vgap=" + vgap + "]"; } }