AccessibleScrollDemo.java Source code

Java tutorial

Introduction

Here is the source code for AccessibleScrollDemo.java

Source

/* 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.
    }
}