Path: blob/master/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/ui/HighPrecisionJScrollBar.java
41161 views
/*1* Copyright (c) 2000, 2020, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation.7*8* This code is distributed in the hope that it will be useful, but WITHOUT9* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or10* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License11* version 2 for more details (a copy is included in the LICENSE file that12* accompanied this code).13*14* You should have received a copy of the GNU General Public License version15* 2 along with this work; if not, write to the Free Software Foundation,16* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.17*18* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA19* or visit www.oracle.com if you need additional information or have any20* questions.21*22*/2324package sun.jvm.hotspot.ui;2526import java.awt.event.*;27import javax.swing.*;28import javax.swing.event.*;29import java.math.*;30import java.util.*;3132/** A JScrollBar which uses BigIntegers as the representation for the33minimum, maximum, unit increment, etc. Interaction with the34buttons and track is accurate to unit and block increments;35however, if the scale of the scrollbar (defined by36getMaximumHP().subtract(getMinimumHP())) is very large, each37interaction with the thumb will necessarily cause extremely large38motion of the value. */3940public class HighPrecisionJScrollBar extends JScrollBar {41private BigInteger valueHP;42private BigInteger visibleHP;43private BigInteger minimumHP;44private BigInteger maximumHP;45private BigInteger unitIncrementHP;46private BigInteger blockIncrementHP;47private BigDecimal scaleFactor;48private BigInteger rangeHP;49// The underlying scrollbar runs a range from 0..BIG_RANGE-150private static final int BIG_RANGE = 10000;51// Do we need to scale HP values up/down to fit in 0..BIG_RANGE-1?52private boolean down;53private java.util.List<ChangeListener> changeListeners = new ArrayList<>();54// Number of digits after decimal point to use when scaling between55// high and low precision56private static final int SCALE = 20;575859// This is a hack to allow us to differentiate between clicks on the60// arrow and track since we can't get useful information from61// JScrollBars' AdjustmentListener (bug in design of BasicUI62// classes; FIXME: file RFE.)63private static final int UNIT_INCREMENT = 1;64private static final int BLOCK_INCREMENT = 2;65private static final int MINIMUM = 0;66private static final int MAXIMUM = 65536;67private boolean updating = false;68private int lastValueSeen = -1;6970public HighPrecisionJScrollBar() {71super();72initialize();73installListener();74}7576public HighPrecisionJScrollBar(int orientation) {77super(orientation);78initialize();79installListener();80}8182/** value, minimum and maximum should be positive */83public HighPrecisionJScrollBar(int orientation, BigInteger value, BigInteger minimum, BigInteger maximum) {84super(orientation);85initialize(value, minimum, maximum);86installListener();87}8889public BigInteger getValueHP() {90return valueHP;91}929394/** NOTE: the real value will always be set to be (value mod95unitIncrement) == 0, subtracting off the mod of the passed value96if necessary. */9798public void setValueHP(BigInteger value) {99if (value.compareTo(getMaximumHP()) > 0) {100value = getMaximumHP();101} else if (value.compareTo(getMinimumHP()) < 0) {102value = getMinimumHP();103}104valueHP = value.subtract(value.mod(unitIncrementHP));105int lpValue = toUnderlyingRange(this.valueHP);106if (getValueHP().add(getVisibleAmountHP()).compareTo(getMaximumHP()) >= 0 ) {107lpValue = BIG_RANGE - getVisibleAmount();108}109lastValueSeen = lpValue;110setValue(lpValue);111fireStateChanged();112}113public BigInteger getMinimumHP() {114return minimumHP;115}116117public void setMinimumHP(BigInteger minimum) {118setRange(minimum, maximumHP);119updateScrollBarValues();120}121122public BigInteger getMaximumHP() {123return maximumHP;124}125126public void setMaximumHP(BigInteger maximum) {127setRange(minimumHP, maximum);128updateScrollBarValues();129}130131public BigInteger getVisibleAmountHP() {132return visibleHP;133}134135public void setVisibleAmountHP(BigInteger visibleAmount) {136this.visibleHP = visibleAmount;137// int lpVisAmt = toUnderlyingRange(visibleAmount);138// Make certain that visibleAmount value that are full range come out looking like full range139int lpVisAmt;140if (visibleAmount.compareTo(rangeHP) < 0) {141lpVisAmt = scaleToUnderlying(visibleAmount);142if (lpVisAmt == 0) {143lpVisAmt = 1;144}145setVisible(true);146} else {147lpVisAmt = BIG_RANGE;148setVisible(false);149}150setVisibleAmount(lpVisAmt);151}152153public BigInteger getBlockIncrementHP() {154return blockIncrementHP;155}156157public void setBlockIncrementHP(BigInteger blockIncrement) {158this.blockIncrementHP = blockIncrement;159// NOTE we do not forward this to the underlying scrollBar because of160// the earlier mentioned hack.161}162163public BigInteger getUnitIncrementHP() {164return unitIncrementHP;165}166167public void setUnitIncrementHP(BigInteger unitIncrement) {168this.unitIncrementHP = unitIncrement;169// NOTE we do not forward this to the underlying scrollBar because of170// the earlier mentioned hack.171}172173174public void addChangeListener(ChangeListener l) {175changeListeners.add(l);176}177178public void removeChangeListener(ChangeListener l) {179changeListeners.remove(l);180}181182//----------------------------------------------------------------------183// Programmatic access to scrollbar functionality184// (Causes change events to be sent)185186public void scrollUpOrLeft() {187if (updating) return;188beginUpdate();189setValueHP(getValueHP().subtract(getUnitIncrementHP()));190endUpdate();191}192193public void scrollDownOrRight() {194if (updating) return;195beginUpdate();196setValueHP(getValueHP().add(getUnitIncrementHP()));197endUpdate();198}199200public void pageUpOrLeft() {201if (updating) return;202beginUpdate();203setValueHP(getValueHP().subtract(getBlockIncrementHP()));204endUpdate();205}206207public void pageDownOrRight() {208if (updating) return;209beginUpdate();210setValueHP(getValueHP().add(getBlockIncrementHP()));211endUpdate();212}213214//----------------------------------------------------------------------215// Internals only below this point216//217218private void beginUpdate() {219updating = true;220}221222private void endUpdate() {223updating = false;224}225226private void initialize(BigInteger value, BigInteger minimum, BigInteger maximum) {227// Initialize the underlying scrollbar to the standard range values228// The increments are important and are how we differentiate arrow from track events229setMinimum(0);230setMaximum(BIG_RANGE - 1);231setValue(0);232setVisibleAmount(1);233setUnitIncrement(UNIT_INCREMENT);234setBlockIncrement(BLOCK_INCREMENT);235236setUnitIncrementHP(new BigInteger(Integer.toString(getUnitIncrement())));237setBlockIncrementHP(new BigInteger(Integer.toString(getBlockIncrement())));238239// Must set range and value first (it sets min/max)240setRange(minimum, maximum);241242setVisibleAmountHP(new BigInteger(Integer.toString(getVisibleAmount())));243setValueHP(value);244}245246private void initialize() {247BigInteger min = new BigInteger(Integer.toString(getMinimum()));248BigInteger max = new BigInteger(Integer.toString(getMaximum()));249initialize(min, min, max);250}251252private void setRange(BigInteger minimum, BigInteger maximum) {253if (minimum.compareTo(maximum) > 0 ) {254throw new RuntimeException("Bad scrollbar range " + minimum + " > " + maximum);255}256minimumHP = minimum;257maximumHP = maximum;258rangeHP = maximum.subtract(minimum).add(BigInteger.ONE);259BigInteger range2 = new BigInteger(Integer.toString(BIG_RANGE));260if (rangeHP.compareTo(range2) >= 0 ) {261down = true;262scaleFactor = new BigDecimal(rangeHP, SCALE).divide(new BigDecimal(range2, SCALE), RoundingMode.DOWN).max(new BigDecimal(BigInteger.ONE));263} else {264down = false;265scaleFactor = new BigDecimal(range2, SCALE).divide(new BigDecimal(rangeHP, SCALE), RoundingMode.DOWN).max(new BigDecimal(BigInteger.ONE));266}267// FIXME: should put in original scaling algorithm (shifting by268// number of bits) as alternative when scale between low and high269// precision is very large270}271272// A range update is complete. Rescale our computed values and273// inform the underlying scrollBar as needed.274private void updateScrollBarValues() {275setValueHP(getValueHP());276setVisibleAmountHP(getVisibleAmountHP());277setBlockIncrementHP(getBlockIncrementHP());278setUnitIncrementHP(getUnitIncrementHP());279}280281private BigDecimal getScaleFactor() {282return scaleFactor;283}284285286// Value scaling routines287private BigInteger scaleToHP(int i) {288BigDecimal ib = new BigDecimal(Integer.toString(i));289if (down) return ib.multiply(getScaleFactor()).toBigInteger();290else return ib.divide(getScaleFactor(), RoundingMode.DOWN).toBigInteger();291}292293private int scaleToUnderlying(BigInteger i) {294BigDecimal d = new BigDecimal(i);295if (down) return d.divide(getScaleFactor(), RoundingMode.DOWN).intValue();296else return d.multiply(getScaleFactor()).intValue();297}298299// Range scaling routines300private BigInteger toHPRange(int i) {301return scaleToHP(i).add(minimumHP);302// return ib.shiftLeft(Math.max(2, maximumHP.bitLength() - 33));303}304305private int toUnderlyingRange(BigInteger i) {306return scaleToUnderlying(i.subtract(minimumHP));307// return i.shiftRight(Math.max(2, maximumHP.bitLength() - 33)).intValue();308}309310private void installListener() {311super.addAdjustmentListener(new AdjustmentListener() {312public void adjustmentValueChanged(AdjustmentEvent e) {313if (updating) {314return;315}316beginUpdate();317switch (e.getAdjustmentType()) {318case AdjustmentEvent.TRACK:319int val = e.getValue();320int diff = val - lastValueSeen;321int absDiff = Math.abs(diff);322// System.err.println("diff: " + diff + " absDiff: " + absDiff);323if (absDiff == UNIT_INCREMENT) {324if (diff > 0) {325// System.err.println("case 1");326setValueHP(getValueHP().add(getUnitIncrementHP()));327} else {328// System.err.println("case 2");329setValueHP(getValueHP().subtract(getUnitIncrementHP()));330}331} else if (absDiff == BLOCK_INCREMENT) {332if (diff > 0) {333// System.err.println("case 3");334setValueHP(getValueHP().add(getBlockIncrementHP()));335} else {336// System.err.println("case 4");337setValueHP(getValueHP().subtract(getBlockIncrementHP()));338}339} else {340// System.err.println("case 5");341// FIXME: seem to be getting spurious update events,342// with diff = 0, upon mouse down/up on the track343if (absDiff != 0) {344// Convert low-precision value to high precision345// (note we lose the low bits)346BigInteger i = null;347if (e.getValue() == getMinimum()) {348i = getMinimumHP();349} else if (e.getValue() >= getMaximum() - 1) {350i = getMaximumHP();351} else {352i = toHPRange(e.getValue());353}354setValueHP(i);355}356}357break;358default:359// Should not reach here, but leaving it a no-op in case360// we later get the other events (should revisit code in361// that case)362break;363}364endUpdate();365}366});367}368369private void fireStateChanged() {370ChangeEvent e = null;371for (Iterator iter = changeListeners.iterator(); iter.hasNext(); ) {372ChangeListener l = (ChangeListener) iter.next();373if (e == null) {374e = new ChangeEvent(this);375}376l.stateChanged(e);377}378}379380public static void main(String[] args) {381JFrame frame = new JFrame();382frame.setSize(300, 300);383// 32-bit version384/*385HighPrecisionJScrollBar hpsb =386new HighPrecisionJScrollBar(387JScrollBar.VERTICAL,388new BigInteger(1, new byte[] {389(byte) 0x80, (byte) 0x00, (byte) 0x00, (byte) 0x00}),390new BigInteger(1, new byte[] {391(byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00}),392new BigInteger(1, new byte[] {393(byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF}));394hpsb.setUnitIncrementHP(new BigInteger(1, new byte[] {395(byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x01}));396hpsb.setBlockIncrementHP(new BigInteger(1, new byte[] {397(byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x10}));398*/399400// 64-bit version401HighPrecisionJScrollBar hpsb =402new HighPrecisionJScrollBar(403JScrollBar.VERTICAL,404new BigInteger(1, new byte[] {405(byte) 0x80, (byte) 0x00, (byte) 0x00, (byte) 0x00,406(byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00}),407new BigInteger(1, new byte[] {408(byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,409(byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00}),410new BigInteger(1, new byte[] {411(byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF,412(byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF}));413hpsb.setUnitIncrementHP(new BigInteger(1, new byte[] {414(byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,415(byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x01}));416hpsb.setBlockIncrementHP(new BigInteger(1, new byte[] {417(byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,418(byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x10}));419hpsb.addChangeListener(new ChangeListener() {420public void stateChanged(ChangeEvent e) {421HighPrecisionJScrollBar h = (HighPrecisionJScrollBar) e.getSource();422System.out.println("New value = 0x" + h.getValueHP().toString(16));423}424});425frame.getContentPane().add(hpsb);426frame.setVisible(true);427}428429}430431432