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