001/*
002 * Grid.java
003 */
004package minesweeper;
005
006import java.awt.*;
007//import java.awt.event.*;
008import javax.swing.*;
009
010import org.apache.logging.log4j.*;
011
012/**
013 * DESCRIBE {@link Grid} HERE.
014 *
015 * @author 2017-2018 APCS
016 * @author ADD @author TAG FOR EVERYONE WHO CONTRIBUTED TO THIS FILE
017 * @author <a href="https://github.com/wps-dpetty">David C. Petty</a>
018 */
019public class Grid extends JPanel {
020
021    //////////////////////////////// FIELDS ////////////////////////////////
022
023    /**
024     * log4j {@link Logger}.
025     */
026    private static Logger logger = LogManager.getLogger(Minesweeper.SHORT);
027    /**
028     * Constant minimum size of square buttons.
029     */
030    public static final int MINIMUM_SIDE = 20;
031    /**
032     * Default number of rows in grid.
033     */
034    public static final int DEFAULT_ROWS = 16;
035    /**
036     * Default number of columns in grid.
037     */
038    public static final int DEFAULT_COLS = 30;
039
040    /**
041     * Actual number of rows in grid.
042     */
043    private int rows;
044    /**
045     * Actual number of columns in grid.
046     */
047    private int cols;
048    /**
049     * Actual size of square buttons.
050     */
051    private int side;
052
053    ///////////////////////////// CONSTRUCTORS /////////////////////////////
054
055    /**
056     * Create {@link Grid} of square {@link Button}s.
057     *
058     * @param rows number of rows in grid
059     * @param cols number of columns in grid
060     * @param side minimum size of square buttons
061     */
062    public Grid(int rows, int cols, int side) {
063        super(new GridLayout(rows, cols, 0, 0));
064        this.rows = rows;
065        this.cols = cols;
066        this.side = side;
067        for (int row = 0; row < rows; row++)
068            for (int col = 0; col < cols; col++)
069                add(new Button(row, col, side));
070        //final int width = side * rows;    // no need to set preferred size
071        //final int height = side * cols;   // no need to set preferred size
072        setBackground(Color.BLACK);
073        setVisible(true);
074        logger.info(this);
075    }
076
077    /**
078     * Create {@link Grid} of square {@link Button}s with {@link #MINIMUM_SIDE}
079     * size of square buttons.
080     *
081     * @param rows number of rows in grid
082     * @param cols number of columns in grid
083     */
084    public Grid(int rows, int cols) {
085        this(rows, cols, MINIMUM_SIDE);
086    }
087
088    /**
089     * Create {@link Grid} of square {@link Button}s with {@link #DEFAULT_ROWS}
090     * number of rows, {@link #DEFAULT_COLS} number of columns, and
091     * {@link #MINIMUM_SIDE} size of square buttons.
092     */
093    public Grid() {
094        this(DEFAULT_ROWS, DEFAULT_COLS);
095    }
096
097    //////////////////////////////// METHODS ///////////////////////////////
098
099    /**
100     * Return number of rows in {@link Grid}.
101     *
102     * @return number of rows
103     */
104    public int getRows() {
105        return rows;
106    }
107
108    /**
109     * Return number of columns in {@link Grid}.
110     *
111     * @return number of columns
112     */
113    public int getCols() {
114        return cols;
115    }
116
117    /**
118     * Return side of square {@link Button}s in {@link Grid}.
119     *
120     * @return side of square {@link Button}s
121     */
122    public int getSide() {
123        return side;
124    }
125
126    /**
127     * Return {@link Button} at column row <code>row</code> and <code>col</code>.
128     *
129     * @param row row of {@link Button} to find
130     * @param col column of {@link Button} to find
131     * @return {@link Button} at row <code>row</code> and column <code>col</code>,
132     * or null if no button matches "<code>row</code>,<code>col</code>"
133     */
134    public Button findButton(int row, int col) {
135        Component[] components = getComponents();
136        for (Component component : components)
137            if (component instanceof AbstractButton) {
138                String ac = ((Button) component).getActionCommand();
139                Point point = Button.parseActionCommand(ac);
140                // Note: actionCommand is "row,col", but parsed point is (x,y).
141                if (row == point.x && col == point.y)
142                    return (Button) component;
143            }
144        return null;
145    }
146
147    /**
148     * Paint the component using a {@link Graphics} rendering object.
149     *
150     * @param g the Graphics rendering object
151     */
152    @Override
153    public void paintComponent(Graphics g) {
154        super.paintComponent(g);
155    }
156
157    /**
158     * Resizes this component so that it has width <code>width</code> and
159     * height <code>height</code>. This method changes layout-related
160     * information, and therefore, invalidates the component hierarchy.
161     *
162     * @param width  new width of this component in pixels
163     * @param height new height of this component in pixels
164     */
165    @Override
166    public void setSize(int width, int height) {
167        logger.info("{}.setSize: (from) {} (to) {}",
168                getClass().getName(), getSize(), new Dimension(width, height));
169        logger.info("({},{}) ({},{}) {} == {}?",
170                width, height, rows, cols, width * rows, height * cols);
171        assert width * rows == height * cols : "bad aspect ratio";
172        int side = Math.max(width / cols, MINIMUM_SIDE);// side = height / rows;
173        Component[] components = getComponents();
174        for (Component component : components)
175            if (component instanceof AbstractButton)
176                component.setSize(side, side);
177        validate();
178        repaint();
179    }
180
181    /**
182     * Resizes this component so that it has width <code>size.width</code> and
183     * height <code>size.height</code>. This method changes layout-related
184     * information, and therefore, invalidates the component hierarchy.
185     *
186     * @param size dimension specifying the new size of this component
187     */
188    @Override
189    public void setSize(Dimension size) {
190        // TODO: setSize(Dimension) calls setSize(int, int) else infinite recursion
191        setSize(size.width, size.height);
192    }
193
194/*
195    @Override
196    public Dimension getSize() {
197        return size;
198    }
199
200    @Override
201    public Dimension getPreferredSize() {
202        return preferredSize;
203    }
204
205    @Override
206    public Dimension getPreferredSize() {
207        return ((JFrame) SwingUtilities.getWindowAncestor(this)).getPreferredSize();
208    }
209*/
210
211    /**
212     * Return {@link String} representation of <code>this</code>.
213     *
214     * @return {@link String} representation of <code>this</code>
215     */
216    @Override
217    public String toString() {
218        return new StringBuilder()
219                .append(getClass().getName()).append(":")
220                .append(getSize()).append(":").append("[")
221                .append(rows).append(",").append(cols).append(",")
222                .append(side).append("]")
223                .toString();
224    }
225}