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

/**
 *  A panel that shows an animated "sun" in a large drawing
 *  area, with a few control buttons below it.  The sun is
 *  defined by an object of type Pulsar.  The user can drag
 *  the sun and, when the drawing panel has input focus,
 *  move it with the arrow keys.  Also, when the drawing panel
 *  has focus, the G, R, and Y keys will change the color of
 *  the sun.  When the drawing area has the input focus, the
 *  color of its border changes to cyan.  Clicking on the
 *  drawing area will give it focus.
 *     This class also includes a main() routine that enables
 *  it to be run as an application.  The main() routine simply
 *  opens a window whose content pane is a panel of type
 *  EventDemo.
 */
public class EventDemo extends JPanel {

   public static void main(String[] args) {
       JFrame window = new JFrame("Pulsar");
       window.setContentPane(new EventDemo());
       window.pack();
       window.setLocation(200,100);
       window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
       window.setVisible(true);
   }
   
   private Pulsar pulsar;          // The object that represents the sun.
   private DisplayPanel display;   // The drawing panel, where the sun lives.
   
   private Timer animationTimer;         // A Timer that drives the animation.
   private JButton animateButton;        // A button that starts the animation (by starting the Timer).
   private JButton stopAnimationButton;  // A button that stops the animation (by stopping the Timer).
   
   private JButton centerButton;  // A button that moves the sun to the center of the display panel.
      
   /**
    * The constructor creates all the objects, arranges them in the 
    * main panel, configures listening, and starts the animation.
    */
   public EventDemo() {
   
      /* Create the objects that will listen and respond to events of various kinds.
       * These objects belong to nested classes, which are defined below.
       */
   
      ActionHandler actionHandler = new ActionHandler();
      MouseHandler mouseHandler = new MouseHandler();
      KeyAndFocusHandler keyHandler = new KeyAndFocusHandler();

      /* Create the display panel, which is defined by the nested class DisplayPanel
       * which is defined below.  Add a border that is initially gray and add
       * listeners that will handle keyboard and mouse events from the panel.
       */

      display = new DisplayPanel();
      display.setPreferredSize( new Dimension(400,400) );
      display.setBackground(Color.WHITE);
      display.setBorder(BorderFactory.createLineBorder(Color.GRAY,4));
      display.addKeyListener(keyHandler);
      display.addFocusListener(keyHandler);
      display.addMouseListener(keyHandler);

      /**
       *  Create the Pulsar object that represents the sun (that is, holds data
       *  about the sun and is capable of drawing it on demand).
       */
       
      pulsar = new Pulsar();
      pulsar.setCenter(100,100);
      
      /**
       * Create the buttons, and set them to send action events to the
       * actionHandler.  The animationButton is initially disabled because
       * the animation is already running when the program starts up.
       */
       
      animateButton = new JButton("Animate");
      animateButton.addActionListener(actionHandler);
      animateButton.setEnabled(false);
      
      stopAnimationButton = new JButton("Stop Animation");
      stopAnimationButton.addActionListener(actionHandler);
      
      centerButton = new JButton("Center");
      centerButton.addActionListener(actionHandler);
      
      /**
       * Lay out the main panel, with a sub-panel to hold the buttons.
       */
      
      JPanel bottom = new JPanel();
      bottom.setLayout(new GridLayout(1,3));
      bottom.add(centerButton);
      bottom.add(animateButton);
      bottom.add(stopAnimationButton);
      
      setLayout(new BorderLayout());
      add(display, BorderLayout.CENTER);
      add(bottom, BorderLayout.SOUTH);

   }
   
   /**
    *  A simple convenience method for moving the sun.  It calls display.repaint()
    *  so that the sun will be redrawn in its new position.
    *  @param dx horizontal displacement. The amount that is added to the x-coordinate.
    *  @param dy vertical displacement. The amount that is added to the y-coordinate.
    */
   private void movePulsar(int dx, int dy) {
      int px = pulsar.getCenterX();
      int py = pulsar.getCenterY();
      pulsar.setCenter(px + dx, py + dy);
      display.repaint();
   }
      
   /**
    *  A class for defining the drawing area.  It has a paintComponent() 
    *  method that draws the sun.
    */
   private class DisplayPanel extends JPanel {
      protected void paintComponent(Graphics g) {
          super.paintComponent(g); // fills panel with background color
          pulsar.draw(g);
      }
   }
   
   /**
    *  A class for defining the object that handles ActionEvents.
    */
   private class ActionHandler implements ActionListener {
      public void actionPerformed(ActionEvent evt) {
         Object source = evt.getSource();
         if (source == animationTimer) {  // drives the animation
            pulsar.advancePhase();
            display.repaint();
         }
         else if (source == animateButton) {  // restart the animation
            animateButton.setEnabled(false);
            stopAnimationButton.setEnabled(true);
         }
         else if (source == stopAnimationButton) {  // stop the animation
            animateButton.setEnabled(true);
            stopAnimationButton.setEnabled(false);
         }
         else if (source == centerButton) {  // move pulsar to center of drawing area
            pulsar.setCenter(display.getWidth()/2,display.getHeight()/2);
            display.repaint();
         }
      }
   }   
   
   /**
    *  A class for defining the object that handles KeyEvents.  To do this effectively,
    *  it also implements FocusListener and MouseListener.  It should be registered as
    *  a listener for all three types of events. The FocusListener is necessary so that
    *  the appearance of the display can be changed when the display has input focus.
    *  The MouseListener has only one function:  It calls display.requestFocus() when
    *  the user clicks the display.  This is necessary because a JPanel does not 
    *  automatically get the input focus when the user clicks on it.
    */
   private class KeyAndFocusHandler implements KeyListener, FocusListener, MouseListener {
      public void keyPressed(KeyEvent evt) {  // user depressed a key
         int code = evt.getKeyCode(); // The "virtual keycode" for the key that was pressed.
         if (code == KeyEvent.VK_G)
            pulsar.setColor(Color.GREEN);
         else if (code == KeyEvent.VK_RIGHT)
            movePulsar(10,0);
         display.repaint();
      }
      public void keyReleased(KeyEvent evt) {  // user released a key
         int code = evt.getKeyCode(); // The "virtual keycode" for the key that was pressed.
      }
      public void keyTyped(KeyEvent evt) {  // user's typing has generated a character
         char ch = evt.getKeyChar();  // The character that was typed.
      }
      public void focusLost(FocusEvent evt) {
         display.setBorder(BorderFactory.createLineBorder(Color.GRAY,4));
      }
      public void focusGained(FocusEvent evt) {
         display.setBorder(BorderFactory.createLineBorder(Color.CYAN,4));
      }
      public void mousePressed(MouseEvent evt) {
         display.requestFocus();
      }
      public void mouseReleased(MouseEvent evt) {
      }
      public void mouseEntered(MouseEvent evt) {
      }
      public void mouseExited(MouseEvent evt) {
      }
      public void mouseClicked(MouseEvent evt) {
      }
   }
   
   /**
    *  A class for defining the class that handles MouseEvents, capable
    *  of handling both simple events and drag actions.  (A drag action
    *  consists of a mousePressed event, followed by a sequence of 
    *  mouseDragged events, followed by a mouseReleased event.  The
    *  instance variables dragging, startX, startY, prevX, and prevY
    *  are often needed to carry information form one of these events
    *  to the next.   The listener object should be added as a
    *  MouseListener to the object that will generate the events.  To
    *  handle mouseDragged or mouseMoved events, it should also be
    *  added as a MouseMotionListener.
    */
   private class MouseHandler implements MouseListener, MouseMotionListener {
      boolean dragging;  // Set to true while a drag operation is in progress.
                         // (This is necessary because not all mouse presses
                         // start a drag operation.)
      int startX, startY;   // The (x,y) coordinates when the drag operation started.
      int prevX, prevY;    // The previous position of the mouse (from the initial
                           // mousePressed or the previous mouseDragged).
      public void mousePressed(MouseEvent evt) {
         startX = prevX = evt.getX();
         startY = prevY = evt.getY();
         // ... React to mouse press, if appropriate.
         // ... Decide whether to start a drag and, if so, set dragging = true.
      }
      public void mouseDragged(MouseEvent evt) {
         if (!dragging)
            return;
         int x = evt.getX();
         int y = evt.getY();
         // ... Process a motion from (prevX,prevY) to (x,y).
         prevX = x;
         prevY = y;
      }
      public void mouseReleased(MouseEvent evt) {
         if (!dragging)
            return;
         // ... Finish the drag operation (if needed).
         dragging = false;
      }
      public void mouseMoved(MouseEvent evt) {
      }
      public void mouseEntered(MouseEvent evt) {
      }
      public void mouseExited(MouseEvent evt) {
      }
      public void mouseClicked(MouseEvent evt) {
      }
   }

}
