User: juha
Date: 01/01/23 10:21:29
Added: src/org/hs/jfc FormPanel.java RowLayout.java hVector.java
FormLayout.java ColumnLayout.java
Log:
Initial import of the LayoutManager package.
Author: Byron Hawkins
http://www.oz.net/~bhawkins/software.html
Revision Changes Path
1.1 admin/src/org/hs/jfc/FormPanel.java
Index: FormPanel.java
===================================================================
package org.hs.jfc;
import java.awt.*;
import java.util.*;
import java.sql.*;
import javax.swing.*;
/**
* This convenient class is just a JPanel with FormLayout as its default layout
manager. For all uncommented entities, refer to {@link FormLayout}.
* @author Byron Hawkins
*/
public class FormPanel extends JPanel
{
public FormPanel()
{
super.setLayout(new FormLayout());
}
public FormPanel(int internalHGap, int internalVGap, int externalHGap, int
externalVGap)
{
super.setLayout(new FormLayout(internalHGap, internalVGap,
externalHGap, externalVGap));
}
public int getInternalHGap()
{
return ((FormLayout)getLayout()).getInternalHGap();
}
public int getInternalVGap()
{
return ((FormLayout)getLayout()).getInternalVGap();
}
public int getExternalHGap()
{
return ((FormLayout)getLayout()).getExternalHGap();
}
public int getExternalVGap()
{
return ((FormLayout)getLayout()).getExternalVGap();
}
public void setInternalVGap(int gap)
{
((FormLayout)getLayout()).setInternalVGap(gap);
}
public void setInternalHGap(int gap)
{
((FormLayout)getLayout()).setInternalHGap(gap);
}
public void setExternalVGap(int gap)
{
((FormLayout)getLayout()).setExternalVGap(gap);
}
public void setExternalHGap(int gap)
{
((FormLayout)getLayout()).setExternalHGap(gap);
}
public Dimension getPreferredSize()
{
long currentTime = System.currentTimeMillis();
Dimension preferredSize =
((FormLayout)getLayout()).preferredLayoutSize(this);
return preferredSize;
}
public void add(Component component, int row, int column)
{
super.add(component);
long currentTime = System.currentTimeMillis();
((FormLayout)getLayout()).add(component, row, column);
}
public void add(Component label, Component field, int row, int column)
{
super.add(label);
super.add(field);
long currentTime = System.currentTimeMillis();
((FormLayout)getLayout()).add(label, field, row, column);
}
public void add(Component label, Component field, int row, int column, int
mode)
{
if ((mode < FormLayout.DEFAULT) || (mode > FormLayout.LABEL_ON_TOP))
{
add(label, field, row, column);
}
super.add(label);
super.add(field);
long currentTime = System.currentTimeMillis();
((FormLayout)getLayout()).add(label, field, row, column, mode);
}
// all addMultiRow() methods add components that span multiple rows
public void addMultiRow(Component component, int startRow, int endRow, int
column)
{
super.add(component);
long currentTime = System.currentTimeMillis();
((FormLayout)getLayout()).addMultiRow(component, startRow, endRow,
column);
}
public void addMultiRow(Component label, Component field, int startRow, int
endRow, int column)
{
super.add(label);
super.add(field);
long currentTime = System.currentTimeMillis();
((FormLayout)getLayout()).addMultiRow(label, field, startRow, endRow,
column);
}
public void addMultiRow(Component label, Component field, int startRow, int
endRow, int column, int mode)
{
super.add(label);
super.add(field);
long currentTime = System.currentTimeMillis();
((FormLayout)getLayout()).addMultiRow(label, field, startRow, endRow,
column, mode);
}
public void add(Component component, int row, int column, double fillRightPct)
{
super.add(component);
long currentTime = System.currentTimeMillis();
((FormLayout)getLayout()).add(component, row, column, fillRightPct);
}
public void add(Component label, Component field, int row, int column, double
fillRightPct)
{
super.add(label);
super.add(field);
long currentTime = System.currentTimeMillis();
((FormLayout)getLayout()).add(label, field, row, column, fillRightPct);
}
public void add(Component label, Component field, int row, int column, int
mode, double fillRightPct)
{
if ((mode < FormLayout.DEFAULT) || (mode > FormLayout.LABEL_ON_TOP))
{
add(label, field, row, column);
}
super.add(label);
super.add(field);
long currentTime = System.currentTimeMillis();
((FormLayout)getLayout()).add(label, field, row, column, mode,
fillRightPct);
}
public void addMultiRow(Component component, int startRow, int endRow, int
column, double fillRightPct)
{
super.add(component);
long currentTime = System.currentTimeMillis();
((FormLayout)getLayout()).addMultiRow(component, startRow, endRow,
column, fillRightPct);
}
public void addMultiRow(Component label, Component field, int startRow, int
endRow, int column, double fillRightPct)
{
super.add(label);
super.add(field);
long currentTime = System.currentTimeMillis();
((FormLayout)getLayout()).addMultiRow(label, field, startRow, endRow,
column, fillRightPct);
}
public void addMultiRow(Component label, Component field, int startRow, int
endRow, int column, int mode, double fillRightPct)
{
super.add(label);
super.add(field);
long currentTime = System.currentTimeMillis();
((FormLayout)getLayout()).addMultiRow(label, field, startRow, endRow,
column, mode, fillRightPct);
}
public void remove(Component comp)
{
super.remove(comp);
long currentTime = System.currentTimeMillis();
((FormLayout)getLayout()).removeLayoutComponent(comp);
}
public void setDefaultFillRightPct(double fillRightPct)
{
((FormLayout)getLayout()).setDefaultFillRightPct(fillRightPct);
}
/** Warning: not implemented! Prints a warning to System.err.
*/
public Component add(Component comp)
{
warn("FormPanel.FormPanel.add(Component): Warning! Use of unsupported
add method");
return null;
}
/** Warning: not implemented! Prints a warning to System.err.
*/
public Component add(String name, Component comp)
{
warn("FormPanel.FormPanel.add(String, Component): Warning! Use of
unsupported add method");
return null;
}
/** Warning: not implemented! Prints a warning to System.err.
*/
public Component add(Component comp, int index)
{
warn("FormPanel.FormPanel.add(Component, int): Warning! Use of
unsupported add method");
return null;
}
/** Warning: not implemented! Prints a warning to System.err.
*/
public void add(Component comp, Object constraints) {
warn("FormPanel.FormPanel.add(Component, Object): Warning! Use of unsupported add
method"); }
/** Warning: not implemented! Prints a warning to System.err.
*/
public void add(Component comp, Object constraints, int index) {
warn("FormPanel.FormPanel.add(Component, Object, int): Warning! Use of unsupported
add method"); }
// --------------------------- //
// warn out
/** Spew <CODE>message</CODE> to System.err.
* @param message spew this
*/
protected void warn(String message)
{
System.out.println(message);
}
public void setChronologicalFocus()
{
((FormLayout)getLayout()).setChronologicalFocus();
}
}
1.1 admin/src/org/hs/jfc/RowLayout.java
Index: RowLayout.java
===================================================================
package org.hs.jfc;
import java.awt.*;
import java.util.*;
import java.lang.*;
import javax.swing.*;
/**
* An abstract layout manager to set the horizontal positions of
* a set of components based on unique, ordered, non-sequential
* row numbers, layout size, and component minimum and preferred
* sizes.<p>
* Each RowLayout is responsible for a single row of components.
* Each component is stored in a component layout, which performs
* the actual layout functions. Rows are linked together in a
* doubly linked list. Rows use the same minimum and preferred
* location floating point scheme as ColumnLayout, except that they
* will not expand to fill extra space.<p>
* Copyright 1999 HawkinsSoftware<br>
* This code is free for distribution and/or modification.<br>
* Please do not remove the copyright.
*
* @author Byron Hawkins
*/
class RowLayout
{
/** The components for which the y-coordinate is governed by this RowLayout,
wrapped in ComponentLayout instances.
*/
private hVector m_components = null;
/** The RowLayout immediately above this one.
*/
private RowLayout m_previousRow = null;
/** The RowLayout immediately below this one.
*/
private RowLayout m_followingRow = null;
/** The FormLayout that contains this RowLayout.
*/
FormLayout m_containingLayout = null;
/** The y-coordinate of the tallest component governed by this RowLayout, in
the minimum layout scenario.
*/
protected int m_minLocation = 0;
/** The y-coordinate of the tallest component governed by this RowLayout, in
the preferred layout scenario.
*/
protected int m_prefLocation = 0;
/** The y-coordinate of the tallest component governed by this RowLayout, in
the current layout scenario.
*/
protected int m_currLocation = 0;
/** The minimum height of this RowLayout, calculated as the greatest minimum
height of the components governed by it.
*/
private int m_minSize = 0;
/** The preferred height of this RowLayout, calculated as the greatest
preferred height of the components governed by it.
*/
private int m_prefSize = 0;
/** The height of this RowLayout in the current layout.
*/
private int m_currSize = 0;
/** The user-specified index of this row.
*/
private int m_index = 0;
/** Create a new RowLayout with the user-specified <CODE>index</CODE> within
<CODE>containingLayout</CODE>.
* @param index the user-specified index of the row of Component governed by
this RowLayout.
* @param containingLayout contains this RowLayout.
*/
public RowLayout(int index, FormLayout containingLayout)
{
m_index = index;
m_components = new hVector();
m_containingLayout = containingLayout;
}
/** Accessor for the user-specified index of this row.
* @return the index of this row.
*/
public int getIndex()
{
return m_index;
}
/** Include <CODE>addMe</CODE> in the components governed by this RowLayout.
* @param addMe the component gor which to set the y-coordinate.
*/
public void add(Component addMe)
{
m_components.add(new ComponentLayout(addMe));
}
/** Include <CODE>addMe</CODE> in the components governed by this RowLayout,
but do not set its y-coordinate.
* @param addMe The new component to regard when calculating layout locations.
* @param anchorRow The index of the RowLayout that sets the y-coordinate of
<CODE>addMe</CODE>.
*/
public void addFloater(Component addMe, int anchorRow)
{
if (anchorRow == m_index)
{
try
{
((JComponent)addMe).setAlignmentY(Component.TOP_ALIGNMENT);
add(addMe);
return;
}
catch (ClassCastException e)
{}
}
ComponentLayout componentLayout = new ComponentLayout(addMe);
m_components.add(componentLayout);
getRow(anchorRow).setAnchor(componentLayout);
}
/** Include <CODE>addMe</CODE> in the ComponentLayouts contained in this
RowLayout.
* @param addMe The ComponentLayout to add.
*/
private void setAnchor(ComponentLayout addMe)
{
addMe.setAnchor(this);
m_components.add(addMe);
}
/** Recurse through the linked list of RowLayout and locate the one with index
<CODE>row</CODE>.
* @param row The user-specified index of the RowLayout to obtain.
* @return The RowLayout with user-specified index <CODE>row</CODE>, or null
if no such row is contained in this layout.
*/
public RowLayout getRow(int row)
{
if (row == m_index)
{
return this;
}
if (row < m_index)
{
// requested row belongs above this
if (m_previousRow == null)
{
// row does not exist: create it as first row
RowLayout rowLayout = new RowLayout(row, m_containingLayout);
rowLayout.m_previousRow = null;
rowLayout.m_followingRow = this;
m_previousRow = rowLayout;
return rowLayout;
}
else
{
// it's above the this' previous row -- let it deal
with it
return m_previousRow.getRow(row);
}
}
else
{
// requested row belongs below and does not exist -- create it
RowLayout rowLayout = new RowLayout(row, m_containingLayout);
m_followingRow.m_previousRow = rowLayout;
rowLayout.m_followingRow = m_followingRow;
m_followingRow = rowLayout;
rowLayout.m_previousRow = this;
return rowLayout;
}
}
/** Remove <CODE>removeMe</CODE> from the list of components to govern.
* @param removeMe The component to no longer govern.
*/
public void removeLayoutComponent(Component removeMe)
{
if (m_previousRow != null)
{
m_previousRow.removeLayoutComponent(removeMe);
}
Enumeration e = m_components.elements();
while (e.hasMoreElements())
{
ComponentLayout componentLayout =
(ComponentLayout)e.nextElement();
if ( componentLayout.hasComponent(removeMe) )
{
m_components.remove(componentLayout);
}
}
removeIfEmpty();
}
/** If this RowLayout contains no ColumnLayouts, remove it from the linked
list of rows.
*/
protected void removeIfEmpty()
{
if (m_components.size() == 0)
{
if (m_previousRow == null)
{
m_followingRow.m_previousRow = null;
}
else
{
m_followingRow.m_previousRow = m_previousRow;
m_previousRow.m_followingRow = m_followingRow;
}
}
}
/** Calculate the y-coordinate of the tallest component governed by this
RowLayout given the preferred layout scenario.
*/
protected void findPreferredLocation()
{
if (m_previousRow != null)
{
m_previousRow.findPreferredLocation();
int prefLocation = m_previousRow.getPrefLowerBoundary();
if (m_followingRow != null)
{
prefLocation += m_containingLayout.getInternalVGap();
}
setPreferredLocation(prefLocation);
}
else
{
m_prefLocation = m_containingLayout.getTopInset();
}
// ComponentLayout.getPreferredSize() relies on m_prefLocation!
Enumeration e = m_components.elements();
m_prefSize = 0;
while (e.hasMoreElements())
{
ComponentLayout nextComponent;
nextComponent = (ComponentLayout)e.nextElement();
if (nextComponent.getPreferredSize(this) > m_prefSize)
{
m_prefSize = nextComponent.getPreferredSize(this);
}
}
}
/** Calculate the y-coordinate of the tallest component governed by this
RowLayout given the minimum layout scenario.
*/
protected void findMinimumLocation()
{
if (m_previousRow != null)
{
m_previousRow.findMinimumLocation();
int minLoc = m_previousRow.getMinLowerBoundary();
if (m_followingRow != null) // RowLeader uses external gap
{
minLoc += m_containingLayout.getInternalVGap();
}
setMinimumLocation(minLoc);
}
else
{
m_minLocation = m_containingLayout.getTopInset();
}
// ComponentLayout.getMinimumSize() relies on m_minLocation!
Enumeration e = m_components.elements();
m_minSize = 0;
while (e.hasMoreElements())
{
ComponentLayout nextComponent;
nextComponent = (ComponentLayout)e.nextElement();
if (nextComponent.getMinimumSize(this) > m_minSize)
{
m_minSize = nextComponent.getMinimumSize(this);
}
}
}
/** A hook for subclasses that need to do something here.
* @param prefLoc The new preferred location of this RowLayout (the
y-coordinate of the tallest component in the preferred layout scenario).
*/
protected void setPreferredLocation(int prefLoc)
{
m_prefLocation = prefLoc;
}
/** A hook for subclasses that need to do something here.
* @param minLoc The new minimum location of this RowLayout (the y-coordinate
of the tallest component in the minimum layout scenario).
*/
protected void setMinimumLocation(int minLoc)
{
m_minLocation = minLoc;
}
/** Calculate the location of this RowLayout in terms of the previously
calculated minimum and preferred locations, and <CODE>pct</CODE>.
* @param pct The percentage of the extra space allowed by the preferred
layout scenario to make use of.
*/
protected void setLocation(double pct)
{
if (m_previousRow != null)
{
m_previousRow.setLocation(pct);
}
m_currLocation = (int) (m_minLocation + ((m_prefLocation - m_minLocation) *
pct));
}
/** Calculate and return the y-coordinate of the bottom of the tallest
component in this RowLayout, given the preferred layout scenario.
* @return the lower boundary of this RowLayout, in the preferred layout
scenario.
*/
protected int getPrefLowerBoundary()
{
return m_prefLocation + m_prefSize;
}
/** Calculate and return the y-coordinate of the bottom of the tallest
component in this RowLayout, given the minimum layout scenario.
* @return the lower boundary of this RowLayout, in the minimum layout
scenario.
*/
protected int getMinLowerBoundary()
{
if (m_minSize == 0)
{
return m_minLocation;
}
return m_minLocation + m_minSize;
}
/** Calculate and return the y-coordinate of the top of the tallest component
in this RowLayout, as currently layed out.
* @return the upper boundary of this RowLayout.
*/
protected int getUpperBoundary()
{
return m_currLocation;
}
/** Set the y-coordinates of all the components governed by this RowLayout
(except floaters that are not anchored here).
*/
public void doLayout()
{
if (m_previousRow != null)
{
m_previousRow.doLayout();
}
if (m_followingRow != null)
{
int room =
(m_followingRow.getUpperBoundary() -
m_containingLayout.getInternalVGap())
- m_currLocation;
Enumeration e = m_components.elements();
while (e.hasMoreElements())
{
((ComponentLayout)e.nextElement()).doLayout(room, this);
}
}
}
/** A wrapper for each component governed by this RowLayout.
*/
class ComponentLayout
{
/** The component governed by this ComponentLayout.
*/
private Component m_component = null;
// m_component spans multiple rows, m_anchor is the first, and this is
// the last; intermediate rows do not contain or know about m_component
/** If {@link #m_component} is a floater, m_anchor is the RowLayout
that sets its y-coordinate; otherwise, m_anchor is null.
*/
private RowLayout m_anchor = null;
/** Create a new ComponentLayout to govern the y-coordinate of
<CODE>component</CODE>.
* @param component The component for which to set the y-coordinate.
*/
public ComponentLayout(Component component)
{
m_component = component;
}
/** Identify this ComponentLayout as the anchor of a floater.
* @param thisRow Bogus -- java does not scope <CODE>this</CODE> for
inner classes.
*/
public void setAnchor(RowLayout thisRow)
{
m_anchor = thisRow;
}
/** Check to see if this ComponentLayout governs
<CODE>component</CODE>.
* @param component The component in question.
* @return true if this ComponentLayout governs <CODE>component</CODE>.
*/
public boolean hasComponent(Component component)
{
// address comparison
return (m_component == component);
}
/** Set the y-coordinate of {@link #m_component}.
* @param room The amount of y-coordinate space available in which
{@link #m_component} can be drawn.
* @param thisRow Bogus -- java does not scope <CODE>this</CODE> for
inner classes.
*/
public void doLayout(int room, RowLayout thisRow)
{
if ((m_anchor == null) || (m_anchor.getIndex() == thisRow.getIndex()))
// no anchor || this is the anchor
{
int location = thisRow.m_currLocation;
if (m_component.getPreferredSize().height <= room)
{
location += ((room -
m_component.getPreferredSize().height) * m_component.getAlignmentY());
}
m_component.setLocation(m_component.getLocation().x, location);
}
// don't set size on an anchor
if ((m_anchor == null) || (m_anchor.getIndex() != thisRow.getIndex()))
// no anchor || this is not the anchor
{
if ((m_anchor != null) && (m_anchor.getIndex() !=
thisRow.getIndex())) // this is the end of the multiRow
{
room += (thisRow.m_currLocation - m_anchor.getUpperBoundary());
}
if (m_component.getPreferredSize().height <= room)
{
m_component.setSize(m_component.getSize().width,
m_component.getPreferredSize().height);
}
else if (m_component.getMinimumSize().height < room)
{
m_component.setSize(m_component.getSize().width, room);
}
else
{
m_component.setSize(m_component.getSize().width,
m_component.getMinimumSize().height);
}
}
}
/** Calculate and return the minimum height required by this
ComponentLayout.
* @param thisRow Bogus -- java does not scope <CODE>this</CODE> for
inner classes.
* @return the minimum height required by this ComponentLayout.
*/
public int getMinimumSize(RowLayout thisRow)
{
if ((m_anchor != null) && (m_anchor.getIndex() == thisRow.getIndex()))
// anchor
{
// anchor expects no size allocation (will take it per
below)
return 0;
}
else if ((m_anchor != null) && (m_anchor.getIndex() !=
thisRow.getIndex())) // last component in multi-row
{
// find out how much of m_component is contained in
the above rows, and then
// how much space we need to give it in this row
int portionInThisRow = m_component.getMinimumSize().height -
(thisRow.m_minLocation - m_anchor.m_minLocation);
if (portionInThisRow <= 0)
{
// it fits in the above rows
return 0;
}
return portionInThisRow;
}
else
{
// single row
return m_component.getMinimumSize().height;
}
}
/** Calculate and return the preferred height required by this
ComponentLayout.
* @param thisRow Bogus -- java does not scope <CODE>this</CODE> for
inner classes.
* @return the preferred height required by this ComponentLayout.
*/
public int getPreferredSize(RowLayout thisRow)
{
if ((m_anchor != null) && (m_anchor.getIndex() == thisRow.getIndex()))
// (m_mode == FLOAT_ANCHOR)
{
return 0;
}
else if ((m_anchor != null) && (m_anchor.getIndex() !=
thisRow.getIndex())) // (m_mode == FLOAT_BUOY)
{
int portionInThisRow = m_component.getPreferredSize().height -
(thisRow.m_prefLocation - m_anchor.m_prefLocation);
if (portionInThisRow <= 0)
{
return 0;
}
return portionInThisRow;
}
else
{
return m_component.getPreferredSize().height;
}
}
}
}
1.1 admin/src/org/hs/jfc/hVector.java
Index: hVector.java
===================================================================
package org.hs.jfc;
import java.util.Vector;
public class hVector extends Vector
{
public boolean add(Object o) { super.addElement(o); return true; }
public boolean remove(Object o)
{
if (super.indexOf(o) == -1)
return false;
else
{
super.removeElement(o);
return true;
}
}
public Object remove(int i)
{
Object o = super.elementAt(i);
super.removeElementAt(i);
return o;
}
}
1.1 admin/src/org/hs/jfc/FormLayout.java
Index: FormLayout.java
===================================================================
package org.hs.jfc;
import java.awt.*;
import java.awt.event.*;
import java.util.*;
import javax.swing.*;
import javax.swing.text.*;
/**
* A layout manager modeled after the HTML <TABLE> tag. Components
* are added to a specified row and column, and can span multiple rows.
* Column widths are calculated in layoutComponents(), and adjustments
* for compacting forms can be made with mode parameters.<p>
* Separate interleaved objects called RowLayout and ColumnLayout
* manage the horizontal and vertical layout settings, respectively.
* The objects do not speak to each other. RowLeader and ColumnLeader
* sit here in FormLayout and have access to all Rows and all Columns,
* respectively; they are responsible for coordinating addition of
* new Rows and Columns, which are organized as linked lists. A
* floating point scheme is used to deal with extra space. See the
* HTML documentation for details about the interface and behavior.<p>
* Copyright 1999 HawkinsSoftware<br>
* This code is free for distribution and/or modification.<br>
* Please do not remove the copyright.
*
* @author Byron Hawkins
*/
public class FormLayout implements LayoutManager, ComponentListener
{
/** The gaps along the x-axis between the components.
*/
private int m_hgap = 5;
/** The gaps along the y-axis between the components.
*/
private int m_vgap = 5;
/** The two gaps along the x-axis between the rectangle occupied by the
components of the layout and the edge of the container.
*/
private int m_externalHGap = 0;
/** The two gaps along the y-axis between the rectangle occupied by the
components of the layout and the edge of the container.
*/
private int m_externalVGap = 0;
/** Justification percentage: consider a value that is <CODE>m_pct</CODE> of
the difference between the actual width of a component and the width it would have to
be to right-justify with the rest of the column. If this value is large enough to
extend the component such that it right-justifies, then do so; otherwise leave it as
is.
*/
private double m_pct = 0;
/** This flag indicates that the locations and sizes for all the components in
the minimum layout scenario must be recalculated.
*/
private boolean m_refreshMinimum = true;
/** This flag indicates that the locations and sizes for all the components in
the preferred layout scenario must be recalculated.
*/
private boolean m_refreshPreferred = true;
/** The ColumnLayouts that govern the x-coordinates of all components governed
by this FormLayout.
*/
private hVector m_Columns = new hVector();
/** A invisible entity that sits at the right end of the ColumnLayouts.
*/
private ColumnLeader m_ColumnLeader = null;
/** An invisible entity that sits at the bottom of the linked list of
RowLayouts; all recursive row operations start here.
*/
private RowLeader m_RowLeader = null;
/** The object that has employed this FormLayout to lay out its components.
*/
private Container m_container = null;
/** A collection of the components contained in this FormPanel, in the
chronological order in which the components were added to the layout.
*/
private Vector m_ListOfAddedComponents = new Vector();
/** Specifies default alignment for the associated component (used in the
<code>FormLayout.add()</code> methods).
*/
public static final int DEFAULT = 0;
/** Special alignment: the associated label will not align with other labels in
this column (used in certain <code>FormLayout.add()</code> methods).
*/
public static final int FREE_LABEL = 1;
/** Special alignment: the associated field will not align with other fields in
this column (used in certain <code>FormLayout.add()</code> methods).
*/
public static final int FREE_FIELD = 2;
/** Special alignment: the label and field will appear in subsequent columns,
starting with the one specified (used in certain <code>FormLayout.add()</code>
methods).
*/
public static final int LABEL_ON_TOP = 3;
/** Create a FormLayout with default gaps.
*/
public FormLayout()
{
init();
}
/** Most specific gap access available.
* @param internalHGap the amount of space to reserve along the x-axis between
components.
* @param internalVGap the amount of space to reserve along the y-axis between
components.
* @param externalHGap the amount of space along the x-axis between the
leftmost and rightmost components and the edge of the container (the left and right
insets).
* @param externalVGap the amount of space along the y-axis between the
topmost and bottommost components and the edge of the container (the top and bottom
insets).
*/
public FormLayout(int internalHGap, int internalVGap, int externalHGap, int
externalVGap)
{
m_hgap = internalHGap;
m_vgap = internalVGap;
m_externalHGap = externalHGap;
m_externalVGap = externalVGap;
init();
}
/** The amount of space along the x-axis between the leftmost component and the
edge of the container.
* @return externalHGap + left inset of the container.
*/
protected int getLeftInset()
{
return m_externalHGap + m_container.getInsets().left;
}
/** The amount of space along the x-axis between the rightmost component and the
edge of the container.
* @return externalHGap + right inset of the container.
*/
protected int getRightInset()
{
return m_externalHGap + m_container.getInsets().right;
}
/** The amount of space along the y-axis between the topmost component and the
edge of the container.
* @return externalVGap + top inset of the container.
*/
protected int getTopInset()
{
return m_externalVGap + m_container.getInsets().top;
}
/** The amount of space along the y-axis between the bottommost component and
the edge of the container.
* @return externalVGap + bottom inset of the container
*/
protected int getBottomInset()
{
return m_externalVGap + m_container.getInsets().bottom;
}
/** Accessor for the gap between components along the x-axis.
* @return the gap between components along the x-axis.
*/
public int getInternalHGap()
{
return m_hgap;
}
/** Accessor for the gap between components along the y-axis.
* @return the gap between components along the y-axis.
*/
public int getInternalVGap()
{
return m_vgap;
}
/** Accessor for the space between the block of components and the edge of the
container along the x-axis.
* @return the amount of space between the block of components and the edge of
the container along the x-axis.
*/
public int getExternalHGap()
{
return m_externalHGap;
}
/** Accessor for the space between the block of components and the edge of the
container along the y-axis.
* @return the amount of space between the block of components and the edge of
the container along the y-axis.
*/
public int getExternalVGap()
{
return m_externalVGap;
}
/** Set the amount of space between components along the y-axis.
* @param gap the amount of space between components along the y-axis.
*/
public void setInternalVGap(int gap)
{
m_vgap = gap;
m_refreshMinimum = true;
m_refreshPreferred = true;
}
/** Set the amount of space between components along the x-axis.
* @param gap the amount of space between components along the x-axis.
*/
public void setInternalHGap(int gap)
{
m_hgap = gap;
m_refreshMinimum = true;
m_refreshPreferred = true;
}
/** Set the amount of space between the block of components and the edge of
the container along the y-axis.
* @param gap the amount of space between the block of components and the edge
of the container along the y-axis.
*/
public void setExternalVGap(int gap)
{
m_externalVGap = gap;
m_refreshMinimum = true;
m_refreshPreferred = true;
}
/** Set the amount of space between the block of components and the edge of
the container along the x-axis.
* @param gap the amount of space between the block of components and the edge
of the container along the x-axis.
*/
public void setExternalHGap(int gap)
{
m_externalHGap = gap;
m_refreshMinimum = true;
m_refreshPreferred = true;
}
/** Initialize the layout.
*/
private void init()
{
m_ColumnLeader = new ColumnLeader(this);
m_Columns.add(m_ColumnLeader);
m_RowLeader = new RowLeader(this);
m_refreshMinimum = true;
m_refreshPreferred = true;
}
/** Set the location and size of each component governed by this FormLayout.
* @param parent The container whose components are to be laid out.
*/
public void layoutContainer(Container parent)
{
m_container = parent;
m_RowLeader.layoutRows(parent.getSize().height);
m_ColumnLeader.layoutColumns(parent.getSize().width);
m_refreshMinimum = false;
m_refreshPreferred = false;
}
/** Calculate and return the minimum number of pixels required to lay out the
components in <CODE>parent</CODE>.
* @param parent the container for which to calculate the minimum size.
* @return the number of pixels required to lay out <CODE>parent</CODE>.
*/
public Dimension minimumLayoutSize(Container parent)
{
m_container = parent;
Dimension minimumSize = new Dimension(m_ColumnLeader.getMinimumLocation(),
m_RowLeader.getMinimumLocation());
m_refreshMinimum = false;
return minimumSize;
}
/** Calculate and return the preferred number of pixels required to lay out
the components in <CODE>parent</CODE>.
* @param parent the container for which to calculate the preferred size.
* @return the number of pixels required to optimally lay out
<CODE>parent</CODE>.
*/
public Dimension preferredLayoutSize(Container parent)
{
m_container = parent;
Dimension preferredSize = new
Dimension(m_ColumnLeader.getPreferredLocation(), m_RowLeader.getPreferredLocation());
m_refreshPreferred = false;
return preferredSize;
}
/** remove <CODE>component</CODE> from the layout structure.
* @param component the component for which the location and size will no
longer be governed by this FormLayout.
*/
public void removeLayoutComponent(Component component)
{
m_RowLeader.removeLayoutComponent(component);
m_ColumnLeader.removeLayoutComponent(component);
ignore(component);
}
/** Add a <code>component</code> that will align with the labels in
<code>column</code>.
* @param component the component to add
* @param row the index of the row to add <CODE>component</CODE> to
* @param column the index of the column to add <CODE>component</CODE> to
*/
public void add(Component component, int row, int column)
{
m_RowLeader.add(component, row);
m_ColumnLeader.add(component, row, column, m_pct);
listen(component);
}
/** Add <code>label</code> and <code>field</code> with alignment respective to
the other labels and fields in <code>column</code>.
* @param label the label to add to the layout
* @param field the field to add to the layout
* @param row the index of the row to add <CODE>label</CODE> and
<CODE>field</CODE> to
* @param column the index of the column to add <CODE>label</CODE> and
<CODE>field</CODE> to.
*/
public void add(Component label, Component field, int row, int column)
{
m_RowLeader.add(label, field, row);
m_ColumnLeader.add(label, field, row, column, m_pct);
listen(label);
listen(field);
}
/** Add <code>label</code> and <code>field</code> with alignment respective to
the other labels and fields in <code>column</code>,
* subject to the specified <code>mode</code>.
* @param label the label to add to the layout
* @param field the field to add to the layout
* @param row the index of the row to add <CODE>label</CODE> and
<CODE>field</CODE> to
* @param column the index of the column to add <CODE>label</CODE> and
<CODE>field</CODE> to.
* @param mode identifies a system to apply when positioning the label: one of
{@link #FREE_LABEL}, {@link #FREE_FIELD}, {@link #LABEL_ON_TOP}, {@link #DEFAULT}.
*/
public void add(Component label, Component field, int row, int column, int mode)
{
listen(label);
listen(field);
if ((mode < FormLayout.DEFAULT) || (mode > FormLayout.LABEL_ON_TOP))
{
add(label, field, row, column);
}
if ((column == 0) && (mode == FormLayout.FREE_LABEL))
{
add(label, field, row, column);
}
if (mode == FormLayout.LABEL_ON_TOP)
{
if (row >= (Integer.MAX_VALUE - 1))
{
add(label, field, row, column);
}
try
{
((JComponent)label).setAlignmentY(Component.BOTTOM_ALIGNMENT);
}
catch (ClassCastException e) {}
// label goes on its own row
m_RowLeader.add(label, row);
m_ColumnLeader.add(label, row, column, m_pct);
m_RowLeader.add(field, row + 1);
m_ColumnLeader.add(field, row + 1, column, m_pct);
return;
}
m_RowLeader.add(label, field, row);
m_ColumnLeader.add(label, field, row, column, mode, m_pct);
}
/** Add a <code>component</code> that will align with the labels in
<code>column</code>,
* and span from <code>startRow</code> to <code>endRow</code>.
* @param component the component to be governed by this FormLayout
* @param startRow the index of the first row to be occupied by
<CODE>component</CODE>.
* @param endRow the index of the last row to be occupied by
<CODE>component</CODE>.
* @param column the index of the column to add <CODE>component</CODE> to
*/
public void addMultiRow(Component component, int startRow, int endRow, int
column)
{
listen(component);
m_RowLeader.addMultiRow(component, startRow, endRow);
m_ColumnLeader.addMultiRow(component, startRow, endRow, column, m_pct);
}
/** Add <code>label</code> and <code>field</code> with alignment respective to
the other labels and fields in <code>column</code>.
* and span from <code>startRow</code> to <code>endRow</code>.
* @param label the label to add to the layout
* @param field the field to add to the layout
* @param startRow the index of the first row to be occupied by
<CODE>component</CODE>.
* @param endRow the index of the last row to be occupied by
<CODE>component</CODE>.
* @param column the index of the column to add <CODE>component</CODE> to */
public void addMultiRow(Component label, Component field, int startRow, int
endRow, int column)
{
listen(label);
listen(field);
m_RowLeader.addMultiRow(label, field, startRow, endRow);
m_ColumnLeader.addMultiRow(label, field, startRow, endRow, column, m_pct);
}
/** Add <code>label</code> and <code>field</code> with alignment respective to
the other labels and fields in <code>column</code>,
* subject to the specified <code>mode</code>, and span from
<code>startRow</code> to <code>endRow</code>.
* @param label the label to add to the layout
* @param field the field to add to the layout
* @param startRow the index of the first row to be occupied by
<CODE>component</CODE>.
* @param endRow the index of the last row to be occupied by
<CODE>component</CODE>.
* @param mode identifies a system to apply when positioning the label: one of
{@link #FREE_LABEL}, {@link #FREE_FIELD}, {@link #LABEL_ON_TOP}, {@link #DEFAULT}.
* @param column the index of the column to add <CODE>component</CODE> to */
public void addMultiRow(Component label, Component field, int startRow, int
endRow, int column, int mode)
{
listen(label);
listen(field);
if (startRow > endRow)
{
endRow = startRow;
}
if ((mode < FormLayout.DEFAULT) || (mode > FormLayout.LABEL_ON_TOP))
{
addMultiRow(label, field, startRow, endRow, column);
}
if (mode == FormLayout.LABEL_ON_TOP)
{
if (endRow >= (Integer.MAX_VALUE - 1))
{
add(label, field, startRow, endRow, column);
}
if (startRow >= endRow)
{
endRow = startRow + 1;
}
try
{
((JComponent)label).setAlignmentY(Component.BOTTOM_ALIGNMENT);
}
catch (ClassCastException e) {}
m_RowLeader.add(label, startRow);
m_ColumnLeader.add(label, startRow, column, m_pct);
m_RowLeader.addMultiRow(field, startRow + 1, endRow);
m_ColumnLeader.addMultiRow(field, startRow + 1, endRow,
column, m_pct);
return;
}
m_RowLeader.addMultiRow(label, field, startRow, endRow);
m_ColumnLeader.addMultiRow(label, field, startRow, endRow, column, mode,
m_pct);
}
/** Add a <code>component</code> that will align with the labels in
<code>column</code>,
* and will stretch as far as <code>preferredSize.width * fillRightPct</code>
to right justify.
* @param row the index of the row to add <CODE>component</CODE> to
* @param column the index of the column to add <CODE>component</CODE> to
* @param component The component to be governed by this FormLayout.
* @param fillRightPct the right-justification proximity percentage: see
{@link #m_pct}.
*/
public void add(Component component, int row, int column, double fillRightPct)
{
listen(component);
m_RowLeader.add(component, row);
m_ColumnLeader.add(component, row, column, fillRightPct);
}
/**
* Add <code>label</code> and <code>field</code> with alignment respective to
the other labels and fields in <code>column</code>.
* and will stretch as far as <code>preferredSize.width * fillRightPct</code> to
right justify.
* @param label the label to add to the layout
* @param field the field to add to the layout
* @param row the index of the row to add <CODE>label</CODE> and
<CODE>field</CODE> to
* @param column the index of the column to add <CODE>label</CODE> and
<CODE>field</CODE> to.
* @param fillRightPct the right-justification proximity percentage: see
{@link #m_pct}.
*/
public void add(Component label, Component field, int row, int column, double
fillRightPct)
{
listen(label);
listen(field);
m_RowLeader.add(label, field, row);
m_ColumnLeader.add(label, field, row, column, fillRightPct);
}
/**
* Add <code>label</code> and <code>field</code> with alignment respective to
the other labels and fields in <code>column</code>,
* subject to the specified <code>mode</code>, and will stretch as far as
<code>preferredSize.width * fillRightPct</code> to right justify.
* @param label the label to add to the layout
* @param field the field to add to the layout
* @param row the index of the row to add <CODE>label</CODE> and
<CODE>field</CODE> to
* @param column the index of the column to add <CODE>label</CODE> and
<CODE>field</CODE> to.
* @param mode identifies a system to apply when positioning the label: one of
{@link #FREE_LABEL}, {@link #FREE_FIELD}, {@link #LABEL_ON_TOP}, {@link #DEFAULT}.
* @param fillRightPct the right-justification proximity percentage: see
{@link #m_pct}.
*/
public void add(Component label, Component field, int row, int column, int mode,
double fillRightPct)
{
listen(label);
listen(field);
if ((mode < FormLayout.DEFAULT) || (mode > FormLayout.LABEL_ON_TOP))
{
add(label, field, row, column, fillRightPct);
}
if ((column == 0) && (mode == FormLayout.FREE_LABEL))
{
add(label, field, row, column, fillRightPct);
}
if (mode == FormLayout.LABEL_ON_TOP)
{
if (row >= (Integer.MAX_VALUE - 1))
{
add(label, field, row, column, fillRightPct);
}
try
{
((JComponent)label).setAlignmentY(Component.BOTTOM_ALIGNMENT);
}
catch (ClassCastException e) {}
m_RowLeader.add(label, row);
m_ColumnLeader.add(label, row, column, fillRightPct);
m_RowLeader.add(field, row + 1);
m_ColumnLeader.add(field, row + 1, column, fillRightPct);
}
m_RowLeader.add(label, field, row);
m_ColumnLeader.add(label, field, row, column, mode, fillRightPct);
}
/**
* Add a <code>component</code> that will align with the labels in
<code>column</code>,
* will span from <code>startRow</code> to <code>endRow</code>, and will stretch
as far
* as <code>preferredSize.width * fillRightPct</code> to reach the right side.
* @param component the component to add to the layout
* @param startRow the index of the first row to be occupied by
<CODE>component</CODE>.
* @param endRow the index of the last row to be occupied by
<CODE>component</CODE>.
* @param column the index of the column to add <CODE>component</CODE> to
* @param fillRightPct the right-justification proximity percentage: see
{@link #m_pct}.
*/
public void addMultiRow(Component component, int startRow, int endRow, int
column, double fillRightPct)
{
listen(component);
m_RowLeader.addMultiRow(component, startRow, endRow);
m_ColumnLeader.addMultiRow(component, startRow, endRow, column,
fillRightPct);
}
/**
* Add <code>label</code> and <code>field</code> with alignment respective to
the other labels and fields in <code>column</code>.
* will span from <code>startRow</code> to <code>endRow</code>, and will stretch
as far
* as <code>preferredSize.width * fillRightPct</code> to reach the right side.
* @param label the label to add to the layout
* @param field the field to add to the layout
* @param startRow the index of the first row to be occupied by
<CODE>component</CODE>.
* @param endRow the index of the last row to be occupied by
<CODE>component</CODE>.
* @param column the index of the column to add <CODE>component</CODE> to
* @param fillRightPct the right-justification proximity percentage: see
{@link #m_pct}.
*/
public void addMultiRow(Component label, Component field, int startRow, int
endRow, int column, double fillRightPct)
{
listen(label);
listen(field);
m_RowLeader.addMultiRow(label, field, startRow, endRow);
m_ColumnLeader.addMultiRow(label, field, startRow, endRow, column,
fillRightPct);
}
/**
* Add <code>label</code> and <code>field</code> with alignment respective to
the other labels and fields in <code>column</code>,
* subject to the specified <code>mode</code>, will span from
<code>startRow</code> to <code>endRow</code>, and will stretch as far
* as <code>preferredSize.width * fillRightPct</code> to reach the right side.
* @param label the label to add to the layout
* @param field the field to add to the layout
* @param startRow the index of the first row to be occupied by
<CODE>component</CODE>.
* @param endRow the index of the last row to be occupied by
<CODE>component</CODE>.
* @param column the index of the column to add <CODE>component</CODE> to
* @param mode identifies a system to apply when positioning the label: one of
{@link #FREE_LABEL}, {@link #FREE_FIELD}, {@link #LABEL_ON_TOP}, {@link #DEFAULT}.
* @param fillRightPct the right-justification proximity percentage: see
{@link #m_pct}.
*/
public void addMultiRow(Component label, Component field, int startRow, int
endRow, int column, int mode, double fillRightPct)
{
listen(label);
listen(field);
if (startRow > endRow)
{
endRow = startRow;
}
if ((mode < FormLayout.DEFAULT) || (mode > FormLayout.LABEL_ON_TOP))
{
addMultiRow(label, field, startRow, endRow, column, fillRightPct);
}
if (mode == FormLayout.LABEL_ON_TOP)
{
if (endRow >= (Integer.MAX_VALUE - 1))
{
add(label, field, startRow, endRow, column,
fillRightPct);
}
if (startRow >= endRow)
{
endRow = startRow + 1;
}
try
{
((JComponent)label).setAlignmentY(Component.BOTTOM_ALIGNMENT);
}
catch (ClassCastException e) {}
m_RowLeader.add(label, startRow);
m_ColumnLeader.add(label, startRow, column, fillRightPct);
m_RowLeader.addMultiRow(field, startRow + 1, endRow);
m_ColumnLeader.addMultiRow(field, startRow + 1, endRow,
column, fillRightPct);
return;
}
m_RowLeader.addMultiRow(label, field, startRow, endRow);
m_ColumnLeader.addMultiRow(label, field, startRow, endRow, column, mode,
fillRightPct);
}
/** All subsequent <code>add()</code> calls that do not specify
<code>fillRightPct</code> will use this <code>fillRightPct</code>.
* @param fillRightPct the new default justification proximity percentage: see
{@link #m_pct}.
*/
public void setDefaultFillRightPct(double fillRightPct)
{
m_refreshMinimum = true;
m_refreshPreferred = true;
m_pct = fillRightPct;
}
/** Accessor for the default justification proximity percentage.
* @return the default justification proximity percentage: see {@link #m_pct}.
*/
public double getDefaultFillRightPct()
{
return m_pct;
}
// Return the ColumnLayout for "column"; create it if it doesn't exist
/** Get the ColumnLayout with user-specified index <CODE>column</CODE>.
* @param column the index of the ColumnLayout to locate.
* @return the ColumnLayout with user-specified index <CODE>column</CODE>.
*/
ColumnLayout getColumn(int column)
{
Enumeration e = m_Columns.elements();
ColumnLayout nextColumn = null;
int index = 0;
while (e.hasMoreElements())
{
nextColumn = (ColumnLayout)e.nextElement();
if (column == nextColumn.getIndex())
{
return nextColumn;
}
if (column < nextColumn.getIndex())
{
ColumnLayout newColumn = new ColumnLayout(column, this);
m_Columns.insertElementAt(newColumn, index);
return newColumn;
}
index++;
}
return null;
}
/** Terminated ancestor method.
* @param name ignored
* @param comp ignored
*/
public void addLayoutComponent(String name, Component comp)
{
System.out.println("FormLayout.addLayoutComponent(String, Component):
Warning! Use of unsupported method!");
}
// Oversees and coordinates RowLayouts
/** One instance of RowLeader resides at the end of the linked list of rows.
All recursive row operations start with the RowLeader. The RowLeader coordinates the
addition of all components to the row structure.
*/
class RowLeader extends RowLayout
{
/** Instantiate the single RowLeader of <CODE>containingLayout</CODE>.
* @param containingLayout the FormLayout containing the new RowLeader.
*/
public RowLeader(FormLayout containingLayout)
{
super(Integer.MAX_VALUE, containingLayout);
}
/** Add <CODE>label</CODE> and <CODE>field</CODE> to the row
structure, in a row with index <CODE>row</CODE> (create the row if it does not exist
yet).
* @param label the new label
* @param field the new field
* @param row the user-specified index of the row to add label and
field to; create a row with this index if it does not yet exist.
*/
public void add(Component label, Component field, int row)
{
RowLayout rowLayout = m_RowLeader.getRow(row);
rowLayout.add(label);
rowLayout.add(field);
}
/** Add component to the row structure in a row with index
<CODE>row</CODE>; if a row with this index does not exist, create one.
* @param component the component to add to the row structure.
* @param row the row to add.
*/
public void add(Component component, int row)
{
RowLayout rowLayout = m_RowLeader.getRow(row);
rowLayout.add(component);
}
/** Add <CODE>component</CODE> to the row structure in a RowLayout
with index <CODE>startRow</CODE>, and add a floater to the RowLayout with index
<CODE>endRow</CODE>.
* @param component the component to add
* @param startRow the user-specified index of the row that governs
<CODE>component</CODE>.
* @param endRow the user-specified index of the last row in the
layout that reserves space for <CODE>component</CODE>.
*/
public void addMultiRow(Component component, int startRow, int endRow)
{
getRow(endRow).addFloater(component, startRow);
}
/** Add <CODE>component</CODE> to the row structure in a RowLayout
with index <CODE>startRow</CODE>, and add a floater to the RowLayout with index
<CODE>endRow</CODE>.
* @param label the label to add
* @param field the field to add
* @param startRow the user-specified index of the row that governs
<CODE>component</CODE>.
* @param endRow the user-specified index of the last row in the
layout that reserves space for <CODE>component</CODE>.
*/
public void addMultiRow(Component label, Component field, int startRow, int
endRow)
{
getRow(startRow).add(label);
getRow(endRow).addFloater(field, startRow);
}
// never remove the RowLeader!
/** overriden to never remove the RowLeader (because it is always
empty and must always be present in the row structure).
*/
protected void removeIfEmpty() {}
/** Set the y-coordinate and height of all the components in the
layout, given <CODE>room</CODE> pixels to fit them in.
* @param room the number of y-coordinate pixels in which to fit the
components in the layout.
*/
public void layoutRows(int room)
{
if (room < m_minLocation)
{
room = m_minLocation;
}
findPreferredLocation();
findMinimumLocation();
double pct = 1;
if (m_prefLocation > m_minLocation) // && (room >= m_minLocation))
{
pct = (double) ( ((double)(room - m_minLocation)) /
((double)(m_prefLocation - m_minLocation)) );
}
// never layout larger than the aggregate preferred size
if (pct > 1)
{
pct = 1;
}
setLocation(pct);
doLayout();
}
/** Get the y-coordinate of the RowLeader in the preferred layout
scenario.
* @return the y-coordinate of the RowLeader in the preferred layout
scenario.
*/
public int getPreferredLocation()
{
if (m_refreshPreferred)
//if (true)
{
findPreferredLocation();
}
return m_prefLocation;
}
/** Get the y-coordinate of the RowLeader in the minimum layout
scenario.
* @return the y-coordinate of the RowLeader in the minimum layout
scenario.
*/
public int getMinimumLocation()
{
if (m_refreshMinimum)
//if (true)
{
findMinimumLocation();
}
return m_minLocation;
}
/** called by the location calculation system; in this case, we just
account for the container insets.
* @param minLoc the calculated minLocation, to adjust by the
container insets.
*/
protected void setMinimumLocation(int minLoc)
{
m_minLocation = minLoc + getRightInset();
}
/** called by the location calculation system; in this case, we just
account for the container insets.
* @param prefLoc the calculated minLocation, to adjust by the
container insets.
*/
protected void setPreferredLocation(int prefLoc)
{
m_prefLocation = prefLoc + m_externalVGap;
}
/** Calculate the upper boundary of the RowLeader, based on its
location in the current layout and the externalVGap.
* @return the largest pixel y-coordinate on which any component
governed by this FormLayout is drawn.
*/
protected int getUpperBoundary()
{
return m_currLocation - m_externalVGap;
}
/** obtain the gap that this RowLayout adjusts for: in this case, the
external gap.
* @return m_externalVGap
*/
protected int getGap()
{
return m_externalVGap;
}
}
// oversees and coordinates ColumnLayout`s
/** One ColumnLeader instance resides in each FormLayout; it coordinates the
addition of components to the Column structure; all recursive SegmentLayout operations
start at the ColumnLeader.
*/
class ColumnLeader extends ColumnLayout
{
/** The Ghost instances that reserve space for multi-row components in
the rows that they cover but do not occupy.
*/
private hVector m_invisibleGhosts = new hVector();
/** Create the ColumnLeader for <CODE>containingLayout</CODE>.
* @param containingLayout the FormLayout for which the new
ColumnLeader governs column structure.
*/
public ColumnLeader(FormLayout containingLayout)
{
super(Integer.MAX_VALUE, containingLayout);
}
/** Add component to the ColumnLayout with index <CODE>column</CODE>
and the SegmentLayout with index <CODE>row</CODE>, using the justification proximity
percentage <CODE>pct</CODE>.
* @param component the component to add.
* @param row the index of the SegmentLayout to add
<CODE>component</CODE> to.
* @param column the index of the ColumnLayout to add
<CODE>component</CODE> to.
* @param pct the justification proximity percentage to apply to
<CODE>component</CODE> (see {@link #m_pct}).
*/
public void add(Component component, int row, int column, double pct)
{
SegmentLayout segmentLayout = getSegment(getColumn(column), row);
segmentLayout.add(component, FormLayout.FREE_FIELD, pct);
}
/** Add label and field to the ColumnLayout with index
<CODE>column</CODE> and the SegmentLayout with index <CODE>row</CODE>, using the
justification proximity percentage <CODE>pct</CODE>.
* @param label the label to add.
* @param field the field to add.
* @param row the index of the SegmentLayout to add
<CODE>component</CODE> to.
* @param column the index of the ColumnLayout to add
<CODE>component</CODE> to.
* @param pct the justification proximity percentage to apply to
<CODE>component</CODE> (see {@link #m_pct}).
*/
public void add(Component label, Component field, int row, int column,
double pct)
{
SegmentLayout segmentLayout = getSegment(getColumn(column), row);
segmentLayout.add(label, pct);
segmentLayout.add(field, pct);
}
/** Add label and field to the ColumnLayout with index
<CODE>column</CODE> and the SegmentLayout with index <CODE>row</CODE>, using the
justification proximity percentage <CODE>pct</CODE>.
* @param label the label to add.
* @param field the field to add.
* @param row the index of the SegmentLayout to add
<CODE>component</CODE> to.
* @param column the index of the ColumnLayout to add
<CODE>component</CODE> to.
* @param mode the system to apply when choosing the relationship
between label and field and the line dividing labels and fields in the column.
* @param pct the justification proximity percentage to apply to
<CODE>component</CODE> (see {@link #m_pct}).
*/
public void add(Component label, Component field, int row, int column, int
mode, double pct)
{
SegmentLayout segmentLayout = getSegment(getColumn(column), row);
segmentLayout.add(label, mode, pct);
segmentLayout.add(field, pct);
}
/** Add component to the ColumnLayout with index <CODE>column</CODE>
and the SegmentLayout with index <CODE>row</CODE>, using the justification proximity
percentage <CODE>pct</CODE>.
* @param component the component to add.
* @param startRow the index of the SegmentLayout to add
<CODE>component</CODE> to.
* @param endRow the index of the SegmentLayout to add a ghost for
<CODE>component</CODE> to.
* @param column the index of the ColumnLayout to add
<CODE>component</CODE> to.
* @param pct the justification proximity percentage to apply to
<CODE>component</CODE> (see {@link #m_pct}).
*/
public void addMultiRow(Component component, int startRow, int endRow, int
column, double pct)
{
add(component, startRow, column, pct);
addMultiRows(component, startRow, endRow, column,
FormLayout.FREE_FIELD);
}
/** Add label and field to the ColumnLayout with index
<CODE>column</CODE> and the SegmentLayout with index <CODE>row</CODE>, using the
justification proximity percentage <CODE>pct</CODE>.
* @param label the label to add.
* @param field the field to add.
* @param startRow the index of the SegmentLayout to add
<CODE>component</CODE> to.
* @param endRow the index of the SegmentLayout to add a ghost for
<CODE>component</CODE> to.
* @param column the index of the ColumnLayout to add
<CODE>component</CODE> to.
* @param pct the justification proximity percentage to apply to
<CODE>component</CODE> (see {@link #m_pct}).
*/
public void addMultiRow(Component label, Component field, int startRow, int
endRow, int column, double pct)
{
add(label, field, startRow, column, pct);
addMultiRows(field, startRow, endRow, column, FormLayout.DEFAULT);
}
/** Add label and field to the ColumnLayout with index
<CODE>column</CODE> and the SegmentLayout with index <CODE>row</CODE>, using the
justification proximity percentage <CODE>pct</CODE>.
* @param label the label to add.
* @param field the field to add.
* @param startRow the index of the SegmentLayout to add
<CODE>component</CODE> to.
* @param endRow the index of the SegmentLayout to add a ghost for
<CODE>component</CODE> to.
* @param column the index of the ColumnLayout to add
<CODE>component</CODE> to.
* @param mode the system to apply when choosing the relationship
between label and field and the line dividing labels and fields in the column.
* @param pct the justification proximity percentage to apply to
<CODE>component</CODE> (see {@link #m_pct}).
*/
public void addMultiRow(Component label, Component field, int startRow, int
endRow, int column, int mode, double pct)
{
add(label, field, startRow, column, mode, pct);
addMultiRows(field, startRow, endRow, column, FormLayout.DEFAULT);
}
/** internal facility to add a ghost to each SegmentLayout between
(startRow + 1) and endRow.
* @param field the field to add
* @param startRow the index of the row that actually governs
<CODE>field</CODE>
* @param endRow the index of the last row to add ghosts to.
* @param column the index of the column to add the ghosts in
* @param mode the system used by the SegmentLayout and
<CODE>startRow</CODE> to lay out <CODE>field</CODE> and its corresponding label.
*/
private void addMultiRows(Component field, int startRow, int endRow,
int column, int mode)
{
ColumnLayout columnLayout = getColumn(column);
getSegment(columnLayout, startRow).setLastGhost(endRow);
Enumeration e = m_segments.elements();
SegmentLayout nextLeaderSegment = null;
SegmentLayout nextSegment = null;
while (e.hasMoreElements())
{
nextLeaderSegment = (SegmentLayout)e.nextElement();
if ((nextLeaderSegment.getIndex() > startRow) &&
(nextLeaderSegment.getIndex() <= endRow))
{
nextSegment = nextLeaderSegment.getSegment(columnLayout);
nextSegment.addGhost(new JLabel(""));
nextSegment.add(field, mode, 0);
}
}
m_invisibleGhosts.add(new Ghost(startRow, endRow, mode, field,
columnLayout));
}
/** Remove <CODE>component</CODE> from the column structure, and any
Ghost instances that reserve space for it.
* @param component the component to no longer govern in the column
structure.
*/
public void removeLayoutComponent(Component component)
{
Enumeration e = m_segments.elements();
while(e.hasMoreElements())
{
((SegmentLayout)e.nextElement()).removeLayoutComponent(component);
}
}
// create new if not found
/** Same as {@link #findSegment(int)}, but create the SegmentLayout if
it doesn't exist.
* @param columnLayout the ColumnLayout in which to seek the
SegmentLayout
* @param row the index of the sought SegmentLayout.
* @return the SegmentLayout sought, if it exists in the layout.
*/
public SegmentLayout getSegment(ColumnLayout columnLayout, int row)
{
SegmentLayout segmentLayout = findSegment(row);
if (segmentLayout == null)
{
segmentLayout = new SegmentLayout(row);
m_segments.add(segmentLayout);
Enumeration e = m_invisibleGhosts.elements();
while (e.hasMoreElements())
{
((Ghost) e.nextElement()).makeVisible(segmentLayout);
}
}
return segmentLayout.getSegment(columnLayout);
}
/** Get the SegmentLayout contained by the ColumnLeader and the
RowLayout with index <CODE>row</CODE>.
* @param row the index of the RowLayout that contains the
SegmentLayout in question.
* @return The specified SegmentLayout, or null if it does not exist.
*/
private SegmentLayout findSegment(int row)
{
Enumeration e = m_segments.elements();
SegmentLayout nextSegment = null;
while (e.hasMoreElements())
{
nextSegment = (SegmentLayout)e.nextElement();
if (nextSegment.getIndex() == row)
{
return nextSegment;
}
}
return null;
}
/** Set the x-coordinate and width of each component governed by this
FormLayout, given that there are <CODE>room</CODE> pixels along the x-axis to work
with.
* @param room the number of x-axis pixels to work with.
*/
public void layoutColumns(int room)
{
if (room < m_minLocation)
{
room = m_minLocation;
}
else if (room > m_prefLocation)
{
//if (m_pct < 1.0)
//{
room = m_prefLocation;
//}
}
room -= m_externalHGap;
recalcMinimumLocations();
recalcPreferredLocations();
double pct = 1;
if ((m_prefLocation - m_minLocation) > 0)
{
pct = (double)((double)(room - (m_minLocation - m_externalHGap)) /
(double)((m_prefLocation - m_externalHGap) - (m_minLocation - m_externalHGap)));
}
// instruct each ColumnLayout to find its location (left
extent of labels) for this layout size (by pct)
// (roundoff error from pct made up in
SegmentLayout.doLayout())
Enumeration e = m_Columns.elements();
while (e.hasMoreElements())
{
ColumnLayout nextColumn = (ColumnLayout)e.nextElement();
nextColumn.findIntermediateLocation(pct);
}
m_currLocation = room;
e = m_Columns.elements();
while (e.hasMoreElements())
{
ColumnLayout nextColumn = (ColumnLayout)e.nextElement();
nextColumn.doLayout();
}
}
/** Calculate and return the preferred location of the ColumnLeader;
recursively calls getPreferredLocation() on each ColumnLayout in this FormLayout.
Values are cached.
* @return the preferred location of the ColumnLeader.
*/
public int getPreferredLocation()
{
if (m_refreshPreferred)
//if (true)
{
recalcPreferredLocations();
}
return m_prefLocation;
}
/** Calculate and return the location of the ColumnLeader given the
minimum amount of space; recursively calls getMinimumLocation() on each ColumnLayout
in this FormLayout. Values are cached.
* @return the minimum location of the ColumnLeader.
*/
public int getMinimumLocation()
{
if (m_refreshMinimum)
//if (true)
{
recalcMinimumLocations();
}
return m_minLocation;
}
/** Set the minimum location of the ColumnLeader, adjusting for the
externalHGap.
* @param minLoc the minimum location as calculated for the
ColumnLeader.
*/
protected void setMinimumLocation(int minLoc)
{
m_minLocation = minLoc + m_externalHGap;
}
/** Set the preferred location of the ColumnLeader, adjusting for the
externalHGap.
* @param prefLoc the preferred location calculated for the
ColumnLeader.
*/
protected void setPreferredLocation(int prefLoc)
{
m_prefLocation = prefLoc + m_externalHGap;
}
// Recalculate the minimum locations of each Column.
// Call this every time m_minLocation and m_prefLocation are accessed,
// because the componentry may have changed in some relevant way since
// the last time this was called.
/** Calculate the minimum location of each ColumnLayout in this
FormLayout.
*/
void recalcMinimumLocations()
{
Enumeration e = m_Columns.elements();
ColumnLayout nextColumn = null;
while (e.hasMoreElements())
{
nextColumn = (ColumnLayout)e.nextElement();
nextColumn.findMinimumLocation();
}
}
// Recalculate the preferred locations of each Column.
// Call this every time m_minLocation and m_prefLocation are accessed,
// because the componentry may have changed in some relevant way since
// the last time this was called.
/** Calculate the preferred location of each ColumnLayout in this
FormLayout.
*/
void recalcPreferredLocations()
{
Enumeration e = m_Columns.elements();
ColumnLayout nextColumn = null;
while (e.hasMoreElements())
{
nextColumn = (ColumnLayout)e.nextElement();
nextColumn.findPreferredLocation();
}
}
/** Reserves space for multi-row components it in the rows that they
cover but do not occupy.
*/
class Ghost
{
/** The index of the row occupied by the multi-row component
represented by this Ghost.
*/
private int m_startRow;
/** The index of the last row covered by the multi-row
component represented by this Ghost.
*/
private int m_endRow;
/** The mode with which the component represented by this
Ghost was added.
*/
private int m_mode;
/** The component represented by this Ghost.
*/
private Component m_field;
/** The ColumnLayout in which this Ghost reserves space.
*/
private ColumnLayout m_columnLayout;
/** Create a Ghost for a component <CODE>field</CODE> that is
governed by the RowLayout with index <CODE>startRow</CODE>, that covers all subsequent
rows up to that with index <CODE>endRow</CODE>, which behaves according to the rules
of <CODE>mode</CODE>, and is contained in <CODE>columnLayout</CODE>.
* @param startRow index of the RowLayout that governs
<CODE>field</CODE>
* @param endRow index of the last RowLayout covered by
<CODE>field</CODE> (not necessarily instantiated)
* @param mode specifies the behavior of
<CODE>columnLayout</CODE> in regard to <CODE>field</CODE>.
* @param field the field for which to reserve space
* @param columnLayout the ColumnLayout in which to reserve
space for <CODE>field</CODE>
*/
public Ghost(int startRow, int endRow, int mode, Component field,
ColumnLayout columnLayout)
{
m_startRow = startRow;
m_endRow = endRow;
m_mode = mode;
m_field = field;
m_columnLayout = columnLayout;
}
/** When a RowLayout is added to a FormLayout and overlaps the
space reserved for a multi-row component by a Ghost, this method will create the
necessary space reservation for the Ghost's component in the RowLayout. If the
RowLayout does not overlap the space reservation of this Ghost, then this method does
nothing.
* @param segmentLayout The SegmentLayout of the ColumnLeader
with the index of the RowLayout that has been added.
*/
public void makeVisible(SegmentLayout segmentLayout)
{
if ((segmentLayout.getIndex() >= m_startRow) &&
(segmentLayout.getIndex() <= m_endRow))
{
SegmentLayout ghostSegment =
segmentLayout.getSegment(m_columnLayout);
ghostSegment.addGhost(new JLabel(""));
ghostSegment.add(m_field, m_mode, 0);
}
}
}
}
/** ComponentListener method -- null implementation
* @param e ignored
*/
public void componentHidden(ComponentEvent e) {}
/** ComponentListener method -- null implementation
* @param e ignored
*/
public void componentMoved(ComponentEvent e) {}
/** FormLayout listens to all components it governs: when a component is
resized, the flags {@link #m_refreshMinimum} and {@link #m_refreshPreferred} are set,
so that sizes will be recalculated on the next accessor calls, or the next call to
{@link #layoutContainer(Container)}.
* @param e ignored
*/
public void componentResized(ComponentEvent e)
{
m_refreshMinimum = true;
m_refreshPreferred = true;
}
/** ComponentListener method -- null implementation
* @param e ignored
*/
public void componentShown(ComponentEvent e) {}
/** Establish this FormLayout as a listener to <CODE>component</CODE>: see
{@link #componentResized(ComponentEvent)}.
* @param component the component to listen to.
*/
protected void listen(Component component)
{
addComponent(component);
component.addComponentListener(this);
m_refreshMinimum = true;
m_refreshPreferred = true;
}
/** Remove component from the list of components listened to by this
FormLayout.
* @param component the component to no longer listen to.
*/
protected void ignore(Component component)
{
removeComponent(component);
component.removeComponentListener(this);
m_refreshMinimum = true;
m_refreshPreferred = true;
}
/** Remove <CODE>comp</CODE> from the list of components that is maintained in
the order they were added: m_ListOfAddedComponents.
* @param comp The component to remove.
*/
void removeComponent(Component comp)
{
m_ListOfAddedComponents.remove(comp);
}
/** Add <CODE>comp</CODE> to the list of components that is maintained in the
order they were added: m_ListOfAddedComponents.
* @param comp The component to add.
*/
void addComponent(Component comp)
{
m_ListOfAddedComponents.add(comp);
}
/** For each component in the FormPanel, excluding disabled subclasses of
javax.swing.text.JTextComponent, set the focus order to the order in which the
components were added to the panel.
*/
public void setChronologicalFocus()
{
Vector ListOfFocusableComponents = new Vector();
Component comp;
for(int index=0; index < m_ListOfAddedComponents.size(); index++)
{
comp = (Component) m_ListOfAddedComponents.get(index);
if(comp instanceof JComponent && comp.isEnabled())
{
if(comp instanceof JTextComponent)
{
if( ((JTextComponent) comp).isEditable() )
{
ListOfFocusableComponents.add(comp);
}
}
else
{
ListOfFocusableComponents.add(comp);
}
}
}
for(int index=0; index < ListOfFocusableComponents.size(); index++)
{
JComponent current = (JComponent)
ListOfFocusableComponents.get(index);
JComponent next;
if(index == ListOfFocusableComponents.size() - 1)
{
next = (JComponent) ListOfFocusableComponents.get(0);
}
else
{
next = (JComponent)
ListOfFocusableComponents.get(index+1);
}
current.setNextFocusableComponent(next);
}
}
}
1.1 admin/src/org/hs/jfc/ColumnLayout.java
Index: ColumnLayout.java
===================================================================
package org.hs.jfc;
import java.awt.*;
import java.util.*;
/**
* An abstract layout manager to set the vertical position and height
* of a set of components based on unique, ordered, non-sequential
* column numbers, layout size, and component minimum and preferred
* sizes.
* <P>
* Each ColumnLayout is responsible for a single column of components,
* which may be in the form of label/field pairs, or single components
* spanning the entire column. Each component or pair of components
* is stored in a SegmentLayout. Columns are linked together by their
* SegmentLayouts' links to other Columns' SegmentLayouts. Each Column
* calculates its minimum and preferred locations as the maximum of
* its Segments' minimum and preferred locations. ColumnLayout sets
* its actual location on the basis of a percentage distance from
* its minimum location to its preferred location; the percentage
* is calculated by ColumnLeader (see FormLayout inner class).
* <P>
* Copyright 1999 HawkinsSoftware<br>
* This code is free for distribution and/or modification.<br>
* Please do not remove the copyright.
*
* @author Byron Hawkins
*/
class ColumnLayout
{
/** The x-coordinate of the leftmost component governed by this ColumnLayout,
when the minimum amount of space is available for the layout.
*/
protected int m_minLocation = 0;
/** The x-coordinate of the leftmost component governed by this ColumnLayout,
when the preferred amount of space (or more) is available for the layout.
*/
protected int m_prefLocation = 0;
/** The x-coordinate of the leftmost component of this ColumnLayout as
currently layed out.
*/
protected int m_currLocation = 0;
/** The number of pixels covered on the x-axis by the label of this
ColumnLayout.
*/
private int m_labelWidth = 0;
/** The user-specified index of the column governed by this ColumnLayout.
*/
private int m_columnIndex = 0;
/** The FormLayout that contains this ColumnLayout.
*/
protected FormLayout m_containingLayout = null;
/** The SegmentLayout instances contained by this ColumnLayout.
*/
protected hVector m_segments = null;
/** Create a new ColumnLayout to govern the column specified by the user as
<CODE>index</CODE> within the context of <CODE>containingLayout</CODE>.
* @param index The user-specified index of the column to be governed by the
new ColumnLayout.
* @param containingLayout The FormLayout that contains the new ColumnLayout.
*/
public ColumnLayout(int index, FormLayout containingLayout)
{
m_segments = new hVector();
m_columnIndex = index;
m_containingLayout = containingLayout;
}
/** Create a segment with the specified index, and add it to this ColumnLayout.
* @param segmentIndex The index of the new segment.
* @return The newly instantiated SegmentLayout.
*/
private SegmentLayout addSegment(int segmentIndex)
{
SegmentLayout newSegment = new SegmentLayout(segmentIndex);
m_segments.add(newSegment);
return newSegment;
}
/** Calculate the value for m_minLocation based on the minimum locations of
the SegmentLayout instances contained in {@link #m_segments}.
*/
protected void findMinimumLocation()
{
calculateLabelWidth();
Enumeration e = m_segments.elements();
int loc = 0;
int segmentIndexLoc = 0;
while (e.hasMoreElements())
{
segmentIndexLoc = ((SegmentLayout)e.nextElement()).getMinimumLocation();
if (segmentIndexLoc > loc)
{
loc = segmentIndexLoc;
}
}
setMinimumLocation(loc);
}
/** Calculate the value for m_prefLocation based on the preferred locations of
the SegmentLayout instances contained in {@link #m_segments}.
*/
protected void findPreferredLocation()
{
calculateLabelWidth();
Enumeration e = m_segments.elements();
int loc = 0;
int segmentLoc = 0;
while (e.hasMoreElements())
{
segmentLoc = ((SegmentLayout)e.nextElement()).getPreferredLocation();
if (segmentLoc > loc)
{
loc = segmentLoc;
}
}
setPreferredLocation(loc);
}
/** Calculate the location that is <CODE>pct</CODE> of the way from the
minimum to the preferred location.
* @param pct The percent of the extra space allowed by the preferred location
to use.
*/
protected void findIntermediateLocation(double pct)
{
m_currLocation = m_minLocation + (int)((m_prefLocation - m_minLocation) *
pct);
}
/** A hook for subclasses who need to do something here.
* @param minLoc the new minimumlocation.
*/
protected void setMinimumLocation(int minLoc)
{
m_minLocation = minLoc;
}
/** A hook for sublcasses that need to do something here.
* @param prefLoc The new preferred location.
*/
protected void setPreferredLocation(int prefLoc)
{
m_prefLocation = prefLoc;
}
/** Layout the components governed by this ColumnLayout.
*/
protected void doLayout()
{
Enumeration e = m_segments.elements();
while (e.hasMoreElements())
{
((SegmentLayout)e.nextElement()).doLayout();
}
}
/** Accessor the the minimum x-coordinate of the leftmost component governed
by this ColumnLayout.
* @return the ColumnLayout's minimum x-coordinate.
*/
public int getMinimumLocation()
{
return m_minLocation;
}
/** Accessor the the preferred x-coordinate of the leftmost component governed
by this ColumnLayout.
* @return The ColumnLayout's preferred x-coordinate.
*/
public int getPreferredLocation()
{
return m_prefLocation;
}
/** Accessor for the location of the leftmost component of this ColumnLayout,
as last layed out.
* @return the x-coordinate of this ColumnLayout, as last layed out.
*/
private int getCurrentLocation()
{
return m_currLocation;
}
/** Accessor for the user-specified index of the column governed by this
ColumnLayout.
* @return the user-specified index of this ColumnLayout.
*/
public int getIndex()
{
return m_columnIndex;
}
/** Calculate the number of pixels required to display the label governed by
this ColumnLayout.
*/
private void calculateLabelWidth()
{
m_labelWidth = 0;
Enumeration e = m_segments.elements();
SegmentLayout nextSegment;
while (e.hasMoreElements())
{
nextSegment = (SegmentLayout)e.nextElement();
if (nextSegment.m_label == null)
{
continue;
}
if (nextSegment.m_labelMode == FormLayout.FREE_FIELD)
{
continue;
}
if (nextSegment.m_labelMode == FormLayout.FREE_LABEL)
{
continue;
}
if (nextSegment.m_label.getPreferredSize().width >
m_labelWidth)
{
m_labelWidth =
nextSegment.m_label.getPreferredSize().width;
}
}
}
/** Governs the x-coordinates of the components conatained by this
ColumnLayout at a specific row.
*/
public class SegmentLayout
{
// May not be a label, but it will be treated like one regardless.
// m_labelMode will be set to FREE_FIELD if m_label is not a label, so
// it will not be forced to line up inside the other fields in this
Column.
/** The label governed by this SegmentLayout.
*/
private Component m_label = null;
// the rest of the components. Their positions are always treated as
relative
// to the space available; only the first of m_components will be
aligned with
/** The components governed by this SegmentLayout.
*/
private hVector m_components = null; // Component
/** The user-specified index of the RowLayout that also contains the
components of this SegmentLayout.
*/
private int m_segmentIndex = 0;
/** Specifies the system to apply when determining the alignment of
the components that follow the label in relation to the other labels in this
ColumnLayout: one of {@link FormLayout#DEFAULT}, {@link FormLayout#FREE_LABEL}, {@link
FormLayout#FREE_FIELD}, {@link FormLayout#LABEL_ON_TOP}.
*/
private int m_labelMode = 0;
/** The SegmentLayout subsequent to <CODE>this</CODE> along the x-axis.
*/
private SegmentLayout m_followingSegment = null;
/** The SegmentLayout previous to <CODE>this</CODE> along the x-axis;
null if <CODE>this</CODE> is the leftmost segment in the row specified by {@link
#m_segmentIndex}.
*/
private SegmentLayout m_previousSegment = null;
/** true if this SegmentLayout serves as a placeholder for a column
structure that spans multiple rows; see FormLayout.addMultiRow(...).
*/
private boolean m_isGhost = false;
/** Justification proximity percentage: if a component can be
right-justified by stretching (m_pct * (preferredWidth - minimumWidth)), then it will
be
*/
private double m_pct = 0;
/** If <CODE>this</CODE> is a ghost, m_lastGhost identifies the index
of the RowLayout in which the last ghost of the multi-row structure is contained.
*/
private int m_lastGhost = Integer.MAX_VALUE;
/** Create a SegmentLayout to govern components in this ColumnLayout
and the RowLayout specified by the user as <CODE>segmentIndex</CODE>.
* @param segmentIndex The user-specified index of the RowLayout that
also governs the components in this SegmentLayout.
*/
public SegmentLayout(int segmentIndex)
{
m_components = new hVector();
// prev and following columns will be set via ColumnLeader
m_segmentIndex = segmentIndex;
}
/** Add <CODE>component</CODE> to this SegmentLayout, and apply pct to
the extra space allowed by the preferred layout scenario, when it is available.
* @param component The component to be governed by this SegmentLayout.
* @param pct The percentage of the extra space allowed by the
preferred layout scenario to use when available.
*/
public void add(Component component, double pct)
{
add(component, m_labelMode, pct);
}
/** Add <CODE>component</CODE> to this SegmentLayout; apply pct to the
extra space allowed by the preferred layout scenario, when it is available; and apply
<CODE>mode</CODE> to the location of the first component following the label.
* @param component The component to be governed by this SegmentLayout.
* @param mode The mode to apply in calculating the location of the
first component following the label: see {@link #m_labelMode}.
* @param pct The percentage of the extra space allowed by the
preferred layout scenario to use when available.
*/
public void add(Component component, int mode, double pct)
{
setPct(pct);
if (m_label == null)
{
m_label = component;
m_labelMode = mode;
if ((mode == FormLayout.DEFAULT) &&
(component.getPreferredSize().width > m_labelWidth))
{
m_labelWidth = component.getPreferredSize().width;
}
}
else
{
m_components.add(component);
if (m_isGhost)
{
m_labelMode = mode;
}
}
}
/** Identify this SegmentLayout as a ghost that supports a multi-row
extension of <CODE>component</CODE>.
* @param component The component for which to reserve space in the
layout.
*/
public void addGhost(Component component)
{
m_isGhost = true;
add(component, 0);
}
/** Identify this SegmentLayout as a ghost that supports a multi-row
extension of <CODE>component</CODE>, which has the label mode <CODE>mode</CODE>.
* @param component The component for which to reserve space in the
layout.
* @param mode The label mode of <CODE>component</CODE>.
*/
public void addGhost(Component component, int mode)
{
m_isGhost = true;
add(component, mode, 0);
}
/** When <CODE>this</CODE> is a ghost for a component, identify the
index of the RowLayout that contains the last ghost that reserves space for the
component.
* @param lastGhost The index of the last row that reserves space for
the component for which this ghost is reserving space.
*/
public void setLastGhost(int lastGhost)
{
m_lastGhost = lastGhost;
}
/** Remove <CODE>component</CODE> from this SegmentLayout.
* @param component The component to no longer govern.
*/
protected void removeLayoutComponent(Component component)
{
// removing the label
if (m_label == component)
{
if (m_components.size() == 0)
{
// nothing left in this segmentIndex
removeThisSegment();
}
else
{
// make the first component the new label
m_label = (Component)m_components.remove(0);
m_labelMode = 0;
int labelWidth =
m_label.getPreferredSize().width;
if (labelWidth > m_labelWidth)
{
m_labelWidth = labelWidth;
}
}
return;
}
if (m_components.remove(component))
{
return;
}
// not here. check leftwards
if (m_previousSegment != null)
{
m_previousSegment.removeLayoutComponent(component);
}
}
/** Remove this segment from the x-coordinate-oriented linked list of
segments.
*/
private void removeThisSegment()
{
if (m_previousSegment == null)
{
m_followingSegment.m_previousSegment = null;
}
else
{
m_followingSegment.m_previousSegment =
m_previousSegment;
m_previousSegment.m_followingSegment =
m_followingSegment;
}
// this will be cleaned up, right?
}
/** Set the percentage of the extra space allowed by the preferred
layout scenario to make use of.
* @param pct the percentage of the extra space allowed by the
preferred layout scenario to make use of.
*/
private void setPct(double pct)
{
if (pct > 1)
{
pct = 1;
}
if (pct < 0)
{
pct = 0;
}
m_pct = pct;
}
/** This method is used to find the SegmentLayout that governs the
components of a specified RowLayout and ColumnLayout. The method traces recursively
leftward across the x-coordinate-oriented linked list of SegmentLayout's until the
SegmentLayout in the requested <CODE>column</CODE> is found.
* @param column The ColumnLayout that contains the SegmentLayout to
be obtained.
* @return The SegmentLayout to be obtained.
*/
public SegmentLayout getSegment(ColumnLayout column)
{
// looking for this Segment
if (column.getIndex() == m_columnIndex)
{
return this;
}
// it belongs somewhere to the left of here
else if (column.getIndex() < m_columnIndex)
{
// it does not exist: create it and link it up
if (m_previousSegment == null)
{
// it's new, and is the first in this segmentIndex
SegmentLayout newSegment = column.addSegment(m_segmentIndex);
newSegment.m_previousSegment = null;
newSegment.m_followingSegment = this;
m_previousSegment = newSegment;
return newSegment;
}
// it might exist: let the leftward neighbor deal with
it
else
{
return m_previousSegment.getSegment(column);
}
}
// it belongs to the right of here and does not exist: create
it and link it up
else
{
SegmentLayout newSegment = column.addSegment(m_segmentIndex);
newSegment.m_followingSegment = m_followingSegment;
m_followingSegment.m_previousSegment = newSegment;
newSegment.m_previousSegment = this;
m_followingSegment = newSegment;
return newSegment;
}
}
/** Layout the components governed by this SegmentLayout.
*/
public void doLayout()
{
// don't layout ghosts
if (m_isGhost)
{
return;
}
// must have m_label, even if it isn't a label per se
if (m_label == null)
{
return;
}
int labelLocation = 0;
if (m_labelMode == FormLayout.FREE_LABEL)
{
// FREE_LABELs will always go as far to the left as
they possibly can;
// the whole column will move right to accomodate if
necessary
int offset = m_label.getPreferredSize().width - m_labelWidth;
if (offset < 0)
{
// this label doesn't need any freedom!
offset = 0;
}
labelLocation = m_currLocation - offset;
}
else
{
labelLocation = m_currLocation;
}
int labelMinWidth = m_label.getMinimumSize().width;
int labelMaxWidth = m_label.getMaximumSize().width;
int labelPrefWidth = m_label.getPreferredSize().width;
if ((m_labelMode == FormLayout.FREE_FIELD) &&
(m_components.size() == 0))
{
// This is the only component in this Segment; treat
it like a field.
// Identify the amount of space available up to the
right neighbor's
// left boundary.
int followingLeftBoundary =
m_followingSegment.getLeftBoundary();
if (m_lastGhost < Integer.MAX_VALUE)
{
// There are ghosts of this component in
Segments below this one.
// Account for the left boundaries of the
segments to the right of
// the ghosts
SegmentLayout nextSegment = null;
int nextFollowingLeftBoundary;
Enumeration e = m_segments.elements();
while (e.hasMoreElements())
{
nextSegment =
(SegmentLayout)e.nextElement();
if ((nextSegment.m_segmentIndex >
m_segmentIndex) && (nextSegment.m_segmentIndex <= m_lastGhost))
{
nextFollowingLeftBoundary =
nextSegment.m_followingSegment.getLeftBoundary();
if (nextFollowingLeftBoundary
< followingLeftBoundary)
{
followingLeftBoundary
= nextFollowingLeftBoundary;
}
}
}
}
int room = followingLeftBoundary - m_currLocation;
int labelWidth;
if (labelMinWidth >= room)
{
labelWidth = labelMinWidth;
}
if (m_pct == 1)
{
// always stretching all the way
if ((room <= labelMinWidth) || (labelMaxWidth <= labelPrefWidth))
{
labelWidth = room;
}
else
{
labelWidth = labelMaxWidth;
}
}
else
{
if (labelPrefWidth >= room) // squeezing
{
labelWidth = room;
}
else // have extra space beyond preferred width
{
// can I stretch that far?
if ( (labelPrefWidth +
((labelPrefWidth - labelMinWidth) * m_pct)) >= room)
{
// yes
if ((room <= labelMaxWidth) || (labelMaxWidth <=
labelPrefWidth))
{
labelWidth = room;
}
else
{
labelWidth = labelMaxWidth;
}
}
else
{
// no, stop at preferred width
labelWidth = labelPrefWidth;
}
}
}
m_label.setSize(labelWidth, m_label.getSize().height);
if ((labelWidth < m_labelWidth) &&
(m_label.getAlignmentX() == Component.RIGHT_ALIGNMENT))
{
labelLocation += (m_labelWidth - labelWidth);
}
m_label.setLocation(labelLocation, m_label.getLocation().y);
return;
}
// labels never change size (unless they change content, but
that's
// static to this perspective)
m_label.setSize(labelPrefWidth, m_label.getSize().height);
if ((labelPrefWidth < m_labelWidth) &&
(m_label.getAlignmentX() == Component.RIGHT_ALIGNMENT))
{
labelLocation += (m_labelWidth - labelPrefWidth);
}
m_label.setLocation(labelLocation, m_label.getLocation().y);
int minSizeSum = 0;
int prefSizeSum = 0;
if (m_components.size() == 0)
{
// all done
return;
}
// total min and pref widths
Enumeration e = m_components.elements();
Component nextComponent = null;
while (e.hasMoreElements())
{
nextComponent = (Component)e.nextElement();
minSizeSum += nextComponent.getMinimumSize().width;
prefSizeSum += nextComponent.getPreferredSize().width;
}
int gapSum = (m_components.size() - 1) *
m_containingLayout.getInternalHGap();
minSizeSum += gapSum;
prefSizeSum += gapSum;
int followingLeftBoundary =
m_followingSegment.getLeftBoundary();
if (m_lastGhost < Integer.MAX_VALUE)
{
// There are ghosts of this component in Segments
below this one.
// Account for the left boundaries of the segments to
the right of
// the ghosts
int nextFollowingLeftBoundary;
SegmentLayout nextSegment = null;
e = m_segments.elements();
while (e.hasMoreElements())
{
nextSegment = (SegmentLayout)e.nextElement();
nextFollowingLeftBoundary =
nextSegment.m_followingSegment.getLeftBoundary();
if ((nextFollowingLeftBoundary <
followingLeftBoundary) &&
( (nextSegment.m_segmentIndex >
m_segmentIndex) && (nextSegment.m_segmentIndex <= m_lastGhost) ) )
{
followingLeftBoundary =
nextFollowingLeftBoundary;
}
}
}
int room = followingLeftBoundary - m_currLocation;
int startx = m_currLocation;
if (m_labelMode == FormLayout.FREE_FIELD)
{
room -= labelPrefWidth;
startx += labelPrefWidth;
// label might be blank
if (labelPrefWidth > 0)
{
room -= m_containingLayout.getInternalHGap();
startx += m_containingLayout.getInternalHGap();
}
}
else
{
room -= m_labelWidth;
startx += m_labelWidth;
if ((m_labelWidth > 0) || (m_labelMode ==
FormLayout.FREE_LABEL))
{
room -= m_containingLayout.getInternalHGap();
startx += m_containingLayout.getInternalHGap();
}
}
// check if this component can reach the right boundary with
(preferred size + (stretchability * m_pct))
// where stretchability is (prefSizeSum - minSizeSum)
if ( (room > prefSizeSum) && ( (prefSizeSum + ((prefSizeSum -
minSizeSum) * m_pct)) < room ) && (m_pct < 1) )
{
// can't reach -- set to preferred and leave space to
the right
room = prefSizeSum;
}
double pctEx = 0;
if ((prefSizeSum - minSizeSum) > 0)
{
pctEx = (double)((double)(room - minSizeSum) / (double)(prefSizeSum
- minSizeSum));
}
e = m_components.elements();
Component previous = null;
Component current = null;
if (e.hasMoreElements())
{
// place the first component
current = (Component)e.nextElement();
if (m_components.size() > 1)
{
current.setLocation(startx, current.getLocation().y);
}
}
while (e.hasMoreElements())
{
previous = current;
current = (Component)e.nextElement();
int previousMinWidth = previous.getMinimumSize().width;
int previousPrefWidth =
previous.getPreferredSize().width;
int currentPrefWidth =
current.getPreferredSize().width;
int currentMaxWidth = current.getMaximumSize().width;
// set the starting location of the next component
if (previousMinWidth < previousPrefWidth) // stretchability > 0
{
// account for stretch
startx += previousMinWidth + ((previousPrefWidth -
previousMinWidth) * pctEx);
}
else
{
// can't stretch this one
startx += previousMinWidth;
}
if (previousMinWidth > 0)
{
// no gaps for invisible componentry
startx += m_containingLayout.getInternalHGap();
}
current.setLocation(startx, current.getLocation().y);
int width = startx - previous.getLocation().x -
m_containingLayout.getInternalHGap();
if ((width <= currentMaxWidth) || (currentMaxWidth <=
currentPrefWidth))
{
previous.setSize(width, previous.getSize().height);
}
else
{
width = currentMaxWidth;
previous.setSize(width, previous.getSize().height);
}
}
int currentWidth = 0;
int currentPrefWidth = current.getPreferredSize().width;
int currentMaxWidth = current.getMaximumSize().width;
// will there be roundoff error?
if (pctEx == 1)
{
// no
currentWidth = currentPrefWidth;
}
else
{
// yes, so stretch per right neighbor's left boundary,
regardless
// of m_pct, etc.
currentWidth = (followingLeftBoundary - startx);
if (!((currentWidth <= currentMaxWidth) || (currentMaxWidth <=
currentPrefWidth)))
{
currentWidth = currentMaxWidth;
}
}
current.setSize(currentWidth, current.getSize().height);
// if there's only one component and extra space, account for
its X alignment
if (m_components.size() == 1)
{
if ( ((currentWidth + startx) < followingLeftBoundary)
&& (current.getAlignmentX() == Component.RIGHT_ALIGNMENT) )
{
startx += (followingLeftBoundary - (currentWidth +
startx));
}
current.setLocation(startx, current.getLocation().y);
}
}
/** Calculate and return the x-coordinate of the leftmost component
governed by this SegmentLayout, on the basis of the containing FormLayout's insets,
should only the minimum size be available.
* @return the x-coordinate of the leftmost component governed by this
SegmentLayout in the minimum layout scenario.
*/
public int getMinimumLocation()
{
if (m_previousSegment == null)
{
return m_containingLayout.getLeftInset();
}
if (m_followingSegment == null)
{
return m_previousSegment.getMinRightBoundary() + getLabelOffset()
+ m_containingLayout.getInternalHGap();
}
return m_previousSegment.getMinRightBoundary() + getLabelOffset() +
m_containingLayout.getInternalHGap();
}
/** Calculate and return the x-coordinate of the leftmost component
governed by this SegmentLayout, on the basis of the containing FormLayout's insets,
should the preferred size be available.
* @return the x-coordinate of the leftmost component governed by this
SegmentLayout in the preferred layout scenario.
*/
public int getPreferredLocation()
{
if (m_previousSegment == null)
{
return m_containingLayout.getLeftInset();
}
if (m_followingSegment == null)
{
return m_previousSegment.getPrefRightBoundary() + getLabelOffset();
}
return m_previousSegment.getPrefRightBoundary() + getLabelOffset() +
m_containingLayout.getInternalHGap();
}
/** Calculate and return the left boundary of this SegmentLayout,
based on the ColumnLayout's current location, and the offsets required by the label
mode and the containing FormLayout's insets.
* @return The left boundary of this SegmentLayout.
*/
public int getLeftBoundary()
{
if (m_followingSegment == null)
{
return m_currLocation;
}
return m_currLocation - getLabelOffset() -
m_containingLayout.getInternalHGap();
}
/** Calculate and return the right boundary of this SegmentLayout,
based on the ColumnLayout's current location, the offsets required by the label mode
and the containing FormLayout's insets, and the sizes of the components governed by
the SegmentLayout, in the minimum layout scenario.
* @return the right boundary of this SegmentLayout in the minimum
layout scenario.
*/
public int getMinRightBoundary()
{
int width = m_labelWidth;
if ((m_labelMode == FormLayout.FREE_FIELD) ||
((m_label.getMinimumSize().width == 0) && !m_isGhost) ) //&&
(m_label.getMinimumSize().width > m_labelWidth))
{
width = m_label.getMinimumSize().width;
}
Enumeration e = m_components.elements();
while (e.hasMoreElements())
{
Component nextComponent = (Component)e.nextElement();
width += nextComponent.getMinimumSize().width;
if ((nextComponent.getMinimumSize().width > 0) /**/ && !m_isGhost)
{
width += m_containingLayout.getInternalHGap();
}
}
return m_minLocation + width;
}
/** Calculate and return the right boundary of this SegmentLayout,
based on the ColumnLayout's current location, the offsets required by the label mode
and the containing FormLayout's insets, and the sizes of the components governed by
the SegmentLayout, in the preferred layout scenario.
* @return the right boundary of this SegmentLayout in the preferred
layout scenario.
*/
public int getPrefRightBoundary()
{
int width = m_labelWidth;
if ( (m_labelMode == FormLayout.FREE_FIELD) ||
((m_label.getPreferredSize().width == 0) && !m_isGhost) )
{
width = m_label.getPreferredSize().width;
}
Enumeration e = m_components.elements();
while (e.hasMoreElements())
{
Component nextComponent = (Component)e.nextElement();
width += nextComponent.getPreferredSize().width;
if ((nextComponent.getPreferredSize().width > 0) && !m_isGhost)
{
width += m_containingLayout.getInternalHGap();
}
}
return m_prefLocation + width;
}
/** Calculate the offset for the label as specified by the label mode.
* @return the offset required to properly layout the label according
to the system specified by the label mode.
*/
private int getLabelOffset()
{
int offset = 0;
if (m_labelMode == FormLayout.FREE_LABEL)
{
if (m_label.getPreferredSize().width > m_labelWidth)
{
offset = m_label.getPreferredSize().width - m_labelWidth;
}
}
return offset;
}
/** Accessor for the index of the RowLayout that also governs the
components in this SegmentLayout.
* @return The row index of this SegmentLayout (global to the entire
FormLayout, not just the index within this ColumnLayout).
*/
public int getIndex()
{
return m_segmentIndex;
}
}
}