/*
* ProportionalLayout.java 18 sept 08
*
* Sweet Home 3D, Copyright (c) 2008 Emmanuel PUYBARET / eTeks <info@eteks.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package com.eteks.sweethome3d.swing;
import java.awt.Component;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.Insets;
import java.awt.LayoutManager2;
/**
* A layout manager that displays two components at the top of each other.
* The component at top is sized at container width and at its preferred height.
* The component at bottom is centered in the rest of the space and sized proportionally
* to its preferred size.
* @author Emmanuel Puybaret
*/
public class ProportionalLayout implements LayoutManager2 {
/**
* The two locations where components managed by a {@link ProportionalLayout} instance can be placed.
*/
public enum Constraints {TOP, BOTTOM}
private Component topComponent;
private Component bottomComponent;
private int gap;
/**
* Creates a layout manager which layouts its component
* with a default gap of 5 pixels between them.
*/
public ProportionalLayout() {
this(5);
}
/**
* Creates a layout manager which layouts its component
* with a given <code>gap</code> between them.
*/
public ProportionalLayout(int gap) {
this.gap = gap;
}
/**
* Records a given <code>component</code> in this layout manager as the component at
* <code>Constraints.TOP</code> or at <code>Constraints.BOTTOM</code> of its container.
*/
public void addLayoutComponent(Component component, Object constraints) {
if (constraints == Constraints.TOP) {
this.topComponent = component;
} else if (constraints == Constraints.BOTTOM) {
this.bottomComponent = component;
}
}
/**
* Do not use.
*/
public void addLayoutComponent(String name, Component comp) {
throw new IllegalArgumentException("Use addLayoutComponent with a Constraints object");
}
/**
* Removes the given <code>component</code> from the ones managed by this layout manager.
*/
public void removeLayoutComponent(Component component) {
}
/**
* Returns 0.5.
*/
public float getLayoutAlignmentX(Container target) {
return 0.5f;
}
/**
* Return 0.
*/
public float getLayoutAlignmentY(Container target) {
return 0f;
}
/**
* Invalidates layout.
*/
public void invalidateLayout(Container target) {
// Sizes are computed on the fly each time
}
/**
* Layouts the container.
*/
public void layoutContainer(Container parent) {
Insets parentInsets = parent.getInsets();
int parentAvailableWidth = parent.getWidth() - parentInsets.left - parentInsets.right;
int parentAvailableHeight = parent.getHeight() - parentInsets.top - parentInsets.bottom;
// Component at top is sized at container width and at its preferred height
boolean topComponentUsed = this.topComponent != null && this.topComponent.getParent() != null;
if (topComponentUsed) {
this.topComponent.setBounds(parentInsets.left, parentInsets.top,
parentAvailableWidth,
Math.min(this.topComponent.getPreferredSize().height, parentAvailableHeight));
}
// Component is centered in the rest of the space and sized proportionally to its preferred size
if (this.bottomComponent != null && this.bottomComponent.getParent() != null) {
Dimension bottomComponentPreferredSize = this.bottomComponent.getPreferredSize();
int bottomComponentHeight = parentAvailableHeight;
int bottomComponentY = parentInsets.top;
if (topComponentUsed) {
int occupiedHeight = this.topComponent.getHeight() + this.gap;
bottomComponentHeight -= occupiedHeight;
bottomComponentY += occupiedHeight;
}
int bottomComponentWidth = bottomComponentHeight * bottomComponentPreferredSize.width
/ bottomComponentPreferredSize.height;
int bottomComponentX = parentInsets.left;
// Adjust component width and height if it's larger than parent
if (bottomComponentWidth > parentAvailableWidth) {
bottomComponentWidth = parentAvailableWidth;
int previousHeight = bottomComponentHeight;
bottomComponentHeight = bottomComponentWidth * bottomComponentPreferredSize.height
/ bottomComponentPreferredSize.width;
bottomComponentY += (previousHeight - bottomComponentHeight) / 2;
} else {
// Center component in width
bottomComponentX += (parentAvailableWidth - bottomComponentWidth) / 2;
}
this.bottomComponent.setBounds(bottomComponentX, bottomComponentY,
bottomComponentWidth, bottomComponentHeight);
}
}
/**
* Returns the largest minimum width of the components managed by this layout manager,
* and the sum of their minimum heights.
*/
public Dimension minimumLayoutSize(Container parent) {
Insets parentInsets = parent.getInsets();
int minWidth = 0;
int minHeight = 0;
boolean topComponentUsed = this.topComponent != null && this.topComponent.getParent() != null;
if (topComponentUsed) {
Dimension topComponentMinSize = this.topComponent.getMinimumSize();
minWidth = Math.max(minWidth, topComponentMinSize.width);
minHeight = topComponentMinSize.height;
}
if (this.bottomComponent != null && this.bottomComponent.getParent() != null) {
Dimension bottomComponentMinSize = this.bottomComponent.getMinimumSize();
minWidth = Math.max(minWidth, bottomComponentMinSize.width);
minHeight += bottomComponentMinSize.height;
if (topComponentUsed) {
minHeight += this.gap;
}
}
return new Dimension(minWidth + parentInsets.left + parentInsets.right,
minHeight + parentInsets.top + parentInsets.bottom);
}
/**
* Returns the largest maximum width of the components managed by this layout manager,
* and the sum of their maximum heights.
*/
public Dimension maximumLayoutSize(Container parent) {
Insets parentInsets = parent.getInsets();
int maxWidth = 0;
int maxHeight = 0;
boolean topComponentUsed = this.topComponent != null && this.topComponent.getParent() != null;
if (topComponentUsed) {
Dimension topComponentMaxSize = this.topComponent.getMaximumSize();
maxWidth = Math.max(maxWidth, topComponentMaxSize.width);
maxHeight = topComponentMaxSize.height;
}
if (this.bottomComponent != null && this.bottomComponent.getParent() != null) {
Dimension bottomComponentMaxSize = this.bottomComponent.getMaximumSize();
maxWidth = Math.max(maxWidth, bottomComponentMaxSize.width);
maxHeight += bottomComponentMaxSize.height;
if (topComponentUsed) {
maxHeight += this.gap;
}
}
return new Dimension(maxWidth + parentInsets.left + parentInsets.right,
maxHeight + parentInsets.top + parentInsets.bottom);
}
/**
* Returns the largest preferred width of the components managed by this layout manager,
* and the sum of their preferred heights.
*/
public Dimension preferredLayoutSize(Container parent) {
Insets parentInsets = parent.getInsets();
int preferredWidth = 0;
int preferredHeight = 0;
boolean topComponentUsed = this.topComponent != null && this.topComponent.getParent() != null;
if (topComponentUsed) {
Dimension topComponentPreferredSize = this.topComponent.getPreferredSize();
preferredWidth = Math.max(preferredWidth, topComponentPreferredSize.width);
preferredHeight = topComponentPreferredSize.height;
}
if (this.bottomComponent != null && this.bottomComponent.getParent() != null) {
Dimension bottomComponentPreferredSize = this.bottomComponent.getPreferredSize();
preferredWidth = Math.max(preferredWidth, bottomComponentPreferredSize.width);
preferredHeight += bottomComponentPreferredSize.height;
if (topComponentUsed) {
preferredHeight += this.gap;
}
}
return new Dimension(preferredWidth + parentInsets.left + parentInsets.right,
preferredHeight + parentInsets.top + parentInsets.bottom);
}
}