package com.scottlangley.elect98;
import java.awt.*;
/* Modified by Scott Langley, October 1998.
*/
// This appears in Core Web Programming from
// Prentice Hall Publishers, and may be freely used
// or adapted. 1997 Marty Hall, hall@apl.jhu.edu.

/** A Slider with a label centered above it. */

public class LabeledSlider extends Panel {
  private Label label;
  private Label leftLabel;
  private Label rightLabel;    
  private Slider slider;

  public Label getLabel() {
    return(label);
  }
  public Slider getSlider() {
    return(slider);
  }
  public int getValue() {
    return slider.getValue();
  }
  public LabeledSlider(String labelString, String leftLabelString, String rightLabelString,
		       int minValue, int maxValue,
		       int initialValue) {
    setLayout(new BorderLayout());
    label = new Label(labelString, Label.CENTER);
    leftLabel = new Label(leftLabelString, Label.LEFT);
    rightLabel = new Label(rightLabelString, Label.RIGHT);
    label.setFont(new Font("DIALOG",Font.BOLD,10));
    rightLabel.setFont(new Font("DIALOG",Font.BOLD,10));
    leftLabel.setFont(new Font("DIALOG",Font.BOLD,10));
    add("North", label);
    slider = new Slider(minValue, maxValue, initialValue);
    add("Center", slider);
    Panel panel = new Panel();
    BorderLayout bl = new BorderLayout();
    panel.setLayout(bl);
    panel.add("West",leftLabel);
    panel.add("East",rightLabel);
    add("South",panel);

  }
}
// This appears in Core Web Programming from
// Prentice Hall Publishers, and may be freely used
// or adapted. 1997 Marty Hall, hall@apl.jhu.edu.

/** A class that combines a horizontal Scrollbar and
 *  a TextField (to the right of the Scrollbar).
 *  The TextField shows the current scrollbar value,
 *  plus, if setEditable(true) is set, it can be used
 *  to change the value as well.
 *
 * @author Marty Hall (hall@apl.jhu.edu)
 */

 class Slider extends Panel {
  private Scrollbar scrollbar;
  private TextField textfield;
  private TextField nTextfield;
  private ScrollbarPanel scrollbarPanel;
  private int preferredWidth = 250;

  //----------------------------------------------------
  /** Construct a slider with the specified min, max
   *  and initial values. The "bubble" (thumb)
   *  size is set to 1/10th the scrollbar range.
   *  In JDK 1.1.x, it tries to adjust for the max
   *  value bug by adding the bubble thickness to
   *  the max value.
   */
  public Slider(int minValue, int maxValue,
                int initialValue) {
    this(minValue, maxValue, initialValue,
         (maxValue - minValue)/10);
  }

  /** Construct a slider with the specified min, max,
   *  and initial values, plus the specified "bubble"
   *  (thumb) value. This bubbleSize should be
   *  specified in the units that min and max use,
   *  not in pixels. Thus, if min is 20 and max is
   *  320, then a bubbleSize of 30 is 10% of the
   *  visible range.
   */
  public Slider(int minValue, int maxValue,
                int initialValue, int bubbleSize) {
    setLayout(new BorderLayout());
    //maxValue = adjustFor1_1(maxValue, bubbleSize);
    scrollbar = new Scrollbar(Scrollbar.HORIZONTAL,
                              initialValue,
                              bubbleSize,
                              minValue, maxValue);
    scrollbarPanel = new ScrollbarPanel(6);
    scrollbarPanel.add("Center", scrollbar);
    add("Center", scrollbarPanel);
    textfield = new TextField(numDigits(maxValue) + 1);
    nTextfield = new TextField(numDigits(maxValue) + 1);    
    setFontSize(12);
//    textfield.setEditable(false);
//    textfield.setEditable(true);
//    nTextfield.setEditable(true);
    setTextFieldValue();
    add("East", textfield);
    add("West", nTextfield);
//    add("East", textfield);
//    add("West", nTextfield);

  }
  
  //----------------------------------------------------
  /** A place holder to override for action to be taken
   *  when scrollbar changes
   */
  public void doAction(int value) {
  }

  //----------------------------------------------------
  /** When scrollbar changes, sets the textfield */
  
  public boolean handleEvent(Event event) {
    if (event.target == scrollbar &&
        isScrollEvent(event.id)) {
      setTextFieldValue();
      doAction(scrollbar.getValue());
      fixWindowsProblem(event.id);
      return(true);
    }
    else
        return(super.handleEvent(event));
  }

  //----------------------------------------------------
  /** When textfield changes, sets the scrollbar */
  
  public boolean action(Event event, Object object) {
    String value;
    int sign =1;
    if (event.target == textfield){
      value = textfield.getText();
    }
    else if(event.target == nTextfield){
      value = nTextfield.getText();
      sign = -1;
    }
    else
      return(false);
      //return  super.action(event, object);
    int oldValue = getValue();
    try {
      value.replace('%',' ');
      value = value.trim();
      int pos = value.indexOf('.');
      if(pos != -1)                   {
        if((value.length() - pos)>1)
          value = value.substring(0,pos)+value.charAt(pos+1);
        else
          value = value.substring(0,pos);
      }
      else{
        value = value +"0";
      }
      if(value.charAt(0) == '+')
        value = value.substring(1);
      setValue(sign*Integer.parseInt(value.trim()));
    } catch(NumberFormatException nfe) {
        setValue(oldValue);
    }
    return(true);
  }

  //----------------------------------------------------
  /** Returns the Scrollbar part of the Slider. */
  
  public Scrollbar getScrollbar() {
    return(scrollbar);
  }

  /** Returns the TextField part of the Slider */
  
  public TextField getTextField() {
    return(textfield);
  }

  //----------------------------------------------------
  /** Changes the preferredSize to take a minimum
   *  width, since super-tiny scrollbars are
   *  hard to manipulate.
   *
   * @see #getPreferredWidth
   * @see #setPreferredWidth
   */
  public Dimension preferredSize() {
    Dimension d = super.preferredSize();
    d.height = textfield.preferredSize().height;
    d.width = Math.max(d.width, preferredWidth);
    return(d);
  }

  /** This just calls preferredSize */
  
  public Dimension minimumSize() {
    return(preferredSize());
  }
  
  //----------------------------------------------------
  /** To keep scrollbars legible, a minimum width is
   *  set. This returns the current value (default is
   *  150).
   *
   * @see #setPreferredWidth
   */
  public int getPreferredWidth() {
    return(preferredWidth);
  }

  /** To keep scrollbars legible, a minimum width is
   *  set. This sets the current value (default is
   *  150).
   *
   * @see #getPreferredWidth
   */
  public void setPreferredWidth(int preferredWidth) {
    this.preferredWidth = preferredWidth;
  }

  //----------------------------------------------------
  /** This returns the current scrollbar value */
  
  public int getValue() {
    return(scrollbar.getValue());
  }

  /** This assigns the scrollbar value. If it is below
   *  the minimum value or above the maximum, the value
   *  is set to the min and max value, respectively.
   */
  public void setValue(int value) {
    scrollbar.setValue(value);
//Following is a recommended fix
//    scrollbar.setValue(this.getValue());
    setTextFieldValue();
  }

  //----------------------------------------------------
  /** Sometimes horizontal scrollbars look odd if they
   *  are very tall. So empty top/bottom margins
   *  can be set. This returns the margin setting.
   *  The default is four.
   *
   * @see setMargins
   */
  public int getMargins() {
    return(scrollbarPanel.getMargins());
  }
  
  /** Sometimes horizontal scrollbars look odd if they
   *  are very tall. So empty top/bottom margins
   *  can be set. This sets the margin setting.
   *
   * @see getMargins
   */
  public void setMargins(int margins) {
    scrollbarPanel.setMargins(margins);
  }

  //----------------------------------------------------
  /** Returns the current textfield string. In most
   *  cases this is just the same as a String version
   *  of getValue, except that there may be padded
   *  blank spaces at the left.
   */
  public String getText() {
    return(textfield.getText());
  }

  /** This sets the TextField value directly. Use with
   *  extreme caution since it does not right-align
   *  or check if value is numeric.
   */
  public void setText(String text) {
    textfield.setText(text);
  }

  //----------------------------------------------------
  /** Returns the Font being used by the textfield.
   *  Courier bold 12 is the default.
   */
  
  public Font getFont() {
    return(textfield.getFont());
  }

  /** Changes the Font being used by the textfield. */
  
  public void setFont(Font textFieldFont) {
    textfield.setFont(textFieldFont);
  }

  //---------------------------------------------------
  /** The size of the current font */

  public int getFontSize() {
    return(getFont().getSize());
  }

  /** Rather than setting the whole font, you can
   *  just set the size (Courier bold will be used
   *  for the family/face).
   */
  public void setFontSize(int size) {
    setFont(new Font("Courier", Font.BOLD, size));
  }
  
  //----------------------------------------------------
  /** Determines if the textfield is editable. If it
   *  is, you can enter a number to change the
   *  scrollbar value. In such a case, entering a value
   *  outside the legal range results in the min or
   *  max legal value. A non-integer is ignored.
   *
   * @see #setEditable
   */
/*  public boolean isEditable() {
    return(textfield.isEditable());
  }*/

  /** Determines if you can enter values directly
   *  into the textfield to change the scrollbar.
   *
   * @see #isEditable
   */
/*  public void setEditable(boolean editable) {
    textfield.setEditable(editable);
  }
  */

  //----------------------------------------------------
  // Sets a right-aligned textfield number.
  
  private void setTextFieldValue() {
    int value = scrollbar.getValue();
    int nValue = -1*value;
    int digits = numDigits(scrollbar.getMaximum());
    String valueString = padString(value, digits);
    String nValueString = padString(nValue, digits);
    if(value>=0){
      textfield.setText(valueString);
      nTextfield.setText(nValueString);
    }
    else {
      textfield.setText(valueString);
      nTextfield.setText(nValueString);
    }

  }

  //----------------------------------------------------
  // Repeated String concatenation is expensive, but
  // this is only used to add a small amount of
  // padding, so converting to a StringBuffer would
  // not pay off.
  
  private String padString(int value, int digits) {
    String result = String.valueOf(value);
    if(value>=0){
      result = "+"+result;
      //digits--;
    }
    int length = result.length();
    result = result.substring(0,length-1)+"."+result.charAt(length-1)+"%";
    length+=2;
    for(int i=length; i<digits; i++)
      result = " " + result;
    return(result + " ");
  }
  
  //----------------------------------------------------
  // Determines the number of digits in a decimal
  // number.
  
  private static final double LN10 = Math.log(10.0);
  
  private static int numDigits(int num) {
    return(1 + 
           (int)Math.floor(Math.log((double)num)/LN10));
  }

  //----------------------------------------------------
  // Since several implementations generate extraneous
  // scrollbar events, you shouldn't just check
  // the event target, but verify a correct
  // event type also. Used by handleEvent.
  
  private boolean isScrollEvent(int eventID) {
    return(eventID == Event.SCROLL_LINE_UP ||
           eventID == Event.SCROLL_LINE_DOWN ||
           eventID == Event.SCROLL_PAGE_UP ||
           eventID == Event.SCROLL_PAGE_DOWN ||
           eventID == Event.SCROLL_ABSOLUTE);
  }

  //----------------------------------------------------
  // KLUDGE ALERT!
  // Many Windows 95 Java implementations (including
  // most browsers and Sun's JDK through version 1.1.3)
  // fail when you drag the "thumb", often bouncing back
  // to their original location when the button is
  // released. Enforcing short pauses between the events
  // appears to solve the problem in many cases, but
  // it slows things down, the exact sleep amount
  // needed depends on the system speed, and there is
  // absolutely no guarantee that this will always work.
  // The only "real" solution is to get the vendors to
  // fix the implementations.

  private void fixWindowsProblem(int eventID) {
    if (eventID == Event.SCROLL_ABSOLUTE)
      pause(100);
  }

  private void pause(long millis) {
    try {
      Thread.sleep(millis);
    } catch(InterruptedException ie) {}
  }

  //----------------------------------------------------
  // KLUDGE ALERT!
  // In all or most Java 1.02 implementations (JDK,
  // Netscape 2, 3, 4, Internet Explorer 3.01),
  // the max value is the largest possible value that
  // can be set. But in JDK 1.1.1-1.1.3 on Unix and
  // Windows, you can only set values as big
  // as max-bubble (ie getMaximum() *minus*
  // getVisible()), despite what getMaximum()
  // returns. This adjusts for that, but of course
  // has the problem that a 1.1 implementation that
  // *was* consistent with 1.02 would no longer work.
  // Trying to fix bugs on a per-implementation basis
  // is a risky proposition indeed; this is not the
  // high point of Java's platform independence.

  private int adjustFor1_1(int max, int bubble) {
    String version = System.getProperty("java.version");
    if (version.startsWith("1.1"))
      return(max+bubble);
    else
      return(max);
  }

  //----------------------------------------------------
}

// This appears in Core Web Programming from
// Prentice Hall Publishers, and may be freely used
// or adapted. 1997 Marty Hall, hall@apl.jhu.edu.

/** A Panel with adjustable top/bottom insets value.
 *  Used to hold a Scrollbar in the Slider class
 */

class ScrollbarPanel extends Panel {
  private Insets insets;

  public ScrollbarPanel(int margins) {
    setLayout(new BorderLayout());
    setMargins(margins);
  }

  public Insets insets() {
    return(insets);
  }

  public int getMargins() {
    return(insets.top);
  }

  public void setMargins(int margins) {
    this.insets = new Insets(margins, 0, margins, 0);
  }
}

// This appears in Core Web Programming from
// Prentice Hall Publishers, and may be freely used
// or adapted. 1997 Marty Hall, hall@apl.jhu.edu.

/** A Frame that you can actually quit. Used as
 *  the starting point for most Java 1.0 graphical
 *  applications. CloseableFrame is the Java 1.1 version.
 * @see CloseableFrame
 */

/*class QuittableFrame extends Frame {
  public QuittableFrame(String title) {
    super(title);
  }*/

  /** Catch window destroy events. Pass all other
   *  events to the original handler.
   */

/*  public boolean handleEvent(Event event) {
    if (event.id == Event.WINDOW_DESTROY)
      System.exit(0);
    return(super.handleEvent(event));
  }
} */

