001 /* ScrollPane.java -- Scrolling window 002 Copyright (C) 1999, 2002, 2004 Free Software Foundation, Inc. 003 004 This file is part of GNU Classpath. 005 006 GNU Classpath is free software; you can redistribute it and/or modify 007 it under the terms of the GNU General Public License as published by 008 the Free Software Foundation; either version 2, or (at your option) 009 any later version. 010 011 GNU Classpath is distributed in the hope that it will be useful, but 012 WITHOUT ANY WARRANTY; without even the implied warranty of 013 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 014 General Public License for more details. 015 016 You should have received a copy of the GNU General Public License 017 along with GNU Classpath; see the file COPYING. If not, write to the 018 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 019 02110-1301 USA. 020 021 Linking this library statically or dynamically with other modules is 022 making a combined work based on this library. Thus, the terms and 023 conditions of the GNU General Public License cover the whole 024 combination. 025 026 As a special exception, the copyright holders of this library give you 027 permission to link this library with independent modules to produce an 028 executable, regardless of the license terms of these independent 029 modules, and to copy and distribute the resulting executable under 030 terms of your choice, provided that you also meet, for each linked 031 independent module, the terms and conditions of the license of that 032 module. An independent module is a module which is not derived from 033 or based on this library. If you modify this library, you may extend 034 this exception to your version of the library, but you are not 035 obligated to do so. If you do not wish to do so, delete this 036 exception statement from your version. */ 037 038 039 package java.awt; 040 041 import java.awt.event.MouseEvent; 042 import java.awt.peer.ComponentPeer; 043 import java.awt.peer.ScrollPanePeer; 044 045 import javax.accessibility.Accessible; 046 import javax.accessibility.AccessibleContext; 047 import javax.accessibility.AccessibleRole; 048 049 050 /** 051 * This widget provides a scrollable region that allows a single 052 * subcomponent to be viewed through a smaller window. 053 * 054 * @author Aaron M. Renn (arenn@urbanophile.com) 055 */ 056 public class ScrollPane extends Container implements Accessible 057 { 058 059 /* 060 * Static Variables 061 */ 062 063 /** 064 * Constant indicating that scrollbars are created as needed in this 065 * windows. 066 */ 067 public static final int SCROLLBARS_AS_NEEDED = 0; 068 069 /** 070 * Constant indicating that scrollbars are always displayed in this 071 * window. 072 */ 073 public static final int SCROLLBARS_ALWAYS = 1; 074 075 /** 076 * Constant indicating that scrollbars are never displayed in this window. 077 */ 078 public static final int SCROLLBARS_NEVER = 2; 079 080 /** 081 * The number used to generate the name returned by getName. 082 */ 083 private static transient long next_scrollpane_number; 084 085 // Serialization constant 086 private static final long serialVersionUID = 7956609840827222915L; 087 088 /*************************************************************************/ 089 090 /* 091 * Instance Variables 092 */ 093 094 /** 095 * @serial The horizontal scrollbar for this window. The methods 096 * <code>setMinimum()</code>, <code>setMaximum</code>, and 097 * <code>setVisibleAmount</code> must not be called on this scrollbar. 098 */ 099 private ScrollPaneAdjustable hAdjustable; 100 101 /** 102 * @serial The vertical scrollbar for this window. The methods 103 * <code>setMinimum()</code>, <code>setMaximum</code>, and 104 * <code>setVisibleAmount</code> must not be called on this scrollbar. 105 */ 106 private ScrollPaneAdjustable vAdjustable; 107 108 /** 109 * @serial Indicates when scrollbars are displayed in this window, will 110 * be one of the constants from this class. 111 */ 112 private int scrollbarDisplayPolicy; 113 114 // Current scroll position 115 private Point scrollPosition = new Point(0, 0); 116 117 private boolean wheelScrollingEnabled; 118 119 /*************************************************************************/ 120 121 /* 122 * Constructors 123 */ 124 125 /** 126 * Initializes a new instance of <code>ScrollPane</code> with a default 127 * scrollbar policy of <code>SCROLLBARS_AS_NEEDED</code>. 128 * 129 * @exception HeadlessException If GraphicsEnvironment.isHeadless() is true. 130 */ 131 public 132 ScrollPane() 133 { 134 this(SCROLLBARS_AS_NEEDED); 135 } 136 137 /*************************************************************************/ 138 139 /** 140 * Initializes a new instance of <code>ScrollPane</code> with the 141 * specified scrollbar policy. 142 * 143 * @param scrollbarDisplayPolicy When to display scrollbars, which must 144 * be one of the constants defined in this class. 145 * 146 * @exception HeadlessException If GraphicsEnvironment.isHeadless() is true. 147 */ 148 public 149 ScrollPane(int scrollbarDisplayPolicy) 150 { 151 if (GraphicsEnvironment.isHeadless ()) 152 throw new HeadlessException (); 153 154 this.scrollbarDisplayPolicy = scrollbarDisplayPolicy; 155 156 if (scrollbarDisplayPolicy != SCROLLBARS_ALWAYS 157 && scrollbarDisplayPolicy != SCROLLBARS_AS_NEEDED 158 && scrollbarDisplayPolicy != SCROLLBARS_NEVER) 159 throw new IllegalArgumentException("Bad scrollbarDisplayPolicy: " + 160 scrollbarDisplayPolicy); 161 162 if (scrollbarDisplayPolicy != SCROLLBARS_NEVER) 163 { 164 hAdjustable = new ScrollPaneAdjustable (this, Scrollbar.HORIZONTAL); 165 vAdjustable = new ScrollPaneAdjustable (this, Scrollbar.VERTICAL); 166 } 167 168 wheelScrollingEnabled = true; 169 170 // Default size. 171 setSize(100,100); 172 } 173 174 /*************************************************************************/ 175 176 /* 177 * Instance Variables 178 */ 179 180 /** 181 * Returns the current scrollbar display policy. 182 * 183 * @return The current scrollbar display policy. 184 */ 185 public int 186 getScrollbarDisplayPolicy() 187 { 188 return(scrollbarDisplayPolicy); 189 } 190 191 /*************************************************************************/ 192 193 /** 194 * Returns the horizontal scrollbar for this object. If the scrollbar 195 * display policy is set to <code>SCROLLBARS_NEVER</code> then this 196 * will be <code>null</code>. 197 * 198 * @return The horizontal scrollbar for this window. 199 */ 200 public Adjustable 201 getHAdjustable() 202 { 203 return(hAdjustable); 204 } 205 206 /*************************************************************************/ 207 208 /** 209 * Returns the vertical scrollbar for this object. If the scrollbar 210 * display policy is set to <code>SCROLLBARS_NEVER</code> then this 211 * will be <code>null</code>. 212 * 213 * @return The horizontal scrollbar for this window. 214 */ 215 public Adjustable 216 getVAdjustable() 217 { 218 return(vAdjustable); 219 } 220 221 /*************************************************************************/ 222 223 /** 224 * Returns the current viewport size. The viewport is the region of 225 * this object's window where the child is actually displayed. 226 * 227 * @return The viewport size. 228 */ 229 public Dimension getViewportSize () 230 { 231 Dimension viewsize = getSize (); 232 Insets insets = getInsets (); 233 234 viewsize.width -= (insets.left + insets.right); 235 viewsize.height -= (insets.top + insets.bottom); 236 237 Component[] list = getComponents(); 238 if ((list == null) || (list.length <= 0)) 239 return viewsize; 240 241 Dimension dim = list[0].getPreferredSize(); 242 243 if (dim.width <= 0 && dim.height <= 0) 244 return viewsize; 245 246 int vScrollbarWidth = getVScrollbarWidth (); 247 int hScrollbarHeight = getHScrollbarHeight (); 248 249 if (scrollbarDisplayPolicy == SCROLLBARS_ALWAYS) 250 { 251 viewsize.width -= vScrollbarWidth; 252 viewsize.height -= hScrollbarHeight; 253 return viewsize; 254 } 255 256 if (scrollbarDisplayPolicy == SCROLLBARS_NEVER) 257 return viewsize; 258 259 // The scroll policy is SCROLLBARS_AS_NEEDED, so we need to see if 260 // either scrollbar is needed. 261 262 // Assume we don't need either scrollbar. 263 boolean mayNeedVertical = false; 264 boolean mayNeedHorizontal = false; 265 266 boolean needVertical = false; 267 boolean needHorizontal = false; 268 269 // Check if we need vertical scrollbars. If we do, then we need to 270 // subtract the width of the vertical scrollbar from the viewport's 271 // width. 272 if (dim.height > viewsize.height) 273 needVertical = true; 274 else if (dim.height > (viewsize.height - hScrollbarHeight)) 275 // This is tricky. In this case the child is tall enough that its 276 // bottom edge would be covered by a horizontal scrollbar, if one 277 // were present. This means that if there's a horizontal 278 // scrollbar then we need a vertical scrollbar. 279 mayNeedVertical = true; 280 281 if (dim.width > viewsize.width) 282 needHorizontal = true; 283 else if (dim.width > (viewsize.width - vScrollbarWidth)) 284 mayNeedHorizontal = true; 285 286 if (needVertical && mayNeedHorizontal) 287 needHorizontal = true; 288 289 if (needHorizontal && mayNeedVertical) 290 needVertical = true; 291 292 if (needHorizontal) 293 viewsize.height -= hScrollbarHeight; 294 295 if (needVertical) 296 viewsize.width -= vScrollbarWidth; 297 298 return viewsize; 299 } 300 301 /*************************************************************************/ 302 303 /** 304 * Returns the height of a horizontal scrollbar. 305 * 306 * @return The height of a horizontal scrollbar. 307 */ 308 public int 309 getHScrollbarHeight() 310 { 311 ScrollPanePeer spp = (ScrollPanePeer)getPeer(); 312 if (spp != null) 313 return(spp.getHScrollbarHeight()); 314 else 315 return(0); // FIXME: What to do here? 316 } 317 318 /*************************************************************************/ 319 320 /** 321 * Returns the width of a vertical scrollbar. 322 * 323 * @return The width of a vertical scrollbar. 324 */ 325 public int 326 getVScrollbarWidth() 327 { 328 ScrollPanePeer spp = (ScrollPanePeer)getPeer(); 329 if (spp != null) 330 return(spp.getVScrollbarWidth()); 331 else 332 return(0); // FIXME: What to do here? 333 } 334 335 /*************************************************************************/ 336 337 /** 338 * Returns the current scroll position of the viewport. 339 * 340 * @return The current scroll position of the viewport. 341 * 342 * @throws NullPointerException if the scrollpane does have a child. 343 */ 344 public Point 345 getScrollPosition() 346 { 347 if (getComponentCount() == 0) 348 throw new NullPointerException(); 349 350 int x = 0; 351 int y = 0; 352 353 Adjustable v = getVAdjustable(); 354 Adjustable h = getHAdjustable(); 355 356 if (v != null) 357 y = v.getValue(); 358 if (h != null) 359 x = h.getValue(); 360 361 return(new Point(x, y)); 362 } 363 364 /*************************************************************************/ 365 366 /** 367 * Sets the scroll position to the specified value. 368 * 369 * @param scrollPosition The new scrollPosition. 370 * 371 * @exception IllegalArgumentException If the specified value is outside 372 * the legal scrolling range. 373 */ 374 public void 375 setScrollPosition(Point scrollPosition) throws IllegalArgumentException 376 { 377 setScrollPosition(scrollPosition.x, scrollPosition.y); 378 } 379 380 /*************************************************************************/ 381 382 /** 383 * Sets the scroll position to the specified value. 384 * 385 * @param x The new X coordinate of the scroll position. 386 * @param y The new Y coordinate of the scroll position. 387 * 388 * @throws NullPointerException if scrollpane does not have a child. 389 * 390 * @exception IllegalArgumentException If the specified value is outside 391 * the legal scrolling range. 392 */ 393 public void 394 setScrollPosition(int x, int y) 395 { 396 if (getComponentCount() == 0) 397 throw new NullPointerException("child is null"); 398 399 if (x > (int) (getComponent(0).getWidth() - getViewportSize().getWidth())) 400 x = (int) (getComponent(0).getWidth() - getViewportSize().getWidth()); 401 if (y > (int) (getComponent(0).getHeight() - getViewportSize().getHeight())) 402 y = (int) (getComponent(0).getHeight() - getViewportSize().getHeight()); 403 404 if (x < 0) 405 x = 0; 406 if (y < 0) 407 y = 0; 408 409 Adjustable h = getHAdjustable(); 410 Adjustable v = getVAdjustable(); 411 412 if (h != null) 413 h.setValue(x); 414 if (v != null) 415 v.setValue(y); 416 417 ScrollPanePeer spp = (ScrollPanePeer)getPeer(); 418 if (spp != null) 419 spp.setScrollPosition(x, y); 420 } 421 422 /*************************************************************************/ 423 424 /** 425 * Notifies this object that it should create its native peer. 426 */ 427 public void 428 addNotify() 429 { 430 if (peer != null) 431 return; 432 433 setPeer((ComponentPeer)getToolkit().createScrollPane(this)); 434 super.addNotify(); 435 436 Component[] list = getComponents(); 437 if (list != null && list.length > 0 && list[0].isLightweight()) 438 { 439 Panel panel = new Panel(); 440 panel.setLayout(new BorderLayout()); 441 panel.add(list[0], BorderLayout.CENTER); 442 add(panel); 443 } 444 } 445 446 /*************************************************************************/ 447 448 /** 449 * Notifies this object that it should destroy its native peers. 450 */ 451 public void 452 removeNotify() 453 { 454 super.removeNotify(); 455 } 456 457 /*************************************************************************/ 458 459 /** 460 * Adds the specified child component to this container. A 461 * <code>ScrollPane</code> can have at most one child, so if a second 462 * one is added, then first one is removed. 463 * 464 * @param component The component to add to this container. 465 * @param constraints A list of layout constraints for this object. 466 * @param index The index at which to add the child, which is ignored 467 * in this implementation. 468 */ 469 protected final void addImpl (Component component, Object constraints, 470 int index) 471 { 472 Component[] list = getComponents(); 473 if ((list != null) && (list.length > 0)) 474 remove(list[0]); 475 476 super.addImpl(component, constraints, index); 477 } 478 479 /*************************************************************************/ 480 481 /** 482 * Lays out this component. This consists of resizing the sole child 483 * component to its perferred size. 484 */ 485 public void 486 doLayout() 487 { 488 layout (); 489 } 490 491 /*************************************************************************/ 492 493 /** 494 * Lays out this component. This consists of resizing the sole child 495 * component to its perferred size. 496 * 497 * @deprecated This method is deprecated in favor of 498 * <code>doLayout()</code>. 499 */ 500 public void 501 layout() 502 { 503 Component[] list = getComponents (); 504 if ((list != null) && (list.length > 0)) 505 { 506 Dimension dim = list[0].getPreferredSize (); 507 Dimension vp = getViewportSize (); 508 509 if (dim.width < vp.width) 510 dim.width = vp.width; 511 512 if (dim.height < vp.height) 513 dim.height = vp.height; 514 515 ScrollPanePeer peer = (ScrollPanePeer) getPeer (); 516 if (peer != null) 517 peer.childResized (dim.width, dim.height); 518 519 list[0].setSize (dim); 520 521 Point p = getScrollPosition (); 522 if (p.x > dim.width) 523 p.x = dim.width; 524 if (p.y > dim.height) 525 p.y = dim.height; 526 527 setScrollPosition (p); 528 529 list[0].setLocation(new Point()); 530 } 531 } 532 533 /*************************************************************************/ 534 535 /** 536 * This method overrides its superclass method to ensure no layout 537 * manager is set for this container. <code>ScrollPane</code>'s do 538 * not have layout managers. 539 * 540 * @param layoutManager Ignored 541 * @throws AWTError Always throws this error when called. 542 */ 543 public final void 544 setLayout(LayoutManager layoutManager) 545 { 546 throw new AWTError("ScrollPane controls layout"); 547 } 548 549 /*************************************************************************/ 550 551 /** 552 * Prints all of the components in this container. 553 * 554 * @param graphics The desired graphics context for printing. 555 */ 556 public void 557 printComponents(Graphics graphics) 558 { 559 super.printComponents(graphics); 560 } 561 562 /*************************************************************************/ 563 564 /** 565 * Returns a debug string for this object. 566 * 567 * @return A debug string for this object. 568 */ 569 public String 570 paramString() 571 { 572 Insets insets = getInsets(); 573 return getName() + "," 574 + getX() + "," 575 + getY() + "," 576 + getWidth() + "x" + getHeight() + "," 577 + getIsValidString() + "," 578 + "ScrollPosition=(" + scrollPosition.x + "," 579 + scrollPosition.y + ")," 580 + "Insets=(" + insets.top + "," 581 + insets.left + "," 582 + insets.bottom + "," 583 + insets.right + ")," 584 + "ScrollbarDisplayPolicy=" + getScrollbarDisplayPolicyString() + "," 585 + "wheelScrollingEnabled=" + isWheelScrollingEnabled(); 586 } 587 588 private String 589 getScrollbarDisplayPolicyString() 590 { 591 if (getScrollbarDisplayPolicy() == 0) 592 return "as-needed"; 593 else if (getScrollbarDisplayPolicy() == 1) 594 return "always"; 595 else 596 return "never"; 597 } 598 599 private String 600 getIsValidString() 601 { 602 if (isValid()) 603 return "valid"; 604 else 605 return "invalid"; 606 } 607 608 /** 609 * Tells whether or not an event is enabled. 610 * 611 * @since 1.4 612 */ 613 protected boolean eventTypeEnabled (int type) 614 { 615 if (type == MouseEvent.MOUSE_WHEEL) 616 return wheelScrollingEnabled; 617 618 return super.eventTypeEnabled (type); 619 } 620 621 /** 622 * Tells whether or not wheel scrolling is enabled. 623 * 624 * @since 1.4 625 */ 626 public boolean isWheelScrollingEnabled () 627 { 628 return wheelScrollingEnabled; 629 } 630 631 /** 632 * Enables/disables wheel scrolling. 633 * 634 * @since 1.4 635 */ 636 public void setWheelScrollingEnabled (boolean enable) 637 { 638 wheelScrollingEnabled = enable; 639 } 640 641 protected class AccessibleAWTScrollPane extends AccessibleAWTContainer 642 { 643 private static final long serialVersionUID = 6100703663886637L; 644 645 public AccessibleRole getAccessibleRole() 646 { 647 return AccessibleRole.SCROLL_PANE; 648 } 649 } 650 651 /** 652 * Gets the AccessibleContext associated with this <code>ScrollPane</code>. 653 * The context is created, if necessary. 654 * 655 * @return the associated context 656 */ 657 public AccessibleContext getAccessibleContext() 658 { 659 /* Create the context if this is the first request */ 660 if (accessibleContext == null) 661 accessibleContext = new AccessibleAWTScrollPane(); 662 return accessibleContext; 663 } 664 665 /** 666 * Generate a unique name for this <code>ScrollPane</code>. 667 * 668 * @return A unique name for this <code>ScrollPane</code>. 669 */ 670 String generateName() 671 { 672 return "scrollpane" + getUniqueLong(); 673 } 674 675 private static synchronized long getUniqueLong() 676 { 677 return next_scrollpane_number++; 678 } 679 680 } // class ScrollPane