/* dIntProg Browser. A webbrowser written in Java. * Copyright (C) 2001 Martin Geisler <[email protected]> * * 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. */ import java.awt.Graphics; import java.awt.Color; import javax.swing.*; import java.util.*; /** This class models a table. A table is a box with several * child-boxes. The child-boxes are laid out in rows and columns. You * have to call {@link #endTableRow} at the end of a row, so that the * table knows how many boxes to put into each row. */ public class Table extends AbstractFlexibleBox { private int rows = 0; private int columns = 0; private int column_widths[]; private int row_heights[]; private boolean empty_row = true; private boolean layout_error = false; /** Ends the current row. This method should be called at least * once at the end of a row, so that the table knows how many * cells to put in each row. */ public void endTableRow() { if (!empty_row) { rows = rows + 1; empty_row = true; } } public void insert(Box b) { super.insert(b); empty_row = false; } /** Makes the table calculate it's width and height. This is done * by laying out the child-boxes in rows and columns. Because of * the complicated calculations, each box will be asked to do * layout twice. * * <p>The columns will be sized according to the contents. As a * minimum, each column will be as wide as the greatest minimum * width of the boxes in that column, so that the boxes doens't * bleed into each other. If there's still room left, the rest of * the width is distributed amoung the columns, so that the * column that needs the most space gets the most space. If a * column is at it's preferred size, then it won't be resized. * * <p>The rows will be as heigh as necessary to accomodate for * the contents. * * @param g the graphics context. Some boxes, like TextFragments, * need a graphics context before they can determine their width * and height. * @param c the component into which the box will be drawn. An * ImageBox needs to know the component to calculate the width * and height. * @param w the width available for to the Box. */ public void doLayout(Graphics g, JComponent comp, int w) { /* A table without any rows is no good. And a table where rows*columns != cells doesn't work either. */ if (rows == 0) { layout_error = true; return; } else if (boxes.size() % rows != 0) { layout_error = true; return; } columns = boxes.size()/rows; row_heights = new int[rows]; column_widths = new int[columns]; Browser.debug("Table.doLayout: " + rows + " rows and " + columns + " columns: " + boxes.size() + " cells."); int c = 0; int[] pref_widths = new int[columns]; int[] min_widths = new int[columns]; height = 0; width = 0; min_width = 0; pref_width = 0; ListIterator iterator = boxes.listIterator(); while (iterator.hasNext()) { Box b = (Box)iterator.next(); /* We give the box a chance to calculate it's width and height. */ b.doLayout(g, comp, w); /* We then give each column as much space as it needs. */ if (b.getMinimumWidth() > min_widths[c]) { min_width = min_width - min_widths[c] + b.getMinimumWidth(); min_widths[c] = b.getMinimumWidth(); column_widths[c] = b.getMinimumWidth(); } /* We store the maximum preferred width of the column. */ if (b.getPreferredWidth() > pref_widths[c]) { pref_width = pref_width - pref_widths[c] + b.getPreferredWidth(); pref_widths[c] = b.getPreferredWidth(); } c = c + 1; /* End of row */ if (c == columns) { c = 0; } } if (min_width < w && pref_width != min_width) { /* We have some extra space */ int extra = w - min_width; for (int i = 0; i < columns; i = i + 1) { column_widths[i] = column_widths[i] + (pref_widths[i] - min_widths[i]) * extra / (pref_width - min_width); } width = w; } else { width = min_width; } int r = 0; iterator = boxes.listIterator(); while (iterator.hasNext()) { Box b = (Box)iterator.next(); b.doLayout(g, comp, column_widths[c]); if (b.getHeight() > row_heights[r]) { height = height - row_heights[r] + b.getHeight(); row_heights[r] = b.getHeight(); } c = c + 1; /* End of row */ if (c == columns) { c = 0; r = r + 1; } } } /** The preferred width of this box. This is the sum of the * preferred widths of the columns. The preferred width of a * column is the greatest of the preferred widths of the cells in * that column. * * <p>If the box is resized to it's preferred width, all the * cells in each row would fit on a single line. * @return the preferred width of the box. */ public int getPreferredWidth() { return pref_width; } /** The minimum width of this box. This is the sum of the minimum * widths of the columns. The minimum width of a column is the * greatest of the minimum widths of the cells in that column. * @return the preferred width of the box. */ public int getMinimumWidth() { return min_width; } public void drawAt(int x, int y, DocumentView v) { /* We can't draw the table if we couldn't do the layout. */ if (layout_error) { return; } int c = 0, x_offset = 0; int r = 0, y_offset = 0; ListIterator iterator = boxes.listIterator(); while (iterator.hasNext()) { Box b = (Box)iterator.next(); b.drawAt(x + x_offset, y + y_offset, v); x_offset = x_offset + column_widths[c]; c = c + 1; /* End of row */ if (c == columns) { x_offset = 0; c = 0; y_offset = y_offset + row_heights[r]; r = r + 1; } } } }