001/* ScrollPane.java -- Scrolling window 002 Copyright (C) 1999, 2002, 2004 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 java.awt; 040 041import java.awt.event.MouseEvent; 042import java.awt.peer.ComponentPeer; 043import java.awt.peer.ScrollPanePeer; 044 045import javax.accessibility.Accessible; 046import javax.accessibility.AccessibleContext; 047import 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 */ 056public 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 */ 067public static final int SCROLLBARS_AS_NEEDED = 0; 068 069/** 070 * Constant indicating that scrollbars are always displayed in this 071 * window. 072 */ 073public static final int SCROLLBARS_ALWAYS = 1; 074 075/** 076 * Constant indicating that scrollbars are never displayed in this window. 077 */ 078public static final int SCROLLBARS_NEVER = 2; 079 080/** 081 * The number used to generate the name returned by getName. 082 */ 083private static transient long next_scrollpane_number; 084 085// Serialization constant 086private 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 */ 099private 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 */ 106private 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 */ 112private int scrollbarDisplayPolicy; 113 114// Current scroll position 115private Point scrollPosition = new Point(0, 0); 116 117private 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 */ 131public 132ScrollPane() 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 */ 148public 149ScrollPane(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 */ 185public int 186getScrollbarDisplayPolicy() 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 */ 200public Adjustable 201getHAdjustable() 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 */ 215public Adjustable 216getVAdjustable() 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 */ 229public 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 */ 308public int 309getHScrollbarHeight() 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 */ 325public int 326getVScrollbarWidth() 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 */ 344public Point 345getScrollPosition() 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 */ 374public void 375setScrollPosition(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 */ 393public void 394setScrollPosition(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 */ 427public void 428addNotify() 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 */ 451public void 452removeNotify() 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 */ 485public void 486doLayout() 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 */ 500public void 501layout() 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 */ 543public final void 544setLayout(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 */ 556public void 557printComponents(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 */ 569public String 570paramString() 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 588private String 589getScrollbarDisplayPolicyString() 590{ 591 if (getScrollbarDisplayPolicy() == 0) 592 return "as-needed"; 593 else if (getScrollbarDisplayPolicy() == 1) 594 return "always"; 595 else 596 return "never"; 597} 598 599private String 600getIsValidString() 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