FileDocCategorySizeDatePackage
RelativeLayout.javaAPI DocExample6013Mon May 31 22:51:38 BST 2004com.darwinsys.swingui.layout

RelativeLayout.java

package com.darwinsys.swingui.layout;

import java.awt.*;
import java.util.*;

/** 
 * <p>
 * RelativeLayout, a Relative Layout Manager for Java J2SE.
 * Mainly for porting tired old code that uses x,y locations.
 * You really can't just assign x,y locations to components 
 * in Java Java J2SE - it breaks badly when the user resizes (and you can
 * <em>not</em> mandate that the user can't resize you -- see any book
 * on UI design for <em>that</em> little discussion -- and can also look
 * bad due to resolution independance.  Symantec Cafe 1.x, for example,
 * used to spit out unfortunate (and unmaintainable) code like this:
 * <pre>
 *       setLayout(null);
 *       setSize(331,241);
 *       label1=new Label("Info Applet", Label.CENTER);
 *       add(label1);
 *       label1.setBounds(91,19,107,15);
 * </pre>
 * </p> <p>
 * <em>Bleaarrgghh!!!</em>
 * To make it work properly at all resolutions and survive
 * user-initiated resize actions, change it to
 * <pre>
 *	setLayout(new RelativeLayout(331,241,false);
 *	label1=new Label("Info Applet", Label.CENTER);
 *	add("91,19", label1);
 * </pre>
 * Note that it's actually <EM>less</EM> work to get it right.
 * Symantec, Microsoft, and others, please take note!</p>
 * @author Ian Darwin, http://www.darwinsys.com/
 */

public class RelativeLayout implements LayoutManager {
	/** requested absolute width of canvas */
	protected int reqWid;
	/** requested absolute height of canvas */
	protected int reqHgt;

	/** actual size width when laid out */
	protected int curWid;
	/** actual size height when laid out */
	protected int curHgt;

	/** to track Components added by named add form. */
	protected Vector curComps = new Vector();

	/**
	 * Constructs an RelativeLayout, given original hard-coded size of panel.
	 */
	public RelativeLayout(int wid, int ht) {
		this.reqWid = wid;
		this.reqHgt = ht;
	}

	/**
	 * Called by AWT when the user uses the form add(name, Component).
	 * Adds the specified component with the specified name to the layout. 
	 *
	 * @param	name	String with location for component c
	 * <EM>Note</EM>: the "name" <EM>must</EM> contain x, y location, ie.,
	 * <BR>add("" + 320 + "," + 100, new Button("Quit"));
	 * <BR>or
	 * <BR>add("320,100", new Button("Quit").
	 * <BR>This adds the Button at x=320, y=100 when the Panel is 
	 * at its original full size.
	 * @param	c	Component to be added.
	 */
	public void addLayoutComponent(String name, Component c) {
		int x, y;
		StringTokenizer st = new StringTokenizer(name, ",");
		x = Integer.parseInt(st.nextToken());
		y = Integer.parseInt(st.nextToken());
		// System.out.println("Adding: Name " + name +"; obj " + c
		//	 + "; x " + x + "; y " + y);
		Tracker t = new Tracker(x, y, c);
		curComps.addElement(t);
	}

	/**
	 * Called by AWT to lay out the components 
	 * in the target Container at its current size.
	 *
	 * @param	target	Container whose components are to be laid out.
	 */
	public void layoutContainer(Container target) {
		Dimension targSize = target.getSize();
		Insets ins = target.getInsets();
		// System.out.println("layoutContainer: size " + targSize);
		curWid = targSize.width;
		curHgt = targSize.height;
		float widRatio = (float)curWid / (float)reqWid;
		float hgtRatio = (float)curHgt / (float)reqHgt;
		for (int i = 0; i<curComps.size(); i++) {
			int px, py, pw, ph;
			Tracker t = (Tracker)curComps.elementAt(i);
			Component tc = t.getComponent();
			Dimension d = tc.getPreferredSize();
			px = ins.right+(int)(t.getRequestedLoc().x * widRatio);
			py = ins.top + (int)(t.getRequestedLoc().y * hgtRatio);
			pw = d.width;
			ph = d.height;
			// System.out.println("layoutContainer["+i+"]: move " +
			// tc + " to " + px + ", " + py);
			tc.setBounds(px, py, pw, ph);
		}
	}

	/**
	 * Called from AWT to calculate the minimum size dimensions 
	 * for the target panel given the components in it.
	 * But we use our own list of named insertions, not the
	 * list of Components that the container keeps.
	 *
	 * @param	target	Container to calculate for
	 */
	public Dimension minimumLayoutSize(Container target) {
		int minw = 0, minh = 0;
		for (int i = 0; i<curComps.size(); i++) {
			Tracker t = (Tracker)curComps.elementAt(i);
			Component tc = t.getComponent();
			Dimension d = tc.getMinimumSize();
			Point rl = t.getRequestedLoc();
			minw = Math.max(minw, rl.x +d.width);
			minh = Math.max(minh, rl.y +d.height);
			// System.out.println("minLay, minw = " + minw 
			// + "; minh = " + minh);
		}
		return new Dimension(minw, minw);
	}

	/**
	 * Called by AWT to compute the preferred size for the target panel
	 * given our list of the components that it contains.
	 *
	 * @param	target	Container to calculate for
	 */
	public Dimension preferredLayoutSize(Container target) {
		int prefw = 0, prefh = 0;
		for (int i = 0; i<curComps.size(); i++) {
			Tracker t = (Tracker)curComps.elementAt(i);
			Component tc = t.getComponent();
			Dimension d = tc.getMinimumSize();
			Point rl = t.getRequestedLoc();
			prefw = Math.max(prefw, rl.x+d.width);
			prefh = Math.max(prefh, rl.y+d.height);
			// System.out.println("prefLay, prefw = " + 
			// prefw + "; prefh = " + prefh);
		}
		return new Dimension(prefw, prefh);
	}

	/**
	 * Called by AWT to remove a given component from the layout. 
	 *
	 * @param	c	Component to be removed
	 */
	public void  removeLayoutComponent(Component c) {
		curComps.removeElement(c);
	}

	/**
	 * Tracker is a class used only by RelativeLayout,
	 * to track the original "requested" (hard-coded) x,y locations of
	 * each Component.
	 */
	class Tracker {
		int absx, absy;
		Component theComp;

		/** Construct a tracker item given its location and Component. */
		Tracker(int x, int y, Component c) {
			this.absx = x;
			this.absy = y;
			this.theComp = c;
		}

		/** Extract the location as a Point. */
		public Point getRequestedLoc() {
			return new Point(absx, absy);
		}

		/** Extract the Component from this Tracker. */
		public Component getComponent() {
			return theComp;
		}
	}
}