/*
* Copyright (C) 2011 Peransin Nicolas.
* Use is subject to license terms.
*/
package org.mypsycho.swing.layout;
import java.awt.Component;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.LayoutManager;
import java.awt.LayoutManager2;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.swing.JComponent;
/**
* Layout similaire au null layout mais permettant de definir une taille de
* reference qui sera la taille preferee et de faire zoomer les composants.
* Les composant doivent etre ajoutes avec une ZoomConstraint.
*
* Si une dimension de reference est negative, un calcul est effectue de
* maniere a ce que le layout inclut tous les composants sur cet axe.
*
* Si ZoomConstaint est null, le composant utilise tout l'espace possible
*
* Si une contrainte de hauteur ou largeur est negative, la dimension
* prefere du composant associe est utilise
*
* @author Peransin Nicolas
*/
public class ZoomLayout implements LayoutManager2, Serializable {
/**
* Generated serialized version
*/
private static final long serialVersionUID = 9007898665269952279L;
protected int width = ZoomConstraint.NOT_DEFINED; // reference size for container
protected int height = ZoomConstraint.NOT_DEFINED;
protected List<Component> allComponents = new ArrayList<Component>(); // to keep the order
protected Map<Component, ZoomConstraint> allConstraints = new HashMap<Component, ZoomConstraint>();
public ZoomLayout() {}
public ZoomLayout(int w, int h) {
width = w;
height = h;
}
//
// Getters and Setters
//
public int getWidth() { return width; }
public void setWidth(int w) { width = w; }
public int getHeight() { return height; }
public void setHeight(int h) { height = h; }
public ZoomConstraint getConstraint(Component comp) {
return allConstraints.get(comp);
}
public void setConstraint(Component comp, ZoomConstraint cst) {
if (allComponents.contains(comp)) {
allConstraints.put(comp, cst);
}
}
protected Container container = null;
//
// Layout Core
//
public void addLayoutComponent(Component comp, Object constraints) {
if ( (constraints == null) || (constraints instanceof ZoomConstraint)) {
if (allComponents.contains(comp)) {
allComponents.remove(comp);
}
allComponents.add(comp);
allConstraints.put(comp, (ZoomConstraint) constraints);
} else {
throw new IllegalArgumentException(getClass() + " can only handle ZoomConstraint objects");
}
}
public void addLayoutComponent(String name, Component comp) { }
public Dimension preferredLayoutSize(Container parent) {
container = parent;
synchronized (parent.getTreeLock()) {
if ( (width == ZoomConstraint.NOT_DEFINED) || (height == ZoomConstraint.NOT_DEFINED)) {
int w = width;
int h = height;
/* Ascending computation */
Container granParent = container.getParent();
if (granParent != null) {
LayoutManager granLayout = granParent.getLayout();
if ( (granLayout != null) && (granLayout instanceof ZoomLayout)) {
ZoomLayout upperLayout = (ZoomLayout) granLayout;
ZoomConstraint cst = upperLayout.allConstraints.get(parent);
if (cst != null) {
if ( (width == ZoomConstraint.NOT_DEFINED) && (cst.bounds.width != ZoomConstraint.NOT_DEFINED)) {
w = cst.bounds.width;
}
if ( (height == ZoomConstraint.NOT_DEFINED) && (cst.bounds.height != ZoomConstraint.NOT_DEFINED)) {
h = cst.bounds.height;
}
}
}
}
/* Descending computation */
if ( (w == ZoomConstraint.NOT_DEFINED) || (h == ZoomConstraint.NOT_DEFINED)) {
int w2 = w;
int h2 = h;
int nbComps = allComponents.size();
for (int indComp = 0; indComp < nbComps; indComp++) {
Component comp = allComponents.get(indComp);
ZoomConstraint cst = allConstraints.get(comp);
if (cst != null) {
if (w < 0) {
if (cst.bounds.width == ZoomConstraint.NOT_DEFINED) {
w2 = Math.max(w2, cst.bounds.x + comp.getPreferredSize().width);
} else {
w2 = Math.max(w2, cst.bounds.x + cst.bounds.width);
}
}
if (h < 0) {
if (cst.bounds.height == ZoomConstraint.NOT_DEFINED) {
h2 = Math.max(h2, cst.bounds.y + comp.getPreferredSize().height);
} else {
h2 = Math.max(h2, cst.bounds.y + cst.bounds.height);
}
}
}
}
w = w2;
h = h2;
}
return new Dimension(w, h);
} else {
return new Dimension(width, height);
}
}
}
public void layoutContainer(Container parent) {
container = parent;
synchronized (parent.getTreeLock()) {
Dimension dRef = preferredLayoutSize(parent);
Dimension dNow = parent.getSize();
float zX = (dRef.width<=0) ? 1.0f : (float) dNow.width/dRef.width;
float zY = (dRef.height<=0) ? 1.0f : (float) dNow.height/dRef.height;
zoomComponents(parent, 0, 0, zX, zY, Math.min(zX, zY));
}
}
protected void zoomComponents(Container parent, int offsetX, int offsetY,
float zX, float zY, float zF) {
Dimension dNow = parent.getSize();
int nbComps = allComponents.size();
for (int indComp = 0; indComp < nbComps; indComp++) {
Component comp = allComponents.get(indComp);
ZoomConstraint cst = allConstraints.get(comp);
// Zoom axis
if ((cst == null) || (cst.bounds == null)) {
comp.setBounds(0, 0, dNow.width, dNow.height);
} else {
int x = Math.round(cst.bounds.x*zX);
int y = Math.round(cst.bounds.y*zY);
int w = cst.bounds.width;
int h = cst.bounds.height;
if (w == ZoomConstraint.NOT_DEFINED) {
w = Math.round(comp.getPreferredSize().width*zX);
} else if (w < 0) {
x = Math.round((cst.bounds.x+w)*zX);
w = -Math.round(w*zX);
} else {
w = Math.round(w*zX);
}
if (h == ZoomConstraint.NOT_DEFINED) {
h = Math.round(comp.getPreferredSize().height*zY);
} else if (h < 0) {
y = Math.round((cst.bounds.y+h)*zY);
h = -Math.round(h*zY);
} else {
h = Math.round(h*zY);
}
comp.setBounds(x, y, w, h);
}
// Zoom fonts
if ((cst != null) && (cst.getFont() != -1)) {
Font f = comp.getFont();
comp.setFont(f.deriveFont(Math.round(cst.getFont() * zF)));
}
if (comp instanceof Zoomable) {
((Zoomable) comp).zoom(zX, zY);
}
if (comp instanceof JComponent) {
((JComponent) comp).revalidate();
}
}
}
public void removeLayoutComponent(Component comp) {
allComponents.remove(comp);
allConstraints.remove(comp);
}
//
// Generic behaviour
//
public Dimension minimumLayoutSize(Container parent) {
container = parent;
return preferredLayoutSize(parent);
}
public Dimension maximumLayoutSize(Container target) {
return new Dimension(Integer.MAX_VALUE, Integer.MAX_VALUE);
}
public float getLayoutAlignmentX(Container target) { return 0.5f; }
public float getLayoutAlignmentY(Container target) { return 0.5f; }
public void invalidateLayout(Container target) { }
@Override
public String toString() {
if ((container != null) && (container.getLayout() == this)) {
Dimension dRef = preferredLayoutSize(container);
Dimension dNow = container.getSize();
float zX = (dRef.width <= 0) ? 1.0f : (float) dNow.width / dRef.width;
float zY = (dRef.height <= 0) ? 1.0f : (float) dNow.height / dRef.height;
float z = Math.min(zX, zY);
return ("ZoomLayout Ref=" + dRef + "; Real=" + dNow +
"; Zoom=[" + zX + ", " + zY + "] ("+ z +")");
} else {
container = null;
return ("ZoomLayout Ref=[" + ", " + "]; no container");
}
}
/* Serialization for beans */
private void writeObject(ObjectOutputStream oos) throws IOException {
oos.defaultWriteObject();
}
private void readObject(ObjectInputStream ois) throws ClassNotFoundException, IOException {
ois.defaultReadObject();
}
} // endClass ZoomLayout