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 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 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 protected abstract 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 paintAll paint the whole scrollbar if true, just the thumb is false 165 */ 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 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 */ 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 */ 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 */ 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 */ 388 public void setMode(int mode){ 389 this.mode = mode; 390 } 391 392 /** 393 * Scroll one unit. 394 * @see #notifyValue 395 */ 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 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 */ 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 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") 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 531 private void handleTrackEvent(int x, int y, boolean isAdjusting){ 532 if (mode == AdjustmentEvent.TRACK) { 533 notifyValue(calculateCursorOffset(x, y), isAdjusting); 534 } 535 } 536 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 707 synchronized void setUnitIncrement(int unitSize) { 708 line = unitSize; 709 } 710 711 /** 712 * Gets the unit increment for this scrollbar. 713 */ 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 */ 724 synchronized void setBlockIncrement(int blockSize) { 725 page = blockSize; 726 } 727 728 /** 729 * Gets the block increment for this scrollbar. 730 */ 731 int getBlockIncrement() { 732 return page; 733 } 734 735 /** 736 * Width of the arrow image 737 */ 738 int getArrowWidth() { 739 return getArrowAreaWidth() - 2*ARROW_IND; 740 } 741 742 /** 743 * Width of the area reserved for arrow 744 */ 745 int getArrowAreaWidth() { 746 return arrowArea; 747 } 748 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 */ 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 */ 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 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 */ 897 XScrollRepeater(XScrollbar sb) { 898 this.setScrollbar(sb); 899 newScroll = true; 900 } 901 902 public void start() { 903 stop(); 904 shouldSkip = false; 905 XToolkit.schedule(this, beginPause); 906 } 907 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 */ 919 public synchronized void setScrollbar(XScrollbar sb) { 920 this.sb = sb; 921 stop(); 922 newScroll = true; 923 } 924 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 }