/*
    HtmlLayout - A superior Java LayoutManager
    Copyright (C) 1998  Paul Buchheit

    HtmlLayout is free software; you can redistribute it and/or
    modify it under the terms of the GNU Library General Public
    License as published by the Free Software Foundation; either
    version 2 of the License, or (at your option) any later version.

    HtmlLayout 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
    Library General Public License for more details.

    You should have received a copy of the GNU Library General Public
    License along with this library; if not, write to the Free
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

    HtmlLayout was created by Paul Buchheit, paul@braindamage.org
    HtmlLayout lives at http://braindamage.org/HtmlLayout
    There you can find the latest information and software.
*/


package htmllayout;

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


/** 
	* HtmlLayout uses a string containing HTML like table tags to
	* layout components.
	* 
Ex: *
	* <table rows=2 cols=2>
	*  <tr>
	*   <td> A label
	*   <td component=someName>
	*  <tr>
	*   <td component=anotherName vert=max>
	*   <td component="some other name" horz=max>
	* 
*

Tags and their options

*

Table

* * *

TR

* * *

TD

* * * @author Paul Buchheit */ public class HtmlLayout implements LayoutManager { static final String anonLabelName = new String("Anonymous label"); boolean labelsAdded; // x in xLayoutSize() static final int MIN = 0, PREF = 1; // alignment static final int LEFT = 0, RIGHT = 1, CENTER = 2, FIT=3, MAX = 4, TOP = 5, BOTTOM = 6; static final String ALIGNNAMES[] = { "LEFT", "RIGHT", "CENTER", "FIT", "MAX", "TOP", "BOTTOM"}; /* these tables are shared by the layout and all nested layouts */ Hashtable nameToCell; Hashtable compToCell; /* this belongs only to this layout */ Cell cells[], cellsColFirst[]; int rows, cols; int horzAlign = MAX, vertAlign = MAX; Dimension prefDim = new Dimension(-1, -1); /** Creates an HtmlLayout with the specified "HTML" string. @param html The "HTML" which specifies the layout, if this is not valid table-html a BadTableHtmlException will be thrown. Html can be tested ahead of time using HtmlLayoutTest. @see htmllayout.BadTableHtmlException @see htmllayout.HtmlLayoutTest */ public HtmlLayout(String html) { nameToCell = new Hashtable(30); compToCell = new Hashtable(30); parse(new Scanner(html), true, null); addCellsToTable(nameToCell); } HtmlLayout(TableParser parent) { parse(parent.in, false, parent); } /** If this layout includes any automatic lables (text within a TD) * this will add them to container parent. * It is not always necessary to call this, if you don't it will * automatically be called when the container is first sized or * layed out. Ordinarily this is ok but not always, such as when * using CardLayout. Calling addLabels manually at the same time * as the other components are added will never be a problem. */ public void addLabels(Container parent) { for(int i = 0; i < cells.length; i++) cells[i].addLabels(parent); labelsAdded = true; } /** * @see java.awt.LayoutManager#addLayoutComponent */ public void addLayoutComponent(String name, Component comp) { prefDim.width = -1; if(name == anonLabelName) return; if(name == null) throw new IllegalArgumentException("null component name"); Cell c = (Cell)nameToCell.get(name); if(c == null) throw new IllegalArgumentException( "Cannot add to layout: unknown component name " + name); compToCell.put(comp, c); c.comp = comp; } /** * @see java.awt.LayoutManager#removeLayoutComponent */ public void removeLayoutComponent(Component comp) { prefDim.width = -1; Cell c = (Cell)compToCell.remove(comp); if(c != null) c.comp = null; } /** * @see java.awt.LayoutManager#preferredLayoutSize */ public Dimension preferredLayoutSize(Container parent) { return layoutSize(parent, PREF); } /** * @see java.awt.LayoutManager#minimumLayoutSize */ public Dimension minimumLayoutSize(Container parent) { return layoutSize(parent, MIN); } void parse(Scanner s, boolean eatTable, TableParser parent) { TableParser tp = new TableParser(s, eatTable, parent); rows = tp.rows; cols = tp.cols; horzAlign = tp.horz; vertAlign = tp.vert; cells = new Cell[tp.cellCount]; int cpos = 0; cellsColFirst = new Cell[tp.cellCount]; int ccfpos = 0; for(int i = 0; i < rows * cols; i++) { if(tp.cells[i / cols][i % cols] != null) { cells[cpos] = tp.cells[i / cols][i % cols]; cpos++; } if(tp.cells[i % rows][i / rows] != null) { cellsColFirst[ccfpos] = tp.cells[i % rows][i / rows]; ccfpos++; } } } Dimension layoutSize(Container parent, int whichSize) { if(!labelsAdded) addLabels(parent); return layoutSize(parent.getInsets(), whichSize); } Dimension layoutSize(Insets insets, int whichSize) { int ypos[] = new int[rows + 1]; int xpos[] = new int[cols + 1]; for(int i = 0; i < cells.length; i++) { cells[i].updateSize(whichSize); } for(int i = 0; i < cells.length; i++) { cells[i].addToYTable(ypos); cellsColFirst[i].addToXTable(xpos); } Dimension d = new Dimension(xpos[cols], ypos[rows]); prefDim.width = d.width; prefDim.height = d.height; d.width += insets.left + insets.right; d.height += insets.top + insets.bottom; return d; } /** * @see java.awt.LayoutManager#layoutContainer */ public void layoutContainer(Container parent) { if(!labelsAdded) addLabels(parent); Insets insets = parent.getInsets(); Dimension d = parent.getSize(); int top = insets.top; int bottom = d.height - insets.bottom; int left = insets.left; int right = d.width - insets.right; preferredLayoutSize(parent); // will set prefDim if(right - left < prefDim.width || bottom - top < prefDim.height) { minimumLayoutSize(parent); // will set prefDim to minSize } layout(top, bottom, left, right); } void layout(int top, int bottom, int left, int right) { if(right - left > prefDim.width) { if(horzAlign != MAX && horzAlign != FIT) { left = calcTopOrLeft(left, right, prefDim.width, horzAlign); right = left + prefDim.width; } } if(bottom - top > prefDim.height) { if(vertAlign != MAX && vertAlign != FIT) { top = calcTopOrLeft(top, bottom, prefDim.height, vertAlign); bottom = top + prefDim.height; } } doLayout(top, bottom, left, right); } private void doLayout(int top, int bottom, int left, int right) { int ypos[] = new int[rows + 1]; int xpos[] = new int[cols + 1]; layoutDim(xpos, left, right, true); layoutDim(ypos, top, bottom, false); for(int i = 0; i < cells.length; i++) cells[i].finalLayout(xpos, ypos); } private void layoutDim(int pos[], int start, int end, boolean isX) { pos[0] = start; boolean want[] = new boolean[pos.length - 1]; for(int i = 0; i < cells.length; i++) { if(isX) { cellsColFirst[i].firstXLayout(pos, want); } else { cells[i].firstYLayout(pos, want); } } int diff = end - pos[want.length]; if(diff < 0) { squeeze(-diff, pos, isX); } else if(diff > 0) { grow(diff, pos, want); } pos[pos.length - 1] = end; } // squeeze the components to make this 'pixels' pixels smaller private void squeeze(int pixels, int vals[], boolean isX) { int touch[][] = new int[vals.length][isX ? rows : cols]; int limit[] = new int[vals.length]; int downto = vals[vals.length - 1] - pixels; // less than 2 is dumb and I think bad things could happen if(downto < 2) return; while(vals[vals.length - 1] > downto) { int count[] = new int[vals.length]; for(int i = 0; i < limit.length; i++) limit[i] = Integer.MAX_VALUE; for(int i = 0; i < cells.length; i++) { if(isX) { cellsColFirst[i].squeezeX(vals, touch, count, limit); } else { cells[i].squeezeY(vals, touch, count, limit); } } boolean scale[] = new boolean[vals.length]; int max = maxSqueeze( vals.length - 1, touch, count, limit, vals, scale); int amount = Math.min(pixels, max); int width = vals[vals.length - 1] - vals[0]; int slide = 0; for(int i = 0; i < vals.length; i++) { if(scale[i]) { slide = amount * (vals[i] - vals[0]) / width; } vals[i] -= slide; } pixels -= slide; } } private int maxSqueeze(int rule, int touch[][], int count[], int limit[], int vals[], boolean scale[]) { // this rule will be scaled, not slidden scale[rule] = true; // how much can we go before more parts touch our left side? int max = limit[rule]; for(int i = 0; i < count[rule]; i++) { int othermax = maxSqueeze( touch[rule][i], touch, count, limit, vals, scale); if(othermax < max) max = othermax; } return max; } private void grow(int pixels, int vals[], boolean where[]) { int count = 0; for(int i = 0; i < where.length; i++) { if(where[i]) count++; } boolean all = count == 0; if(all) count = where.length; double dif = (pixels - 0.001) / count; double a = 0.0; for(int i = 0; i < where.length; i++) { if(all || where[i]) a += dif; vals[i + 1] += (int)a; } } static int calcTopOrLeft(int tol, int bor, int wid, int align) { int newTol; switch(align) { case LEFT: case TOP: newTol = tol; break; case RIGHT: case BOTTOM: newTol = bor - wid; break; case CENTER: newTol = tol + (bor - tol - wid) / 2; break; default: throw new Error("illegal align value"); } return newTol; } void addCellsToTable(Hashtable nToCell) { for(int i = 0; i < cells.length; i++) { cells[i].addToNameTable(nToCell); } } private String descString() { return " dim = " + cols + ", " + rows + " fill = " + horzAlign + ", " + vertAlign; } void dump(int space) { for(int i = 0; i < space; i++) System.err.print(' '); System.err.println("HtmlLayout" + descString()); for(int i = 0; i < cells.length; i++) cells[i].dump(space + 2); } public String toString() { String s = "[HtmlLayout" + descString() + " cells ="; for(int i = 0; i < cells.length; i++) s += " " + cells[i]; return s + "]"; } }

Ads help maintain this website.