/*
 * @(#)RoundButton.java	1.1 98/07/18
 *
 * Copyright (c) 1995-1997 Sun Microsystems, Inc. All Rights Reserved.
 *
 */
package actual;

import java.applet.*;
import java.lang.*;
import java.util.*;
import java.awt.*;
import java.awt.event.*;

/**
 * RoundButton - a class that produces a lightweight button.
 *
 * Lightweight components can have "transparent" areas, meaning that
 * you can see the background of the container behind these areas.
 *
 */
public class RoundButton extends Component {

  ActionListener actionListener;     // Post action events to listeners
  String label;                      // The Button's text
  protected boolean pressed = false; // true if the button is detented.
  
  
  /**
   * Constructs a RoundButton with no label.
   */
  public RoundButton() {
      this("");
  }

  /**
   * Constructs a RoundButton with the specified label.
   * @param label the label of the button
   */
  public RoundButton(String label) {
      this.label = label;
      enableEvents(AWTEvent.MOUSE_EVENT_MASK);
  }

  /**
   * gets the label
   * @see setLabel
   */
  public String getLabel() {
      return label;
  }
  
  /**
   * sets the label
   * @see getLabel
   */
  public void setLabel(String label) {
      this.label = label;
      invalidate();
      repaint();
  }
  
  /**
   * paints the RoundButton
   */
  public void paint(Graphics g) {
      int s = Math.min(getSize().width - 1, getSize().height - 1);
      
      // paint the interior of the button
      if(pressed) {
	  g.setColor(getBackground().darker().darker());
      } else {
	  g.setColor(getBackground());
      }
      g.fillArc(0, 0, s, s, 0, 360);
      
      // draw the perimeter of the button
      g.setColor(getBackground().darker().darker().darker());
      g.drawArc(0, 0, s, s, 0, 360);
      
      // draw the label centered in the button
      Font f = getFont();
      if(f != null) {
	  FontMetrics fm = getFontMetrics(getFont());
	  g.setColor(getForeground());
	  g.drawString(label,
		       s/2 - fm.stringWidth(label)/2,
		       s/2 + fm.getMaxDescent());
      }
  }
  
  /**
   * The preferred size of the button. 
   */
  public Dimension getPreferredSize() {
      Font f = getFont();
      if(f != null) {
	  FontMetrics fm = getFontMetrics(getFont());
	  int max = Math.max(fm.stringWidth(label) + 40, fm.getHeight() + 40);
	  return new Dimension(max, max);
      } else {
	  return new Dimension(100, 100);
      }
  }
  
  /**
   * The minimum size of the button. 
   */
  public Dimension getMinimumSize() {
      return new Dimension(100, 100);
  }

  /**
   * Adds the specified action listener to receive action events
   * from this button.
   * @param listener the action listener
   */
   public void addActionListener(ActionListener listener) {
       actionListener = AWTEventMulticaster.add(actionListener, listener);
       enableEvents(AWTEvent.MOUSE_EVENT_MASK);
   }
 
   /**
    * Removes the specified action listener so it no longer receives
    * action events from this button.
    * @param listener the action listener
    */
   public void removeActionListener(ActionListener listener) {
       actionListener = AWTEventMulticaster.remove(actionListener, listener);
   }

  /**
   * Determine if click was inside round button.
   */
   public boolean contains(int x, int y) {
       int mx = getSize().width/2;
       int my = getSize().height/2;
       return (((mx-x)*(mx-x) + (my-y)*(my-y)) <= mx*mx);
   }
   
   /**
    * Paints the button and distribute an action event to all listeners.
    */
   public void processMouseEvent(MouseEvent e) {
       Graphics g;
       switch(e.getID()) {
          case MouseEvent.MOUSE_PRESSED:
	    // render myself inverted....
	    pressed = true;

            // Repaint might flicker a bit. To avoid this, you can use
            // double buffering (see the Gauge example).
	    repaint(); 
	    break;
          case MouseEvent.MOUSE_RELEASED:
	    if(actionListener != null) {
	       actionListener.actionPerformed(new ActionEvent(
		   this, ActionEvent.ACTION_PERFORMED, label));
	    }
	    // render myself normal again
	    if(pressed == true) {
		pressed = false;

                // Repaint might flicker a bit. To avoid this, you can use
                // double buffering (see the Gauge example).
		repaint();
	    }
	    break;
          case MouseEvent.MOUSE_ENTERED:

	    break;
          case MouseEvent.MOUSE_EXITED:
	    if(pressed == true) {
		// Cancel! Don't send action event.
		pressed = false;

                // Repaint might flicker a bit. To avoid this, you can use
                // double buffering (see the Gauge example).
		repaint();

		// Note: for a more complete button implementation,
		// you wouldn't want to cancel at this point, but
		// rather detect when the mouse re-entered, and
		// re-highlight the button. There are a few state
		// issues that that you need to handle, which we leave
		// this an an excercise for the reader (I always
		// wanted to say that!)
	    }
	    break;
       }
       super.processMouseEvent(e);
   }
   
}

