alvinalexander.com | career | drupal | java | mac | mysql | perl | scala | uml | unix  

Java example source code file (HighPrecisionJScrollBar.java)

This example Java source code file (HighPrecisionJScrollBar.java) is included in the alvinalexander.com "Java Source Code Warehouse" project. The intent of this project is to help you "Learn Java by Example" TM.

Learn more about this Java project at its project page.

Java - Java tags/keywords

awt, bad, big_range, bigdecimal, biginteger, block_increment, changeevent, changelistener, event, gui, highprecisionjscrollbar, iterator, jframe, math, minimum, runtimeexception, scale, swing, unit_increment, util

The HighPrecisionJScrollBar.java Java example source code

/*
 * Copyright (c) 2000, 2008, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 *
 */

package sun.jvm.hotspot.ui;

import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.*;
import java.math.*;
import java.util.*;

/** A JScrollBar which uses BigIntegers as the representation for the
    minimum, maximum, unit increment, etc. Interaction with the
    buttons and track is accurate to unit and block increments;
    however, if the scale of the scrollbar (defined by
    getMaximumHP().subtract(getMinimumHP())) is very large, each
    interaction with the thumb will necessarily cause extremely large
    motion of the value. */

public class HighPrecisionJScrollBar extends JScrollBar {
  private BigInteger valueHP;
  private BigInteger visibleHP;
  private BigInteger minimumHP;
  private BigInteger maximumHP;
  private BigInteger unitIncrementHP;
  private BigInteger blockIncrementHP;
  private BigDecimal scaleFactor;
  private BigInteger rangeHP;
  // The underlying scrollbar runs a range from 0..BIG_RANGE-1
  private static final int BIG_RANGE = 10000;
  // Do we need to scale HP values up/down to fit in 0..BIG_RANGE-1?
  private boolean    down;
  private java.util.List changeListeners = new ArrayList();
  // Number of digits after decimal point to use when scaling between
  // high and low precision
  private static final int SCALE = 20;


  // This is a hack to allow us to differentiate between clicks on the
  // arrow and track since we can't get useful information from
  // JScrollBars' AdjustmentListener (bug in design of BasicUI
  // classes; FIXME: file RFE.)
  private static final int UNIT_INCREMENT  = 1;
  private static final int BLOCK_INCREMENT = 2;
  private static final int MINIMUM = 0;
  private static final int MAXIMUM = 65536;
  private boolean updating = false;
  private int lastValueSeen = -1;

  public HighPrecisionJScrollBar() {
    super();
    initialize();
    installListener();
  }

  public HighPrecisionJScrollBar(int orientation) {
    super(orientation);
    initialize();
    installListener();
  }

  /** value, minimum and maximum should be positive */
  public HighPrecisionJScrollBar(int orientation, BigInteger value, BigInteger minimum, BigInteger maximum) {
    super(orientation);
    initialize(value, minimum, maximum);
    installListener();
  }

  public BigInteger getValueHP() {
    return valueHP;
  }


  /** NOTE: the real value will always be set to be (value mod
      unitIncrement) == 0, subtracting off the mod of the passed value
      if necessary. */

  public void setValueHP(BigInteger value) {
    if (value.compareTo(getMaximumHP()) > 0) {
      value = getMaximumHP();
    } else if (value.compareTo(getMinimumHP()) < 0) {
      value = getMinimumHP();
    }
    valueHP = value.subtract(value.mod(unitIncrementHP));
    int lpValue = toUnderlyingRange(this.valueHP);
    if (getValueHP().add(getVisibleAmountHP()).compareTo(getMaximumHP()) >= 0 ) {
      lpValue = BIG_RANGE - getVisibleAmount();
    }
    lastValueSeen = lpValue;
    setValue(lpValue);
    fireStateChanged();
  }
  public BigInteger getMinimumHP() {
    return minimumHP;
  }

  public void setMinimumHP(BigInteger minimum) {
    setRange(minimum, maximumHP);
    updateScrollBarValues();
  }

  public BigInteger getMaximumHP() {
    return maximumHP;
  }

  public void setMaximumHP(BigInteger maximum) {
    setRange(minimumHP, maximum);
    updateScrollBarValues();
  }

  public BigInteger getVisibleAmountHP() {
    return visibleHP;
  }

  public void setVisibleAmountHP(BigInteger visibleAmount) {
    this.visibleHP = visibleAmount;
    // int lpVisAmt = toUnderlyingRange(visibleAmount);
    // Make certain that visibleAmount value that are full range come out looking like full range
    int lpVisAmt;
    if (visibleAmount.compareTo(rangeHP) < 0) {
      lpVisAmt = scaleToUnderlying(visibleAmount);
      if (lpVisAmt == 0) {
        lpVisAmt = 1;
      }
      setVisible(true);
    } else {
      lpVisAmt = BIG_RANGE;
      setVisible(false);
    }
    setVisibleAmount(lpVisAmt);
  }

  public BigInteger getBlockIncrementHP() {
    return blockIncrementHP;
  }

  public void setBlockIncrementHP(BigInteger blockIncrement) {
    this.blockIncrementHP = blockIncrement;
    // NOTE we do not forward this to the underlying scrollBar because of
    // the earlier mentioned hack.
  }

  public BigInteger getUnitIncrementHP() {
    return unitIncrementHP;
  }

  public void setUnitIncrementHP(BigInteger unitIncrement) {
    this.unitIncrementHP = unitIncrement;
    // NOTE we do not forward this to the underlying scrollBar because of
    // the earlier mentioned hack.
  }


  public void addChangeListener(ChangeListener l) {
    changeListeners.add(l);
  }

  public void removeChangeListener(ChangeListener l) {
    changeListeners.remove(l);
  }

  //----------------------------------------------------------------------
  // Programmatic access to scrollbar functionality
  // (Causes change events to be sent)

  public void scrollUpOrLeft() {
    if (updating) return;
    beginUpdate();
    setValueHP(getValueHP().subtract(getUnitIncrementHP()));
    endUpdate();
  }

  public void scrollDownOrRight() {
    if (updating) return;
    beginUpdate();
    setValueHP(getValueHP().add(getUnitIncrementHP()));
    endUpdate();
  }

  public void pageUpOrLeft() {
    if (updating) return;
    beginUpdate();
    setValueHP(getValueHP().subtract(getBlockIncrementHP()));
    endUpdate();
  }

  public void pageDownOrRight() {
    if (updating) return;
    beginUpdate();
    setValueHP(getValueHP().add(getBlockIncrementHP()));
    endUpdate();
  }

  //----------------------------------------------------------------------
  // Internals only below this point
  //

  private void beginUpdate() {
    updating = true;
  }

  private void endUpdate() {
    updating = false;
  }

  private void initialize(BigInteger value, BigInteger minimum, BigInteger maximum) {
    // Initialize the underlying scrollbar to the standard range values
    // The increments are important and are how we differentiate arrow from track events
    setMinimum(0);
    setMaximum(BIG_RANGE - 1);
    setValue(0);
    setVisibleAmount(1);
    setUnitIncrement(UNIT_INCREMENT);
    setBlockIncrement(BLOCK_INCREMENT);

    setUnitIncrementHP(new BigInteger(Integer.toString(getUnitIncrement())));
    setBlockIncrementHP(new BigInteger(Integer.toString(getBlockIncrement())));

    // Must set range and value first (it sets min/max)
    setRange(minimum, maximum);

    setVisibleAmountHP(new BigInteger(Integer.toString(getVisibleAmount())));
    setValueHP(value);
  }

  private void initialize() {
    BigInteger min = new BigInteger(Integer.toString(getMinimum()));
    BigInteger max = new BigInteger(Integer.toString(getMaximum()));
    initialize(min, min, max);
  }

  private void setRange(BigInteger minimum, BigInteger maximum) {
    if (minimum.compareTo(maximum) > 0 ) {
      throw new RuntimeException("Bad scrollbar range " + minimum + " > " + maximum);
    }
    minimumHP = minimum;
    maximumHP = maximum;
    rangeHP = maximum.subtract(minimum).add(BigInteger.ONE);
    BigInteger range2 = new BigInteger(Integer.toString(BIG_RANGE));
    if (rangeHP.compareTo(range2) >= 0 ) {
      down = true;
      scaleFactor = new BigDecimal(rangeHP, SCALE).divide(new BigDecimal(range2, SCALE), BigDecimal.ROUND_DOWN).max(new BigDecimal(BigInteger.ONE));
    } else {
      down = false;
      scaleFactor = new BigDecimal(range2, SCALE).divide(new BigDecimal(rangeHP, SCALE), BigDecimal.ROUND_DOWN).max(new BigDecimal(BigInteger.ONE));
    }
    // FIXME: should put in original scaling algorithm (shifting by
    // number of bits) as alternative when scale between low and high
    // precision is very large
  }

  // A range update is complete. Rescale our computed values and
  // inform the underlying scrollBar as needed.
  private void updateScrollBarValues() {
    setValueHP(getValueHP());
    setVisibleAmountHP(getVisibleAmountHP());
    setBlockIncrementHP(getBlockIncrementHP());
    setUnitIncrementHP(getUnitIncrementHP());
  }

  private BigDecimal getScaleFactor() {
    return scaleFactor;
  }


  // Value scaling routines
  private BigInteger scaleToHP(int i) {
    BigDecimal ib = new BigDecimal(Integer.toString(i));
    if (down) return ib.multiply(getScaleFactor()).toBigInteger();
    else return ib.divide(getScaleFactor(), BigDecimal.ROUND_DOWN).toBigInteger();
  }

  private int scaleToUnderlying(BigInteger i) {
    BigDecimal d = new BigDecimal(i);
    if (down) return d.divide(getScaleFactor(), BigDecimal.ROUND_DOWN).intValue();
    else return d.multiply(getScaleFactor()).intValue();
  }

  // Range scaling routines
  private BigInteger toHPRange(int i) {
    return scaleToHP(i).add(minimumHP);
    // return ib.shiftLeft(Math.max(2, maximumHP.bitLength() - 33));
  }

  private int toUnderlyingRange(BigInteger i) {
    return scaleToUnderlying(i.subtract(minimumHP));
    // return i.shiftRight(Math.max(2, maximumHP.bitLength() - 33)).intValue();
  }

  private void installListener() {
    super.addAdjustmentListener(new AdjustmentListener() {
        public void adjustmentValueChanged(AdjustmentEvent e) {
          if (updating) {
            return;
          }
          beginUpdate();
          switch (e.getAdjustmentType()) {
          case AdjustmentEvent.TRACK:
            int val = e.getValue();
            int diff = val - lastValueSeen;
            int absDiff = Math.abs(diff);
            //            System.err.println("diff: " + diff + " absDiff: " + absDiff);
            if (absDiff == UNIT_INCREMENT) {
              if (diff > 0) {
                //                System.err.println("case 1");
                setValueHP(getValueHP().add(getUnitIncrementHP()));
              } else {
                //                System.err.println("case 2");
                setValueHP(getValueHP().subtract(getUnitIncrementHP()));
              }
            } else if (absDiff == BLOCK_INCREMENT) {
              if (diff > 0) {
                //                System.err.println("case 3");
                setValueHP(getValueHP().add(getBlockIncrementHP()));
              } else {
                //                System.err.println("case 4");
                setValueHP(getValueHP().subtract(getBlockIncrementHP()));
              }
            } else {
              //              System.err.println("case 5");
              // FIXME: seem to be getting spurious update events,
              // with diff = 0, upon mouse down/up on the track
              if (absDiff != 0) {
                // Convert low-precision value to high precision
                // (note we lose the low bits)
                BigInteger i = null;
                if (e.getValue() == getMinimum()) {
                  i = getMinimumHP();
                } else if (e.getValue() >= getMaximum() - 1) {
                  i = getMaximumHP();
                } else {
                  i = toHPRange(e.getValue());
                }
                setValueHP(i);
              }
            }
            break;
          default:
            // Should not reach here, but leaving it a no-op in case
            // we later get the other events (should revisit code in
            // that case)
            break;
          }
          endUpdate();
        }
      });
  }

  private void fireStateChanged() {
    ChangeEvent e = null;
    for (Iterator iter = changeListeners.iterator(); iter.hasNext(); ) {
      ChangeListener l = (ChangeListener) iter.next();
      if (e == null) {
        e = new ChangeEvent(this);
      }
      l.stateChanged(e);
    }
  }

  public static void main(String[] args) {
    JFrame frame = new JFrame();
    frame.setSize(300, 300);
    // 32-bit version
    /*
    HighPrecisionJScrollBar hpsb =
      new HighPrecisionJScrollBar(
        JScrollBar.VERTICAL,
        new BigInteger(1, new byte[] {
          (byte) 0x80, (byte) 0x00, (byte) 0x00, (byte) 0x00}),
        new BigInteger(1, new byte[] {
          (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00}),
        new BigInteger(1, new byte[] {
          (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF}));
    hpsb.setUnitIncrementHP(new BigInteger(1, new byte[] {
      (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x01}));
    hpsb.setBlockIncrementHP(new BigInteger(1, new byte[] {
      (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x10}));
    */

    // 64-bit version
    HighPrecisionJScrollBar hpsb =
      new HighPrecisionJScrollBar(
        JScrollBar.VERTICAL,
        new BigInteger(1, new byte[] {
          (byte) 0x80, (byte) 0x00, (byte) 0x00, (byte) 0x00,
          (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00}),
        new BigInteger(1, new byte[] {
          (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
          (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00}),
        new BigInteger(1, new byte[] {
          (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF,
          (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF}));
    hpsb.setUnitIncrementHP(new BigInteger(1, new byte[] {
      (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
      (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x01}));
    hpsb.setBlockIncrementHP(new BigInteger(1, new byte[] {
      (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
      (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x10}));
    hpsb.addChangeListener(new ChangeListener() {
        public void stateChanged(ChangeEvent e) {
          HighPrecisionJScrollBar h = (HighPrecisionJScrollBar) e.getSource();
          System.out.println("New value = 0x" + h.getValueHP().toString(16));
        }
      });
    frame.getContentPane().add(hpsb);
    frame.setVisible(true);
  }

}

Other Java examples (source code examples)

Here is a short list of links related to this Java HighPrecisionJScrollBar.java source code file:

... this post is sponsored by my books ...

#1 New Release!

FP Best Seller

 

new blog posts

 

Copyright 1998-2021 Alvin Alexander, alvinalexander.com
All Rights Reserved.

A percentage of advertising revenue from
pages under the /java/jwarehouse URI on this website is
paid back to open source projects.