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