# Java Notes

# Example - PaintDemo

| This simple paint applet / application leaves much to be desired, but it illustrates a few things: Event-driven programming. Mouse listeners. Use of `paintComponent()` Interaction between listeners and `paintComponent()` using instance variables. BufferedImage. The standard Graphics class methods are used with two additions. The BufferedImage object is used to accumulate the effect of all drawing. The `paintComponent()` method first draws the BufferedImage, then draws the current shape, which may be changing as the mouse is being dragged When the mouse is released, the current shape is drawn into the BufferedImage.. Graphics2D. The Graphics2D class allows more extensive graphics operations than the Graphics class. In this case it's needed to work with the BufferedImage. Because Graphics2D is a child class (subclass) of Graphics, it's easy to downcast the Graphics object that is passed to `paintComponent()`. After downcasting, all Graphics2D operations become available.

## Source code

The source code is dividing into three classes:

- `PaintDemo.java` builds the applet user interface and main program.
- `PaintPanel.java` is a panel where the graphics are drawn and which listens to mouse events.
- `Shape.java` is an enum class which defines the possible shapes. This is a "classic" enum style of programming, but Java also allows each enum constant to have a method associated with it. Alternative code using using this more sophisticated enum is shown in a section at the end.

      1
      2
      3
      4
      5
      6
      7
      8
      9
     10
     11
     12
     13
     14
     15
     16
     17
     18
     19
     20
     21
     22
     23
     24
     25
     26
     27
     28
     29
     30
     31
     32
     33
     34
     35
     36
     37
     38
     39
     40
     41
     42
     43
     44
     45
     46
     47
     48
     49
     50
     51
     52
     53
     54
     55
     56
     57
     58
     59
     60
     61
     62
     63
     64
     65
     66
     67
     68
     69
     70
     71
     72
     73
     74
     75
     76
     77
     78
     79
     80
     81
     82
     83
     84
     85
     86
     87
     88
     89
     90
     91
     92
     93
     94
     95
     96
     97
     98
     99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    // File   : gui-lowlevel/paintdemo/PaintDemo.java
    // Purpose: A simple painting program.
    //          Illustrates use of mouse listeners and BufferedImage.
    //          Runs as both applet and main program.
    // Author : Fred Swartz 2002, 2006-10-13 - Placed in public domain.
    // Possible Enhancements:
    //   * Clear drawing area
    //   * Other shapes.
    //   * An eraser (define new shape, draw on buffered image during drag).

    package paintdemo;

    import java.awt.*;
    import java.awt.event.*;
    import javax.swing.*;

    ////////////////////////////////////////////////////////////////////// PaintDemo
    public class PaintDemo extends JApplet {
        //=================================================================== fields
        PaintPanel _canvas = new PaintPanel();

        //===================================================================== main
        public static void main(String[] args) {
            //... Create and initialize the applet.
            JApplet theApplet = new PaintDemo();

            //... Create a window (JFrame) and make applet the content pane.
            JFrame window = new JFrame();
            window.setContentPane(theApplet);
            window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            window.setTitle("Paint Demo 2");
            window.pack();
            window.setLocationRelativeTo(null); // Center window.
            window.setResizable(false);
            // System.out.println(theApplet.getSize()); // to get applet size.
            window.setVisible(true);            // Make the window visible.
        }

        //============================================================== constructor
        public PaintDemo() {
            //... Create radio buttons for shapes.........................
            JRadioButton circleButton = new JRadioButton("Oval");
            circleButton.addActionListener(new ActionListener() {
                public void actionPerformed(ActionEvent e) {
                    _canvas.setShape(Shape.OVAL);
                }});

            JRadioButton rectangleButton = new JRadioButton("Rectangle", true);
            rectangleButton.addActionListener(new ActionListener() {
                public void actionPerformed(ActionEvent e) {
                    _canvas.setShape(Shape.RECTANGLE);
                }});

            JRadioButton lineButton = new JRadioButton("Line");
            lineButton.addActionListener(new ActionListener() {
                public void actionPerformed(ActionEvent e) {
                    _canvas.setShape(Shape.LINE);
                }});

            ButtonGroup shapeGroup = new ButtonGroup();
            shapeGroup.add(circleButton);
            shapeGroup.add(rectangleButton);
            shapeGroup.add(lineButton);

            //--- Layout the  shape buttons
            JPanel shapePanel = new JPanel();
            shapePanel.setLayout(new GridLayout(3,1));
            shapePanel.add(circleButton);
            shapePanel.add(rectangleButton);
            shapePanel.add(lineButton);

            //... Create radio buttons for colors...............................
            JRadioButton redButton = new JRadioButton("Red", true);
            redButton.addActionListener(new ActionListener() {
                public void actionPerformed(ActionEvent e) {
                    _canvas.setColor(Color.RED);
                }});

            JRadioButton greenButton = new JRadioButton("Green");
            greenButton.addActionListener(new ActionListener() {
                public void actionPerformed(ActionEvent e) {
                    _canvas.setColor(Color.GREEN);
                }});

            JRadioButton blueButton = new JRadioButton("Blue");
            blueButton.addActionListener(new ActionListener() {
                public void actionPerformed(ActionEvent e) {
                    _canvas.setColor(Color.BLUE);
                }});

            ButtonGroup colorGroup = new ButtonGroup();
            colorGroup.add(redButton);
            colorGroup.add(greenButton);
            colorGroup.add(blueButton);

            //... Layout the color buttons
            JPanel colorPanel = new JPanel();
            colorPanel.setLayout(new GridLayout(3,1));
            colorPanel.add(redButton);
            colorPanel.add(greenButton);
            colorPanel.add(blueButton);

            //... Create a panel to hold the separate button panels.............
            JPanel buttonPanel = new JPanel();
            buttonPanel.setLayout(new BoxLayout(buttonPanel, BoxLayout.X_AXIS));
            buttonPanel.add(shapePanel);
            buttonPanel.add(colorPanel);
            buttonPanel.add(Box.createHorizontalGlue());

            //... layout the applet ...........................................
            setLayout(new BorderLayout(5,5));
            add(buttonPanel, BorderLayout.NORTH);
            add(_canvas    , BorderLayout.CENTER);
        }
    }

### The graphics panel

      1
      2
      3
      4
      5
      6
      7
      8
      9
     10
     11
     12
     13
     14
     15
     16
     17
     18
     19
     20
     21
     22
     23
     24
     25
     26
     27
     28
     29
     30
     31
     32
     33
     34
     35
     36
     37
     38
     39
     40
     41
     42
     43
     44
     45
     46
     47
     48
     49
     50
     51
     52
     53
     54
     55
     56
     57
     58
     59
     60
     61
     62
     63
     64
     65
     66
     67
     68
     69
     70
     71
     72
     73
     74
     75
     76
     77
     78
     79
     80
     81
     82
     83
     84
     85
     86
     87
     88
     89
     90
     91
     92
     93
     94
     95
     96
     97
     98
     99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    // File   : gui-lowlevel/paintpanel/PaintPanel.java
    // Purpose: Draws graphics, handles mouse events.
    //          Has setters for shape and color.
    // Author : Fred Swartz - October 12, 2006 - Placed in public domain.
    // Possible enhancements:
    //          * Allow dragging shapes up or to left, not just down+right.
    //                by using max/min of coordinates.

    package paintdemo;

    import java.awt.*;
    import java.awt.event.*;
    import java.awt.image.BufferedImage;
    import javax.swing.*;

    ///////////////////////////////////////////////////////////////////// PaintPanel
    class PaintPanel extends JPanel implements MouseListener, MouseMotionListener {

        //================================================================ constants
        private static final int SIZE = 300;     // Size of paint area.
        private static final Shape INIIIAL_SHAPE = Shape.RECTANGLE;
        private static final Color INITIAL_COLOR = Color.RED;
        private static final Color COLOR_BACKGROUND = Color.WHITE;
        private enum         State { IDLE, DRAGGING }

        //=================================================================== fields
        private State _state = State.IDLE;
        private Shape _shape = INIIIAL_SHAPE;
        private Color _color = INITIAL_COLOR;

        private Point _start = null; // Where mouse is pressed.
        private Point _end   = null; // Where mouse is dragged to or released.

        //... BufferedImage stores the underlying saved painting.
        //    Initialized first time paintComponent is called.
        private BufferedImage _bufImage = null;

        //============================================================== constructor
        public PaintPanel() {
            setPreferredSize(new Dimension(SIZE, SIZE));
            setBackground(Color.white);

            this.addMouseListener(this);
            this.addMouseMotionListener(this);
        }

        //================================================================= setShape
        public void setShape(Shape shape) {
            _shape = shape;
        }

        //================================================================= setColor
        public void setColor(Color color) {
            _color = color;
        }

        //=========================================================== paintComponent
        @Override public void paintComponent(Graphics g) {
            Graphics2D g2 = (Graphics2D)g;  // Downcast to Graphics2D

            //... One time initialization of in-memory, saved image.
            if (_bufImage == null) {
                //... This is the first time, initialize _bufImage
                int w = this.getWidth();
                int h = this.getHeight();
                _bufImage = (BufferedImage)this.createImage(w, h);
                Graphics2D gc = _bufImage.createGraphics();
                gc.setColor(COLOR_BACKGROUND);
                gc.fillRect(0, 0, w, h); // fill in background
            }

            //... Display the saved image.
            g2.drawImage(_bufImage, null, 0, 0);

            //... Overwrite the screen display with currently dragged image.
            if (_state == State.DRAGGING) {
                //... Write shape that is being dragged over the screen image,
                //    but not into the saved buffered image.  It will be written
                //    on the saved image when the mouse is released.
                drawCurrentShape(g2);
            }
        }

        //========================================================= drawCurrentShape
        private void drawCurrentShape(Graphics2D g2) {
            //... Draws current shape on a graphics context, either
            //    on the context passed to paintComponent, or the
            //    context for the BufferedImage.

            g2.setColor(_color);    // Set the color.

            switch (_shape) {
                case OVAL:
                    g2.fillOval(_start.x, _start.y, _end.x - _start.x, _end.y - _start.y);
                    break;

                case RECTANGLE:
                    g2.fillRect(_start.x, _start.y, _end.x - _start.x, _end.y - _start.y);
                    break;

                case LINE:
                    g2.drawLine(_start.x, _start.y, _end.x  , _end.y);
                    break;

                default:  // Should never happen!
                    g2.drawString("Huh?", 10, 20);
                    break;
            }
        }

        //============================================================= mousePressed
        public void mousePressed(MouseEvent e) {
            _state = State.DRAGGING;   // Assume we're starting a drag.

            _start = e.getPoint();     // Save start point, and also initially
            _end   = _start;           // as end point, which drag will change.
        }

        //============================================================= mouseDragged
        public void mouseDragged(MouseEvent e) {
            _state = State.DRAGGING;   // We're dragging to create a shape.

            _end = e.getPoint();       // Set end point of drag.  May change.
            this.repaint();            // After change, show new shape
        }

        //============================================================ mouseReleased
        public void mouseReleased(MouseEvent e) {
            //... If released at end of drag, write shape into the BufferedImage,
            //    which saves it in the drawing.
            _end = e.getPoint();      // Set end point of drag.
            if (_state == State.DRAGGING) {
                _state = State.IDLE;

                //... Draw current shape in saved buffered image.
                drawCurrentShape(_bufImage.createGraphics());

                this.repaint();
            }
        }

        //================================================== ignored mouse listeners
        public void mouseMoved  (MouseEvent e) {}
        public void mouseEntered(MouseEvent e) {}
        public void mouseExited (MouseEvent e) {}
        public void mouseClicked(MouseEvent e) {}
    }

### The enum defining possible shapes

      1
      2
      3
      4
      5
      6
      7
      8
    // File   : gui-lowlevel/paintdemo/Shape.java
    // Purpose: Defines the shapes that can be drawn.
    // Author : Fred Swartz - October 12, 2006 - Placed in public domain.

    package paintdemo;

    /////////////////////////////////////////////////////////////////// Shape
    public enum Shape { RECTANGLE, OVAL, LINE }

## Defining methods associated with enum constants

Whenever you see a switch statement based on an enum value, you might consider whether you can define a method in the enum class for handling this. Such a use of enums goes beyond what is available in many languages, and is quite useful. Here are sections of the above code rewritten in this form.

### Simplifying the PaintPanel drawCurrentShape method

Compare the simplicity of this to the above version with a switch.

        //========================================================= drawCurrentShape
        private void drawCurrentShape(Graphics2D g2) {
            g2.setColor(_color);            // Set the color.
            _shape.draw(g2, _start, _end);  // Draw the shape.
        }

### Moving the drawing to the enum class

The drawing code has to go somewhere. The separate behaviors of the different shapes is probably more appropriately centralized with each shape as below.

    ////////////////////////////////////////////////////////////////////////// Shape
    public enum Shape {
        RECTANGLE {
            void draw(Graphics2D g2, Point from, Point to) {
                g2.fillRect(from.x, from.y, to.x - from.x, to.y - from.y);
            }
        },
        OVAL {
            void draw(Graphics2D g2, Point from, Point to) {
                g2.fillOval(from.x, from.y, to.x - from.x, to.y - from.y);
            }
        },
        LINE {
            void draw(Graphics2D g2, Point from, Point to) {
                g2.drawLine(from.x, from.y, to.x, to.y);
            }
        };

        //... Must define methods implemented by all constants as abstract.
        //    Note semicolon after last enum constant above.
        abstract void draw(Graphics2D g2, Point from, Point to);
    }