Swing JFC Java

/* From http://java.sun.com/docs/books/tutorial/index.html */
/*
 * Copyright (c) 2006 Sun Microsystems, Inc. All Rights Reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * -Redistribution of source code must retain the above copyright notice, this
 *  list of conditions and the following disclaimer.
 *
 * -Redistribution in binary form must reproduce the above copyright notice,
 *  this list of conditions and the following disclaimer in the documentation
 *  and/or other materials provided with the distribution.
 *
 * Neither the name of Sun Microsystems, Inc. or the names of contributors may
 * be used to endorse or promote products derived from this software without
 * specific prior written permission.
 *
 * This software is provided "AS IS," without a warranty of any kind. ALL
 * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING
 * ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
 * OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN MIDROSYSTEMS, INC. ("SUN")
 * AND ITS LICENSORS SHALL NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE
 * AS A RESULT OF USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
 * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR ANY LOST
 * REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL,
 * INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY
 * OF LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE,
 * EVEN IF SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
 *
 * You acknowledge that this software is not designed, licensed or intended
 * for use in the design, construction, operation or maintenance of any
 * nuclear facility.
 */
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Insets;
import java.awt.Rectangle;
import java.awt.Toolkit;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.util.Locale;
import javax.accessibility.Accessible;
import javax.accessibility.AccessibleContext;
import javax.accessibility.AccessibleRole;
import javax.accessibility.AccessibleState;
import javax.accessibility.AccessibleStateSet;
import javax.swing.BorderFactory;
import javax.swing.BoxLayout;
import javax.swing.ImageIcon;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JToggleButton;
import javax.swing.Scrollable;
import javax.swing.SwingConstants;
public class AccessibleScrollDemo extends JPanel {
  private Rule columnView;
  private Rule rowView;
  private JToggleButton isMetric;
  private ScrollablePicture picture;
  public AccessibleScrollDemo() {
    //Load the photograph into an image icon.
    ImageIcon david = new ImageIcon("images/youngdad.jpeg");
    david.setDescription("Photograph of David McNabb in his youth.");
    //Create the row and column headers
    columnView = new Rule(Rule.HORIZONTAL, true);
    columnView.setPreferredWidth(david.getIconWidth());
    columnView.getAccessibleContext().setAccessibleName("Column Header");
    columnView.getAccessibleContext().setAccessibleDescription(
        "Displays horizontal ruler for "
            + "measuring scroll pane client.");
    rowView = new Rule(Rule.VERTICAL, true);
    rowView.setPreferredHeight(david.getIconHeight());
    rowView.getAccessibleContext().setAccessibleName("Row Header");
    rowView.getAccessibleContext().setAccessibleDescription(
        "Displays vertical ruler for "
            + "measuring scroll pane client.");
    //Create the corners
    JPanel buttonCorner = new JPanel();
    isMetric = new JToggleButton("cm", true);
    isMetric.setFont(new Font("SansSerif", Font.PLAIN, 11));
    isMetric.setMargin(new Insets(2, 2, 2, 2));
    isMetric.addItemListener(new UnitsListener());
    isMetric.setToolTipText("Toggles rulers' unit of measure "
        + "between inches and centimeters.");
    buttonCorner.add(isMetric); //Use the default FlowLayout
    buttonCorner.getAccessibleContext().setAccessibleName(
        "Upper Left Corner");
    String desc = "Fills the corner of a scroll pane "
        + "with color for aesthetic reasons.";
    Corner lowerLeft = new Corner();
    lowerLeft.getAccessibleContext().setAccessibleName("Lower Left Corner");
    lowerLeft.getAccessibleContext().setAccessibleDescription(desc);
    Corner upperRight = new Corner();
    upperRight.getAccessibleContext().setAccessibleName(
        "Upper Right Corner");
    upperRight.getAccessibleContext().setAccessibleDescription(desc);
    //Set up the scroll pane
    picture = new ScrollablePicture(david, columnView.getIncrement());
    picture.setToolTipText(david.getDescription());
    picture.getAccessibleContext().setAccessibleName("Scroll pane client");
    JScrollPane pictureScrollPane = new JScrollPane(picture);
    pictureScrollPane.setPreferredSize(new Dimension(300, 250));
    pictureScrollPane.setViewportBorder(BorderFactory
        .createLineBorder(Color.black));
    pictureScrollPane.setColumnHeaderView(columnView);
    pictureScrollPane.setRowHeaderView(rowView);
    pictureScrollPane
        .setCorner(JScrollPane.UPPER_LEFT_CORNER, buttonCorner);
    pictureScrollPane.setCorner(JScrollPane.LOWER_LEFT_CORNER, lowerLeft);
    pictureScrollPane.setCorner(JScrollPane.UPPER_RIGHT_CORNER, upperRight);
    //Put it in this panel
    setLayout(new BoxLayout(this, BoxLayout.X_AXIS));
    add(pictureScrollPane);
    setBorder(BorderFactory.createEmptyBorder(20, 20, 20, 20));
  }
  class UnitsListener implements ItemListener {
    public void itemStateChanged(ItemEvent e) {
      if (e.getStateChange() == ItemEvent.SELECTED) {
        //turn it to metric
        rowView.setIsMetric(true);
        columnView.setIsMetric(true);
      } else {
        //turn it to inches
        rowView.setIsMetric(false);
        columnView.setIsMetric(false);
      }
      picture.setMaxUnitIncrement(rowView.getIncrement());
    }
  }
  public static void main(String s[]) {
    JFrame frame = new JFrame("AccessibleScrollDemo");
    frame.addWindowListener(new WindowAdapter() {
      public void windowClosing(WindowEvent e) {
        System.exit(0);
      }
    });
    frame.setContentPane(new AccessibleScrollDemo());
    frame.pack();
    frame.setVisible(true);
  }
}
class Rule extends JComponent implements Accessible {
  public static final int INCH = Toolkit.getDefaultToolkit()
      .getScreenResolution();
  public static final int HORIZONTAL = 0;
  public static final int VERTICAL = 1;
  public static final int SIZE = 35;
  public int orientation;
  public boolean isMetric;
  private int increment;
  private int units;
  public Rule(int o, boolean m) {
    orientation = o;
    isMetric = m;
    setIncrementAndUnits();
  }
  public void setIsMetric(boolean isMetric) {
    if (accessibleContext != null && this.isMetric != isMetric) {
      if (isMetric) {
        accessibleContext.firePropertyChange(
            AccessibleContext.ACCESSIBLE_STATE_PROPERTY,
            AccessibleRulerState.INCHES,
            AccessibleRulerState.CENTIMETERS);
      } else {
        accessibleContext.firePropertyChange(
            AccessibleContext.ACCESSIBLE_STATE_PROPERTY,
            AccessibleRulerState.CENTIMETERS,
            AccessibleRulerState.INCHES);
      }
    }
    this.isMetric = isMetric;
    setIncrementAndUnits();
    repaint();
  }
  private void setIncrementAndUnits() {
    if (isMetric) {
      units = (int) ((double) INCH / (double) 2.54); // dots per
      // centimeter
      increment = units;
    } else {
      units = INCH;
      increment = units / 2;
    }
  }
  public boolean isMetric() {
    return this.isMetric;
  }
  public int getIncrement() {
    return increment;
  }
  public void setPreferredHeight(int ph) {
    setPreferredSize(new Dimension(SIZE, ph));
  }
  public void setPreferredWidth(int pw) {
    setPreferredSize(new Dimension(pw, SIZE));
  }
  public void paintComponent(Graphics g) {
    Rectangle drawHere = g.getClipBounds();
    // Fill clipping area with dirty brown/orange.
    g.setColor(new Color(230, 163, 4));
    g.fillRect(drawHere.x, drawHere.y, drawHere.width, drawHere.height);
    // Do the ruler labels in a small font that's black.
    g.setFont(new Font("SansSerif", Font.PLAIN, 10));
    g.setColor(Color.black);
    // Some vars we need.
    int end = 0;
    int start = 0;
    int tickLength = 0;
    String text = null;
    // Use clipping bounds to calculate first tick
    // and last tick location.
    if (orientation == HORIZONTAL) {
      start = (drawHere.x / increment) * increment;
      end = (((drawHere.x + drawHere.width) / increment) + 1) * increment;
    } else {
      start = (drawHere.y / increment) * increment;
      end = (((drawHere.y + drawHere.height) / increment) + 1)
          * increment;
    }
    // Make a special case of 0 to display the number
    // within the rule and draw a units label.
    if (start == 0) {
      text = Integer.toString(0) + (isMetric ? " cm" : " in");
      tickLength = 10;
      if (orientation == HORIZONTAL) {
        g.drawLine(0, SIZE - 1, 0, SIZE - tickLength - 1);
        g.drawString(text, 2, 21);
      } else {
        g.drawLine(SIZE - 1, 0, SIZE - tickLength - 1, 0);
        g.drawString(text, 9, 10);
      }
      text = null;
      start = increment;
    }
    // ticks and labels
    for (int i = start; i < end; i += increment) {
      if (i % units == 0) {
        tickLength = 10;
        text = Integer.toString(i / units);
      } else {
        tickLength = 7;
        text = null;
      }
      if (tickLength != 0) {
        if (orientation == HORIZONTAL) {
          g.drawLine(i, SIZE - 1, i, SIZE - tickLength - 1);
          if (text != null)
            g.drawString(text, i - 3, 21);
        } else {
          g.drawLine(SIZE - 1, i, SIZE - tickLength - 1, i);
          if (text != null)
            g.drawString(text, 9, i + 3);
        }
      }
    }
  }
  public AccessibleContext getAccessibleContext() {
    if (accessibleContext == null) {
      accessibleContext = new AccessibleRuler();
    }
    return accessibleContext;
  }
  protected class AccessibleRuler extends AccessibleJComponent {
    public AccessibleRole getAccessibleRole() {
      return AccessibleRuleRole.RULER;
    }
    public AccessibleStateSet getAccessibleStateSet() {
      AccessibleStateSet states = super.getAccessibleStateSet();
      if (orientation == VERTICAL) {
        states.add(AccessibleState.VERTICAL);
      } else {
        states.add(AccessibleState.HORIZONTAL);
      }
      if (isMetric) {
        states.add(AccessibleRulerState.CENTIMETERS);
      } else {
        states.add(AccessibleRulerState.INCHES);
      }
      return states;
    }
  }
}
class AccessibleRuleRole extends AccessibleRole {
  public static final AccessibleRuleRole RULER = new AccessibleRuleRole(
      "ruler");
  protected AccessibleRuleRole(String key) {
    super(key);
  }
  //Should really provide localizable versions of these names.
  public String toDisplayString(String resourceBundleName, Locale locale) {
    return key;
  }
}
class AccessibleRulerState extends AccessibleState {
  public static final AccessibleRulerState INCHES = new AccessibleRulerState(
      "inches");
  public static final AccessibleRulerState CENTIMETERS = new AccessibleRulerState(
      "centimeters");
  protected AccessibleRulerState(String key) {
    super(key);
  }
  //Should really provide localizable versions of these names.
  public String toDisplayString(String resourceBundleName, Locale locale) {
    return key;
  }
}
class ScrollablePicture extends JLabel implements Scrollable {
  private int maxUnitIncrement = 1;
  public ScrollablePicture(ImageIcon i, int m) {
    super(i);
    maxUnitIncrement = m;
  }
  public Dimension getPreferredScrollableViewportSize() {
    return getPreferredSize();
  }
  public int getScrollableUnitIncrement(Rectangle visibleRect,
      int orientation, int direction) {
    int currentPosition = 0;
    if (orientation == SwingConstants.HORIZONTAL)
      currentPosition = visibleRect.x;
    else
      currentPosition = visibleRect.y;
    if (direction < 0) {
      int newPosition = currentPosition
          - (currentPosition / maxUnitIncrement) * maxUnitIncrement;
      return (newPosition == 0) ? maxUnitIncrement : newPosition;
    } else {
      return ((currentPosition / maxUnitIncrement) + 1)
          * maxUnitIncrement - currentPosition;
    }
  }
  public int getScrollableBlockIncrement(Rectangle visibleRect,
      int orientation, int direction) {
    if (orientation == SwingConstants.HORIZONTAL)
      return visibleRect.width - maxUnitIncrement;
    else
      return visibleRect.height - maxUnitIncrement;
  }
  public boolean getScrollableTracksViewportWidth() {
    return false;
  }
  public boolean getScrollableTracksViewportHeight() {
    return false;
  }
  public void setMaxUnitIncrement(int pixels) {
    maxUnitIncrement = pixels;
  }
}
class Corner extends JComponent implements Accessible {
  public void paintComponent(Graphics g) {
    // Fill me with dirty brown/orange.
    g.setColor(new Color(230, 163, 4));
    g.fillRect(0, 0, getWidth(), getHeight());
  }
  public AccessibleContext getAccessibleContext() {
    if (accessibleContext == null) {
      accessibleContext = new AccessibleCorner();
    }
    return accessibleContext;
  }
  protected class AccessibleCorner extends AccessibleJComponent {
    //Inherit everything, override nothing.
  }
}