001/* JScrollPane.java --
002   Copyright (C) 2002, 2004, 2005  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 javax.swing;
040
041import java.awt.Component;
042import java.awt.ComponentOrientation;
043import java.awt.Insets;
044import java.awt.LayoutManager;
045import java.awt.Rectangle;
046import java.beans.PropertyChangeEvent;
047import java.beans.PropertyChangeListener;
048
049import javax.accessibility.Accessible;
050import javax.accessibility.AccessibleContext;
051import javax.swing.border.Border;
052import javax.swing.event.ChangeEvent;
053import javax.swing.event.ChangeListener;
054import javax.swing.plaf.ScrollPaneUI;
055import 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 */
081public 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}