001/* BasicScrollBarUI.java -- 002 Copyright (C) 2004, 2005, 2006, Free Software Foundation, Inc. 003 004This file is part of GNU Classpath. 005 006GNU Classpath is free software; you can redistribute it and/or modify 007it under the terms of the GNU General Public License as published by 008the Free Software Foundation; either version 2, or (at your option) 009any later version. 010 011GNU Classpath is distributed in the hope that it will be useful, but 012WITHOUT ANY WARRANTY; without even the implied warranty of 013MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 014General Public License for more details. 015 016You should have received a copy of the GNU General Public License 017along with GNU Classpath; see the file COPYING. If not, write to the 018Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 01902110-1301 USA. 020 021Linking this library statically or dynamically with other modules is 022making a combined work based on this library. Thus, the terms and 023conditions of the GNU General Public License cover the whole 024combination. 025 026As a special exception, the copyright holders of this library give you 027permission to link this library with independent modules to produce an 028executable, regardless of the license terms of these independent 029modules, and to copy and distribute the resulting executable under 030terms of your choice, provided that you also meet, for each linked 031independent module, the terms and conditions of the license of that 032module. An independent module is a module which is not derived from 033or based on this library. If you modify this library, you may extend 034this exception to your version of the library, but you are not 035obligated to do so. If you do not wish to do so, delete this 036exception statement from your version. */ 037 038 039package javax.swing.plaf.basic; 040 041import java.awt.Color; 042import java.awt.Component; 043import java.awt.Container; 044import java.awt.Dimension; 045import java.awt.Graphics; 046import java.awt.Insets; 047import java.awt.LayoutManager; 048import java.awt.Rectangle; 049import java.awt.event.ActionEvent; 050import java.awt.event.ActionListener; 051import java.awt.event.MouseAdapter; 052import java.awt.event.MouseEvent; 053import java.awt.event.MouseMotionListener; 054import java.beans.PropertyChangeEvent; 055import java.beans.PropertyChangeListener; 056 057import javax.swing.AbstractAction; 058import javax.swing.ActionMap; 059import javax.swing.BoundedRangeModel; 060import javax.swing.InputMap; 061import javax.swing.JButton; 062import javax.swing.JComponent; 063import javax.swing.JScrollBar; 064import javax.swing.JSlider; 065import javax.swing.LookAndFeel; 066import javax.swing.SwingConstants; 067import javax.swing.SwingUtilities; 068import javax.swing.Timer; 069import javax.swing.UIManager; 070import javax.swing.event.ChangeEvent; 071import javax.swing.event.ChangeListener; 072import javax.swing.plaf.ActionMapUIResource; 073import javax.swing.plaf.ComponentUI; 074import javax.swing.plaf.ScrollBarUI; 075 076/** 077 * The Basic Look and Feel UI delegate for JScrollBar. 078 */ 079public class BasicScrollBarUI extends ScrollBarUI implements LayoutManager, 080 SwingConstants 081{ 082 /** 083 * A helper class that listens to the two JButtons on each end of the 084 * JScrollBar. 085 */ 086 protected class ArrowButtonListener extends MouseAdapter 087 { 088 089 /** 090 * Move the thumb in the direction specified by the button's arrow. If 091 * this button is held down, then it should keep moving the thumb. 092 * 093 * @param e The MouseEvent fired by the JButton. 094 */ 095 public void mousePressed(MouseEvent e) 096 { 097 scrollTimer.stop(); 098 scrollListener.setScrollByBlock(false); 099 if (e.getSource() == incrButton) 100 scrollListener.setDirection(POSITIVE_SCROLL); 101 else if (e.getSource() == decrButton) 102 scrollListener.setDirection(NEGATIVE_SCROLL); 103 scrollTimer.setDelay(100); 104 scrollTimer.start(); 105 } 106 107 /** 108 * Stops the thumb when the JButton is released. 109 * 110 * @param e The MouseEvent fired by the JButton. 111 */ 112 public void mouseReleased(MouseEvent e) 113 { 114 scrollTimer.stop(); 115 scrollTimer.setDelay(300); 116 if (e.getSource() == incrButton) 117 scrollByUnit(POSITIVE_SCROLL); 118 else if (e.getSource() == decrButton) 119 scrollByUnit(NEGATIVE_SCROLL); 120 } 121 } 122 123 /** 124 * A helper class that listens to the ScrollBar's model for ChangeEvents. 125 */ 126 protected class ModelListener implements ChangeListener 127 { 128 /** 129 * Called when the model changes. 130 * 131 * @param e The ChangeEvent fired by the model. 132 */ 133 public void stateChanged(ChangeEvent e) 134 { 135 calculatePreferredSize(); 136 updateThumbRect(); 137 scrollbar.repaint(); 138 } 139 } 140 141 /** 142 * A helper class that listens to the ScrollBar's properties. 143 */ 144 public class PropertyChangeHandler implements PropertyChangeListener 145 { 146 /** 147 * Called when one of the ScrollBar's properties change. 148 * 149 * @param e The PropertyChangeEvent fired by the ScrollBar. 150 */ 151 public void propertyChange(PropertyChangeEvent e) 152 { 153 if (e.getPropertyName().equals("model")) 154 { 155 ((BoundedRangeModel) e.getOldValue()).removeChangeListener(modelListener); 156 scrollbar.getModel().addChangeListener(modelListener); 157 updateThumbRect(); 158 } 159 else if (e.getPropertyName().equals("orientation")) 160 { 161 uninstallListeners(); 162 uninstallComponents(); 163 uninstallDefaults(); 164 installDefaults(); 165 installComponents(); 166 installListeners(); 167 } 168 else if (e.getPropertyName().equals("enabled")) 169 { 170 Boolean b = (Boolean) e.getNewValue(); 171 if (incrButton != null) 172 incrButton.setEnabled(b.booleanValue()); 173 if (decrButton != null) 174 decrButton.setEnabled(b.booleanValue()); 175 } 176 } 177 } 178 179 /** 180 * A helper class that listens for events from the timer that is used to 181 * move the thumb. 182 */ 183 protected class ScrollListener implements ActionListener 184 { 185 /** The direction the thumb moves in. */ 186 private transient int direction; 187 188 /** Whether movement will be in blocks. */ 189 private transient boolean block; 190 191 /** 192 * Creates a new ScrollListener object. The default is scrolling 193 * positively with block movement. 194 */ 195 public ScrollListener() 196 { 197 direction = POSITIVE_SCROLL; 198 block = true; 199 } 200 201 /** 202 * Creates a new ScrollListener object using the given direction and 203 * block. 204 * 205 * @param dir The direction to move in. 206 * @param block Whether movement will be in blocks. 207 */ 208 public ScrollListener(int dir, boolean block) 209 { 210 direction = dir; 211 this.block = block; 212 } 213 214 /** 215 * Sets the direction to scroll in. 216 * 217 * @param direction The direction to scroll in. 218 */ 219 public void setDirection(int direction) 220 { 221 this.direction = direction; 222 } 223 224 /** 225 * Sets whether scrolling will be done in blocks. 226 * 227 * @param block Whether scrolling will be in blocks. 228 */ 229 public void setScrollByBlock(boolean block) 230 { 231 this.block = block; 232 } 233 234 /** 235 * Called every time the timer reaches its interval. 236 * 237 * @param e The ActionEvent fired by the timer. 238 */ 239 public void actionPerformed(ActionEvent e) 240 { 241 if (block) 242 { 243 // Only need to check it if it's block scrolling 244 // We only block scroll if the click occurs 245 // in the track. 246 if (!trackListener.shouldScroll(direction)) 247 { 248 trackHighlight = NO_HIGHLIGHT; 249 scrollbar.repaint(); 250 return; 251 } 252 scrollByBlock(direction); 253 } 254 else 255 scrollByUnit(direction); 256 } 257 } 258 259 /** 260 * Helper class that listens for movement on the track. 261 */ 262 protected class TrackListener extends MouseAdapter 263 implements MouseMotionListener 264 { 265 /** The current X coordinate of the mouse. */ 266 protected int currentMouseX; 267 268 /** The current Y coordinate of the mouse. */ 269 protected int currentMouseY; 270 271 /** 272 * The offset between the current mouse cursor and the current value of 273 * the scrollbar. 274 */ 275 protected int offset; 276 277 /** 278 * This method is called when the mouse is being dragged. 279 * 280 * @param e The MouseEvent given. 281 */ 282 public void mouseDragged(MouseEvent e) 283 { 284 currentMouseX = e.getX(); 285 currentMouseY = e.getY(); 286 if (scrollbar.getValueIsAdjusting()) 287 { 288 int value; 289 if (scrollbar.getOrientation() == SwingConstants.HORIZONTAL) 290 value = valueForXPosition(currentMouseX) - offset; 291 else 292 value = valueForYPosition(currentMouseY) - offset; 293 294 scrollbar.setValue(value); 295 } 296 } 297 298 /** 299 * This method is called when the mouse is moved. 300 * 301 * @param e The MouseEvent given. 302 */ 303 public void mouseMoved(MouseEvent e) 304 { 305 if (thumbRect.contains(e.getPoint())) 306 thumbRollover = true; 307 else 308 thumbRollover = false; 309 } 310 311 /** 312 * This method is called when the mouse is pressed. When it is pressed, 313 * the thumb should move in blocks towards the cursor. 314 * 315 * @param e The MouseEvent given. 316 */ 317 public void mousePressed(MouseEvent e) 318 { 319 currentMouseX = e.getX(); 320 currentMouseY = e.getY(); 321 322 int value; 323 if (scrollbar.getOrientation() == SwingConstants.HORIZONTAL) 324 value = valueForXPosition(currentMouseX); 325 else 326 value = valueForYPosition(currentMouseY); 327 328 if (! thumbRect.contains(e.getPoint())) 329 { 330 scrollTimer.stop(); 331 scrollListener.setScrollByBlock(true); 332 if (value > scrollbar.getValue()) 333 { 334 trackHighlight = INCREASE_HIGHLIGHT; 335 scrollListener.setDirection(POSITIVE_SCROLL); 336 } 337 else 338 { 339 trackHighlight = DECREASE_HIGHLIGHT; 340 scrollListener.setDirection(NEGATIVE_SCROLL); 341 } 342 scrollTimer.setDelay(100); 343 scrollTimer.start(); 344 } 345 else 346 { 347 // We'd like to keep track of where the cursor 348 // is inside the thumb. 349 // This works because the scrollbar's value represents 350 // "lower" edge of the thumb. The value at which 351 // the cursor is at must be greater or equal 352 // to that value. 353 354 scrollListener.setScrollByBlock(false); 355 scrollbar.setValueIsAdjusting(true); 356 offset = value - scrollbar.getValue(); 357 } 358 scrollbar.repaint(); 359 } 360 361 /** 362 * This method is called when the mouse is released. It should stop 363 * movement on the thumb 364 * 365 * @param e The MouseEvent given. 366 */ 367 public void mouseReleased(MouseEvent e) 368 { 369 scrollTimer.stop(); 370 scrollTimer.setDelay(300); 371 currentMouseX = e.getX(); 372 currentMouseY = e.getY(); 373 374 if (shouldScroll(POSITIVE_SCROLL)) 375 scrollByBlock(POSITIVE_SCROLL); 376 else if (shouldScroll(NEGATIVE_SCROLL)) 377 scrollByBlock(NEGATIVE_SCROLL); 378 379 trackHighlight = NO_HIGHLIGHT; 380 scrollListener.setScrollByBlock(false); 381 scrollbar.setValueIsAdjusting(true); 382 scrollbar.repaint(); 383 } 384 385 /** 386 * A helper method that decides whether we should keep scrolling in the 387 * given direction. 388 * 389 * @param direction The direction to check for. 390 * 391 * @return Whether the thumb should keep scrolling. 392 */ 393 boolean shouldScroll(int direction) 394 { 395 int value; 396 if (scrollbar.getOrientation() == HORIZONTAL) 397 value = valueForXPosition(currentMouseX); 398 else 399 value = valueForYPosition(currentMouseY); 400 401 if (thumbRect.contains(currentMouseX, currentMouseY)) 402 return false; 403 404 if (direction == POSITIVE_SCROLL) 405 return value > scrollbar.getValue(); 406 else 407 return value < scrollbar.getValue(); 408 } 409 } 410 411 /** The listener that listens to the JButtons. */ 412 protected ArrowButtonListener buttonListener; 413 414 /** The listener that listens to the model. */ 415 protected ModelListener modelListener; 416 417 /** The listener that listens to the scrollbar for property changes. */ 418 protected PropertyChangeListener propertyChangeListener; 419 420 /** The listener that listens to the timer. */ 421 protected ScrollListener scrollListener; 422 423 /** The listener that listens for MouseEvents on the track. */ 424 protected TrackListener trackListener; 425 426 /** The JButton that decrements the scrollbar's value. */ 427 protected JButton decrButton; 428 429 /** The JButton that increments the scrollbar's value. */ 430 protected JButton incrButton; 431 432 /** The dimensions of the maximum thumb size. */ 433 protected Dimension maximumThumbSize; 434 435 /** The dimensions of the minimum thumb size. */ 436 protected Dimension minimumThumbSize; 437 438 /** The color of the thumb. */ 439 protected Color thumbColor; 440 441 /** The outer shadow of the thumb. */ 442 protected Color thumbDarkShadowColor; 443 444 /** The top and left edge color for the thumb. */ 445 protected Color thumbHighlightColor; 446 447 /** The outer light shadow for the thumb. */ 448 protected Color thumbLightShadowColor; 449 450 /** The color that is used when the mouse press occurs in the track. */ 451 protected Color trackHighlightColor; 452 453 /** The color of the track. */ 454 protected Color trackColor; 455 456 /** The size and position of the track. */ 457 protected Rectangle trackRect; 458 459 /** The size and position of the thumb. */ 460 protected Rectangle thumbRect; 461 462 /** Indicates that the decrease highlight should be painted. */ 463 protected static final int DECREASE_HIGHLIGHT = 1; 464 465 /** Indicates that the increase highlight should be painted. */ 466 protected static final int INCREASE_HIGHLIGHT = 2; 467 468 /** Indicates that no highlight should be painted. */ 469 protected static final int NO_HIGHLIGHT = 0; 470 471 /** Indicates that the scrolling direction is positive. */ 472 private static final int POSITIVE_SCROLL = 1; 473 474 /** Indicates that the scrolling direction is negative. */ 475 private static final int NEGATIVE_SCROLL = -1; 476 477 /** The cached preferred size for the scrollbar. */ 478 private transient Dimension preferredSize; 479 480 /** The current highlight status. */ 481 protected int trackHighlight; 482 483 /** FIXME: Use this for something (presumably mouseDragged) */ 484 protected boolean isDragging; 485 486 /** The timer used to move the thumb when the mouse is held. */ 487 protected Timer scrollTimer; 488 489 /** The scrollbar this UI is acting for. */ 490 protected JScrollBar scrollbar; 491 492 /** True if the mouse is over the thumb. */ 493 boolean thumbRollover; 494 495 /** 496 * This method adds a component to the layout. 497 * 498 * @param name The name to associate with the component that is added. 499 * @param child The Component to add. 500 */ 501 public void addLayoutComponent(String name, Component child) 502 { 503 // You should not be adding stuff to this component. 504 // The contents are fixed. 505 } 506 507 /** 508 * This method configures the scrollbar's colors. This can be done by 509 * looking up the standard colors from the Look and Feel defaults. 510 */ 511 protected void configureScrollBarColors() 512 { 513 trackColor = UIManager.getColor("ScrollBar.track"); 514 trackHighlightColor = UIManager.getColor("ScrollBar.trackHighlight"); 515 thumbColor = UIManager.getColor("ScrollBar.thumb"); 516 thumbHighlightColor = UIManager.getColor("ScrollBar.thumbHighlight"); 517 thumbDarkShadowColor = UIManager.getColor("ScrollBar.thumbDarkShadow"); 518 thumbLightShadowColor = UIManager.getColor("ScrollBar.thumbShadow"); 519 } 520 521 /** 522 * This method creates an ArrowButtonListener. 523 * 524 * @return A new ArrowButtonListener. 525 */ 526 protected ArrowButtonListener createArrowButtonListener() 527 { 528 return new ArrowButtonListener(); 529 } 530 531 /** 532 * This method creates a new JButton with the appropriate icon for the 533 * orientation. 534 * 535 * @param orientation The orientation this JButton uses. 536 * 537 * @return The increase JButton. 538 */ 539 protected JButton createIncreaseButton(int orientation) 540 { 541 return new BasicArrowButton(orientation); 542 } 543 544 /** 545 * This method creates a new JButton with the appropriate icon for the 546 * orientation. 547 * 548 * @param orientation The orientation this JButton uses. 549 * 550 * @return The decrease JButton. 551 */ 552 protected JButton createDecreaseButton(int orientation) 553 { 554 return new BasicArrowButton(orientation); 555 } 556 557 /** 558 * This method creates a new ModelListener. 559 * 560 * @return A new ModelListener. 561 */ 562 protected ModelListener createModelListener() 563 { 564 return new ModelListener(); 565 } 566 567 /** 568 * This method creates a new PropertyChangeListener. 569 * 570 * @return A new PropertyChangeListener. 571 */ 572 protected PropertyChangeListener createPropertyChangeListener() 573 { 574 return new PropertyChangeHandler(); 575 } 576 577 /** 578 * This method creates a new ScrollListener. 579 * 580 * @return A new ScrollListener. 581 */ 582 protected ScrollListener createScrollListener() 583 { 584 return new ScrollListener(); 585 } 586 587 /** 588 * This method creates a new TrackListener. 589 * 590 * @return A new TrackListener. 591 */ 592 protected TrackListener createTrackListener() 593 { 594 return new TrackListener(); 595 } 596 597 /** 598 * This method returns a new BasicScrollBarUI. 599 * 600 * @param c The JComponent to create a UI for. 601 * 602 * @return A new BasicScrollBarUI. 603 */ 604 public static ComponentUI createUI(JComponent c) 605 { 606 return new BasicScrollBarUI(); 607 } 608 609 /** 610 * This method returns the maximum size for this JComponent. 611 * 612 * @param c The JComponent to measure the maximum size for. 613 * 614 * @return The maximum size for the component. 615 */ 616 public Dimension getMaximumSize(JComponent c) 617 { 618 return new Dimension(Integer.MAX_VALUE, Integer.MAX_VALUE); 619 } 620 621 /** 622 * This method returns the maximum thumb size. 623 * 624 * @return The maximum thumb size. 625 */ 626 protected Dimension getMaximumThumbSize() 627 { 628 return maximumThumbSize; 629 } 630 631 /** 632 * This method returns the minimum size for this JComponent. 633 * 634 * @param c The JComponent to measure the minimum size for. 635 * 636 * @return The minimum size for the component. 637 */ 638 public Dimension getMinimumSize(JComponent c) 639 { 640 return getPreferredSize(c); 641 } 642 643 /** 644 * This method returns the minimum thumb size. 645 * 646 * @return The minimum thumb size. 647 */ 648 protected Dimension getMinimumThumbSize() 649 { 650 return minimumThumbSize; 651 } 652 653 /** 654 * This method calculates the preferred size since calling 655 * getPreferredSize() returns a cached value. 656 * This is package-private to avoid an accessor method. 657 */ 658 void calculatePreferredSize() 659 { 660 int height; 661 int width; 662 height = width = 0; 663 664 if (scrollbar.getOrientation() == SwingConstants.HORIZONTAL) 665 { 666 width += incrButton.getPreferredSize().getWidth(); 667 width += decrButton.getPreferredSize().getWidth(); 668 width += 16; 669 height = UIManager.getInt("ScrollBar.width"); 670 } 671 else 672 { 673 height += incrButton.getPreferredSize().getHeight(); 674 height += decrButton.getPreferredSize().getHeight(); 675 height += 16; 676 width = UIManager.getInt("ScrollBar.width"); 677 } 678 679 Insets insets = scrollbar.getInsets(); 680 681 height += insets.top + insets.bottom; 682 width += insets.left + insets.right; 683 684 preferredSize = new Dimension(width, height); 685 } 686 687 /** 688 * This method returns a cached value of the preferredSize. The only 689 * restrictions are: If the scrollbar is horizontal, the height should be 690 * the maximum of the height of the JButtons and the minimum width of the 691 * thumb. For vertical scrollbars, the calculation is similar (swap width 692 * for height and vice versa). 693 * 694 * @param c The JComponent to measure. 695 * 696 * @return The preferredSize. 697 */ 698 public Dimension getPreferredSize(JComponent c) 699 { 700 calculatePreferredSize(); 701 return preferredSize; 702 } 703 704 /** 705 * This method returns the thumb's bounds based on the current value of the 706 * scrollbar. This method updates the cached value and returns that. 707 * 708 * @return The thumb bounds. 709 */ 710 protected Rectangle getThumbBounds() 711 { 712 return thumbRect; 713 } 714 715 /** 716 * This method calculates the bounds of the track. This method updates the 717 * cached value and returns it. 718 * 719 * @return The track's bounds. 720 */ 721 protected Rectangle getTrackBounds() 722 { 723 return trackRect; 724 } 725 726 /** 727 * This method installs any addition Components that are a part of or 728 * related to this scrollbar. 729 */ 730 protected void installComponents() 731 { 732 int orientation = scrollbar.getOrientation(); 733 switch (orientation) 734 { 735 case JScrollBar.HORIZONTAL: 736 incrButton = createIncreaseButton(EAST); 737 decrButton = createDecreaseButton(WEST); 738 break; 739 default: 740 incrButton = createIncreaseButton(SOUTH); 741 decrButton = createDecreaseButton(NORTH); 742 break; 743 } 744 745 if (incrButton != null) 746 scrollbar.add(incrButton); 747 if (decrButton != null) 748 scrollbar.add(decrButton); 749 } 750 751 /** 752 * This method installs the defaults for the scrollbar specified by the 753 * Basic Look and Feel. 754 */ 755 protected void installDefaults() 756 { 757 LookAndFeel.installColors(scrollbar, "ScrollBar.background", 758 "ScrollBar.foreground"); 759 LookAndFeel.installBorder(scrollbar, "ScrollBar.border"); 760 scrollbar.setOpaque(true); 761 scrollbar.setLayout(this); 762 763 configureScrollBarColors(); 764 765 maximumThumbSize = UIManager.getDimension("ScrollBar.maximumThumbSize"); 766 minimumThumbSize = UIManager.getDimension("ScrollBar.minimumThumbSize"); 767 } 768 769 /** 770 * Installs the input map from the look and feel defaults, and a 771 * corresponding action map. Note the the keyboard bindings will only 772 * work when the {@link JScrollBar} component has the focus, which is rare. 773 */ 774 protected void installKeyboardActions() 775 { 776 InputMap keyMap = getInputMap( 777 JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT); 778 SwingUtilities.replaceUIInputMap(scrollbar, 779 JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT, keyMap); 780 ActionMap map = getActionMap(); 781 SwingUtilities.replaceUIActionMap(scrollbar, map); 782 } 783 784 /** 785 * Uninstalls the input map and action map installed by 786 * {@link #installKeyboardActions()}. 787 */ 788 protected void uninstallKeyboardActions() 789 { 790 SwingUtilities.replaceUIActionMap(scrollbar, null); 791 SwingUtilities.replaceUIInputMap(scrollbar, 792 JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT, null); 793 } 794 795 InputMap getInputMap(int condition) 796 { 797 if (condition == JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT) 798 return (InputMap) UIManager.get("ScrollBar.focusInputMap"); 799 return null; 800 } 801 802 /** 803 * Returns the action map for the {@link JScrollBar}. All scroll bars 804 * share a single action map which is created the first time this method is 805 * called, then stored in the UIDefaults table for subsequent access. 806 * 807 * @return The shared action map. 808 */ 809 ActionMap getActionMap() 810 { 811 ActionMap map = (ActionMap) UIManager.get("ScrollBar.actionMap"); 812 813 if (map == null) // first time here 814 { 815 map = createActionMap(); 816 if (map != null) 817 UIManager.put("ScrollBar.actionMap", map); 818 } 819 return map; 820 } 821 822 /** 823 * Creates the action map shared by all {@link JSlider} instances. 824 * This method is called once by {@link #getActionMap()} when it 825 * finds no action map in the UIDefaults table...after the map is 826 * created, it gets added to the defaults table so that subsequent 827 * calls to {@link #getActionMap()} will return the same shared 828 * instance. 829 * 830 * @return The action map. 831 */ 832 ActionMap createActionMap() 833 { 834 ActionMap map = new ActionMapUIResource(); 835 map.put("positiveUnitIncrement", 836 new AbstractAction("positiveUnitIncrement") { 837 public void actionPerformed(ActionEvent event) 838 { 839 JScrollBar sb = (JScrollBar) event.getSource(); 840 if (sb.isVisible()) 841 { 842 int delta = sb.getUnitIncrement(1); 843 sb.setValue(sb.getValue() + delta); 844 } 845 } 846 } 847 ); 848 map.put("positiveBlockIncrement", 849 new AbstractAction("positiveBlockIncrement") { 850 public void actionPerformed(ActionEvent event) 851 { 852 JScrollBar sb = (JScrollBar) event.getSource(); 853 if (sb.isVisible()) 854 { 855 int delta = sb.getBlockIncrement(1); 856 sb.setValue(sb.getValue() + delta); 857 } 858 } 859 } 860 ); 861 map.put("negativeUnitIncrement", 862 new AbstractAction("negativeUnitIncrement") { 863 public void actionPerformed(ActionEvent event) 864 { 865 JScrollBar sb = (JScrollBar) event.getSource(); 866 if (sb.isVisible()) 867 { 868 int delta = sb.getUnitIncrement(-1); 869 sb.setValue(sb.getValue() + delta); 870 } 871 } 872 } 873 ); 874 map.put("negativeBlockIncrement", 875 new AbstractAction("negativeBlockIncrement") { 876 public void actionPerformed(ActionEvent event) 877 { 878 JScrollBar sb = (JScrollBar) event.getSource(); 879 if (sb.isVisible()) 880 { 881 int delta = sb.getBlockIncrement(-1); 882 sb.setValue(sb.getValue() + delta); 883 } 884 } 885 } 886 ); 887 map.put("minScroll", 888 new AbstractAction("minScroll") { 889 public void actionPerformed(ActionEvent event) 890 { 891 JScrollBar sb = (JScrollBar) event.getSource(); 892 if (sb.isVisible()) 893 { 894 sb.setValue(sb.getMinimum()); 895 } 896 } 897 } 898 ); 899 map.put("maxScroll", 900 new AbstractAction("maxScroll") { 901 public void actionPerformed(ActionEvent event) 902 { 903 JScrollBar sb = (JScrollBar) event.getSource(); 904 if (sb.isVisible()) 905 { 906 sb.setValue(sb.getMaximum()); 907 } 908 } 909 } 910 ); 911 return map; 912 } 913 914 /** 915 * This method installs any listeners for the scrollbar. This method also 916 * installs listeners for things such as the JButtons and the timer. 917 */ 918 protected void installListeners() 919 { 920 scrollListener = createScrollListener(); 921 trackListener = createTrackListener(); 922 buttonListener = createArrowButtonListener(); 923 modelListener = createModelListener(); 924 propertyChangeListener = createPropertyChangeListener(); 925 926 scrollbar.addMouseMotionListener(trackListener); 927 scrollbar.addMouseListener(trackListener); 928 929 incrButton.addMouseListener(buttonListener); 930 decrButton.addMouseListener(buttonListener); 931 932 scrollbar.addPropertyChangeListener(propertyChangeListener); 933 scrollbar.getModel().addChangeListener(modelListener); 934 935 scrollTimer.addActionListener(scrollListener); 936 } 937 938 /** 939 * This method installs the UI for the component. This can include setting 940 * up listeners, defaults, and components. This also includes initializing 941 * any data objects. 942 * 943 * @param c The JComponent to install. 944 */ 945 public void installUI(JComponent c) 946 { 947 super.installUI(c); 948 if (c instanceof JScrollBar) 949 { 950 scrollbar = (JScrollBar) c; 951 952 trackRect = new Rectangle(); 953 thumbRect = new Rectangle(); 954 955 scrollTimer = new Timer(300, null); 956 957 installDefaults(); 958 installComponents(); 959 configureScrollBarColors(); 960 installListeners(); 961 installKeyboardActions(); 962 963 calculatePreferredSize(); 964 } 965 } 966 967 /** 968 * This method lays out the scrollbar. 969 * 970 * @param scrollbarContainer The Container to layout. 971 */ 972 public void layoutContainer(Container scrollbarContainer) 973 { 974 if (scrollbarContainer instanceof JScrollBar) 975 { 976 if (scrollbar.getOrientation() == SwingConstants.HORIZONTAL) 977 layoutHScrollbar((JScrollBar) scrollbarContainer); 978 else 979 layoutVScrollbar((JScrollBar) scrollbarContainer); 980 } 981 } 982 983 /** 984 * This method lays out the scrollbar horizontally. 985 * 986 * @param sb The JScrollBar to layout. 987 */ 988 protected void layoutHScrollbar(JScrollBar sb) 989 { 990 Rectangle vr = new Rectangle(); 991 SwingUtilities.calculateInnerArea(scrollbar, vr); 992 993 Dimension incrDims = incrButton.getPreferredSize(); 994 Dimension decrDims = decrButton.getPreferredSize(); 995 996 // calculate and update the track bounds 997 SwingUtilities.calculateInnerArea(scrollbar, trackRect); 998 trackRect.width -= incrDims.getWidth(); 999 trackRect.width -= decrDims.getWidth(); 1000 trackRect.x += decrDims.getWidth(); 1001 1002 updateThumbRect(); 1003 1004 decrButton.setBounds(vr.x, vr.y, decrDims.width, trackRect.height); 1005 incrButton.setBounds(trackRect.x + trackRect.width, vr.y, incrDims.width, 1006 trackRect.height); 1007 } 1008 1009 /** 1010 * This method lays out the scrollbar vertically. 1011 * 1012 * @param sb The JScrollBar to layout. 1013 */ 1014 protected void layoutVScrollbar(JScrollBar sb) 1015 { 1016 Rectangle vr = new Rectangle(); 1017 SwingUtilities.calculateInnerArea(scrollbar, vr); 1018 1019 Dimension incrDims = incrButton.getPreferredSize(); 1020 Dimension decrDims = decrButton.getPreferredSize(); 1021 1022 // Update rectangles 1023 SwingUtilities.calculateInnerArea(scrollbar, trackRect); 1024 trackRect.height -= incrDims.getHeight(); 1025 trackRect.height -= decrDims.getHeight(); 1026 trackRect.y += decrDims.getHeight(); 1027 1028 updateThumbRect(); 1029 1030 decrButton.setBounds(vr.x, vr.y, trackRect.width, decrDims.height); 1031 incrButton.setBounds(vr.x, trackRect.y + trackRect.height, 1032 trackRect.width, incrDims.height); 1033 } 1034 1035 /** 1036 * Updates the thumb rect. 1037 */ 1038 void updateThumbRect() 1039 { 1040 int max = scrollbar.getMaximum(); 1041 int min = scrollbar.getMinimum(); 1042 int value = scrollbar.getValue(); 1043 int extent = scrollbar.getVisibleAmount(); 1044 if (max - extent <= min) 1045 { 1046 if (scrollbar.getOrientation() == JScrollBar.HORIZONTAL) 1047 { 1048 thumbRect.x = trackRect.x; 1049 thumbRect.y = trackRect.y; 1050 thumbRect.width = getMinimumThumbSize().width; 1051 thumbRect.height = trackRect.height; 1052 } 1053 else 1054 { 1055 thumbRect.x = trackRect.x; 1056 thumbRect.y = trackRect.y; 1057 thumbRect.width = trackRect.width; 1058 thumbRect.height = getMinimumThumbSize().height; 1059 } 1060 } 1061 else 1062 { 1063 if (scrollbar.getOrientation() == JScrollBar.HORIZONTAL) 1064 { 1065 thumbRect.x = trackRect.x; 1066 thumbRect.width = Math.max(extent * trackRect.width / (max - min), 1067 getMinimumThumbSize().width); 1068 int availableWidth = trackRect.width - thumbRect.width; 1069 thumbRect.x += (value - min) * availableWidth / (max - min - extent); 1070 thumbRect.y = trackRect.y; 1071 thumbRect.height = trackRect.height; 1072 } 1073 else 1074 { 1075 thumbRect.x = trackRect.x; 1076 thumbRect.height = Math.max(extent * trackRect.height / (max - min), 1077 getMinimumThumbSize().height); 1078 int availableHeight = trackRect.height - thumbRect.height; 1079 thumbRect.y = trackRect.y 1080 + (value - min) * availableHeight / (max - min - extent); 1081 thumbRect.width = trackRect.width; 1082 } 1083 } 1084 1085 } 1086 1087 /** 1088 * This method returns the minimum size required for the layout. 1089 * 1090 * @param scrollbarContainer The Container that is laid out. 1091 * 1092 * @return The minimum size. 1093 */ 1094 public Dimension minimumLayoutSize(Container scrollbarContainer) 1095 { 1096 return preferredLayoutSize(scrollbarContainer); 1097 } 1098 1099 /** 1100 * This method is called when the component is painted. 1101 * 1102 * @param g The Graphics object to paint with. 1103 * @param c The JComponent to paint. 1104 */ 1105 public void paint(Graphics g, JComponent c) 1106 { 1107 paintTrack(g, c, getTrackBounds()); 1108 paintThumb(g, c, getThumbBounds()); 1109 1110 if (trackHighlight == INCREASE_HIGHLIGHT) 1111 paintIncreaseHighlight(g); 1112 else if (trackHighlight == DECREASE_HIGHLIGHT) 1113 paintDecreaseHighlight(g); 1114 } 1115 1116 /** 1117 * This method is called when repainting and the mouse is pressed in the 1118 * track. It paints the track below the thumb with the trackHighlight 1119 * color. 1120 * 1121 * @param g The Graphics object to paint with. 1122 */ 1123 protected void paintDecreaseHighlight(Graphics g) 1124 { 1125 Color saved = g.getColor(); 1126 1127 g.setColor(trackHighlightColor); 1128 if (scrollbar.getOrientation() == HORIZONTAL) 1129 g.fillRect(trackRect.x, trackRect.y, thumbRect.x - trackRect.x, 1130 trackRect.height); 1131 else 1132 g.fillRect(trackRect.x, trackRect.y, trackRect.width, 1133 thumbRect.y - trackRect.y); 1134 g.setColor(saved); 1135 } 1136 1137 /** 1138 * This method is called when repainting and the mouse is pressed in the 1139 * track. It paints the track above the thumb with the trackHighlight 1140 * color. 1141 * 1142 * @param g The Graphics objet to paint with. 1143 */ 1144 protected void paintIncreaseHighlight(Graphics g) 1145 { 1146 Color saved = g.getColor(); 1147 1148 g.setColor(trackHighlightColor); 1149 if (scrollbar.getOrientation() == HORIZONTAL) 1150 g.fillRect(thumbRect.x + thumbRect.width, trackRect.y, 1151 trackRect.x + trackRect.width - thumbRect.x - thumbRect.width, 1152 trackRect.height); 1153 else 1154 g.fillRect(trackRect.x, thumbRect.y + thumbRect.height, trackRect.width, 1155 trackRect.y + trackRect.height - thumbRect.y 1156 - thumbRect.height); 1157 g.setColor(saved); 1158 } 1159 1160 /** 1161 * This method paints the thumb. 1162 * 1163 * @param g The Graphics object to paint with. 1164 * @param c The Component that is being painted. 1165 * @param thumbBounds The thumb bounds. 1166 */ 1167 protected void paintThumb(Graphics g, JComponent c, Rectangle thumbBounds) 1168 { 1169 g.setColor(thumbColor); 1170 g.fillRect(thumbBounds.x, thumbBounds.y, thumbBounds.width, 1171 thumbBounds.height); 1172 1173 BasicGraphicsUtils.drawBezel(g, thumbBounds.x, thumbBounds.y, 1174 thumbBounds.width, thumbBounds.height, 1175 false, false, thumbDarkShadowColor, 1176 thumbDarkShadowColor, thumbHighlightColor, 1177 thumbHighlightColor); 1178 } 1179 1180 /** 1181 * This method paints the track. 1182 * 1183 * @param g The Graphics object to paint with. 1184 * @param c The JComponent being painted. 1185 * @param trackBounds The track's bounds. 1186 */ 1187 protected void paintTrack(Graphics g, JComponent c, Rectangle trackBounds) 1188 { 1189 Color saved = g.getColor(); 1190 g.setColor(trackColor); 1191 g.fill3DRect(trackBounds.x, trackBounds.y, trackBounds.width, 1192 trackBounds.height, false); 1193 g.setColor(saved); 1194 } 1195 1196 /** 1197 * This method returns the preferred size for the layout. 1198 * 1199 * @param scrollbarContainer The Container to find a size for. 1200 * 1201 * @return The preferred size for the layout. 1202 */ 1203 public Dimension preferredLayoutSize(Container scrollbarContainer) 1204 { 1205 if (scrollbarContainer instanceof JComponent) 1206 return getPreferredSize((JComponent) scrollbarContainer); 1207 else 1208 return null; 1209 } 1210 1211 /** 1212 * This method removes a child component from the layout. 1213 * 1214 * @param child The child to remove. 1215 */ 1216 public void removeLayoutComponent(Component child) 1217 { 1218 // You should not be removing stuff from this component. 1219 } 1220 1221 /** 1222 * The method scrolls the thumb by a block in the direction specified. 1223 * 1224 * @param direction The direction to scroll. 1225 */ 1226 protected void scrollByBlock(int direction) 1227 { 1228 scrollByBlock(scrollbar, direction); 1229 } 1230 1231 /** 1232 * Scrolls the specified <code>scrollBar</code> by one block (according 1233 * to the scrollable protocol) in the specified <code>direction</code>. 1234 * 1235 * This method is here statically to support wheel scrolling from the 1236 * BasicScrollPaneUI without code duplication. 1237 * 1238 * @param scrollBar the scrollbar to scroll 1239 * @param direction the scroll direction 1240 */ 1241 static final void scrollByBlock(JScrollBar scrollBar, int direction) 1242 { 1243 int delta; 1244 if (direction > 0) 1245 delta = scrollBar.getBlockIncrement(direction); 1246 else 1247 delta = - scrollBar.getBlockIncrement(direction); 1248 int oldValue = scrollBar.getValue(); 1249 int newValue = oldValue + delta; 1250 1251 // Overflow check. 1252 if (delta > 0 && newValue < oldValue) 1253 newValue = scrollBar.getMaximum(); 1254 else if (delta < 0 && newValue > oldValue) 1255 newValue = scrollBar.getMinimum(); 1256 1257 scrollBar.setValue(newValue); 1258 } 1259 1260 /** 1261 * The method scrolls the thumb by a unit in the direction specified. 1262 * 1263 * @param direction The direction to scroll. 1264 */ 1265 protected void scrollByUnit(int direction) 1266 { 1267 scrollByUnits(scrollbar, direction, 1); 1268 } 1269 1270 /** 1271 * Scrolls the specified <code>scrollbac/code> by <code>units</code> units 1272 * in the specified <code>direction</code>. 1273 * 1274 * This method is here statically to support wheel scrolling from the 1275 * BasicScrollPaneUI without code duplication. 1276 * 1277 * @param scrollBar the scrollbar to scroll 1278 * @param direction the direction 1279 * @param units the number of units to scroll 1280 */ 1281 static final void scrollByUnits(JScrollBar scrollBar, int direction, 1282 int units) 1283 { 1284 // Do this inside a loop so that we don't clash with the scrollable 1285 // interface, which can return different units at times. For instance, 1286 // a Scrollable could return a unit of 2 pixels only to adjust the 1287 // visibility of an item. If we would simply multiply this by units, 1288 // then we would only get 6 pixels, which is complete crap. 1289 for (int i = 0; i < units; i++) 1290 { 1291 int delta; 1292 if (direction > 0) 1293 delta = scrollBar.getUnitIncrement(direction); 1294 else 1295 delta = - scrollBar.getUnitIncrement(direction); 1296 int oldValue = scrollBar.getValue(); 1297 int newValue = oldValue + delta; 1298 1299 // Overflow check. 1300 if (delta > 0 && newValue < oldValue) 1301 newValue = scrollBar.getMaximum(); 1302 else if (delta < 0 && newValue > oldValue) 1303 newValue = scrollBar.getMinimum(); 1304 1305 scrollBar.setValue(newValue); 1306 } 1307 } 1308 1309 /** 1310 * This method sets the thumb's bounds. 1311 * 1312 * @param x The X position of the thumb. 1313 * @param y The Y position of the thumb. 1314 * @param width The width of the thumb. 1315 * @param height The height of the thumb. 1316 */ 1317 protected void setThumbBounds(int x, int y, int width, int height) 1318 { 1319 thumbRect.x = x; 1320 thumbRect.y = y; 1321 thumbRect.width = width; 1322 thumbRect.height = height; 1323 } 1324 1325 /** 1326 * This method uninstalls any components that are a part of or related to 1327 * this scrollbar. 1328 */ 1329 protected void uninstallComponents() 1330 { 1331 if (incrButton != null) 1332 scrollbar.remove(incrButton); 1333 if (decrButton != null) 1334 scrollbar.remove(decrButton); 1335 } 1336 1337 /** 1338 * This method uninstalls any defaults that this scrollbar acquired from the 1339 * Basic Look and Feel defaults. 1340 */ 1341 protected void uninstallDefaults() 1342 { 1343 scrollbar.setForeground(null); 1344 scrollbar.setBackground(null); 1345 LookAndFeel.uninstallBorder(scrollbar); 1346 incrButton = null; 1347 decrButton = null; 1348 } 1349 1350 /** 1351 * This method uninstalls any listeners that were registered during install. 1352 */ 1353 protected void uninstallListeners() 1354 { 1355 if (scrollTimer != null) 1356 scrollTimer.removeActionListener(scrollListener); 1357 1358 if (scrollbar != null) 1359 { 1360 scrollbar.getModel().removeChangeListener(modelListener); 1361 scrollbar.removePropertyChangeListener(propertyChangeListener); 1362 scrollbar.removeMouseListener(trackListener); 1363 scrollbar.removeMouseMotionListener(trackListener); 1364 } 1365 1366 if (decrButton != null) 1367 decrButton.removeMouseListener(buttonListener); 1368 if (incrButton != null) 1369 incrButton.removeMouseListener(buttonListener); 1370 1371 propertyChangeListener = null; 1372 modelListener = null; 1373 buttonListener = null; 1374 trackListener = null; 1375 scrollListener = null; 1376 } 1377 1378 /** 1379 * This method uninstalls the UI. This includes removing any defaults, 1380 * listeners, and components that this UI may have initialized. It also 1381 * nulls any instance data. 1382 * 1383 * @param c The Component to uninstall for. 1384 */ 1385 public void uninstallUI(JComponent c) 1386 { 1387 uninstallKeyboardActions(); 1388 uninstallListeners(); 1389 uninstallDefaults(); 1390 uninstallComponents(); 1391 1392 scrollTimer = null; 1393 1394 thumbRect = null; 1395 trackRect = null; 1396 1397 trackColor = null; 1398 trackHighlightColor = null; 1399 thumbColor = null; 1400 thumbHighlightColor = null; 1401 thumbDarkShadowColor = null; 1402 thumbLightShadowColor = null; 1403 1404 scrollbar = null; 1405 } 1406 1407 /** 1408 * This method returns the value in the scrollbar's range given the y 1409 * coordinate. If the value is out of range, it will return the closest 1410 * legal value. 1411 * This is package-private to avoid an accessor method. 1412 * 1413 * @param yPos The y coordinate to calculate a value for. 1414 * 1415 * @return The value for the y coordinate. 1416 */ 1417 int valueForYPosition(int yPos) 1418 { 1419 int min = scrollbar.getMinimum(); 1420 int max = scrollbar.getMaximum(); 1421 int len = trackRect.height; 1422 1423 int value; 1424 1425 // If the length is 0, you shouldn't be able to even see where the thumb is. 1426 // This really shouldn't ever happen, but just in case, we'll return the middle. 1427 if (len == 0) 1428 return (max - min) / 2; 1429 1430 value = (yPos - trackRect.y) * (max - min) / len + min; 1431 1432 // If this isn't a legal value, then we'll have to move to one now. 1433 if (value > max) 1434 value = max; 1435 else if (value < min) 1436 value = min; 1437 return value; 1438 } 1439 1440 /** 1441 * This method returns the value in the scrollbar's range given the x 1442 * coordinate. If the value is out of range, it will return the closest 1443 * legal value. 1444 * This is package-private to avoid an accessor method. 1445 * 1446 * @param xPos The x coordinate to calculate a value for. 1447 * 1448 * @return The value for the x coordinate. 1449 */ 1450 int valueForXPosition(int xPos) 1451 { 1452 int min = scrollbar.getMinimum(); 1453 int max = scrollbar.getMaximum(); 1454 int len = trackRect.width; 1455 1456 int value; 1457 1458 // If the length is 0, you shouldn't be able to even see where the slider is. 1459 // This really shouldn't ever happen, but just in case, we'll return the middle. 1460 if (len == 0) 1461 return (max - min) / 2; 1462 1463 value = (xPos - trackRect.x) * (max - min) / len + min; 1464 1465 // If this isn't a legal value, then we'll have to move to one now. 1466 if (value > max) 1467 value = max; 1468 else if (value < min) 1469 value = min; 1470 return value; 1471 } 1472 1473 /** 1474 * Returns true if the mouse is over the thumb. 1475 * 1476 * @return true if the mouse is over the thumb. 1477 * 1478 * @since 1.5 1479 */ 1480 public boolean isThumbRollover() 1481 { 1482 return thumbRollover; 1483 } 1484 1485 /** 1486 * Set thumbRollover to active. This indicates 1487 * whether or not the mouse is over the thumb. 1488 * 1489 * @param active - true if the mouse is over the thumb. 1490 * 1491 * @since 1.5 1492 */ 1493 protected void setThumbRollover(boolean active) 1494 { 1495 thumbRollover = active; 1496 } 1497 1498 /** 1499 * Indicates whether the user can position the thumb with 1500 * a mouse click (i.e. middle button). 1501 * 1502 * @return true if the user can position the thumb with a mouse 1503 * click. 1504 * 1505 * @since 1.5 1506 */ 1507 public boolean getSupportsAbsolutePositioning() 1508 { 1509 // The positioning feature has not been implemented. 1510 // So, false is always returned. 1511 return false; 1512 } 1513}