0
|
1 /*
|
|
2 * Copyright 2000-2002 Sun Microsystems, Inc. All Rights Reserved.
|
|
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
|
4 *
|
|
5 * This code is free software; you can redistribute it and/or modify it
|
|
6 * under the terms of the GNU General Public License version 2 only, as
|
|
7 * published by the Free Software Foundation.
|
|
8 *
|
|
9 * This code is distributed in the hope that it will be useful, but WITHOUT
|
|
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
|
12 * version 2 for more details (a copy is included in the LICENSE file that
|
|
13 * accompanied this code).
|
|
14 *
|
|
15 * You should have received a copy of the GNU General Public License version
|
|
16 * 2 along with this work; if not, write to the Free Software Foundation,
|
|
17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
18 *
|
|
19 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
|
|
20 * CA 95054 USA or visit www.sun.com if you need additional information or
|
|
21 * have any questions.
|
|
22 *
|
|
23 */
|
|
24
|
|
25 package sun.jvm.hotspot.ui;
|
|
26
|
|
27 import java.awt.event.*;
|
|
28 import javax.swing.*;
|
|
29 import javax.swing.event.*;
|
|
30 import java.math.*;
|
|
31 import java.util.*;
|
|
32
|
|
33 /** A JScrollBar which uses BigIntegers as the representation for the
|
|
34 minimum, maximum, unit increment, etc. Interaction with the
|
|
35 buttons and track is accurate to unit and block increments;
|
|
36 however, if the scale of the scrollbar (defined by
|
|
37 getMaximumHP().subtract(getMinimumHP())) is very large, each
|
|
38 interaction with the thumb will necessarily cause extremely large
|
|
39 motion of the value. */
|
|
40
|
|
41 public class HighPrecisionJScrollBar extends JScrollBar {
|
|
42 private BigInteger valueHP;
|
|
43 private BigInteger visibleHP;
|
|
44 private BigInteger minimumHP;
|
|
45 private BigInteger maximumHP;
|
|
46 private BigInteger unitIncrementHP;
|
|
47 private BigInteger blockIncrementHP;
|
|
48 private BigDecimal scaleFactor;
|
|
49 private BigInteger rangeHP;
|
|
50 // The underlying scrollbar runs a range from 0..BIG_RANGE-1
|
|
51 private static final int BIG_RANGE = 10000;
|
|
52 // Do we need to scale HP values up/down to fit in 0..BIG_RANGE-1?
|
|
53 private boolean down;
|
|
54 private java.util.List changeListeners = new ArrayList();
|
|
55 // Number of digits after decimal point to use when scaling between
|
|
56 // high and low precision
|
|
57 private static final int SCALE = 20;
|
|
58
|
|
59
|
|
60 // This is a hack to allow us to differentiate between clicks on the
|
|
61 // arrow and track since we can't get useful information from
|
|
62 // JScrollBars' AdjustmentListener (bug in design of BasicUI
|
|
63 // classes; FIXME: file RFE.)
|
|
64 private static final int UNIT_INCREMENT = 1;
|
|
65 private static final int BLOCK_INCREMENT = 2;
|
|
66 private static final int MINIMUM = 0;
|
|
67 private static final int MAXIMUM = 65536;
|
|
68 private boolean updating = false;
|
|
69 private int lastValueSeen = -1;
|
|
70
|
|
71 public HighPrecisionJScrollBar() {
|
|
72 super();
|
|
73 initialize();
|
|
74 installListener();
|
|
75 }
|
|
76
|
|
77 public HighPrecisionJScrollBar(int orientation) {
|
|
78 super(orientation);
|
|
79 initialize();
|
|
80 installListener();
|
|
81 }
|
|
82
|
|
83 /** value, minimum and maximum should be positive */
|
|
84 public HighPrecisionJScrollBar(int orientation, BigInteger value, BigInteger minimum, BigInteger maximum) {
|
|
85 super(orientation);
|
|
86 initialize(value, minimum, maximum);
|
|
87 installListener();
|
|
88 }
|
|
89
|
|
90 public BigInteger getValueHP() {
|
|
91 return valueHP;
|
|
92 }
|
|
93
|
|
94
|
|
95 /** NOTE: the real value will always be set to be (value mod
|
|
96 unitIncrement) == 0, subtracting off the mod of the passed value
|
|
97 if necessary. */
|
|
98
|
|
99 public void setValueHP(BigInteger value) {
|
|
100 if (value.compareTo(getMaximumHP()) > 0) {
|
|
101 value = getMaximumHP();
|
|
102 } else if (value.compareTo(getMinimumHP()) < 0) {
|
|
103 value = getMinimumHP();
|
|
104 }
|
|
105 valueHP = value.subtract(value.mod(unitIncrementHP));
|
|
106 int lpValue = toUnderlyingRange(this.valueHP);
|
|
107 if (getValueHP().add(getVisibleAmountHP()).compareTo(getMaximumHP()) >= 0 ) {
|
|
108 lpValue = BIG_RANGE - getVisibleAmount();
|
|
109 }
|
|
110 lastValueSeen = lpValue;
|
|
111 setValue(lpValue);
|
|
112 fireStateChanged();
|
|
113 }
|
|
114 public BigInteger getMinimumHP() {
|
|
115 return minimumHP;
|
|
116 }
|
|
117
|
|
118 public void setMinimumHP(BigInteger minimum) {
|
|
119 setRange(minimum, maximumHP);
|
|
120 updateScrollBarValues();
|
|
121 }
|
|
122
|
|
123 public BigInteger getMaximumHP() {
|
|
124 return maximumHP;
|
|
125 }
|
|
126
|
|
127 public void setMaximumHP(BigInteger maximum) {
|
|
128 setRange(minimumHP, maximum);
|
|
129 updateScrollBarValues();
|
|
130 }
|
|
131
|
|
132 public BigInteger getVisibleAmountHP() {
|
|
133 return visibleHP;
|
|
134 }
|
|
135
|
|
136 public void setVisibleAmountHP(BigInteger visibleAmount) {
|
|
137 this.visibleHP = visibleAmount;
|
|
138 // int lpVisAmt = toUnderlyingRange(visibleAmount);
|
|
139 // Make certain that visibleAmount value that are full range come out looking like full range
|
|
140 int lpVisAmt;
|
|
141 if (visibleAmount.compareTo(rangeHP) < 0) {
|
|
142 lpVisAmt = scaleToUnderlying(visibleAmount);
|
|
143 if (lpVisAmt == 0) {
|
|
144 lpVisAmt = 1;
|
|
145 }
|
|
146 setVisible(true);
|
|
147 } else {
|
|
148 lpVisAmt = BIG_RANGE;
|
|
149 setVisible(false);
|
|
150 }
|
|
151 setVisibleAmount(lpVisAmt);
|
|
152 }
|
|
153
|
|
154 public BigInteger getBlockIncrementHP() {
|
|
155 return blockIncrementHP;
|
|
156 }
|
|
157
|
|
158 public void setBlockIncrementHP(BigInteger blockIncrement) {
|
|
159 this.blockIncrementHP = blockIncrement;
|
|
160 // NOTE we do not forward this to the underlying scrollBar because of
|
|
161 // the earlier mentioned hack.
|
|
162 }
|
|
163
|
|
164 public BigInteger getUnitIncrementHP() {
|
|
165 return unitIncrementHP;
|
|
166 }
|
|
167
|
|
168 public void setUnitIncrementHP(BigInteger unitIncrement) {
|
|
169 this.unitIncrementHP = unitIncrement;
|
|
170 // NOTE we do not forward this to the underlying scrollBar because of
|
|
171 // the earlier mentioned hack.
|
|
172 }
|
|
173
|
|
174
|
|
175 public void addChangeListener(ChangeListener l) {
|
|
176 changeListeners.add(l);
|
|
177 }
|
|
178
|
|
179 public void removeChangeListener(ChangeListener l) {
|
|
180 changeListeners.remove(l);
|
|
181 }
|
|
182
|
|
183 //----------------------------------------------------------------------
|
|
184 // Programmatic access to scrollbar functionality
|
|
185 // (Causes change events to be sent)
|
|
186
|
|
187 public void scrollUpOrLeft() {
|
|
188 if (updating) return;
|
|
189 beginUpdate();
|
|
190 setValueHP(getValueHP().subtract(getUnitIncrementHP()));
|
|
191 endUpdate();
|
|
192 }
|
|
193
|
|
194 public void scrollDownOrRight() {
|
|
195 if (updating) return;
|
|
196 beginUpdate();
|
|
197 setValueHP(getValueHP().add(getUnitIncrementHP()));
|
|
198 endUpdate();
|
|
199 }
|
|
200
|
|
201 public void pageUpOrLeft() {
|
|
202 if (updating) return;
|
|
203 beginUpdate();
|
|
204 setValueHP(getValueHP().subtract(getBlockIncrementHP()));
|
|
205 endUpdate();
|
|
206 }
|
|
207
|
|
208 public void pageDownOrRight() {
|
|
209 if (updating) return;
|
|
210 beginUpdate();
|
|
211 setValueHP(getValueHP().add(getBlockIncrementHP()));
|
|
212 endUpdate();
|
|
213 }
|
|
214
|
|
215 //----------------------------------------------------------------------
|
|
216 // Internals only below this point
|
|
217 //
|
|
218
|
|
219 private void beginUpdate() {
|
|
220 updating = true;
|
|
221 }
|
|
222
|
|
223 private void endUpdate() {
|
|
224 updating = false;
|
|
225 }
|
|
226
|
|
227 private void initialize(BigInteger value, BigInteger minimum, BigInteger maximum) {
|
|
228 // Initialize the underlying scrollbar to the standard range values
|
|
229 // The increments are important and are how we differentiate arrow from track events
|
|
230 setMinimum(0);
|
|
231 setMaximum(BIG_RANGE - 1);
|
|
232 setValue(0);
|
|
233 setVisibleAmount(1);
|
|
234 setUnitIncrement(UNIT_INCREMENT);
|
|
235 setBlockIncrement(BLOCK_INCREMENT);
|
|
236
|
|
237 setUnitIncrementHP(new BigInteger(Integer.toString(getUnitIncrement())));
|
|
238 setBlockIncrementHP(new BigInteger(Integer.toString(getBlockIncrement())));
|
|
239
|
|
240 // Must set range and value first (it sets min/max)
|
|
241 setRange(minimum, maximum);
|
|
242
|
|
243 setVisibleAmountHP(new BigInteger(Integer.toString(getVisibleAmount())));
|
|
244 setValueHP(value);
|
|
245 }
|
|
246
|
|
247 private void initialize() {
|
|
248 BigInteger min = new BigInteger(Integer.toString(getMinimum()));
|
|
249 BigInteger max = new BigInteger(Integer.toString(getMaximum()));
|
|
250 initialize(min, min, max);
|
|
251 }
|
|
252
|
|
253 private void setRange(BigInteger minimum, BigInteger maximum) {
|
|
254 if (minimum.compareTo(maximum) > 0 ) {
|
|
255 throw new RuntimeException("Bad scrollbar range " + minimum + " > " + maximum);
|
|
256 }
|
|
257 minimumHP = minimum;
|
|
258 maximumHP = maximum;
|
|
259 rangeHP = maximum.subtract(minimum).add(BigInteger.ONE);
|
|
260 BigInteger range2 = new BigInteger(Integer.toString(BIG_RANGE));
|
|
261 if (rangeHP.compareTo(range2) >= 0 ) {
|
|
262 down = true;
|
|
263 scaleFactor = new BigDecimal(rangeHP, SCALE).divide(new BigDecimal(range2, SCALE), BigDecimal.ROUND_DOWN).max(new BigDecimal(BigInteger.ONE));
|
|
264 } else {
|
|
265 down = false;
|
|
266 scaleFactor = new BigDecimal(range2, SCALE).divide(new BigDecimal(rangeHP, SCALE), BigDecimal.ROUND_DOWN).max(new BigDecimal(BigInteger.ONE));
|
|
267 }
|
|
268 // FIXME: should put in original scaling algorithm (shifting by
|
|
269 // number of bits) as alternative when scale between low and high
|
|
270 // precision is very large
|
|
271 }
|
|
272
|
|
273 // A range update is complete. Rescale our computed values and
|
|
274 // inform the underlying scrollBar as needed.
|
|
275 private void updateScrollBarValues() {
|
|
276 setValueHP(getValueHP());
|
|
277 setVisibleAmountHP(getVisibleAmountHP());
|
|
278 setBlockIncrementHP(getBlockIncrementHP());
|
|
279 setUnitIncrementHP(getUnitIncrementHP());
|
|
280 }
|
|
281
|
|
282 private BigDecimal getScaleFactor() {
|
|
283 return scaleFactor;
|
|
284 }
|
|
285
|
|
286
|
|
287 // Value scaling routines
|
|
288 private BigInteger scaleToHP(int i) {
|
|
289 BigDecimal ib = new BigDecimal(Integer.toString(i));
|
|
290 if (down) return ib.multiply(getScaleFactor()).toBigInteger();
|
|
291 else return ib.divide(getScaleFactor(), BigDecimal.ROUND_DOWN).toBigInteger();
|
|
292 }
|
|
293
|
|
294 private int scaleToUnderlying(BigInteger i) {
|
|
295 BigDecimal d = new BigDecimal(i);
|
|
296 if (down) return d.divide(getScaleFactor(), BigDecimal.ROUND_DOWN).intValue();
|
|
297 else return d.multiply(getScaleFactor()).intValue();
|
|
298 }
|
|
299
|
|
300 // Range scaling routines
|
|
301 private BigInteger toHPRange(int i) {
|
|
302 return scaleToHP(i).add(minimumHP);
|
|
303 // return ib.shiftLeft(Math.max(2, maximumHP.bitLength() - 33));
|
|
304 }
|
|
305
|
|
306 private int toUnderlyingRange(BigInteger i) {
|
|
307 return scaleToUnderlying(i.subtract(minimumHP));
|
|
308 // return i.shiftRight(Math.max(2, maximumHP.bitLength() - 33)).intValue();
|
|
309 }
|
|
310
|
|
311 private void installListener() {
|
|
312 super.addAdjustmentListener(new AdjustmentListener() {
|
|
313 public void adjustmentValueChanged(AdjustmentEvent e) {
|
|
314 if (updating) {
|
|
315 return;
|
|
316 }
|
|
317 beginUpdate();
|
|
318 switch (e.getAdjustmentType()) {
|
|
319 case AdjustmentEvent.TRACK:
|
|
320 int val = e.getValue();
|
|
321 int diff = val - lastValueSeen;
|
|
322 int absDiff = Math.abs(diff);
|
|
323 // System.err.println("diff: " + diff + " absDiff: " + absDiff);
|
|
324 if (absDiff == UNIT_INCREMENT) {
|
|
325 if (diff > 0) {
|
|
326 // System.err.println("case 1");
|
|
327 setValueHP(getValueHP().add(getUnitIncrementHP()));
|
|
328 } else {
|
|
329 // System.err.println("case 2");
|
|
330 setValueHP(getValueHP().subtract(getUnitIncrementHP()));
|
|
331 }
|
|
332 } else if (absDiff == BLOCK_INCREMENT) {
|
|
333 if (diff > 0) {
|
|
334 // System.err.println("case 3");
|
|
335 setValueHP(getValueHP().add(getBlockIncrementHP()));
|
|
336 } else {
|
|
337 // System.err.println("case 4");
|
|
338 setValueHP(getValueHP().subtract(getBlockIncrementHP()));
|
|
339 }
|
|
340 } else {
|
|
341 // System.err.println("case 5");
|
|
342 // FIXME: seem to be getting spurious update events,
|
|
343 // with diff = 0, upon mouse down/up on the track
|
|
344 if (absDiff != 0) {
|
|
345 // Convert low-precision value to high precision
|
|
346 // (note we lose the low bits)
|
|
347 BigInteger i = null;
|
|
348 if (e.getValue() == getMinimum()) {
|
|
349 i = getMinimumHP();
|
|
350 } else if (e.getValue() >= getMaximum() - 1) {
|
|
351 i = getMaximumHP();
|
|
352 } else {
|
|
353 i = toHPRange(e.getValue());
|
|
354 }
|
|
355 setValueHP(i);
|
|
356 }
|
|
357 }
|
|
358 break;
|
|
359 default:
|
|
360 // Should not reach here, but leaving it a no-op in case
|
|
361 // we later get the other events (should revisit code in
|
|
362 // that case)
|
|
363 break;
|
|
364 }
|
|
365 endUpdate();
|
|
366 }
|
|
367 });
|
|
368 }
|
|
369
|
|
370 private void fireStateChanged() {
|
|
371 ChangeEvent e = null;
|
|
372 for (Iterator iter = changeListeners.iterator(); iter.hasNext(); ) {
|
|
373 ChangeListener l = (ChangeListener) iter.next();
|
|
374 if (e == null) {
|
|
375 e = new ChangeEvent(this);
|
|
376 }
|
|
377 l.stateChanged(e);
|
|
378 }
|
|
379 }
|
|
380
|
|
381 public static void main(String[] args) {
|
|
382 JFrame frame = new JFrame();
|
|
383 frame.setSize(300, 300);
|
|
384 // 32-bit version
|
|
385 /*
|
|
386 HighPrecisionJScrollBar hpsb =
|
|
387 new HighPrecisionJScrollBar(
|
|
388 JScrollBar.VERTICAL,
|
|
389 new BigInteger(1, new byte[] {
|
|
390 (byte) 0x80, (byte) 0x00, (byte) 0x00, (byte) 0x00}),
|
|
391 new BigInteger(1, new byte[] {
|
|
392 (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00}),
|
|
393 new BigInteger(1, new byte[] {
|
|
394 (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF}));
|
|
395 hpsb.setUnitIncrementHP(new BigInteger(1, new byte[] {
|
|
396 (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x01}));
|
|
397 hpsb.setBlockIncrementHP(new BigInteger(1, new byte[] {
|
|
398 (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x10}));
|
|
399 */
|
|
400
|
|
401 // 64-bit version
|
|
402 HighPrecisionJScrollBar hpsb =
|
|
403 new HighPrecisionJScrollBar(
|
|
404 JScrollBar.VERTICAL,
|
|
405 new BigInteger(1, new byte[] {
|
|
406 (byte) 0x80, (byte) 0x00, (byte) 0x00, (byte) 0x00,
|
|
407 (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00}),
|
|
408 new BigInteger(1, new byte[] {
|
|
409 (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
|
|
410 (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00}),
|
|
411 new BigInteger(1, new byte[] {
|
|
412 (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF,
|
|
413 (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF}));
|
|
414 hpsb.setUnitIncrementHP(new BigInteger(1, new byte[] {
|
|
415 (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
|
|
416 (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x01}));
|
|
417 hpsb.setBlockIncrementHP(new BigInteger(1, new byte[] {
|
|
418 (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
|
|
419 (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x10}));
|
|
420 hpsb.addChangeListener(new ChangeListener() {
|
|
421 public void stateChanged(ChangeEvent e) {
|
|
422 HighPrecisionJScrollBar h = (HighPrecisionJScrollBar) e.getSource();
|
|
423 System.out.println("New value = 0x" + h.getValueHP().toString(16));
|
|
424 }
|
|
425 });
|
|
426 frame.getContentPane().add(hpsb);
|
|
427 frame.show();
|
|
428 }
|
|
429
|
|
430 }
|