handout #11

CSE190L—Object Oriented Programming & Design

Programming Assignment #4

due: Wednesday, 5/2/07, 11 pm

(checkpoint due Wednesday, 4/25/07, 11 pm)

The assignment is worth a total of 40 points.  Five of these points will be given on the basis of whether or not you can complete a significant portion of this program by the checkpoint due date listed above.  Aim for completing about half of the program to get these five points.  Your checkpoint solution must compile and execute.  Obviously it will have only some of the behavior of the final version.

This assignment will give you practice with simple layout managers and a few of the more advanced user interface components described in chapter 9 of Core Java and will give you practice writing a larger chunk of code (approximately 250 lines).  You are to create an application that allows a user to manipulate a two-dimensional grid of cells.  The user will have controls to modify the number of rows, the number of columns, whether or not to display grid lines and what color to use for coloring the cells.

The initial frame should look as follows.

This frame is 500 pixels wide by 400 pixels tall with 8 rows, 10 columns, color set to red and with the grid lines option selected.  The cells should always start out white and grid lines should always be drawn in black.  As the user clicks inside of any of the cells in the grid, that cell should be colored using the current color (initially red).  If a user clicks on a rectangle that was previously colored, it should be given the current color (e.g., a rectangle previously set to green can be changed to red).  The grid lines should be visible even when rectangles have been colored.

The user should be allowed to change the current color using the buttons at the top of the frame.  At any given time, only one of the three colors should be selected (i.e., use radio buttons).  When the user changes the color, subsequent mouse clicks should turn those rectangles to the new color.  So the typical usage will be for the user to color some rectangles red, then switch to blue and color some rectangles blue, and so on.  This leads to a combination of differently colored rectangles at any given point in time.

The user should be allowed to change the color of a previously colored rectangle by clicking on it a second time with a different color selected.  The user also has the option to turn off the grid lines option, in which case no grid lines will be visible (not even white lines where the black lines were).  Notice that the grid lines element is a JCheckBox.

The user should also be allowed to change the number of rows and columns to be used.  The user should be allowed to enter the values in labeled text boxes that are each 3 columns wide.  You should use JFormattedTextField objects as described in the textbook to limit the user input to integers.  The formatted text field will handle most of the work for you, including implementing the “commit or reject” behavior that will cause it to go back to the last legal answer when the user enters something illegal.  Your code, however, should ignore any request from the user to set the rows or columns to something less than 1.  It would not be simple to get the formatted text field to reject such values, so it’s okay to simply ignore them.  You should not ask the panel to reset rows and columns until the user clicks on the “Reset Rows/Columns” button.

When rows or columns are reset, your panel should resize the rectangles in the grid but not change any of the colors.  For example, if the rectangle in row 2 and column 2 was colored red before, it should still be colored red after resizing.  If, however, the user reduces the number of rows or the number of columns, some rectangles will no longer be legal, in which case their color information should be forgotten.  For example, suppose that the rectangle in row 8 and column 8 of the original figure is colored red.  If the user then changes the number of rows to 6, that rectangle no longer exists, so its color information should be forgotten.  If the user later sets the number of rows back to 8, the rectangle at row 8 and column 8 should be white, not red.

The grid with the colored rectangles should be given all of the extra horizontal and vertical space if the frame is resized.  Each rectangle within the grid should have an integer height and an integer width.  This means that you will generally find that there is extra space because the grid size is not an exact multiple of the number of rows or columns.  Any excess space should surround the grid uniformly and should be colored black.  In other words, you should construct the largest grid possible with cells all the same size (integer height and width) and should center it horizontally and vertically within the available space, with any excess space painted black.

You should implement the grid of colored rectangles using a single JPanel.  In other words, you are to actually draw lines and rectangles rather than trying to use layout managers to do the work for you.

Here is another snapshot of the application with settings for rows and columns that leave a nontrivial amount of space surrounding the grid:

Remember that the outer black area is not part of the grid, so your program must ignore any mouse clicks on the black background.

As mentioned above, the grid lines are to be drawn.  There are some special rules you must observe in doing so.  Consider the problem of drawing the horizontal lines.  If there are supposed to be n rows, you really need to draw only (n-1) lines because the lines separate cells.  But if you draw only (n-1) lines, you will end up with some row having a different height than all of the others.  This is because lines actually take up space on the screen.  They aren’t invisible entities the way they would be in math.  To make the rows all the same size, you should instead draw n lines to form n rows.  This means that you will end up with either a line at the very top of the grid or a line at the very bottom of the grid.  It doesn’t matter whether this line is on the top or the bottom except when you are figuring out the black border that surrounds the grid.  There will be an odd/even issue for this black border.  Sometimes there is an even amount of extra vertical space total, in which case you can have the same amount above and below the grid.  Sometimes there is an odd amount of space total, in which case you have an extra pixel’s worth of space to put either above or below the grid.  To make sure that the grid is centered, you have to make sure that this extra pixel appears on the opposite side to where your extra line is drawn.  If, for example, you draw the extra line on top of the grid, then in the odd case, put the extra pixel below the grid.  That leads to a perfectly centered grid.

The previous paragraph described what to do with rows and horizontal lines.  The same rules, through symmetry, apply to columns and vertical lines: draw n of them, make sure that the extra line appears on the opposite side from where you place the extra pixel of black surrounding space in the “odd” case

When grid lines are turned off, the various cells of the grid should be completely filled.  For example, below is a grid with some of the rectangles colored and grid lines turned on.

When we turn grid lines off, we don’t want what were black lines to be replaced with white lines.  Instead, the rectangles that are drawn should fill in the areas that were previously painted black from the lines, as in the frame below.

As noted above, you should ignore mouse clicks that happen on the black border surrounding the grid.  Clicks that happen on grid lines should be considered to be in the rectangle that will be drawn on that location when grid lines are turned off.

In terms of layout, the text fields and associated button appear at the bottom of the frame using a flow layout.  At the top of the frame the check box appears in the left half centered and the color buttons appear in the right half in a flow layout.

You should again separate the program to have user interface code (controller) in a frame class and drawing code (model and view) in a panel class.  You should store the colors as a Map that associates grid locations with colors.  This will require you to define a new class for storing grid locations (row and column).  You should not use a class like Point to do this, you should define a new class (because it’s not really a Point).  Remember what we saw in chapter 5 of the textbook that to make a class like this work well with a Map that you have to override at least the equals and hashCode methods.  You also might want to override toString to make it easier to debug.  For the hashCode method, come up with a formula that spreads out the values across a wide range of integers.  For example, it would not be appropriate to just add the row and column together because that is likely to lead to lots of different locations having the same hash code (e.g., [3, 2] and [2, 3] and [1, 4] and [4, 1]).

You may use raw numbers for the original frame size and the number of columns to use for the text fields, but you should introduce constants for other arbitrary numbers.  This is a large program, so we will expect you to break it up into a logical set of methods.  You should avoid redundancy when you can.