001: /*
002: * Copyright 2003-2007 Sun Microsystems, Inc. All Rights Reserved.
003: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004: *
005: * This code is free software; you can redistribute it and/or modify it
006: * under the terms of the GNU General Public License version 2 only, as
007: * published by the Free Software Foundation. Sun designates this
008: * particular file as subject to the "Classpath" exception as provided
009: * by Sun in the LICENSE file that accompanied this code.
010: *
011: * This code is distributed in the hope that it will be useful, but WITHOUT
012: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
013: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
014: * version 2 for more details (a copy is included in the LICENSE file that
015: * accompanied this code).
016: *
017: * You should have received a copy of the GNU General Public License version
018: * 2 along with this work; if not, write to the Free Software Foundation,
019: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
020: *
021: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
022: * CA 95054 USA or visit www.sun.com if you need additional information or
023: * have any questions.
024: */
025:
026: package sun.awt.X11;
027:
028: import java.awt.*;
029: import java.awt.event.*;
030: import java.awt.image.BufferedImage;
031: import sun.awt.SunToolkit;
032: import sun.awt.X11GraphicsConfig;
033: import java.util.logging.*;
034:
035: /**
036: * A simple vertical scroll bar.
037: */
038: abstract class XScrollbar {
039:
040: private static Logger log = Logger
041: .getLogger("sun.awt.X11.XScrollbar");
042: /**
043: * The thread that asynchronously tells the scrollbar to scroll.
044: * @see #startScrolling
045: */
046: private static XScrollRepeater scroller = new XScrollRepeater(null);
047: /*
048: * The repeater that used for concurrent scrolling of the vertical and horizontal scrollbar
049: * And so there is not static keyword
050: * See 6243382 for more information
051: */
052: private XScrollRepeater i_scroller = new XScrollRepeater(null);
053:
054: // Thumb length is always >= MIN_THUMB_H
055: private final static int MIN_THUMB_H = 5;
056:
057: private static final int ARROW_IND = 1;
058:
059: XScrollbarClient sb;
060:
061: //Use set methods to set scrollbar parameters
062: private int val;
063: private int min;
064: private int max;
065: private int vis;
066:
067: private int line;
068: private int page;
069: private boolean needsRepaint = true;
070: private boolean pressed = false;
071: private boolean dragging = false;
072:
073: Polygon firstArrow, secondArrow;
074:
075: int width, height; // Dimensions of the visible part of the parent window
076: int barWidth, barLength; // Rotation-independent values,
077: // equal to (width, height) for vertical,
078: // rotated by 90 for horizontal.
079: // That is, barLength is always the length between
080: // the tips of the arrows.
081: int arrowArea; // The area reserved for the scroll arrows
082: int alignment;
083: public static final int ALIGNMENT_VERTICAL = 1,
084: ALIGNMENT_HORIZONTAL = 2;
085:
086: int mode;
087: Point thumbOffset;
088: private Rectangle prevThumb;
089:
090: public XScrollbar(int alignment, XScrollbarClient sb) {
091: this .sb = sb;
092: this .alignment = alignment;
093: }
094:
095: public boolean needsRepaint() {
096: return needsRepaint;
097: }
098:
099: void notifyValue(int v) {
100: notifyValue(v, false);
101: }
102:
103: void notifyValue(int v, final boolean isAdjusting) {
104: if (v < min) {
105: v = min;
106: } else if (v > max - vis) {
107: v = max - vis;
108: }
109: final int value = v;
110: final int mode = this .mode;
111: if ((sb != null) && ((value != val) || (!pressed))) {
112: SunToolkit.executeOnEventHandlerThread(sb.getEventSource(),
113: new Runnable() {
114: public void run() {
115: sb.notifyValue(XScrollbar.this , mode,
116: value, isAdjusting);
117: }
118: });
119: }
120: }
121:
122: abstract protected void rebuildArrows();
123:
124: public void setSize(int width, int height) {
125: if (log.isLoggable(Level.FINER))
126: log.finer("Setting scroll bar " + this + " size to "
127: + width + "x" + height);
128: this .width = width;
129: this .height = height;
130: }
131:
132: /**
133: * Creates oriented directed arrow
134: */
135: protected Polygon createArrowShape(boolean vertical, boolean up) {
136: Polygon arrow = new Polygon();
137: // TODO: this should be done polymorphically in subclasses
138: // FIXME: arrows overlap the thumb for very wide scrollbars
139: if (vertical) {
140: int x = width / 2 - getArrowWidth() / 2;
141: int y1 = (up ? ARROW_IND : barLength - ARROW_IND);
142: int y2 = (up ? getArrowWidth() : barLength
143: - getArrowWidth() - ARROW_IND);
144: arrow.addPoint(x + getArrowWidth() / 2, y1);
145: arrow.addPoint(x + getArrowWidth(), y2);
146: arrow.addPoint(x, y2);
147: arrow.addPoint(x + getArrowWidth() / 2, y1);
148: } else {
149: int y = height / 2 - getArrowWidth() / 2;
150: int x1 = (up ? ARROW_IND : barLength - ARROW_IND);
151: int x2 = (up ? getArrowWidth() : barLength
152: - getArrowWidth() - ARROW_IND);
153: arrow.addPoint(x1, y + getArrowWidth() / 2);
154: arrow.addPoint(x2, y + getArrowWidth());
155: arrow.addPoint(x2, y);
156: arrow.addPoint(x1, y + getArrowWidth() / 2);
157: }
158: return arrow;
159: }
160:
161: /**
162: * Gets the area of the scroll track
163: */
164: protected abstract Rectangle getThumbArea();
165:
166: /**
167: * paint the scrollbar
168: * @param g the graphics context to paint into
169: * @param colors the colors to use when painting the scrollbar
170: * @param width the width of the scrollbar
171: * @param height the height of the scrollbar
172: * @param paintAll paint the whole scrollbar if true, just the thumb is false
173: */
174: void paint(Graphics g, Color colors[], boolean paintAll) {
175: if (log.isLoggable(Level.FINER))
176: log.finer("Painting scrollbar " + this );
177:
178: boolean useBufferedImage = false;
179: Graphics2D g2 = null;
180: BufferedImage buffer = null;
181: if (!(g instanceof Graphics2D)) {
182: // Fix for 5045936, 5055171. While printing, g is an instance
183: // of sun.print.ProxyPrintGraphics which extends Graphics.
184: // So we use a separate buffered image and its graphics is
185: // always Graphics2D instance
186: X11GraphicsConfig graphicsConfig = (X11GraphicsConfig) (sb
187: .getEventSource().getGraphicsConfiguration());
188: buffer = graphicsConfig
189: .createCompatibleImage(width, height);
190: g2 = buffer.createGraphics();
191: useBufferedImage = true;
192: } else {
193: g2 = (Graphics2D) g;
194: }
195: try {
196: Rectangle thumbRect = calculateThumbRect();
197:
198: // if (prevH == thumbH && prevY == thumbPosY) {
199: // return;
200: // }
201:
202: prevThumb = thumbRect;
203:
204: // TODO: Share Motif colors
205: Color back = colors[XComponentPeer.BACKGROUND_COLOR];
206: Color selectColor = new Color(MotifColorUtilities
207: .calculateSelectFromBackground(back.getRed(), back
208: .getGreen(), back.getBlue()));
209: Color darkShadow = new Color(MotifColorUtilities
210: .calculateBottomShadowFromBackground(back.getRed(),
211: back.getGreen(), back.getBlue()));
212: Color lightShadow = new Color(MotifColorUtilities
213: .calculateTopShadowFromBackground(back.getRed(),
214: back.getGreen(), back.getBlue()));
215:
216: XToolkit.awtLock();
217: try {
218: XlibWrapper.XFlush(XToolkit.getDisplay());
219: } finally {
220: XToolkit.awtUnlock();
221: }
222: /* paint the background slightly darker */
223: if (paintAll) {
224: // fill the entire background
225: g2.setColor(selectColor);
226: if (alignment == ALIGNMENT_HORIZONTAL) {
227: g2.fillRect(0, 0, thumbRect.x, height);
228: g2.fillRect(thumbRect.x + thumbRect.width, 0, width
229: - (thumbRect.x + thumbRect.width), height);
230: } else {
231: g2.fillRect(0, 0, width, thumbRect.y);
232: g2.fillRect(0, thumbRect.y + thumbRect.height,
233: width, height
234: - (thumbRect.y + thumbRect.height));
235: }
236:
237: // Paint edges
238: // TODO: Share Motif 3d rect drawing
239:
240: g2.setColor(darkShadow);
241: g2.drawLine(0, 0, width - 1, 0); // top
242: g2.drawLine(0, 0, 0, height - 1); // left
243:
244: g2.setColor(lightShadow);
245: g2.drawLine(1, height - 1, width - 1, height - 1); // bottom
246: g2.drawLine(width - 1, 1, width - 1, height - 1); // right
247: } else {
248: // Clear all thumb area
249: g2.setColor(selectColor);
250: Rectangle thumbArea = getThumbArea();
251: g2.fill(thumbArea);
252: }
253:
254: if (paintAll) {
255: // ************ paint the arrows
256: paintArrows(g2,
257: colors[XComponentPeer.BACKGROUND_COLOR],
258: darkShadow, lightShadow);
259:
260: }
261:
262: // Thumb
263: g2.setColor(colors[XComponentPeer.BACKGROUND_COLOR]);
264: g2.fillRect(thumbRect.x, thumbRect.y, thumbRect.width,
265: thumbRect.height);
266:
267: g2.setColor(lightShadow);
268: g2.drawLine(thumbRect.x, thumbRect.y, thumbRect.x
269: + thumbRect.width, thumbRect.y); // top
270: g2.drawLine(thumbRect.x, thumbRect.y, thumbRect.x,
271: thumbRect.y + thumbRect.height); // left
272:
273: g2.setColor(darkShadow);
274: g2.drawLine(thumbRect.x + 1,
275: thumbRect.y + thumbRect.height, thumbRect.x
276: + thumbRect.width, thumbRect.y
277: + thumbRect.height); // bottom
278: g2.drawLine(thumbRect.x + thumbRect.width, thumbRect.y + 1,
279: thumbRect.x + thumbRect.width, thumbRect.y
280: + thumbRect.height); // right
281: } finally {
282: if (useBufferedImage) {
283: g2.dispose();
284: }
285: }
286: if (useBufferedImage) {
287: g.drawImage(buffer, 0, 0, null);
288: }
289: XToolkit.awtLock();
290: try {
291: XlibWrapper.XFlush(XToolkit.getDisplay());
292: } finally {
293: XToolkit.awtUnlock();
294: }
295: }
296:
297: void paintArrows(Graphics2D g, Color background, Color darkShadow,
298: Color lightShadow) {
299:
300: g.setColor(background);
301:
302: // paint firstArrow
303: if (pressed && (mode == AdjustmentEvent.UNIT_DECREMENT)) {
304: g.fill(firstArrow);
305: g.setColor(lightShadow);
306: g.drawLine(firstArrow.xpoints[0], firstArrow.ypoints[0],
307: firstArrow.xpoints[1], firstArrow.ypoints[1]);
308: g.drawLine(firstArrow.xpoints[1], firstArrow.ypoints[1],
309: firstArrow.xpoints[2], firstArrow.ypoints[2]);
310: g.setColor(darkShadow);
311: g.drawLine(firstArrow.xpoints[2], firstArrow.ypoints[2],
312: firstArrow.xpoints[0], firstArrow.ypoints[0]);
313:
314: } else {
315: g.fill(firstArrow);
316: g.setColor(darkShadow);
317: g.drawLine(firstArrow.xpoints[0], firstArrow.ypoints[0],
318: firstArrow.xpoints[1], firstArrow.ypoints[1]);
319: g.drawLine(firstArrow.xpoints[1], firstArrow.ypoints[1],
320: firstArrow.xpoints[2], firstArrow.ypoints[2]);
321: g.setColor(lightShadow);
322: g.drawLine(firstArrow.xpoints[2], firstArrow.ypoints[2],
323: firstArrow.xpoints[0], firstArrow.ypoints[0]);
324:
325: }
326:
327: g.setColor(background);
328: // paint second Arrow
329: if (pressed && (mode == AdjustmentEvent.UNIT_INCREMENT)) {
330: g.fill(secondArrow);
331: g.setColor(lightShadow);
332: g.drawLine(secondArrow.xpoints[0], secondArrow.ypoints[0],
333: secondArrow.xpoints[1], secondArrow.ypoints[1]);
334: g.setColor(darkShadow);
335: g.drawLine(secondArrow.xpoints[1], secondArrow.ypoints[1],
336: secondArrow.xpoints[2], secondArrow.ypoints[2]);
337: g.drawLine(secondArrow.xpoints[2], secondArrow.ypoints[2],
338: secondArrow.xpoints[0], secondArrow.ypoints[0]);
339:
340: } else {
341: g.fill(secondArrow);
342: g.setColor(darkShadow);
343: g.drawLine(secondArrow.xpoints[0], secondArrow.ypoints[0],
344: secondArrow.xpoints[1], secondArrow.ypoints[1]);
345: g.setColor(lightShadow);
346: g.drawLine(secondArrow.xpoints[1], secondArrow.ypoints[1],
347: secondArrow.xpoints[2], secondArrow.ypoints[2]);
348: g.drawLine(secondArrow.xpoints[2], secondArrow.ypoints[2],
349: secondArrow.xpoints[0], secondArrow.ypoints[0]);
350:
351: }
352:
353: }
354:
355: /**
356: * Tell the scroller to start scrolling.
357: */
358: void startScrolling() {
359: log.finer("Start scrolling on " + this );
360: // Make sure that we scroll at least once
361: scroll();
362:
363: // wake up the scroll repeater
364: if (scroller == null) {
365: // If there isn't a scroller, then create
366: // one and start it.
367: scroller = new XScrollRepeater(this );
368: } else {
369: scroller.setScrollbar(this );
370: }
371: scroller.start();
372: }
373:
374: /**
375: * Tell the instance scroller to start scrolling.
376: * See 6243382 for more information
377: */
378: void startScrollingInstance() {
379: log.finer("Start scrolling on " + this );
380: // Make sure that we scroll at least once
381: scroll();
382:
383: i_scroller.setScrollbar(this );
384: i_scroller.start();
385: }
386:
387: /**
388: * Tell the instance scroller to stop scrolling.
389: * See 6243382 for more information
390: */
391: void stopScrollingInstance() {
392: log.finer("Stop scrolling on " + this );
393:
394: i_scroller.stop();
395: }
396:
397: /**
398: * The set method for mode property.
399: * See 6243382 for more information
400: */
401: public void setMode(int mode) {
402: this .mode = mode;
403: }
404:
405: /**
406: * Scroll one unit.
407: * @see notifyValue
408: */
409: void scroll() {
410: switch (mode) {
411: case AdjustmentEvent.UNIT_DECREMENT:
412: notifyValue(val - line);
413: return;
414:
415: case AdjustmentEvent.UNIT_INCREMENT:
416: notifyValue(val + line);
417: return;
418:
419: case AdjustmentEvent.BLOCK_DECREMENT:
420: notifyValue(val - page);
421: return;
422:
423: case AdjustmentEvent.BLOCK_INCREMENT:
424: notifyValue(val + page);
425: return;
426: }
427: return;
428: }
429:
430: boolean isInArrow(int x, int y) {
431: // Mouse is considered to be in the arrow if it is anywhere in the
432: // arrow area.
433: int coord = (alignment == ALIGNMENT_HORIZONTAL ? x : y);
434: int arrAreaH = getArrowAreaWidth();
435:
436: if (coord < arrAreaH || coord > barLength - arrAreaH + 1) {
437: return true;
438: }
439: return false;
440: }
441:
442: /**
443: * Is x,y in the scroll thumb?
444: *
445: * If we ever cache the thumb rect, we may need to clone the result of
446: * calculateThumbRect().
447: */
448: boolean isInThumb(int x, int y) {
449: Rectangle thumbRect = calculateThumbRect();
450:
451: // If the mouse is in the shadow of the thumb or the shadow of the
452: // scroll track, treat it as hitting the thumb. So, slightly enlarge
453: // our rectangle.
454: thumbRect.x -= 1;
455: thumbRect.width += 3;
456: thumbRect.height += 1;
457: return thumbRect.contains(x, y);
458: }
459:
460: abstract boolean beforeThumb(int x, int y);
461:
462: /**
463: *
464: * @see java.awt.event.MouseEvent
465: * MouseEvent.MOUSE_CLICKED
466: * MouseEvent.MOUSE_PRESSED
467: * MouseEvent.MOUSE_RELEASED
468: * MouseEvent.MOUSE_MOVED
469: * MouseEvent.MOUSE_ENTERED
470: * MouseEvent.MOUSE_EXITED
471: * MouseEvent.MOUSE_DRAGGED
472: */
473: public void handleMouseEvent(int id, int modifiers, int x, int y) {
474: if ((modifiers & InputEvent.BUTTON1_MASK) == 0) {
475: return;
476: }
477:
478: if (log.isLoggable(Level.FINER)) {
479: String type;
480: switch (id) {
481: case MouseEvent.MOUSE_PRESSED:
482: type = new String("press");
483: break;
484: case MouseEvent.MOUSE_RELEASED:
485: type = new String("release");
486: break;
487: case MouseEvent.MOUSE_DRAGGED:
488: type = new String("drag");
489: break;
490: default:
491: type = new String("other");
492: }
493: log.finer("Mouse " + type + " event in scroll bar " + this
494: + "x = " + x + ", y = " + y + ", on arrow: "
495: + isInArrow(x, y) + ", on thumb: "
496: + isInThumb(x, y) + ", before thumb: "
497: + beforeThumb(x, y) + ", thumb rect"
498: + calculateThumbRect());
499: }
500: switch (id) {
501: case MouseEvent.MOUSE_PRESSED:
502: if (isInArrow(x, y)) {
503: pressed = true;
504: if (beforeThumb(x, y)) {
505: mode = AdjustmentEvent.UNIT_DECREMENT;
506: } else {
507: mode = AdjustmentEvent.UNIT_INCREMENT;
508: }
509: sb.repaintScrollbarRequest(this );
510: startScrolling();
511: break;
512: }
513:
514: if (isInThumb(x, y)) {
515: mode = AdjustmentEvent.TRACK;
516: } else {
517: if (beforeThumb(x, y)) {
518: mode = AdjustmentEvent.BLOCK_DECREMENT;
519: } else {
520: mode = AdjustmentEvent.BLOCK_INCREMENT;
521: }
522: startScrolling();
523: }
524: Rectangle pos = calculateThumbRect();
525: thumbOffset = new Point(x - pos.x, y - pos.y);
526: break;
527:
528: case MouseEvent.MOUSE_RELEASED:
529: pressed = false;
530: sb.repaintScrollbarRequest(this );
531: scroller.stop();
532: if (dragging) {
533: handleTrackEvent(x, y, false);
534: dragging = false;
535: }
536: break;
537:
538: case MouseEvent.MOUSE_DRAGGED:
539: dragging = true;
540: handleTrackEvent(x, y, true);
541: }
542: }
543:
544: private void handleTrackEvent(int x, int y, boolean isAdjusting) {
545: if (mode == AdjustmentEvent.TRACK) {
546: notifyValue(calculateCursorOffset(x, y), isAdjusting);
547: }
548: }
549:
550: private int calculateCursorOffset(int x, int y) {
551: if (alignment == ALIGNMENT_HORIZONTAL) {
552: if (dragging)
553: return Math
554: .max(
555: 0,
556: (int) ((x - (thumbOffset.x + getArrowAreaWidth())) / getScaleFactor()))
557: + min;
558: else
559: return Math
560: .max(
561: 0,
562: (int) ((x - (getArrowAreaWidth())) / getScaleFactor()))
563: + min;
564: } else {
565: if (dragging)
566: return Math
567: .max(
568: 0,
569: (int) ((y - (thumbOffset.y + getArrowAreaWidth())) / getScaleFactor()))
570: + min;
571: else
572: return Math
573: .max(
574: 0,
575: (int) ((y - (getArrowAreaWidth())) / getScaleFactor()))
576: + min;
577: }
578: }
579:
580: /*
581: private void updateNeedsRepaint() {
582: Rectangle thumbRect = calculateThumbRect();
583: if (!prevThumb.equals(thumbRect)) {
584: needsRepaint = true;
585: }
586: prevThumb = thumbRect;
587: }
588: */
589:
590: /**
591: * Sets the values for this Scrollbar.
592: * This method enforces the same constraints as in java.awt.Scrollbar:
593: * <UL>
594: * <LI> The maximum must be greater than the minimum </LI>
595: * <LI> The value must be greater than or equal to the minumum
596: * and less than or equal to the maximum minus the
597: * visible amount </LI>
598: * <LI> The visible amount must be greater than 1 and less than or equal
599: * to the difference between the maximum and minimum values. </LI>
600: * </UL>
601: * Values which do not meet these criteria are quietly coerced to the
602: * appropriate boundary value.
603: * @param value is the position in the current window.
604: * @param visible is the amount visible per page
605: * @param minimum is the minimum value of the scrollbar
606: * @param maximum is the maximum value of the scrollbar
607: */
608: synchronized void setValues(int value, int visible, int minimum,
609: int maximum) {
610: if (maximum <= minimum) {
611: maximum = minimum + 1;
612: }
613: if (visible > maximum - minimum) {
614: visible = maximum - minimum;
615: }
616: if (visible < 1) {
617: visible = 1;
618: }
619: if (value < minimum) {
620: value = minimum;
621: }
622: if (value > maximum - visible) {
623: value = maximum - visible;
624: }
625:
626: this .val = value;
627: this .vis = visible;
628: this .min = minimum;
629: this .max = maximum;
630: }
631:
632: /**
633: * Sets param of this Scrollbar to the specified values.
634: * @param value is the position in the current window.
635: * @param visible is the amount visible per page
636: * @param minimum is the minimum value of the scrollbar
637: * @param maximum is the maximum value of the scrollbar
638: * @param unitSize is the unit size for increment or decrement of the value
639: * @param page is the block size for increment or decrement of the value
640: * @see #setValues
641: */
642: synchronized void setValues(int value, int visible, int minimum,
643: int maximum, int unitSize, int blockSize) {
644: /* Use setValues so that a consistent policy
645: * relating minimum, maximum, and value is enforced.
646: */
647: setValues(value, visible, minimum, maximum);
648: setUnitIncrement(unitSize);
649: setBlockIncrement(blockSize);
650: }
651:
652: /**
653: * Returns the current value of this Scrollbar.
654: * @see #getMinimum
655: * @see #getMaximum
656: */
657: int getValue() {
658: return val;
659: }
660:
661: /**
662: * Sets the value of this Scrollbar to the specified value.
663: * @param value the new value of the Scrollbar. If this value is
664: * below the current minimum or above the current maximum minus
665: * the visible amount, it becomes the new one of those values,
666: * respectively.
667: * @see #getValue
668: */
669: synchronized void setValue(int newValue) {
670: /* Use setValues so that a consistent policy
671: * relating minimum, maximum, and value is enforced.
672: */
673: setValues(newValue, vis, min, max);
674: }
675:
676: /**
677: * Returns the minimum value of this Scrollbar.
678: * @see #getMaximum
679: * @see #getValue
680: */
681: int getMinimum() {
682: return min;
683: }
684:
685: /**
686: * Sets the minimum value for this Scrollbar.
687: * @param minimum the minimum value of the scrollbar
688: */
689: synchronized void setMinimum(int newMinimum) {
690: /* Use setValues so that a consistent policy
691: * relating minimum, maximum, and value is enforced.
692: */
693: setValues(val, vis, newMinimum, max);
694: }
695:
696: /**
697: * Returns the maximum value of this Scrollbar.
698: * @see #getMinimum
699: * @see #getValue
700: */
701: int getMaximum() {
702: return max;
703: }
704:
705: /**
706: * Sets the maximum value for this Scrollbar.
707: * @param maximum the maximum value of the scrollbar
708: */
709: synchronized void setMaximum(int newMaximum) {
710: /* Use setValues so that a consistent policy
711: * relating minimum, maximum, and value is enforced.
712: */
713: setValues(val, vis, min, newMaximum);
714: }
715:
716: /**
717: * Returns the visible amount of this Scrollbar.
718: */
719: int getVisibleAmount() {
720: return vis;
721: }
722:
723: /**
724: * Sets the visible amount of this Scrollbar, which is the range
725: * of values represented by the width of the scroll bar's bubble.
726: * @param visible the amount visible per page
727: */
728: synchronized void setVisibleAmount(int newAmount) {
729: setValues(val, newAmount, min, max);
730: }
731:
732: /**
733: * Sets the unit increment for this scrollbar. This is the value
734: * that will be added (subtracted) when the user hits the unit down
735: * (up) gadgets.
736: * @param unitSize is the unit size for increment or decrement of the value
737: */
738: synchronized void setUnitIncrement(int unitSize) {
739: line = unitSize;
740: }
741:
742: /**
743: * Gets the unit increment for this scrollbar.
744: */
745: int getUnitIncrement() {
746: return line;
747: }
748:
749: /**
750: * Sets the block increment for this scrollbar. This is the value
751: * that will be added (subtracted) when the user hits the block down
752: * (up) gadgets.
753: * @param blockSize is the block size for increment or decrement of the value
754: */
755: synchronized void setBlockIncrement(int blockSize) {
756: page = blockSize;
757: }
758:
759: /**
760: * Gets the block increment for this scrollbar.
761: */
762: int getBlockIncrement() {
763: return page;
764: }
765:
766: /**
767: * Width of the arrow image
768: */
769: int getArrowWidth() {
770: return getArrowAreaWidth() - 2 * ARROW_IND;
771: }
772:
773: /**
774: * Width of the area reserved for arrow
775: */
776: int getArrowAreaWidth() {
777: return arrowArea;
778: }
779:
780: void calculateArrowWidth() {
781: if (barLength < 2 * barWidth + MIN_THUMB_H + 2) {
782: arrowArea = (barLength - MIN_THUMB_H + 2 * ARROW_IND) / 2 - 1;
783: } else {
784: arrowArea = barWidth - 1;
785: }
786: }
787:
788: /**
789: * Returns the scale factor for the thumbArea ( thumbAreaH / (max - min)).
790: * @see #getArrowAreaSize
791: */
792: private double getScaleFactor() {
793: double f = (double) (barLength - 2 * getArrowAreaWidth())
794: / Math.max(1, (max - min));
795: return f;
796: }
797:
798: /**
799: * Method to calculate the scroll thumb's size and position. This is
800: * based on CalcSliderRect in ScrollBar.c of Motif source.
801: *
802: * If we ever cache the thumb rect, we'll need to use a clone in
803: * isInThumb().
804: */
805: protected Rectangle calculateThumbRect() {
806: float range;
807: float trueSize; // Area of scroll track
808: float factor;
809: float slideSize;
810: int minSliderWidth;
811: int minSliderHeight;
812: int hitTheWall = 0;
813: int arrAreaH = getArrowAreaWidth();
814: Rectangle retVal = new Rectangle(0, 0, 0, 0);
815:
816: trueSize = barLength - 2 * arrAreaH - 1; // Same if vert or horiz
817:
818: if (alignment == ALIGNMENT_HORIZONTAL) {
819: minSliderWidth = MIN_THUMB_H; // Base on user-set vis?
820: minSliderHeight = height - 3;
821: } else { // Vertical
822: minSliderWidth = width - 3;
823: minSliderHeight = MIN_THUMB_H;
824:
825: }
826:
827: // Total number of user units displayed
828: range = max - min;
829:
830: // A naive notion of pixels per user unit
831: factor = trueSize / range;
832:
833: // A naive notion of the size of the slider in pixels
834: // in thermo, slider_size is 0 ans is ignored
835: slideSize = vis * factor;
836:
837: if (alignment == ALIGNMENT_HORIZONTAL) {
838: // Simulating MAX_SCROLLBAR_DIMENSION macro
839: int localVal = (int) (slideSize + 0.5);
840: int localMin = minSliderWidth;
841: if (localVal > localMin) {
842: retVal.width = localVal;
843: } else {
844: retVal.width = localMin;
845: hitTheWall = localMin;
846: }
847: retVal.height = minSliderHeight;
848: } else { // Vertical
849: retVal.width = minSliderWidth;
850:
851: // Simulating MAX_SCROLLBAR_DIMENSION macro
852: int localVal = (int) (slideSize + 0.5);
853: int localMin = minSliderHeight;
854: if (localVal > localMin) {
855: retVal.height = localVal;
856: } else {
857: retVal.height = localMin;
858: hitTheWall = localMin;
859: }
860: }
861:
862: if (hitTheWall != 0) {
863: trueSize -= hitTheWall; // Actual pixels available
864: range -= vis; // Actual range
865: factor = trueSize / range;
866: }
867:
868: if (alignment == ALIGNMENT_HORIZONTAL) {
869: retVal.x = ((int) (((((float) val) - ((float) min)) * factor) + 0.5))
870: + arrAreaH;
871: retVal.y = 1;
872:
873: } else {
874: retVal.x = 1;
875: retVal.y = ((int) (((((float) val) - ((float) min)) * factor) + 0.5))
876: + arrAreaH;
877: }
878:
879: // There was one final adjustment here in the Motif function, which was
880: // noted to be for backward-compatiblity. It has been left out for now.
881:
882: return retVal;
883: }
884:
885: public String toString() {
886: return getClass() + "[" + width + "x" + height + "," + barWidth
887: + "x" + barLength + "]";
888: }
889: }
890:
891: class XScrollRepeater implements Runnable {
892: /**
893: * Time to pause before the first scroll repeat.
894: */
895: static int beginPause = 500;
896: // Reminder - make this a user definable property
897:
898: /**
899: * Time to pause between each scroll repeat.
900: */
901: static int repeatPause = 100;
902: // Reminder - make this a user definable property
903:
904: /**
905: * The scrollbar that we sending scrolling.
906: */
907: XScrollbar sb;
908:
909: /**
910: * newScroll gets reset when a new scrollbar gets set.
911: */
912: boolean newScroll;
913:
914: boolean shouldSkip;
915:
916: /**
917: * Creates a new scroll repeater.
918: * @param sb the scrollbar that this thread will scroll
919: */
920: XScrollRepeater(XScrollbar sb) {
921: this .setScrollbar(sb);
922: newScroll = true;
923: }
924:
925: public void start() {
926: stop();
927: shouldSkip = false;
928: XToolkit.schedule(this , beginPause);
929: }
930:
931: public void stop() {
932: synchronized (this ) {
933: shouldSkip = true;
934: }
935: XToolkit.remove(this );
936: }
937:
938: /**
939: * Sets the scrollbar.
940: * @param sb the scrollbar that this thread will scroll
941: */
942: public synchronized void setScrollbar(XScrollbar sb) {
943: this .sb = sb;
944: stop();
945: newScroll = true;
946: }
947:
948: public void run() {
949: synchronized (this) {
950: if (shouldSkip) {
951: return;
952: }
953: }
954: sb.scroll();
955: XToolkit.schedule(this, repeatPause);
956: }
957:
958: }
|