package net.sf.colossus.gui; import java.awt.Color; import java.awt.Component; import java.awt.Container; import java.awt.Dimension; import java.awt.Insets; import java.awt.LayoutManager; import java.awt.Rectangle; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; import javax.swing.BorderFactory; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JPanel; import net.sf.colossus.guiutil.INonRecticleJComponent; /** * The <code>TrueHexGridLayout</code> class is a layout manager that * lays out a container's components in a grid with alternate rows * offset by a partial grid component. The vertical gap is automatically * sized so that the Hex components can draw the non base-rectangle portions * of the hexes in the gap. This results in a hexagonal tesselation. * <p> * When constructed, the layout can start with an indented row or not. * The sizing can be done Isometrically or not. * * TODO: implement the containers <code>ComponentOrientation</code> property * * If either the Number of Rows or the Number of Columns is set to Zero either * by constructor or the set rows or set columns method, then the grid will be * assumed to be square and layed out as such. This class is used for battle * Hexes. * * @author Edward Dranathi based loosely on Sun's GridLayout class. * */ public class TrueHexGridLayout implements LayoutManager { int rows; int cols; boolean indentOddRows; private final boolean isometricShape; public TrueHexGridLayout(int pRows, int pColumns, boolean pIndentFirstRow, boolean pIsometricShape) { rows = pRows; cols = pColumns; indentOddRows = pIndentFirstRow; isometricShape = pIsometricShape; } public TrueHexGridLayout(int pRows, int pColumns, boolean pIndentFirstRow) { this(pRows, pColumns, pIndentFirstRow, true); } public void addLayoutComponent(String name, Component comp) { // Nothing to see here. } public void removeLayoutComponent(Component comp) { // TODO Auto-generated method stub } /** * Lays out the specified container using this layout. * <p> * This method resizes the components in the specified target * container in order to satisfy the constraints of the * <code>BattleHexGridLayout</code> object. * <p> * This layout manager determines the size of individual * components by dividing the free space in the container into * equal-sized portions according to the number of rows and columns * in the layout. The container's free space equals the container's * size minus any insets and vertical gap needed. * All components in the layout are given the same size. * * @param parent the container in which to do the layout * @see java.awt.Container * @see java.awt.Container#doLayout */ public void layoutContainer(Container parent) { synchronized (parent.getTreeLock()) { Insets insets = parent.getInsets(); int ncomponents = parent.getComponentCount(); int nrows = rows; int ncols = cols; if (ncomponents == 0) { return; } if (ncols > 0) { nrows = (ncomponents + ncols - 1) / ncols; } else { ncols = (ncomponents + nrows - 1) / nrows; } int containerWidth = parent.getWidth() - (insets.left + insets.right); int containerHeight = parent.getHeight() - (insets.top + insets.bottom); if (isometricShape) { if (containerHeight > containerWidth) { containerHeight = containerWidth; } else { containerWidth = containerHeight; } } int compWidth = (containerWidth * 2) / (ncols * 2 + 1); int rowIndent = compWidth / 2; int vgap = containerHeight / (nrows * 5 + 1); int compHeight = vgap * 4; if (isometricShape) { if (compWidth > compHeight) { compWidth = compHeight; } else { compHeight = compWidth; } vgap = compHeight / 4; } /* layout left to right one row at a time, top to bottom */ int y = insets.top + vgap; // init y for (int row = 0; row < nrows; row++) { int x = indentOddRows ^ (row % 2 == 0) ? insets.left : insets.left + rowIndent; for (int col = 0; col < ncols; col++) { int i = row * ncols + col; if (i < ncomponents) { Component c = parent.getComponent(i); if (c != null) { if (c instanceof INonRecticleJComponent) { INonRecticleJComponent cNR = (INonRecticleJComponent)c; cNR.resizeBaseRectangle(new Rectangle(x, y, compWidth, compHeight)); } else { c.setBounds(x, y, compWidth, compHeight); } } } x += compWidth; // increment X coordinate. } y += compHeight + vgap; // increment Y coordinate. } } } public Dimension minimumLayoutSize(Container parent) { return null; } public Dimension preferredLayoutSize(Container parent) { return null; } /* (non-Javadoc) * @see java.awt.LayoutManager#removeLayoutComponent(java.awt.Component) */ public static void main(String[] args) { JFrame f = new JFrame(); JPanel map = new JPanel(); map.setLayout(new TrueHexGridLayout(6, 6, true)); for (int i = 0; i < 36; i++) { JLabel x = new JLabel(); x.setBorder(BorderFactory.createLineBorder(Color.BLUE, 1)); x.setText("" + i); map.add(x); } f.setTitle("Sample True Hex Grid Layout"); f.setContentPane(map); f.setSize(408, 534); f.setVisible(true); f.addWindowListener(new WindowAdapter() { @Override public void windowClosing(WindowEvent e) { System.exit(0); } }); } }