/*	Copyright (C) 2000, 2001, Geert Vernaeve. All Rights Reserved.	*/
package gve.calc.formula;

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

// PENDING: maybe also store baselines of components in a HashTable?
// PENDING: the hashtable never forgets added components, even if they are
//	removed forever.

public class TabularLayout implements LayoutManager2 {
	int [] colWidth;
	int [] rowHeight;
	int [] rowBaseLine;
	int rows,cols;
	private Container target;
	private Hashtable preferredSizes = new Hashtable();

	
Pixels of space between rows
public static int VSPACE = 5;
Pixels of space between columns
public int HSPACE = 5; public final static byte LEFT = 0; public final static byte CENTER = 1; public final static byte RIGHT = 2;
HGAP pixels to the left and right of the matrix are used for painting the delimiters
private int hgap = 0,vgap = 0; public TabularLayout(int rows,int cols) { this.rows = rows; this.cols = cols; } public void setRows(int rows) { this.rows = rows; forgetData(); } public void setCols(int cols) { this.cols = cols; forgetData(); } private void forgetData() { colWidth = null; rowHeight = null; rowBaseLine = null; } public int getRowHeight(int row) { return getRowHeight(target,row); } public int getRowHeight(Container target,int row) { this.target = target; collectData(); return rowHeight[row]; } public void setHgap(int hgap) { this.hgap = hgap; } public void setVgap(int vgap) { this.vgap = vgap; } public int getHgap() { return hgap; } public int getVgap() { return vgap; }
Create a new int array filled with -1
private static int [] newarray(int size) { int [] result = new int[size]; for (int i = size-1; i>=0; i--) result[i] = -1; return result; } public void addLayoutComponent(String name,Component comp) {} public void addLayoutComponent(Component comp,Object constraints) { forgetData(); } public byte getColAlignment(int j) { return CENTER; } public void removeLayoutComponent(Component comp) { forgetData(); } int sumArray(int [] arr) { int result = 0; for (int i = arr.length-1; i>=0; i--) result += arr[i]; return result; } public Dimension preferredLayoutSize(Container target) { this.target = target; collectData(); int extrawidth = 0; if (target instanceof TabularView && ((TabularView)target).getCursorPos()!=0) extrawidth = target.getFontMetrics(target.getFont()).charWidth(' '); return new Dimension(extrawidth + HSPACE*(cols-1) + hgap*2 + sumArray(colWidth), VSPACE*(rows-1) + vgap*2 + sumArray(rowHeight)); } public Dimension minimumLayoutSize(Container target) { return preferredLayoutSize(target); } public Dimension maximumLayoutSize(Container target) { this.target = target; return new Dimension(Short.MAX_VALUE,Short.MAX_VALUE); } public float getLayoutAlignmentX(Container target) { return 0.5f; } public float getLayoutAlignmentY(Container target) { return 0.5f; } public void invalidateLayout(Container target) { this.target = target; forgetData(); } public void layoutContainer(Container target) { this.target = target; for (int i = target.getComponentCount()-1; i>=0; i--) { Component comp = target.getComponent(i); if (!(comp.isValid())) { preferredSizes.put(comp,comp.getPreferredSize()); comp.validate(); } } collectData(); int xleft = hgap; if (target instanceof TabularView && ((TabularView)target).getCursorPos()==HasCursorPos.Somewhere_LEFT) xleft += target.getFontMetrics(target.getFont()).charWidth(' '); // layout the components int ypos = vgap; for (int i = 0; i < rows; i++) { int xpos = xleft; for (int j = 0; j < cols; j++) { Component comp = target.getComponent(j + i*cols); Dimension siz = getPreferredSize(comp); int bas; if (comp instanceof HasBaseline) bas = ((HasBaseline)comp).getBaseline(); else bas = siz.height / 2; int xdispl = 0; switch (getColAlignment(j)) { case LEFT: break; case CENTER: xdispl += (colWidth[j] - siz.width) / 2; break; case RIGHT: xdispl += colWidth[j] - siz.width; break; } comp.setBounds(xpos + xdispl,ypos + rowBaseLine[i] - bas,siz.width,siz.height); xpos += colWidth[j] + HSPACE; } ypos += rowHeight[i] + VSPACE; } } int pixel2colBetween(int xpixel) { int xscan = hgap; collectData(); for (int j = 0; j < colWidth.length; j++) { if (xpixel < xscan+colWidth[j]/2) { return j; } xscan += colWidth[j]; if (j+1 < colWidth.length) xscan += HSPACE; } return colWidth.length; }
Result is always a valid row index
public int pixel2rowAt(int ypixel) { int yscan = vgap; collectData(); for (int i = 0; i < rowHeight.length; i++) { if (ypixel < yscan+rowHeight[i]) return i; yscan += rowHeight[i]; if (i+1 < rowHeight.length) yscan += VSPACE; } return rowHeight.length - 1; }
If the cursor is to the left of part 0, return 0. If the cursor is between part 0 and 1, return 1 etc. Result is a valid row index OR one more
int pixel2rowBetween(int ypixel) { int yscan = vgap; collectData(); for (int i = 0; i < rowHeight.length; i++) { if (ypixel < yscan+rowHeight[i]/2) return i; yscan += rowHeight[i]; if (i+1 < rowHeight.length) yscan += VSPACE; } return rowHeight.length; }
Does comp.getPreferredSize() but uses an internal buffer
private Dimension getPreferredSize(Component comp) { Dimension siz = null; if (comp.isValid()) siz = (Dimension)preferredSizes.get(comp); if (siz == null) { siz = comp.getPreferredSize(); preferredSizes.put(comp,siz); } return siz; }
Fill colWidth[], rowHeight[], rowBaseLine[]
void collectData() { if (colWidth!=null && rowHeight!=null && rowBaseLine!=null) // Every time the data is invalid, forgetData() has been called. // So we are sure that if the three pointers are non-null, they contain // valid data. // This little check makes table editing a *lot* faster! return; //long start=System.currentTimeMillis(); if (colWidth == null) colWidth = newarray(cols); if (rowHeight == null) rowHeight = newarray(rows); if (rowBaseLine == null) rowBaseLine = newarray(rows); for (int i = 0; i < rows; i++) { int rowbase = 0; int rowdescent = 0; for (int j = 0; j < cols; j++) { Component comp = target.getComponent(j + i*cols); Dimension siz = getPreferredSize(comp); int bas; if (comp instanceof HasBaseline) bas = ((HasBaseline)comp).getBaseline(); else bas = siz.height / 2; if (bas > rowbase) rowbase = bas; if (siz.height-bas > rowdescent) rowdescent = siz.height-bas; if (siz.width > colWidth[j]) colWidth[j] = siz.width; } rowHeight[i] = rowbase + rowdescent; rowBaseLine[i] = rowbase; } //long time=System.currentTimeMillis()-start; //System.out.println("TabularLayout.collectData(): spent "+time+" ("+cols+"x"+rows+")"); //try {throw new Error();} catch (Error e) { e.printStackTrace();} } }