/*
 *  =======================================================================
 *  DemoGeneralPath.java : This applet displays eight general path objects
 *  ranging from very simple to complex shapes. The applet displays a 
 *  control panel containing radio buttons for selection of a winding rule
 *  and push buttons to control the filling of shapes.
 * 
 *  Adapted from : Pantham S., Pure JFC Swing, 1999.
 *  Modified by : Mark Austin                                   March, 2001
 *  =======================================================================
 */

import javax.swing.*;
import javax.swing.border.*;
import java.awt.*;
import java.awt.event.*;
import java.awt.geom.*;
import java.util.Vector;

public class DemoGeneralPath extends JApplet {
    DemoGeneralPathCanvas        canvas;
    JRadioButton oddEvenBox, nonZeroBox;
    JButton    fillButton, noFillButton;

    public void init() {

        // 1. Get the content pane of the applet

        Container container = getContentPane();

        // 2. Create a control panel with titled border

        JPanel panel = new JPanel();
        panel.setLayout(new GridLayout(1, 3));
        TitledBorder border = new TitledBorder(
            "Focus mouse on a shape, select a winding rule, "
            + " and click fill button");
        panel.setBorder(border);

        // 3. Create and add radio buttons and fill buttons to the panel

        nonZeroBox = new JRadioButton("Non-Zero Rule", true);
        oddEvenBox = new JRadioButton("Odd-Even Rule");
        ButtonGroup group = new ButtonGroup();
        group.add(nonZeroBox); group.add(oddEvenBox);

        nonZeroBox.addActionListener(new ActionEventHandler1());
        oddEvenBox.addActionListener(new ActionEventHandler1());

        fillButton   = new JButton("Fill");
        noFillButton = new JButton("No Fill");
        noFillButton.addActionListener(new ActionEventHandler2());
        fillButton.addActionListener(new ActionEventHandler2());

        panel.add(nonZeroBox);
        panel.add(oddEvenBox);
        panel.add(fillButton);
        panel.add(noFillButton);

        // 4. Add the panel to the applet

        container.add(panel, BorderLayout.SOUTH);

        // 5. Add the drawing canvas to the applet

        canvas = new DemoGeneralPathCanvas();
        container.add(canvas);
    }

    // 6. Definition of the 'DemoGeneralPathCanvas' class

    class DemoGeneralPathCanvas extends Canvas {
        Vector      generalPaths;
        GeneralPath selectedGPath = null;
        Rectangle2D boundingRec = null;
        int selectedRule = GeneralPath.WIND_NON_ZERO;
        boolean drawNoFill = false;

        // 7. Constructor

        public DemoGeneralPathCanvas() {

            setBackground(Color.white);
            addMouseListener(new MouseEventHandler());

            // Initialize the vector that stores general paths

            generalPaths = new Vector();

            // General Path references

            GeneralPath gp1, gp2, gp3, gp4,
                        gp5, gp6, gp7, gp8;

            // Create different general paths and add them to the vector

            gp1 = new GeneralPath();
            gp1.moveTo(50, 10);
            gp1.lineTo(70, 80); gp1.lineTo(90, 40);
            gp1.lineTo(10, 40); gp1.lineTo(50, 80);
            gp1.closePath();
            generalPaths.addElement(gp1);

            gp2 = new GeneralPath();
            gp2.moveTo(120, 20);
            gp2.lineTo(180, 20); gp2.lineTo(120, 80);
            gp2.lineTo(180, 80); 
            gp2.closePath();
            generalPaths.addElement(gp2);

            gp3 = new GeneralPath();
            gp3.moveTo(220, 20);
            gp3.lineTo(280, 20); gp3.lineTo(280, 60);
            gp3.lineTo(240, 60); gp3.lineTo(240, 40);
            gp3.lineTo(260, 40); gp3.lineTo(260, 80);
            gp3.lineTo(220, 80);
            gp3.closePath();
            generalPaths.addElement(gp3);

            gp4 = new GeneralPath();
            gp4.moveTo(310, 20);
            gp4.lineTo(380, 20); gp4.lineTo(380, 80);
            gp4.lineTo(320, 80); gp4.lineTo(320, 10);
            gp4.lineTo(340, 10); gp4.lineTo(340, 60);
            gp4.lineTo(360, 60); gp4.lineTo(360, 40);
            gp4.lineTo(310, 40);
            gp4.closePath();
            generalPaths.addElement(gp4);

            gp5 = new GeneralPath();
            gp5.moveTo(50, 120);
            gp5.lineTo(70, 180); gp5.lineTo(20, 140);
            gp5.lineTo(80, 140); gp5.lineTo(30, 180);
            gp5.closePath();
            generalPaths.addElement(gp5);

            gp6 = new GeneralPath();
            gp6.moveTo(120, 180);
            gp6.quadTo(150, 120, 180, 180);
            gp6.closePath();
            generalPaths.addElement(gp6);

            gp7 = new GeneralPath();
            gp7.moveTo(220, 150);
            gp7.curveTo(240, 130, 280, 160, 300, 140);
            gp7.lineTo(300, 180);
            gp7.quadTo(260, 160, 220, 180);
            gp7.closePath();
            generalPaths.addElement(gp7);

            gp8 = new GeneralPath();
            gp8.moveTo(360, 100);
            gp8.lineTo(360, 200); gp8.lineTo(400, 140);
            gp8.lineTo(320, 120); gp8.lineTo(400, 180);
            gp8.lineTo(320, 180); 
            gp8.closePath();
            generalPaths.addElement(gp8);
        }

        // 8. The Overriding paint method

        public void paint(Graphics g) {
            Graphics2D g2D = (Graphics2D) g;

            // 9. Display the general paths from the vector

            for (int i=0; i<generalPaths.size(); i++) {
                if (drawNoFill) { // Render non-filled shapes
                    g2D.draw((GeneralPath) generalPaths.elementAt(i));
                }
                else { // Render filled shapes
                    g2D.fill((GeneralPath) generalPaths.elementAt(i));
                }
            }

            // 10. Conditionally display the bounding rectangle of
            // the selected shape.

            if (boundingRec != null) {
                drawHighlightSquares(g2D, boundingRec);
            }
        }

        // 11. Method to draw small highlight squares with fill style

        public void drawHighlightSquares(Graphics2D g2D, Rectangle2D r) {
            double x = r.getX();
            double y = r.getY();
            double w = r.getWidth();
            double h = r.getHeight();
            g2D.setColor(Color.black);
            
            g2D.fill(new Rectangle.Double(x-3.0, y-3.0, 6.0, 6.0));
            g2D.fill(new Rectangle.Double(x+w*0.5-3.0, y-3.0, 6.0, 6.0));
            g2D.fill(new Rectangle.Double(x+w-3.0, y-3.0, 6.0, 6.0));
            g2D.fill(new Rectangle.Double(x-3.0, y+h*0.5-3.0, 6.0, 6.0));
            g2D.fill(new Rectangle.Double(x+w-3.0, y+h*0.5-3.0, 6.0, 6.0));
            g2D.fill(new Rectangle.Double(x-3.0, y+h-3.0, 6.0, 6.0));
            g2D.fill(new Rectangle.Double(x+w*0.5-3.0, y+h-3.0, 6.0, 6.0));
            g2D.fill(new Rectangle.Double(x+w-3.0, y+h-3.0, 6.0, 6.0));
        }

        // 12. The mouse listener class

        class MouseEventHandler extends MouseAdapter {
            public void mouseClicked(MouseEvent e) {

                // Check if a general path received mouse focus

                for (int i=0; i<generalPaths.size(); i++) {
                    GeneralPath gPath = (GeneralPath) generalPaths.elementAt(i);

                    // If the mouse click is inside the shape...

                    if (gPath.contains(e.getX(), e.getY())) {

                        // Store a reference to the selected path and
                        // its bounding rectangle.

                        selectedGPath = gPath;
                        boundingRec = gPath.getBounds2D();

                        // Finally, call repaint() and return when a
                        // general path has been selected.

                        canvas.repaint();
                        return;
                    }
                }
            }
        }
    }

    // 13. Event handler for events from radio buttons: Implements the
    // functionality to register the selected winding rule.

    class ActionEventHandler1 implements ActionListener {
        public void actionPerformed(ActionEvent e) {
            JRadioButton temp = (JRadioButton) e.getSource();

            if (temp.equals(oddEvenBox)) {
                canvas.selectedRule = GeneralPath.WIND_EVEN_ODD;
            }
            else if (temp.equals(nonZeroBox)) {
                canvas.selectedRule = GeneralPath.WIND_NON_ZERO;
            }
        }
    }

    // 14. Event handler for events from buttons: Implements the functionality
    // to repaint canvas with the modified values of winding rule, fill style.

    class ActionEventHandler2 implements ActionListener {
        public void actionPerformed(ActionEvent e) {
            JButton temp = (JButton) e.getSource(); 

            if (temp.equals(fillButton)) {
                if (canvas.selectedGPath != null) {
                    canvas.selectedGPath.setWindingRule(canvas.selectedRule);
                    canvas.drawNoFill = false;
                    canvas.repaint();
                }
            }
            else if (temp.equals(noFillButton)) {
                canvas.drawNoFill = true;
                canvas.repaint();
            }
        }
    }
}
