001    /* JScrollPane.java --
002       Copyright (C) 2002, 2004, 2005  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 javax.swing;
040    
041    import java.awt.Component;
042    import java.awt.ComponentOrientation;
043    import java.awt.Insets;
044    import java.awt.LayoutManager;
045    import java.awt.Rectangle;
046    import java.beans.PropertyChangeEvent;
047    import java.beans.PropertyChangeListener;
048    
049    import javax.accessibility.Accessible;
050    import javax.accessibility.AccessibleContext;
051    import javax.swing.border.Border;
052    import javax.swing.event.ChangeEvent;
053    import javax.swing.event.ChangeListener;
054    import javax.swing.plaf.ScrollPaneUI;
055    import javax.swing.plaf.UIResource;
056    
057    /**
058     * A component that embeds another component and enables it to be scrolled
059     * both in horizontal and vertical direction.
060     *
061     * <table>
062     * <tr><th>Property                    </th><th>Stored in       </th><th>Bound?</th></tr>
063     * <tr><td>columnHeader                </td><td>scrollPane      </td><td>yes   </td></tr>
064     * <tr><td>columnHeaderView            </td><td>columnHeader    </td><td>no    </td></tr>
065     * <tr><td>componentOrientation        </td><td>scrollPane      </td><td>yes   </td></tr>
066     * <tr><td>horizontalScrollBar         </td><td>scrollPane      </td><td>yes   </td></tr>
067     * <tr><td>horizontalScrollBarPolicy   </td><td>scrollPane      </td><td>yes   </td></tr>
068     * <tr><td>layout                      </td><td>scrollPane      </td><td>yes   </td></tr>
069     * <tr><td>rowHeader                   </td><td>scrollPane      </td><td>yes   </td></tr>
070     * <tr><td>rowHeaderView               </td><td>rowHeader       </td><td>no    </td></tr>
071     * <tr><td>validateRoot                </td><td>scrollPane      </td><td>no    </td></tr>
072     * <tr><td>verticalScrollBar           </td><td>scrollPane      </td><td>yes   </td></tr>
073     * <tr><td>verticalScrollBarPolicy     </td><td>scrollPane      </td><td>yes   </td></tr>
074     * <tr><td>viewport                    </td><td>scrollPane      </td><td>yes   </td></tr>
075     * <tr><td>viewportBorder              </td><td>scrollPane      </td><td>yes   </td></tr>
076     * <tr><td>viewportBorderBounds        </td><td>scrollPane      </td><td>no    </td></tr>
077     * <tr><td>viewportView                </td><td>viewport        </td><td>no    </td></tr>
078     * <tr><td>wheelScrollingEnabled       </td><td>scrollPane      </td><td>yes   </td></tr>
079     * </table>
080     */
081    public class JScrollPane extends JComponent
082      implements Accessible, ScrollPaneConstants
083    {
084      /**
085       * Provides accessibility support for the <code>JScrollPane</code>.
086       *
087       * @author Roman Kennke (kennke@aicas.com)
088       */
089      protected class AccessibleJScrollPane extends AccessibleJComponent
090        implements ChangeListener, PropertyChangeListener
091      {
092    
093        /**
094         * The viewport of the underlying scrollpane.
095         */
096        protected JViewport viewPort;
097    
098        /**
099         * Creates a new <code>AccessibleJScrollPane</code> object. This
100         * initializes the <code>viewport</code> field with the current viewport
101         * from the scrollpane associated with this
102         * <code>AccessibleJScrollPane</code>.
103         */
104        public AccessibleJScrollPane()
105        {
106          viewPort = getViewport();
107          viewPort.addChangeListener(this);
108          viewPort.addPropertyChangeListener(this);
109        }
110    
111        /**
112         * Receives notification when the state of the viewport changes.
113         *
114         * @param event the change event
115         */
116        public void stateChanged(ChangeEvent event)
117        {
118          // TODO: Figure out what should be done here, if anything.
119        }
120    
121        /**
122         * Receives notification if any of the viewport's bound properties changes.
123         *
124         * @param e the propery change event
125         */
126        public void propertyChange(PropertyChangeEvent e)
127        {
128          // TODO: Figure out what should be done here, if anything.
129        }
130    
131        /**
132         * Resets the <code>viewPort</code> field when the scrollpane's viewport
133         * changes. This method is called by
134         * {@link JScrollPane#setViewport(JViewport)} in order to update the
135         * <code>viewPort</code> field and set up the listeners on this viewport
136         * correctly.
137         */
138        public void resetViewPort()
139        {
140          viewPort.removeChangeListener(this);
141          viewPort.removePropertyChangeListener(this);
142          viewPort = getViewport();
143          viewPort.addChangeListener(this);
144          viewPort.addPropertyChangeListener(this);
145        }
146      }
147    
148      private static final long serialVersionUID = 5203525440012340014L;
149    
150      protected JViewport columnHeader;
151      protected JViewport rowHeader;
152    
153      protected Component lowerLeft;
154      protected Component lowerRight;
155      protected Component upperLeft;
156      protected Component upperRight;
157    
158      protected JScrollBar horizontalScrollBar;
159      protected int horizontalScrollBarPolicy;
160      protected JScrollBar verticalScrollBar;
161      protected int verticalScrollBarPolicy;
162    
163      protected JViewport viewport;
164    
165      private Border viewportBorder;
166    
167      private boolean wheelScrollingEnabled;
168    
169      public JViewport getColumnHeader()
170      {
171        return columnHeader;
172      }
173    
174      public Component getCorner(String key)
175      {
176        if (getComponentOrientation()
177            == ComponentOrientation.LEFT_TO_RIGHT)
178          {
179            if (key == LOWER_LEADING_CORNER)
180              key = LOWER_LEFT_CORNER;
181            else if (key == LOWER_TRAILING_CORNER)
182              key = LOWER_RIGHT_CORNER;
183            else if (key == UPPER_LEADING_CORNER)
184              key = UPPER_LEFT_CORNER;
185            else if (key == UPPER_TRAILING_CORNER)
186              key = UPPER_RIGHT_CORNER;
187          }
188        else if (getComponentOrientation()
189                 == ComponentOrientation.RIGHT_TO_LEFT)
190          {
191            if (key == LOWER_LEADING_CORNER)
192              key = LOWER_RIGHT_CORNER;
193            else if (key == LOWER_TRAILING_CORNER)
194              key = LOWER_LEFT_CORNER;
195            else if (key == UPPER_LEADING_CORNER)
196              key = UPPER_RIGHT_CORNER;
197            else if (key == UPPER_TRAILING_CORNER)
198              key = UPPER_LEFT_CORNER;
199          }
200    
201        if (key == LOWER_RIGHT_CORNER)
202          return lowerRight;
203        else if (key == UPPER_RIGHT_CORNER)
204          return upperRight;
205        else if (key == LOWER_LEFT_CORNER)
206          return lowerLeft;
207        else if (key == UPPER_LEFT_CORNER)
208          return upperLeft;
209        return null;
210      }
211    
212      public JScrollBar getHorizontalScrollBar()
213      {
214        return horizontalScrollBar;
215      }
216    
217      public int getHorizontalScrollBarPolicy()
218      {
219        return horizontalScrollBarPolicy;
220      }
221    
222      public JViewport getRowHeader()
223      {
224        return rowHeader;
225      }
226    
227      public JScrollBar getVerticalScrollBar()
228      {
229        return verticalScrollBar;
230      }
231    
232      public int getVerticalScrollBarPolicy()
233      {
234        return verticalScrollBarPolicy;
235      }
236    
237      public JViewport getViewport()
238      {
239        return viewport;
240      }
241    
242      public Border getViewportBorder()
243      {
244        return viewportBorder;
245      }
246    
247      public Rectangle getViewportBorderBounds()
248      {
249        if (viewportBorder == null)
250          {
251            if (getViewport() == null)
252              return new Rectangle(0, 0, 0, 0);
253            else
254              return getViewport().getBounds();
255          }
256        else
257          {
258            Insets i = viewportBorder.getBorderInsets(getViewport());
259            if (getViewport() == null)
260              return new Rectangle(0, 0, i.left + i.right, i.top + i.bottom);
261            else
262              {
263                Rectangle b = getViewport().getBounds();
264                return new Rectangle(b.x - i.left,
265                                     b.y - i.top,
266                                     b.width + i.left + i.right,
267                                     b.height + i.top + i.bottom);
268              }
269          }
270      }
271    
272      public boolean isWheelScrollingEnabled()
273      {
274        return wheelScrollingEnabled;
275      }
276    
277    
278    
279      private void sync()
280      {
281        LayoutManager m = super.getLayout();
282        if (m != null && m instanceof ScrollPaneLayout)
283          {
284            ScrollPaneLayout sl = (ScrollPaneLayout) m;
285            sl.syncWithScrollPane(this);
286          }
287      }
288    
289      private void removeNonNull(Component c)
290      {
291        if (c != null)
292          remove(c);
293      }
294    
295      private void addNonNull(Component c, Object constraints)
296      {
297        if (c != null)
298          add(c, constraints);
299      }
300    
301      public void setComponentOrientation(ComponentOrientation co)
302      {
303        ComponentOrientation old = super.getComponentOrientation();
304        super.setComponentOrientation(co);
305        firePropertyChange("componentOrientation", old, co);
306        sync();
307      }
308    
309      public void setColumnHeader(JViewport h)
310      {
311        if (columnHeader == h)
312          return;
313    
314        JViewport old = columnHeader;
315        removeNonNull(old);
316        columnHeader = h;
317        addNonNull(h, JScrollPane.COLUMN_HEADER);
318        firePropertyChange("columnHeader", old, h);
319        sync();
320      }
321    
322      public void setColumnHeaderView(Component c)
323      {
324        if (columnHeader == null)
325          setColumnHeader(createViewport());
326        columnHeader.setView(c);
327        sync();
328      }
329    
330      public void setCorner(String key, Component c)
331      {
332        if (getComponentOrientation()
333            == ComponentOrientation.LEFT_TO_RIGHT)
334          {
335            if (key == LOWER_LEADING_CORNER)
336              key = LOWER_LEFT_CORNER;
337            else if (key == LOWER_TRAILING_CORNER)
338              key = LOWER_RIGHT_CORNER;
339            else if (key == UPPER_LEADING_CORNER)
340              key = UPPER_LEFT_CORNER;
341            else if (key == UPPER_TRAILING_CORNER)
342              key = UPPER_RIGHT_CORNER;
343          }
344        else if (getComponentOrientation()
345                 == ComponentOrientation.RIGHT_TO_LEFT)
346          {
347            if (key == LOWER_LEADING_CORNER)
348              key = LOWER_RIGHT_CORNER;
349            else if (key == LOWER_TRAILING_CORNER)
350              key = LOWER_LEFT_CORNER;
351            else if (key == UPPER_LEADING_CORNER)
352              key = UPPER_RIGHT_CORNER;
353            else if (key == UPPER_TRAILING_CORNER)
354              key = UPPER_LEFT_CORNER;
355          }
356    
357        if (key == LOWER_RIGHT_CORNER)
358          {
359            removeNonNull(lowerRight);
360            lowerRight = c;
361            addNonNull(c, JScrollPane.LOWER_RIGHT_CORNER);
362          }
363        else if (key == UPPER_RIGHT_CORNER)
364          {
365            removeNonNull(upperRight);
366            upperRight = c;
367            addNonNull(c, JScrollPane.UPPER_RIGHT_CORNER);
368          }
369        else if (key == LOWER_LEFT_CORNER)
370          {
371            removeNonNull(lowerLeft);
372            lowerLeft = c;
373            addNonNull(c, JScrollPane.LOWER_LEFT_CORNER);
374          }
375        else if (key == UPPER_LEFT_CORNER)
376          {
377            removeNonNull(upperLeft);
378            upperLeft = c;
379            addNonNull(c, JScrollPane.UPPER_LEFT_CORNER);
380          }
381        else
382          throw new IllegalArgumentException("unknown corner " + key);
383        sync();
384      }
385    
386      public void setHorizontalScrollBar(JScrollBar h)
387      {
388        if (horizontalScrollBar == h)
389          return;
390    
391        JScrollBar old = horizontalScrollBar;
392        removeNonNull(old);
393        horizontalScrollBar = h;
394        addNonNull(h, JScrollPane.HORIZONTAL_SCROLLBAR);
395        firePropertyChange("horizontalScrollBar", old, h);
396        sync();
397    
398      }
399    
400      public void setHorizontalScrollBarPolicy(int h)
401      {
402        if (horizontalScrollBarPolicy == h)
403          return;
404    
405        if (h != HORIZONTAL_SCROLLBAR_AS_NEEDED
406            && h != HORIZONTAL_SCROLLBAR_NEVER
407            && h != HORIZONTAL_SCROLLBAR_ALWAYS)
408          throw new IllegalArgumentException("unknown horizontal scrollbar policy");
409    
410        int old = horizontalScrollBarPolicy;
411        horizontalScrollBarPolicy = h;
412        firePropertyChange("horizontalScrollBarPolicy", old, h);
413        sync();
414        revalidate();
415      }
416    
417      public void setLayout(LayoutManager l)
418      {
419        LayoutManager old = super.getLayout();
420        ScrollPaneLayout tmp = (ScrollPaneLayout) l;
421        super.setLayout(l);
422        tmp.syncWithScrollPane(this);
423        firePropertyChange("layout", old, l);
424        sync();
425      }
426    
427      public void setRowHeader(JViewport v)
428      {
429        if (rowHeader == v)
430          return;
431    
432        JViewport old = rowHeader;
433        removeNonNull(old);
434        rowHeader = v;
435        addNonNull(v, JScrollPane.ROW_HEADER);
436        firePropertyChange("rowHeader", old, v);
437        sync();
438      }
439    
440      public void setRowHeaderView(Component c)
441      {
442        if (rowHeader == null)
443          setRowHeader(createViewport());
444        rowHeader.setView(c);
445        sync();
446      }
447    
448      public void setVerticalScrollBar(JScrollBar v)
449      {
450        if (verticalScrollBar == v)
451          return;
452    
453        JScrollBar old = verticalScrollBar;
454        removeNonNull(old);
455        verticalScrollBar = v;
456        addNonNull(v, JScrollPane.VERTICAL_SCROLLBAR);
457        firePropertyChange("verticalScrollBar", old, v);
458        sync();
459      }
460    
461      public void setVerticalScrollBarPolicy(int v)
462      {
463        if (verticalScrollBarPolicy == v)
464          return;
465    
466        if (v != VERTICAL_SCROLLBAR_AS_NEEDED
467            && v != VERTICAL_SCROLLBAR_NEVER
468            && v != VERTICAL_SCROLLBAR_ALWAYS)
469          throw new IllegalArgumentException("unknown vertical scrollbar policy");
470    
471        int old = verticalScrollBarPolicy;
472        verticalScrollBarPolicy = v;
473        firePropertyChange("verticalScrollBarPolicy", old, v);
474        sync();
475        revalidate();
476      }
477    
478      public void setWheelScrollingEnabled(boolean b)
479      {
480        if (wheelScrollingEnabled == b)
481          return;
482    
483        boolean old = wheelScrollingEnabled;
484        wheelScrollingEnabled = b;
485        firePropertyChange("wheelScrollingEnabled", old, b);
486        sync();
487      }
488    
489      public void setViewport(JViewport v)
490      {
491        if (viewport == v)
492          return;
493    
494        JViewport old = viewport;
495        removeNonNull(old);
496        viewport = v;
497        addNonNull(v, JScrollPane.VIEWPORT);
498        revalidate();
499        repaint();
500        firePropertyChange("viewport", old, v);
501        sync();
502        if (accessibleContext != null)
503          {
504            AccessibleJScrollPane asp = (AccessibleJScrollPane) accessibleContext;
505            asp.resetViewPort();
506          }
507      }
508    
509      public void setViewportBorder(Border b)
510      {
511        if (viewportBorder == b)
512          return;
513    
514        Border old = viewportBorder;
515        viewportBorder = b;
516        firePropertyChange("viewportBorder", old, b);
517        sync();
518      }
519    
520      public void setViewportView(Component view)
521      {
522        if (getViewport() == null)
523          {
524            setViewport(createViewport());
525          }
526    
527        if (view != null)
528          {
529            getViewport().setView(view);
530          }
531        sync();
532      }
533    
534      public boolean isValidateRoot()
535      {
536        return true;
537      }
538    
539      /**
540       * Creates a new <code>JScrollPane</code> without a view. The scrollbar
541       * policy is set to {@link #VERTICAL_SCROLLBAR_AS_NEEDED} and
542       * {@link #HORIZONTAL_SCROLLBAR_AS_NEEDED}.
543       */
544      public JScrollPane()
545      {
546        this(null);
547      }
548    
549      /**
550       * Creates a new <code>JScrollPane</code> that embeds the specified
551       * <code>view</code> component, displaying vertical and horizontal scrollbars
552       * as needed.
553       *
554       * @param view the component that is embedded inside the JScrollPane
555       */
556      public JScrollPane(Component view)
557      {
558        this(view,
559             VERTICAL_SCROLLBAR_AS_NEEDED,
560             HORIZONTAL_SCROLLBAR_AS_NEEDED);
561      }
562    
563      /**
564       * Creates a new <code>JScrollPane</code> without a view; The scrollbar
565       * policies are set to <code>vsbPolicy</code> and <code>hsbPolicy</code>.
566       *
567       * @param vsbPolicy the vertical scrollbar policy to set
568       * @param hsbPolicy the vertical scrollbar policy to set
569       *
570       * @see ScrollPaneConstants#HORIZONTAL_SCROLLBAR_ALWAYS
571       * @see ScrollPaneConstants#HORIZONTAL_SCROLLBAR_AS_NEEDED
572       * @see ScrollPaneConstants#HORIZONTAL_SCROLLBAR_NEVER
573       * @see ScrollPaneConstants#VERTICAL_SCROLLBAR_ALWAYS
574       * @see ScrollPaneConstants#VERTICAL_SCROLLBAR_AS_NEEDED
575       * @see ScrollPaneConstants#VERTICAL_SCROLLBAR_NEVER
576       */
577      public JScrollPane(int vsbPolicy, int hsbPolicy)
578      {
579        this(null, vsbPolicy, hsbPolicy);
580      }
581    
582      /**
583       * Creates a new <code>JScrollPane</code> that embeds the specified
584       * <code>view</code> component; The scrollbar
585       * policies are set to <code>vsbPolicy</code> and <code>hsbPolicy</code>.
586       *
587       * @param vsbPolicy the vertical scrollbar policy to set
588       * @param hsbPolicy the vertical scrollbar policy to set
589       *
590       * @see ScrollPaneConstants#HORIZONTAL_SCROLLBAR_ALWAYS
591       * @see ScrollPaneConstants#HORIZONTAL_SCROLLBAR_AS_NEEDED
592       * @see ScrollPaneConstants#HORIZONTAL_SCROLLBAR_NEVER
593       * @see ScrollPaneConstants#VERTICAL_SCROLLBAR_ALWAYS
594       * @see ScrollPaneConstants#VERTICAL_SCROLLBAR_AS_NEEDED
595       * @see ScrollPaneConstants#VERTICAL_SCROLLBAR_NEVER
596       */
597      public JScrollPane(Component view, int vsbPolicy, int hsbPolicy)
598      {
599        wheelScrollingEnabled = true;
600        setVerticalScrollBarPolicy(vsbPolicy);
601        setVerticalScrollBar(createVerticalScrollBar());
602        setHorizontalScrollBarPolicy(hsbPolicy);
603        setHorizontalScrollBar(createHorizontalScrollBar());
604        viewport = createViewport();
605        if (view != null)
606          getViewport().setView(view);
607        add(viewport,0);
608        setLayout(new ScrollPaneLayout());
609        setOpaque(false);
610        updateUI();
611      }
612    
613    
614      public JScrollBar createHorizontalScrollBar()
615      {
616        return new ScrollBar(SwingConstants.HORIZONTAL);
617      }
618    
619      public JScrollBar createVerticalScrollBar()
620      {
621        return new ScrollBar(SwingConstants.VERTICAL);
622      }
623    
624      protected JViewport createViewport()
625      {
626        return new JViewport();
627      }
628    
629      public String getUIClassID()
630      {
631        return "ScrollPaneUI";
632      }
633    
634      public void updateUI()
635      {
636        setUI((ScrollPaneUI) UIManager.getUI(this));
637      }
638    
639      /**
640       * This method returns the scrollpane's UI delegate.
641       *
642       * @return The scrollpane's UI delegate.
643       */
644      public ScrollPaneUI getUI()
645      {
646        return (ScrollPaneUI) ui;
647      }
648    
649      /**
650       * This method sets the scrollpane's UI delegate.
651       *
652       * @param ui The scrollpane's UI delegate.
653       */
654      public void setUI(ScrollPaneUI ui)
655      {
656        super.setUI(ui);
657      }
658    
659      protected class ScrollBar
660        extends JScrollBar
661        implements UIResource
662      {
663        private static final long serialVersionUID = -42032395320987283L;
664    
665        public ScrollBar(int orientation)
666        {
667          super(orientation);
668        }
669    
670        public int getBlockIncrement(int direction)
671        {
672          Component view = JScrollPane.this.getViewport().getView();
673          if (view == null || (! (view instanceof Scrollable)))
674            return super.getBlockIncrement(direction);
675          else
676            {
677              Scrollable s = (Scrollable) view;
678              return s.getScrollableBlockIncrement(JScrollPane.this.getViewport().getViewRect(),
679                                                   this.getOrientation(),
680                                                   direction);
681            }
682        }
683    
684        public int getUnitIncrement(int direction)
685        {
686          Component view = JScrollPane.this.getViewport().getView();
687          if (view == null || (! (view instanceof Scrollable)))
688            return super.getUnitIncrement(direction);
689          else
690            {
691              Scrollable s = (Scrollable) view;
692              return s.getScrollableUnitIncrement(JScrollPane.this.getViewport().getViewRect(),
693                                                  this.getOrientation(),
694                                                  direction);
695            }
696        }
697      }
698    
699      /**
700       * Returns the accessible context associated with this
701       * <code>JScrollPane</code>.
702       *
703       * @return the accessible context associated with this
704       *         <code>JScrollPane</code>
705       */
706      public AccessibleContext getAccessibleContext()
707      {
708        if (accessibleContext == null)
709          accessibleContext = new AccessibleJScrollPane();
710        return accessibleContext;
711      }
712    }