import java.awt.*;
import java.awt.event.*;
import java.awt.geom.*;
import java.util.*;
import javax.swing.*;
import javax.swing.border.*;

/** DemoMouseInk - demonstrate mouse events.
* A bit fancier than DemoMouseEvents
* <p>
*
* Line segments (viz. "ink") are defined by the movement of the mouse
* while the primary mouse button in pressed (aka "dragging").  Each
* consecutive pair of button-down mouse samples defines a line segment.  The
* segments are 
* drawn by invoking the <code>drawInk</code> method defined in our custom
* paint panel.<p>
*
* The approach taken in <code>drawInk</code> improves on the deficiency
* noted earlier for the <code>drawEntity</code> method in
* <code>DemoPaintPanel3</code>.  <code>drawInk</code>
* creates a line segment object based on its arguments,
* then draws the segment,
* then adds the segment to the vector that stores the complete set of
* segments.  Organized this way, we avoid repainting all the line
* segments each time a new segment is added.<p>
*
* Screen snap (launch)...<br>
* <center><img src = "DemoMouseInk-1.gif"></center><p>
*
* Screen snap (resized)...<br>
* <center><img src = "DemoMouseInk-2.gif"></center><p>
*
* @see <a href="DemoMouseInk.java">source code</a>
* @author Scott MacKenzie, 2002
*/
public class DemoMouseInk
{
   public static void main(String[] args)
   {
      // use look and feel for my system (Win32)
      try {
         UIManager.setLookAndFeel(
            UIManager.getSystemLookAndFeelClassName());
      } catch (Exception e) {}

      DemoMouseInkFrame frame = new DemoMouseInkFrame();
      frame.setTitle("DemoMouseInk");
      frame.pack();
      frame.show();
   }
}

class DemoMouseInkFrame extends JFrame
implements MouseMotionListener, MouseListener, ActionListener
{
   final int MAX_SAMPLES = 500;

   private JTextField xMotion;
   private JTextField yMotion;
   private JTextField leftButtonStatus;
   private JTextField rightButtonStatus;
   private JTextField sampleField;
   private JTextField strokeField;
   private PaintPanel inkPanel;
   private JButton clearButton;

   private Point[] stroke;
   private int sampleCount;
   private int strokeCount;

   // constructor

   public DemoMouseInkFrame()
   {
      final int FIELD_WIDTH = 10;

      stroke = new Point[MAX_SAMPLES];
      sampleCount = 0;
      strokeCount = 0;

      // ----------------------------------
      // construct and configure components
      // ----------------------------------

      inkPanel = new PaintPanel();

      xMotion = new JTextField("0", FIELD_WIDTH);
      xMotion.setEditable(false);
      xMotion.setBackground(Color.white);
      xMotion.setMargin(new Insets(0, 3, 0, 0));
      xMotion.setAlignmentX(Component.LEFT_ALIGNMENT);

      Dimension d = xMotion.getPreferredSize();
      xMotion.setMaximumSize(d);

      yMotion = new JTextField("0", FIELD_WIDTH);
      yMotion.setEditable(false);
      yMotion.setBackground(Color.white);
      yMotion.setMargin(new Insets(0, 3, 0, 0));
      yMotion.setAlignmentX(Component.LEFT_ALIGNMENT);
      yMotion.setMaximumSize(d);

      leftButtonStatus = new JTextField("up", FIELD_WIDTH);
      leftButtonStatus.setEditable(false);
      leftButtonStatus.setBackground(Color.white);
      leftButtonStatus.setMargin(new Insets(0, 3, 0, 0));
      leftButtonStatus.setAlignmentX(Component.LEFT_ALIGNMENT);
      leftButtonStatus.setMaximumSize(d);

      rightButtonStatus = new JTextField("up", FIELD_WIDTH);
      rightButtonStatus.setEditable(false);
      rightButtonStatus.setBackground(Color.white);
      rightButtonStatus.setMargin(new Insets(0, 3, 0, 0));
      rightButtonStatus.setAlignmentX(Component.LEFT_ALIGNMENT);
      rightButtonStatus.setMaximumSize(d);

      sampleField = new JTextField("0", FIELD_WIDTH);
      sampleField.setEditable(false);
      sampleField.setBackground(Color.white);
      sampleField.setMargin(new Insets(0, 3, 0, 0));
      sampleField.setAlignmentX(Component.LEFT_ALIGNMENT);
      sampleField.setMaximumSize(d);

      strokeField = new JTextField("0", FIELD_WIDTH);
      strokeField.setEditable(false);
      strokeField.setBackground(Color.white);
      strokeField.setMargin(new Insets(0, 3, 0, 0));
      strokeField.setAlignmentX(Component.LEFT_ALIGNMENT);
      strokeField.setMaximumSize(d);

      clearButton = new JButton("Clear");
      clearButton.setMaximumSize(d);

      // -------------
      // add listeners
      // -------------

      this.addWindowListener(new WindowCloser());
      inkPanel.addMouseMotionListener(this);
      inkPanel.addMouseListener(this);
      clearButton.addActionListener(this);

      // ------------------
      // arrange components
      // ------------------

      JPanel p1 = new JPanel(new BorderLayout());
      p1.setBorder(BorderFactory.createEmptyBorder(4, 4, 4, 4));
      p1.add(inkPanel, "Center");

      JPanel p2 = new JPanel();
      p2.setLayout(new BoxLayout(p2, BoxLayout.Y_AXIS));
      p2.add(Box.createRigidArea(new Dimension(0, 3)));
      p2.add(new JLabel("x motion"));
      p2.add(xMotion);
      p2.add(Box.createRigidArea(new Dimension(0, 5)));
      p2.add(new JLabel("y motion"));
      p2.add(yMotion);
      p2.add(Box.createRigidArea(new Dimension(0, 5)));
      p2.add(new JLabel("Left button"));
      p2.add(leftButtonStatus);
      p2.add(Box.createRigidArea(new Dimension(0, 5)));
      p2.add(new JLabel("Right button"));
      p2.add(rightButtonStatus);
      p2.setBorder
         (new TitledBorder(new EtchedBorder(), "Mouse status"));
      p2.setMaximumSize(p2.getPreferredSize());

      JPanel p3 = new JPanel();
      p3.setLayout(new BoxLayout(p3, BoxLayout.Y_AXIS));
      p3.add(Box.createRigidArea(new Dimension(0, 5)));
      p3.add(new JLabel("Number of samples"));
      p3.add(sampleField);
      p3.add(Box.createRigidArea(new Dimension(0, 5)));
      p3.add(new JLabel("Number of strokes"));
      p3.add(strokeField);
      p3.add(Box.createVerticalGlue());
      p3.add(clearButton);
      p3.setBorder
         (new TitledBorder(new EtchedBorder(), "Ink status"));
      p3.setPreferredSize(p2.getPreferredSize());
      p3.setMaximumSize(p2.getPreferredSize());

      d = p2.getMaximumSize();
      p1.setPreferredSize(new Dimension(d.height, d.height));
      p1.setMaximumSize(new Dimension(d.height, d.height));

      JPanel contentPane = new JPanel(); // flow layout is fine
      contentPane.add(p1);
      contentPane.add(p2);
      contentPane.add(p3);

      // make this panel the JFrame's content pane

      this.setContentPane(contentPane);
   }

   // -------------------------------
   // implement ActionListener method
   // -------------------------------

   public void actionPerformed(ActionEvent ae)
   {
      inkPanel.clear();
      strokeCount = 0;
      strokeField.setText("" + strokeCount);
      sampleCount = 0;
      sampleField.setText("" + sampleCount);
   }

   // -----------------------------------------
   // implement MouseMotionListener methods (2)
   // -----------------------------------------

   public void mouseDragged(MouseEvent me)
   {
      int x = me.getX();
      int y = me.getY();
      xMotion.setText("" + x);
      yMotion.setText("" + y);

      if (SwingUtilities.isLeftMouseButton(me))
      {
         stroke[sampleCount] = new Point(x, y);
         int x1 = (int)stroke[sampleCount - 1].getX();
         int y1 = (int)stroke[sampleCount - 1].getY();
         int x2 = (int)stroke[sampleCount].getX();
         int y2 = (int)stroke[sampleCount].getY();
         if (sampleCount < MAX_SAMPLES - 1)
            ++sampleCount;
   
         sampleField.setText("" + sampleCount);

         // draw ink trail from previous point to current point
         inkPanel.drawInk(x1, y1, x2, y2);
      }
   }

   public void mouseMoved(MouseEvent me)
   {
      xMotion.setText("" + me.getX());
      yMotion.setText("" + me.getY());
   }

   // -----------------------------------
   // implement MouseListener methods (5)
   // -----------------------------------

   public void mouseClicked(MouseEvent me) {}
   public void mouseEntered(MouseEvent me) {}
   public void mouseExited(MouseEvent me) {}

   // a mouse button was pressed

   public void mousePressed(MouseEvent me)
   {
      int x = me.getX();
      int y = me.getY();

      // indicated which mouse button was pressed
      if (SwingUtilities.isLeftMouseButton(me))
      {
         leftButtonStatus.setForeground(Color.red);
         leftButtonStatus.setText("down");
      } else if (SwingUtilities.isRightMouseButton(me))
      {
         rightButtonStatus.setForeground(Color.red);
         rightButtonStatus.setText("down");
      }

      stroke[sampleCount] = new Point(x, y);
      if (sampleCount < MAX_SAMPLES - 1)
         ++sampleCount;
   }

   // a mouse button was released

   public void mouseReleased(MouseEvent me)
   {
      // indicate which mouse button was released
      if (SwingUtilities.isLeftMouseButton(me))
      {
         leftButtonStatus.setForeground(Color.black);
         leftButtonStatus.setText("up");
      } else if (SwingUtilities.isRightMouseButton(me))
      {
         rightButtonStatus.setForeground(Color.black);
         rightButtonStatus.setText("up");
      }

      if (SwingUtilities.isLeftMouseButton(me))
      {
         ++strokeCount;
         strokeField.setText("" + strokeCount);
         sampleCount = 0;
      }
   }

   // --------------------
   // define inner classes
   // --------------------

   // Note: WindowAdapter implements WindowListener
   private class WindowCloser extends WindowAdapter
   {
      public void windowClosing(WindowEvent event)
      {
         System.exit(0);
      }
   }

   /** A class for drawing with digital ink
   */
   class PaintPanel extends JPanel
   {
      private final Color INK_COLOR = new Color(0, 0, 128);
      private final Stroke INK_STROKE = new BasicStroke(5.0f,
            BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND);

      private Vector v;

      PaintPanel()
      {
         v = new Vector();
         this.setBackground(Color.pink);
         this.setBorder(BorderFactory.createLineBorder(Color.gray));
      }

      public void paintComponent(Graphics g)
      {
         super.paintComponent(g);
         paintInkStrokes(g);
      }  

      /** Paint all the line segments stored in the vector
      */
      private void paintInkStrokes(Graphics g)
      {
         Graphics2D g2 = (Graphics2D)g;

         // set the inking color
         g2.setColor(INK_COLOR);

         // set the stroke thickness, and cap and join attributes ('round')
         Stroke s = g2.getStroke(); // save current stroke
         g2.setStroke(INK_STROKE);  // set desired stroke

         // retrive each line segment and draw it
         for (int i = 0; i < v.size(); ++i)
            g2.draw((Line2D.Double)v.elementAt(i));

         g2.setStroke(s); // restore stroke
      }

      /** Draw one line segment, then add it to the vector.<p>
      */
      public void drawInk(int x1, int y1, int x2, int y2)
      {
         // get graphics context
         Graphics2D g2 = (Graphics2D)this.getGraphics();

         // create the line
         Line2D.Double inkSegment = new Line2D.Double(x1, y1, x2, y2);

         g2.setColor(INK_COLOR);    // set the inking color
         Stroke s = g2.getStroke(); // save current stroke
         g2.setStroke(INK_STROKE);  // set desired stroke 
         g2.draw(inkSegment);       // draw it!   
         g2.setStroke(s);           // restore stroke 
         v.add(inkSegment);         // add to vector
      }

      public void clear()
      {
         v.clear();
         this.repaint();
      }
   }
}

