/*
 * Filename: QuickCoolButton.java
 * Written By: Sunit Katkar
 * E-Mail:sunitkatkar@hotmail.com
 * Home-Page : www.vidyut.com/sunit
 * Java Page : www.vidyut.com/sunit/JavaPage.html
 ***************************************************************
 * Description:
 * Are you bored waiting for an image to download in a applet ?
 * Do you site behind a firewall? Does a ImageButton which you see
 * everywhere take long to load ? Then here is a simple NON-IMAGE
 * and BORDERLESS button which loads quickly.
 * Features :
 * 1) You can have same sized buttons. All you have to do is -
 *    a) plan what will the (text) labels for each button.
 *    b) pass the text string you want for a button followed
 *       by the LONGEST string you have planned for a button
 * 2) You can change the Button label (text) programatically
 ***************************************************************
 * Copyright (c) 1997 Sunit Katkar All Rights Reserved.
 *
 * Permission to use, copy, modify, and distribute this software
 * for NON-COMMERCIAL or COMMERCIAL purposes and without fee
 * is hereby granted.
 *
 * Whew ! That was too much legalese..even to have copied and pasted
 * from some other place... PLEASE DO NOT BLAME ME in any way
 * if your system crashes because of this code, or if anything else
 * bad happens. In short "DO WHAT YOU WANT BUT DONT BLAME ME !"
 *****************************************************************/

import java.awt.*;

public class QuickCoolButton extends Canvas
{
    //Button labels and font name
     public  String font,firstlabel,secondlabel;
     public  Font fnt;//font for the label
     public  int fontsize; //size of label font
     public  Color c; //color of label text
     public  Dimension QuickCoolButtonsize; //size of button

/**
 * Three booleans to test if :
 * 1) the mouse is down
 * 2) the enablebutton()/disablebutton() method was called by
 *     the parent applet
 * 3) the mouse is inside/outside button area
**/
    //Is the mouse down? default => false
    private boolean is_the_mouse_down = false;
    //true => enable the button
    private boolean you_want_to_disableMe = false;
    //Has the mouse entered the button area ?
    private boolean is_mouse_in_button_area = false;
    //false => paintNormalFlatButton(), true => paintRaisedButton()

   /**
    * Default constructor
   **/
 public QuickCoolButton()
 {
   //Nothing spectacular happening :)
   super();
 }
/********************************************************************
* Constructor : String firstlabel - text of the 1st button's label  *
* (1)         : String secondlabel - text of the 2nd button's label *
*             : String font  -  Font to construct the label         *
*             : int fontsize - Size of font                         *
*             : Color c      - Color to use as foreground of label  *
********************************************************************/

   public QuickCoolButton(String firstlabel, //1st label
                          String secondlabel,//put the longest label you plan here
                          String font,
                          int fontsize,
                          Color c)
   {
      this.firstlabel = firstlabel;
      this.secondlabel = secondlabel;
      this.font = font;
      this.fontsize = fontsize;
      this.c = c;
      //We use a fixed Font style, try changing this to suit your
      //needs. Maybe include this style parameter in the constructor
      fnt = new Font(font,Font.BOLD,fontsize);
 }

/****************************************************************
* If user wants to have only one button, user can specify only  *
* ONE label string and that will be used as longest string      *
****************************************************************/
/********************************************************************
* Constructor : String firstlabel - text of the 1st button's label  *
* (2)         : String secondlabel - text of the 2nd button's label *
*             : String font  -  Font to construct the label         *
*             : int fontsize - Size of font                         *
*             : Color c      - Color to use as foreground of label  *
********************************************************************/
   public QuickCoolButton(String firstlabel, //1st label only
                          String font,
                          int fontsize,
                          Color c)
   {
      this.firstlabel = firstlabel;
      this.secondlabel = firstlabel;
      this.font = font;
      this.fontsize = fontsize;
      this.c = c;
      //We use a fixed Font style, try changing this to suit your
      //needs. Maybe include this style parameter in the constructor
      fnt = new Font(font,Font.BOLD,fontsize);
   }

 /**
  * Modifier method of QuickCoolButton label
 **/
 public void setButtonLabel(String firstlabel)
 {
     //set the button label
     this.firstlabel = firstlabel;
    /**
     * refresh display as we want to see effect
     * immediately; and not wait for user to take the
     * mouse over the button.
    **/
    repaint();
 }

 /**
  * Accessor method for QuickCoolButton label
 **/
 public String getButtonLabel()
 {
    return firstlabel;
 }

 /**
  * Returns the preferred size of this component. This method has to be
  * over-ridden, as it is called during addNotify() to establish the initial
  * size of the Canvas. The default size of Canvas is (0, 0).
 **/
    public Dimension preferredSize()
    {
      //must over-ride this, or there is NO canvas to draw!
      return QuickCoolButtonsize;
    }
 /**
  * Returns the minimum size of this component. This method has to be
  * over-ridden, as it is called by many layout managers. Connected AWT
  * components call the peer for this information.
 **/
    public Dimension minimumSize()
    {
       //must over-ride this, or the layout manager may not function as desired
       return QuickCoolButtonsize;
    }
 /**
  * Creates the peer.  This peer allows you to change the
  * user interface of the canvas without changing its functionality.
 **/
    public void addNotify()
    {
      super.addNotify();
    /*****************************************************
    * Button size is dependent on the fontmetrics of the *
    * secondlabel and its font.                          *
    * For instance, we are measuring the stringWidth()   *
    * of the secondlabel and then adjusting size of      *
    * 1st button                                         *
    *****************************************************/
     QuickCoolButtonsize = measureMe();
   }
     //measureme for equal buttons
   protected Dimension measureMe()
   {
      FontMetrics fmt = this.getFontMetrics(fnt);
      if (fmt == null)
      {
           return new Dimension(0,0);
      }
      else
      {
        //added pixels for visual adjustment
        //Note that here we measure width of the secondlabel, which is
        //the longest string among the buttons you have planned.

        int width =  fmt.stringWidth(secondlabel)+15;
        int actualheight = fmt.getHeight() + fmt.getAscent() + 5;
        int height = actualheight;
        return new Dimension(width,height) ;
      }
    }

 /**
  * Disables QuickCoolButton.
 **/
 protected boolean disablebutton()
 {
      //Call the disable() method of super class java.awt.Component
      //from which the Canvas is derived and our QuickButton1 is
      //derived from Canvas
        super.disable();
      //set the boolean to true indicating that parent asked
      //that the button be disabled.
      you_want_to_disableMe = true;
      //Call repaint to refresh button display
      repaint();
      //return true as this action has been performed
      return true;
 }
 /**
  * Enables QuickCoolButton.
 **/
    protected boolean enablebutton()
    {
        //Call the enable() method of super class java.awt.Component
        //from which the Canvas is derived and our QuickButton1 is
        //derived from Canvas
        super.enable();
        //set boolean to false that you set to true in disablebutton()
        you_want_to_disableMe = false;
        //Call repaint to refresh button display
        repaint();
        //return true as this action has been performed
        return true;
    }
  /**
   * Calls the appropriate paint() method of QuickCoolButton
   * according to the button states
  **/
 public void paint(Graphics g)
 {
      //Check conditions and make method calls accordingly
      if(is_the_mouse_down)
      {
          /**
           * if mouse is DOWN draw a
           * down state/DISABLED button
          **/
          paintDepressedButton(g);
      }
      else
      {
         /**
          * Else draw a up state/NORMAL button
         **/
         paintNormalFlatButton(g);
      }
    /************/
      if(is_mouse_in_button_area)
      {
       /**
        * If mouse has ENTERED button area,
        * then draw a RAISED/up state button
       **/
        paintRaisedButton(g);
      }
      else
      {
       /**
        * Else If mouse has EXITED button area,
        * then draw a RAISED/up state button
       **/
       paintNormalFlatButton(g);
      }
       /************/
      if(is_the_mouse_down && is_mouse_in_button_area )
      {
       /**
        * if mouse is down AND mouse is INSIDE
        * button area, then draw a DEPRESSED/down
        * state button
       **/
       paintDepressedButton(g);
     }
       /************/
     if(you_want_to_disableMe)
     {
      /**
       * If parent has asked that button be disabled,
       * then draw a down state/DISABLED button
      **/
        paintDisabledButton(g);
      }
      if((is_the_mouse_down) && !(is_mouse_in_button_area))
      {
       /**
        * If mouse is down AND it is OUTSIDE
        * button area, then draw a down state /
        * DEPRESSED button
       **/
         paintDepressedButton(g);
      }
   }

   /************************************
   * QUICKCOOLBUTTON PAINTING ROUTINES *
   ************************************/

  /**
   * Paints the 'NORMAL BORDERLESS & ENABLED' QuickCoolButton
  **/
protected void paintNormalFlatButton(Graphics g)
{
    //set the font
    g.setFont(fnt);

    //Get the required width and height of the button
    int w = measureMe().width;
    int h = measureMe().height;
    //Again calculate the font height & width
    int fonttotalheight = g.getFontMetrics().getHeight()
                          + g.getFontMetrics().getLeading();

    //Get the stringwidth of the firstlabel
    int fonttotalwidth = g.getFontMetrics().stringWidth(firstlabel);
    int drawfonty = (h-((h - fonttotalheight)/2)-4);
    int drawfontx = ((w - fonttotalwidth)/2)+2;

    //Refresh the area to be painted
    g.clearRect(0,0,w-1,h-1);
    //Set the button color
    g.setColor(Color.gray);
    /**
     * No drawing borders. We want a FLAT button like MSIE or
     * Netscape Coomunicator 4.02
    **/
    //Button label drawing
    g.setColor(Color.white);
    //Shadow of label text
    g.drawString(firstlabel,drawfontx+1,drawfonty+1);
    //Actual label text. Button is active,
    //so make label color darker
    g.setColor(c.darker());
    g.drawString(firstlabel,drawfontx,drawfonty);
}
  /**
   * Paints the 'DISABLED' QuickCoolButton
  **/
protected void paintDisabledButton(Graphics g)
{
    //set the font
    g.setFont(fnt);

    //Get the required width and height of the button
    int w = measureMe().width;
    int h = measureMe().height;
    //Again calculate the font height & width
    int fonttotalheight = g.getFontMetrics().getHeight()
                        + g.getFontMetrics().getLeading();

    //Get the stringwidth of the firstlabel
    int fonttotalwidth = g.getFontMetrics().stringWidth(firstlabel);
    int drawfonty = (h-((h - fonttotalheight)/2)-4);
    int drawfontx = ((w - fonttotalwidth)/2)+2;

    //Refresh the area to be painted
    g.clearRect(0,0,w-1,h-1);
    //Refer QuickPanel code in the paint method for etched panel
    g.setColor(Color.gray);
    //Draw Line : top-left to bottom-left
    g.drawLine(0, 0, 0, h - 1);
    //Draw Line : top-left to top-right
    g.drawLine(0, 0, w - 1, 0);
    g.setColor(Color.white);
    //Draw Line : top-left to bottom-left
    g.drawLine(1, 1, 1, h - 2);
    //Draw Line : top-left to top-right
    g.drawLine(1, 1, w - 3, 1);
    g.setColor(Color.gray);
    //Draw Line : bottom-left to bottom-right
    g.drawLine(0, h - 1, w - 1, h - 1);
    //Draw Line : top-right to bottom-right
    g.drawLine(w - 1, 2, w - 1, h);
    g.setColor(Color.white);
    //Draw Line : bottom-left to bottom-right
    g.drawLine(0, h, w, h);
    //Draw Line : top-right to bottom-right
    g.drawLine(w, 0, w, h);

    //Button label drawing
    g.setColor(Color.darkGray);
    //Shadow of label text
    g.drawString(firstlabel,drawfontx+1,drawfonty+1);
    //Actual label text. Button is disabled so make label color gray
    g.setColor(Color.gray);
    g.drawString(firstlabel,drawfontx,drawfonty);
}

   /**
    * Paints the 'RAISED BORDER ACTIVATED' QuickCoolButton
    * Activated means that mouse is over the button.
   **/
protected void paintRaisedButton(Graphics g)
{
    //set the font
    g.setFont(fnt);

   //Get the required width and height of the button
   int w = measureMe().width;
   int h = measureMe().height;
   //Again calculate the font height & width
    int fonttotalheight = g.getFontMetrics().getHeight()
                            + g.getFontMetrics().getLeading();
    //Get the stringwidth of the firstlabel
    int fonttotalwidth = g.getFontMetrics().stringWidth(firstlabel);
    int drawfonty = (h-((h - fonttotalheight)/2)-4);
    int drawfontx = ((w - fonttotalwidth)/2)+2;

    //Refer QuickPanel code in the paint method for raised panel
    g.setColor(Color.black);
    // Draw lines : bottom-left to bottom-right
    g.drawLine(0, h, w - 1, h);
    g.drawLine(0+1, h-1, w - 2, h-1);//for a 'thick' line
    //Draw lines : top-right to bottom-right
    g.drawLine(w, 0, w, h);
    g.drawLine(w-1, 0+1, w-1, h-1);
    g.setColor(Color.darkGray);
    //Draw lines : bottom-left to bottom-right
    g.drawLine(0 + 1 +1, h - 1 -1, w - 2 -1, h - 1 - 1);
    //Draw line : top-right to bottom-right
    g.drawLine(w - 1 -1, 0 + 1 + 1, w - 1 -1, h - 1-1);
    g.setColor(Color.white);
    //Draw lines : top-left to bottom-left
    g.drawLine(0, 0, 0, h - 1);
    g.drawLine(0+1, 0, 0+1, h - 1 -2); //for a 'thick' line
    //Draw lines : top-left to top-right
    g.drawLine(0, 0, w - 1, 0);
    g.drawLine(0+1, 0+1, w - 1-1, 0+1);//for a 'thick' line

    //Button label drawing
    g.setColor(Color.white);
    //Shadow of label text
    g.drawString(firstlabel,drawfontx+1,drawfonty+1);
    //Actual label text. Button is active, so make label color  brighter
    g.setColor(c.brighter());
    g.drawString(firstlabel,drawfontx,drawfonty);
 }
   /**
    * Paints the 'DEPRESSED DOWN STATE' QuickCoolButton
   **/
protected void paintDepressedButton(Graphics g)
{
    g.setFont(fnt);
    int w = measureMe().width;
    int h = measureMe().height;
    int fonttotalheight = g.getFontMetrics().getHeight()
                          + g.getFontMetrics().getLeading();
    //Get the stringwidth of the firstlabel
    int fonttotalwidth = g.getFontMetrics().stringWidth(firstlabel);
    int drawfonty = (h-((h - fonttotalheight)/2)-4);
    int drawfontx = ((w - fonttotalwidth)/2)+2;
    //Refresh the area to be painted
    g.clearRect(0,0,w-1,h-1);
    //Refer QuickPanel code in the paint method for sunken panel
    g.setColor(Color.darkGray);
    //Draw lines : top-left to top-right
    g.drawLine(0, 0, w - 1, 0);
    g.drawLine(0+1, 0+1, w - 2, 0+1);//for a 'thick' line
    //Draw lines :  top-left to bottom-left
    g.drawLine(0, 0, 0, h - 1);
    g.drawLine(0+1, 0+1, 0+1, h-1-1);//for a 'thick' line
    g.setColor(Color.black);
    //Draw line : top-left to top-right
    g.drawLine(1+1, 1+1, w - 2-1, 1+1);
    //Draw line : top-left to bottom-left
    g.drawLine(1+1, 1+1, 1+1, h - 2-1);
    g.setColor(Color.white);
    //Draw lines : bottom-left to bottom-right
    g.drawLine(0, h, w, h);
    g.drawLine(1, h - 1, w - 1, h - 1); //for a 'thick' line
    g.drawLine(1+1, h - 1-1, w - 1-1, h - 1-1); //for a 'thick' line
    //Draw lines : top-right to bottom-right
    g.drawLine(w, 0, w, h);
    g.drawLine(w - 1, 1, w - 1, h - 1); //for a 'thick' line
    g.drawLine(w - 1 -1, 1+1, w - 1 -1 , h - 1 - 1); //for a 'thick' line
    //Button label drawing
    g.setColor(Color.darkGray);
    //Shadow of label text
    g.drawString(firstlabel,drawfontx+3,drawfonty+3);
    //Actual label text. Button is active but DEPRESSED, so make label color lightGray
    g.setColor(Color.lightGray);
    g.drawString(firstlabel,drawfontx+2,drawfonty+2);
 }
 /**
  * Mouse handling routines
 **/
 public boolean mouseDown (Event evt, int x, int y)
 {
      is_the_mouse_down = true;
      repaint();
      //handle the event here itself
      return true;
 }

 public boolean mouseUp (Event evt, int x, int y)
 {
  //Check whether a mouse down occured on the button
  //and if it did we ned to 'post' this event
  if(is_the_mouse_down)
  {
       //condition satisfied, so 'post' the event
          buttonwasClicked();
          is_the_mouse_down = false;
         repaint();
   }
    //handle the event here itself
    return true ;
  }

 public boolean mouseDrag (Event evt, int x, int y)
 {
    return true ;
 }
 
 public boolean mouseEnter(Event evt, int x, int y)
 {
    //mouse has entered button area so set boolean true
    is_mouse_in_button_area = true;
    repaint();
    return true;
 }

 public boolean mouseExit(Event evt, int x, int y)
 {
    //mouse has left button area so set boolean false
    is_mouse_in_button_area = false;
    repaint();
    return true;
 }
 /**
  * Action handling routines
 **/
 public boolean action(Event e, Object o)
 {
  /**
   * Let parent applet handle this,
   * as parent may want to enable/disable certain
   * instances of this button depending on
   * certain conditions. Hence return false
  **/
   return false;
 }
 /**
  * If mouse is down on a button and a mouseUp follows,
  * it means that user wants to 'click' our button.
  * So we need to 'post' a event to the parent
 **/
 public void buttonwasClicked()
 {
      Event theEvent = new Event(this, Event.ACTION_EVENT, firstlabel);
      postEvent(theEvent);
 }

 } //All ends :)


See the QuickCoolButton Applet in action here.