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