Description of the MiniPaint program
Design Steps
Step1: Designing the GUI
Step 2: Specifying Actions
Step 3: Defining the member data for GUI components
Step 4: Defining the Actions
Refactoring: Creating Point2DView and Xpoint2D class
This program we will introduce BooleanView and OptionsView and shows how how the text from the TextAreaView can be easily saved to a file through the use of FileView. The tutorial will also illustrate the design process, rather than commenting on a completed program.
The MiniPaint application allows the user to select one of four possible shapes to be drawn: a line, a rectangle, an oval, or a circle. The user selects the shape, the color, and then selects two points with a mouse click. Once the second point is selected, the shape is drawn. For a rectangle, the two selected points represent the opposite corners. The oval will be drawn inside the selected rectangle region. When selecting a circle, the user first selects the center, then selects a point on the circle. Every time a shape is painted, the color, the shape type, and the two selected points are saved to the history.
In addition, the user can choose to clear all information, to save the history to a file, and to indicate that a new "first point" should be selected.
We start by identifying all the views that we will need to display the information user needs to see. Of course, there will be BufferedPanel for the graphics. There should be TextfieldViews to display the points that select the size and location of the shape to be drawn and the mouse position. There will be ColorView for choosing the color. We will need a place where to keep the history - this will be a TextAreaView. The choice of shape will be determined by selecting one button among four in an OptionsView. Finally, we will have theee actions: Clear and Save and NewFirstPoint. Later we will see how a pair of TextfieldViews can be used to build a single Point2DView.
We can sketch the layout of these views and their enclosing display type objects until we are quite comfortable with the design. You may try some designs before committing to ours. We sketch below a picture that illustrates our GUI design. In reality, a designer makes several such sketches before the final design emerges. The key point is to capture all GUI components – not necessarily their final arrangement in the overall display.
Our design for the GUI
Clear Action
This is the standard action and will be copied verbatim from the SimplePainter program.
New First Point Action
In a manner similar to the SimplePainter, we will need a state variable firstPoint that will specify which point is being selected. The action method is like the startNewPolygon action in SimplePainter : it sets the state variable firstPoint to true.
Save to the File Action
Here we want to save the entire context of the TextAreaView to a file. The method will create a FileView that brings up the file chooser dialog. The action starts by disabling all other fields until the file save is completed or aborted. We then create a new JFileChooser and decide that the files we will save will have a suffix .paint. We can set a file filter, so that the chooser suggests that the files have the selected suffix. The suffix choice is not enforced. If no file was selected, the file save is aborted, we enable previously disabled fields and return. If a file was selected, we try to write to the file the String extracted from the history TextAreaView through the use of getViewState function. Finally, we enable previously disabled fields and return.
Notice that any String extracted from arbitrarily complex view can be saved to the file in one step. Later, we will save a whole array of data at once. Similarly, if we can set the view state for a complex view from a String, then the entire view state may be set by reading one String from a file.
Also notice, that anyone working with files can follow verbatim the pattern used here – supplying only the suffix and the name of the view object for which the data should be saved.
Mouse Actions
MouseMoved Action
This is the tracking action from SimplePainter. The only difference is that we need to consult the state variable firstPoint to see which set of TextFieldViews should be set.
MouseClicked Action
This is the workhorse of this application. First, the mouse location is used to set the TextFieldViews for the second point and the data for both points is extracted to two Point2D objects P1 and P2. Next, the color is set using the information from the ColorView. The information from the OptionsView, an integer indicating the current selection, is used in a switch statement to perform the painting of appropriate shape. The details, like making sure any two opposing points of a rectangle specify a valid rectangle, or the computation of the bounding box for the circle from the computed radius should be delegated to helper functions. Once the shape is painted, the information about the shape should be saved in history TextAreaView. Finally, the firstPoint has to be set to true to indicate we need to select the first point again.
We need to do this before we can define the actions methods, because these methods will be extracting data from the views.
The three pairs of TextFieldViews were originally contained in DisplayCollections named firstPointData, secondPointData, and mousePointData. We will show how these were replaced by three Point2Dviews named firstPoint2DView, secondPoint2DView, and mousePoint2Dview. The history and color views are the same as in SimplePainter program. The BooleanView nextPointChoice has a label First Point next to the checkbox. The OptionsView drawSelectionView is initialized by supplying an array of Strings that will be used as labels for the possible choices – the size of this array determines the number of options available. Of course, we need a BufferedPanel for the graphics.
CIRCLE
(255, 0, 0)
(195,
196) : (185, 28)
OVAL
(51, 51, 255)
(100,
133) : (162, 155)
OVAL
(51, 51, 255)
(225,
150) : (297, 130)
RECTANGLE
(51, 255, 51)
(127,
240) : (261, 270)
LINE
(51, 255, 51)
(194,
156) : (163, 205)
LINE
(51, 255, 51)
(163,
205) : (235, 200)
LINE
(51, 255, 51)
(235,
200) : (194, 156)
At this point we can write the actual code for the actions. The specification given earlier guides us through the details and no further discussion is necessary.
If a collection of several views appears in the program several times in the same configuration, it is desirable to package several views into one compound view. The code in the two files Point2DView.java and Xpoint2D.java illustrates how this is done.
The Point2Dview class needs to implement TypedView, because it contains two TypedView components. This means we need to define the requestObject and demandObject functions, the getDataType function that returns the class this view represents, and the getInputProperties and setInputProperties functions.
Because TypedView interface extends Displayable interface, we also need to implement the functions setViewState, getViewState, setDefaultViewState, and getDefaultViewState. We need not redefine the reset and setEnabled functions because we are extending the Display class.
The setViewState and getViewState functions illustrate the use of codec utilities that encode a collection of items into a String and perform the inverse operation to extract the data values from the encoded String.
The demandObject and requestObject functions return Stringable. This means that we also have to build a Stringable version of the Point2D.Double class as seen in XPoint2D.java. This class extends the Point2D.Double class and implements the Stringable interface by defining the fromStringData and toStringData functions. Additional setValue functions allow us to change the values stored in an XPoint2D object. Note that the primitive Stringable classes in the JPT extend XObject but it is more convenient here to have XPoint2D extend Point2D.Double.
BooleanView
BooleanView constructor:
private BooleanView nextPtChoice =
new BooleanView
("First Point", // String shown with the check box
FIRSTPOINT); // initial and default value
nextPtChoice identifier
for the BooleanView
object
"First Point" String - label shown with the check box
FIRSTPOINT Boolean - initial value that will be displayed in the Boolean View
and the one that will be used as a default
Extracting a value from a BooleanView
Boolean firstPoint = nextPtChoice.getBooleanValue();
firstPoint identifier for the Boolean object that will get the selected value
nextPtChoice identifier
for the BooleanView
object
OptionsView
OptionsView constructor:
private OptionsView drawSelectionView =
new OptionsView
(drawSelection, // array of label Strings to identify
// the options
defaultSelection, // integer to specify default choice
new GridLayout(4,0)); // layout for the view
drawSelectionView identifier
for the OptionsView
object
drawSelection array
of Strings:
labels for the OptionsView
items
defaultSelection int – the initial and default
choice
new GridLayout(4,0) LayoutManager –the desired layout for the view
Extracting a value from a OptionsView
int selection = drawSelectionView.getSelectedIndex();
selection identifier for the int object that will get the selected option index
drawSelectionView identifier for the OptionsView object
JFileChooser, FileView, and writing to a file
We need to build a JFileChooser, (a Java object), use the FileUtilities to write to a file, and, optionally, set the desired file extension.
JFileChooser declaration and initialization (java.swing class):
JFileChooser chooser = new JFileChooser(".");
chooser identifier for the JFileChooser object
"." String specifies current directory path for the chooser
our choice indicates that the chooser starts in the current directory
Setting the file filter:
chooser.setFileFilter(new FileViewExtensionFileFilter("paint");
chooser identifier for the JFileChooser object
"paint" String specifies the file suffix to be used
Checking if a file selection has been made (java.swing code):
if (chooser.showSaveDialog(null) != JFileChooser.APPROVEOPTION){
//perform cancelled action}
chooser identifier for the JFileChooser object
Writing to a selected file:
try{
FileUtilities.writeFile(chooser.getSelectedFile(),
history.getViewState(),
true);
}
catch(Exception e){
System.out.println(“Exception:” + e);
}
chooser identifier for the JFileChooser object
history identifier for the view from whose contents will be written
true Boolean - true value indicates it is OK to rewrite file