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