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