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