/* FlowLayoutCorrectMinimumSize.java of project jchart2d, <purpose>
* Copyright (c) 2006 - 2011 Achim Westermann, created on 08.10.2006 14:44:18.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA*
* If you modify or optimize the code in a useful way please let me know.
* Achim.Westermann@gmx.de
*
*/
package info.monitorenter.gui.chart.layouts;
import java.awt.Component;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Insets;
/**
* A flow layout that claims the correct height of the component managed in case
* the available width is known. The standard <code>{@link java.awt.FlowLayout}</code> does
* not claim the correct size but chooses the maximum width of all components to
* render which is worthless as the fact of flow breaks is not taken into
* account.
* <p>
*
* This class is inspired by the sun class
* <code>{@link java.awt.FlowLayout}</code> with modifications to the methods
* <code>{@link #preferredLayoutSize(Container)}</code> and
* <code>{@link #minimumLayoutSize(Container)}</code>.
* <p>
*
* @author <a href="mailto:Achim.Westermann@gmx.de">Achim Westermann</a>
*
* @version $Revision: 1.13 $
*/
public class FlowLayoutCorrectMinimumSize extends FlowLayout {
/** Generated <code>serialVersionUID</code>. */
private static final long serialVersionUID = 5192358035459949687L;
/**
* Constructs a new <code>FlowLayout</code> with the specified alignment and
* a default 5-unit horizontal and vertical gap. The value of the alignment
* argument must be one of <code>FlowLayout.LEFT</code>,
* <code>FlowLayout.RIGHT</code>, <code>FlowLayout.CENTER</code>,
* <code>FlowLayout.LEADING</code>, or <code>FlowLayout.TRAILING</code>.
*
* @param align
* the alignment value
*/
public FlowLayoutCorrectMinimumSize(final int align) {
this(align, 5, 5);
}
/**
* Creates a new flow layout manager with the indicated alignment and the
* indicated horizontal and vertical gaps.
* <p>
* The value of the alignment argument must be one of
* <code>FlowLayout.LEFT</code>, <code>FlowLayout.RIGHT</code>,
* <code>FlowLayout.CENTER</code>, <code>FlowLayout.LEADING</code>, or
* <code>FlowLayout.TRAILING</code>.
*
* @param align
* the alignment value
* @param hgap
* the horizontal gap between components and between the components
* and the borders of the <code>Container</code>
* @param vgap
* the vertical gap between components and between the components
* and the borders of the <code>Container</code>
*/
public FlowLayoutCorrectMinimumSize(final int align, final int hgap, final int vgap) {
super(align, hgap, vgap);
}
/**
* Lays out the container. This method lets each <i>visible</i> component
* take its preferred size by reshaping the components in the target container
* in order to satisfy the alignment of this <code>FlowLayout</code> object.
*
* @param target
* the specified component being laid out
* @see Container
* @see java.awt.Container#doLayout
*/
@Override
public void layoutContainer(final Container target) {
synchronized (target.getTreeLock()) {
int hgap = this.getHgap();
int vgap = this.getVgap();
Insets insets = target.getInsets();
int maxwidth = target.getWidth() - (insets.left + insets.right + hgap * 2);
int nmembers = target.getComponentCount();
int x = 0;
int y = insets.top + vgap;
int rowh = 0;
int start = 0;
boolean ltr = target.getComponentOrientation().isLeftToRight();
for (int i = 0; i < nmembers; i++) {
Component m = target.getComponent(i);
if (m.isVisible()) {
Dimension d = m.getPreferredSize();
m.setSize(d.width, d.height);
if ((x == 0) || ((x + d.width) <= maxwidth)) {
if (x > 0) {
x += hgap;
}
x += d.width;
rowh = Math.max(rowh, d.height);
} else {
this.moveComponents(target, insets.left + hgap, y, maxwidth - x, rowh, start, i, ltr);
x = d.width;
y += vgap + rowh;
rowh = d.height;
start = i;
}
}
}
this.moveComponents(target, insets.left + hgap, y, maxwidth - x, rowh, start, nmembers, ltr);
}
}
/**
* Returns the minimum dimensions needed to layout the <i>visible</i>
* components contained in the specified target container.
*
* @param target
* the container that needs to be laid out
* @return the minimum dimensions to lay out the subcomponents of the
* specified container
* @see #preferredLayoutSize
* @see java.awt.Container
* @see java.awt.Container#doLayout
*/
@Override
public Dimension minimumLayoutSize(final Container target) {
synchronized (target.getTreeLock()) {
int hgap = this.getHgap();
int vgap = this.getVgap();
Insets insets = target.getInsets();
int maxwidth = target.getWidth() - (insets.left + insets.right + hgap * 2);
int nmembers = target.getComponentCount();
boolean firstVisibleComponent = true;
int rowWidth = 0;
int maxRowWidth = 0;
int height = 0;
for (int i = 0; i < nmembers; i++) {
Component m = target.getComponent(i);
if (m.isVisible()) {
Dimension d = m.getMinimumSize();
if (firstVisibleComponent) {
height = d.height;
firstVisibleComponent = false;
}
if (rowWidth + hgap + d.width > maxwidth) {
maxRowWidth = Math.max(rowWidth, maxRowWidth);
height += (vgap + d.height);
rowWidth = d.width;
} else {
rowWidth += (hgap + d.width);
}
}
}
Dimension dim = new Dimension(maxRowWidth, height);
dim.width += insets.left + insets.right + hgap * 2;
dim.height += insets.top + insets.bottom + vgap * 2;
return dim;
}
}
/**
* Centers the elements in the specified row, if there is any slack.
*
* @param target
* the component which needs to be moved
* @param x
* the x coordinate
* @param y
* the y coordinate
* @param width
* the width dimensions
* @param height
* the height dimensions
* @param rowStart
* the beginning of the row
* @param rowEnd
* the the ending of the row
* @param ltr
* if true, left to right alignment is used.
*/
private void moveComponents(final Container target, final int x, final int y, final int width,
final int height, final int rowStart, final int rowEnd, final boolean ltr) {
synchronized (target.getTreeLock()) {
int xMod = x;
int hgap = this.getHgap();
switch (this.getAlignment()) {
case LEFT:
xMod += ltr ? 0 : width;
break;
case CENTER:
xMod += width / 2;
break;
case RIGHT:
xMod += ltr ? width : 0;
break;
case LEADING:
break;
case TRAILING:
xMod += width;
break;
default:
break;
}
for (int i = rowStart; i < rowEnd; i++) {
Component m = target.getComponent(i);
if (m.isVisible()) {
if (ltr) {
m.setLocation(xMod, y + (height - m.getHeight()) / 2);
} else {
m
.setLocation(target.getWidth() - xMod - m.getWidth(), y + (height - m.getHeight())
/ 2);
}
xMod += m.getWidth() + hgap;
}
}
}
}
/**
* Returns the preferred dimensions for this layout given the <i>visible</i>
* components in the specified target container.
*
* @param target
* the container that needs to be laid out
* @return the preferred dimensions to lay out the subcomponents of the
* specified container
* @see Container
* @see #minimumLayoutSize
* @see java.awt.Container#getPreferredSize
*/
@Override
public Dimension preferredLayoutSize(final Container target) {
synchronized (target.getTreeLock()) {
int hgap = this.getHgap();
int vgap = this.getVgap();
Insets insets = target.getInsets();
int maxwidth = target.getWidth() - (insets.left + insets.right + hgap * 2);
int nmembers = target.getComponentCount();
boolean firstVisibleComponent = true;
int rowWidth = 0;
int maxRowWidth = 0;
int height = 0;
for (int i = 0; i < nmembers; i++) {
Component m = target.getComponent(i);
if (m.isVisible()) {
Dimension d = m.getPreferredSize();
if (firstVisibleComponent) {
height = d.height;
firstVisibleComponent = false;
}
if (rowWidth + hgap + d.width > maxwidth) {
maxRowWidth = Math.max(rowWidth, maxRowWidth);
height += (vgap + d.height);
rowWidth = d.width;
} else {
rowWidth += (hgap + d.width);
}
}
}
Dimension dim = new Dimension(maxRowWidth, height);
dim.width += insets.left + insets.right + hgap * 2;
dim.height += insets.top + insets.bottom + vgap * 2;
return dim;
}
}
}