001/* BasicScrollPaneUI.java
002   Copyright (C) 2002, 2004, 2005, 2006, 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.plaf.basic;
040
041import java.awt.Dimension;
042import java.awt.Graphics;
043import java.awt.Point;
044import java.awt.Rectangle;
045import java.awt.event.ActionEvent;
046import java.awt.event.ContainerEvent;
047import java.awt.event.ContainerListener;
048import java.awt.event.MouseWheelEvent;
049import java.awt.event.MouseWheelListener;
050import java.beans.PropertyChangeEvent;
051import java.beans.PropertyChangeListener;
052
053import javax.swing.AbstractAction;
054import javax.swing.ActionMap;
055import javax.swing.InputMap;
056import javax.swing.JComponent;
057import javax.swing.JScrollBar;
058import javax.swing.JScrollPane;
059import javax.swing.JSlider;
060import javax.swing.JViewport;
061import javax.swing.LookAndFeel;
062import javax.swing.ScrollPaneConstants;
063import javax.swing.ScrollPaneLayout;
064import javax.swing.SwingUtilities;
065import javax.swing.UIManager;
066import javax.swing.border.Border;
067import javax.swing.event.ChangeEvent;
068import javax.swing.event.ChangeListener;
069import javax.swing.plaf.ActionMapUIResource;
070import javax.swing.plaf.ComponentUI;
071import javax.swing.plaf.ScrollPaneUI;
072import javax.swing.plaf.UIResource;
073
074/**
075 * A UI delegate for the {@link JScrollPane} component.
076 */
077public class BasicScrollPaneUI extends ScrollPaneUI
078  implements ScrollPaneConstants
079{
080
081  /**
082   * Listens for changes in the state of the horizontal scrollbar's model and
083   * updates the scrollpane accordingly.
084   *
085   * @author Roman Kennke (kennke@aicas.com)
086   */
087  public class HSBChangeListener implements ChangeListener
088  {
089
090    /**
091     * Receives notification when the state of the horizontal scrollbar
092     * model has changed.
093     *
094     * @param event the change event
095     */
096    public void stateChanged(ChangeEvent event)
097    {
098      JScrollBar hsb = scrollpane.getHorizontalScrollBar();
099      JViewport vp = scrollpane.getViewport();
100      Point viewPosition = vp.getViewPosition();
101      viewPosition.x = hsb.getValue();
102      vp.setViewPosition(viewPosition);
103    }
104
105  }
106
107  /**
108   * Listens for changes in the state of the vertical scrollbar's model and
109   * updates the scrollpane accordingly.
110   *
111   * @author Roman Kennke (kennke@aicas.com)
112   */
113  public class VSBChangeListener implements ChangeListener
114  {
115
116    /**
117     * Receives notification when the state of the vertical scrollbar
118     * model has changed.
119     *
120     * @param event the change event
121     */
122    public void stateChanged(ChangeEvent event)
123    {
124      JScrollBar vsb = scrollpane.getVerticalScrollBar();
125      JViewport vp = scrollpane.getViewport();
126      Point viewPosition = vp.getViewPosition();
127      viewPosition.y = vsb.getValue();
128      vp.setViewPosition(viewPosition);
129    }
130 
131  }
132
133  /**
134   * Listens for changes of the viewport's extent size and updates the
135   * scrollpane accordingly.
136   *
137   * @author Roman Kennke (kennke@aicas.com)
138   */
139  public class ViewportChangeHandler implements ChangeListener
140  {
141
142    /**
143     * Receives notification when the view's size, position or extent size
144     * changes. When the extents size has changed, this method calls
145     * {@link BasicScrollPaneUI#syncScrollPaneWithViewport()} to adjust the
146     * scrollbars extents as well.
147     * 
148     * @param event the change event
149     */
150    public void stateChanged(ChangeEvent event)
151    {
152      syncScrollPaneWithViewport();
153    }
154
155  }
156
157  /**
158   * Listens for property changes on the scrollpane and update the view
159   * accordingly.
160   *
161   * @author Roman Kennke (kennke@aicas.com)
162   */
163  public class PropertyChangeHandler implements PropertyChangeListener
164  {
165
166    /**
167     * Receives notification when any of the scrollpane's bound property
168     * changes. This method calls the appropriate update method on the
169     * <code>ScrollBarUI</code>.
170     *
171     * @param e the property change event
172     *
173     * @see BasicScrollPaneUI#updateColumnHeader(PropertyChangeEvent)
174     * @see BasicScrollPaneUI#updateRowHeader(PropertyChangeEvent)
175     * @see BasicScrollPaneUI#updateScrollBarDisplayPolicy(PropertyChangeEvent)
176     * @see BasicScrollPaneUI#updateViewport(PropertyChangeEvent)
177     */
178    public void propertyChange(PropertyChangeEvent e)
179    {
180      String propName = e.getPropertyName();
181      if (propName.equals("viewport"))
182        updateViewport(e);
183      else if (propName.equals("rowHeader"))
184        updateRowHeader(e);
185      else if (propName.equals("columnHeader"))
186        updateColumnHeader(e);
187      else if (propName.equals("horizontalScrollBarPolicy")
188          || e.getPropertyName().equals("verticalScrollBarPolicy"))
189        updateScrollBarDisplayPolicy(e);
190      else if (propName.equals("verticalScrollBar"))
191        {
192          JScrollBar oldSb = (JScrollBar) e.getOldValue();
193          oldSb.getModel().removeChangeListener(vsbChangeListener);
194          JScrollBar newSb = (JScrollBar) e.getNewValue();
195          newSb.getModel().addChangeListener(vsbChangeListener);
196        }
197      else if (propName.equals("horizontalScrollBar"))
198        {
199          JScrollBar oldSb = (JScrollBar) e.getOldValue();
200          oldSb.getModel().removeChangeListener(hsbChangeListener);
201          JScrollBar newSb = (JScrollBar) e.getNewValue();
202          newSb.getModel().addChangeListener(hsbChangeListener);
203        }
204    }
205
206  }
207
208  /**
209   * Listens for mouse wheel events and update the scrollpane accordingly.
210   *
211   * @author Roman Kennke (kennke@aicas.com)
212   *
213   * @since 1.4
214   */
215  protected class MouseWheelHandler implements MouseWheelListener
216  {
217    /**
218     * Use to compute the visible rectangle.
219     */
220    final Rectangle rect = new Rectangle();
221
222    /**
223     * Scroll with the mouse wheel.
224     * 
225     * @author Audrius Meskauskas (audriusa@Bioinformatics.org)
226     */
227    public void mouseWheelMoved(MouseWheelEvent e)
228    {
229      if (scrollpane.isWheelScrollingEnabled() && e.getScrollAmount() != 0)
230        {
231          // Try to scroll vertically first.
232          JScrollBar scrollBar = scrollpane.getVerticalScrollBar();
233          if (scrollBar == null || ! scrollBar.isVisible())
234            scrollBar = scrollpane.getHorizontalScrollBar();
235          if (scrollBar != null && scrollBar.isVisible())
236            {
237              int direction = e.getWheelRotation() < 0 ? -1 : 1;
238              int scrollType = e.getScrollType();
239              if (scrollType == MouseWheelEvent.WHEEL_UNIT_SCROLL)
240                BasicScrollBarUI.scrollByUnits(scrollBar, direction,
241                                               e.getScrollAmount());
242              else if (scrollType == MouseWheelEvent.WHEEL_BLOCK_SCROLL)
243                BasicScrollBarUI.scrollByBlock(scrollBar, direction);
244            }
245        }
246    }
247  }
248  
249  /**
250   * Adds/removes the mouse wheel listener when the component is added/removed
251   * to/from the scroll pane view port.
252   * 
253   * @author Audrius Meskauskas (audriusa@bioinformatics.org)
254   */
255  class ViewportContainerListener implements ContainerListener
256  {
257    /**
258     * Add the mouse wheel listener, allowing to scroll with the mouse.
259     */
260    public void componentAdded(ContainerEvent e)
261    {
262      e.getChild().addMouseWheelListener(mouseWheelListener);
263    }
264    
265    /**
266     * Remove the mouse wheel listener.
267     */
268    public void componentRemoved(ContainerEvent e)
269    {
270      e.getChild().removeMouseWheelListener(mouseWheelListener);
271    }
272  }
273  
274  /**
275   * The number of pixels by that we should scroll the content that does
276   * not implement Scrollable.
277   */
278  static int SCROLL_NON_SCROLLABLES = 10;
279  
280  /**
281   * The number of rows to scroll per mouse wheel click. From impression,
282   * Sun seems using the value 3.
283   */
284  static int ROWS_PER_WHEEL_CLICK = 3;     
285
286  /** The Scrollpane for which the UI is provided by this class. */
287  protected JScrollPane scrollpane;
288
289  /**
290   * The horizontal scrollbar listener.
291   */
292  protected ChangeListener hsbChangeListener;
293
294  /**
295   * The vertical scrollbar listener.
296   */
297  protected ChangeListener vsbChangeListener;
298
299  /**
300   * The viewport listener.
301   */
302  protected ChangeListener viewportChangeListener;
303
304  /**
305   * The scrollpane property change listener.
306   */
307  protected PropertyChangeListener spPropertyChangeListener;
308
309  /**
310   * The mousewheel listener for the scrollpane.
311   */
312  MouseWheelListener mouseWheelListener;
313  
314  /**
315   * The listener to add and remove the mouse wheel listener to/from
316   * the component container.
317   */
318  ContainerListener containerListener;
319
320  public static ComponentUI createUI(final JComponent c) 
321  {
322    return new BasicScrollPaneUI();
323  }
324
325  protected void installDefaults(JScrollPane p)
326  {
327    scrollpane = p;
328    LookAndFeel.installColorsAndFont(p, "ScrollPane.background",
329                                     "ScrollPane.foreground",
330                                     "ScrollPane.font");
331    LookAndFeel.installBorder(p, "ScrollPane.border");
332
333    // Install Viewport border.
334    Border vpBorder = p.getViewportBorder();
335    if (vpBorder == null || vpBorder instanceof UIResource)
336      {
337        vpBorder = UIManager.getBorder("ScrollPane.viewportBorder");
338        p.setViewportBorder(vpBorder);
339      }
340
341    p.setOpaque(true);
342  }
343
344  protected void uninstallDefaults(JScrollPane p)
345  {
346    LookAndFeel.uninstallBorder(p);
347    Border vpBorder = p.getViewportBorder();
348    if (vpBorder != null && vpBorder instanceof UIResource)
349      p.setViewportBorder(null);
350  }
351    
352  public void installUI(final JComponent c) 
353  {
354    super.installUI(c);
355    installDefaults((JScrollPane) c);
356    installListeners((JScrollPane) c);
357    installKeyboardActions((JScrollPane) c);
358  }
359
360  /**
361   * Installs the listeners on the scrollbars, the viewport and the scrollpane.
362   *
363   * @param sp the scrollpane on which to install the listeners
364   */
365  protected void installListeners(JScrollPane sp)
366  {
367    if (spPropertyChangeListener == null)
368      spPropertyChangeListener = createPropertyChangeListener();
369    sp.addPropertyChangeListener(spPropertyChangeListener);
370
371    if (hsbChangeListener == null)
372      hsbChangeListener = createHSBChangeListener();
373    sp.getHorizontalScrollBar().getModel().addChangeListener(hsbChangeListener);
374    
375    if (vsbChangeListener == null)
376      vsbChangeListener = createVSBChangeListener();
377    sp.getVerticalScrollBar().getModel().addChangeListener(vsbChangeListener);
378
379    if (viewportChangeListener == null)
380      viewportChangeListener = createViewportChangeListener();
381    
382    if (mouseWheelListener == null)
383      mouseWheelListener = createMouseWheelListener();
384    
385    if (containerListener == null)
386      containerListener = new ViewportContainerListener();
387    
388    JViewport v = sp.getViewport();
389    v.addChangeListener(viewportChangeListener);
390    v.addContainerListener(containerListener);
391    
392    // Add mouse wheel listeners to the componets that are probably already
393    // in the view port.
394    for (int i = 0; i < v.getComponentCount(); i++)
395      v.getComponent(i).addMouseWheelListener(mouseWheelListener);
396  }
397
398  InputMap getInputMap(int condition) 
399  {
400    if (condition == JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT)
401      return (InputMap) UIManager.get("ScrollPane.ancestorInputMap");
402    return null;
403  }
404
405  /**
406   * Returns the action map for the {@link JScrollPane}.  All scroll panes 
407   * share a single action map which is created the first time this method is 
408   * called, then stored in the UIDefaults table for subsequent access.
409   * 
410   * @return The shared action map.
411   */
412  ActionMap getActionMap() 
413  {
414    ActionMap map = (ActionMap) UIManager.get("ScrollPane.actionMap");
415
416    if (map == null) // first time here
417      {
418        map = createActionMap();
419        if (map != null)
420          UIManager.put("ScrollPane.actionMap", map);
421      }
422    return map;
423  }
424
425  /**
426   * Creates the action map shared by all {@link JSlider} instances.
427   * This method is called once by {@link #getActionMap()} when it 
428   * finds no action map in the UIDefaults table...after the map is 
429   * created, it gets added to the defaults table so that subsequent 
430   * calls to {@link #getActionMap()} will return the same shared 
431   * instance.
432   * 
433   * @return The action map.
434   */
435  ActionMap createActionMap()
436  {
437    ActionMap map = new ActionMapUIResource();
438    map.put("scrollLeft", 
439            new AbstractAction("scrollLeft") {
440              public void actionPerformed(ActionEvent event)
441              {
442                JScrollPane sp = (JScrollPane) event.getSource();
443                JScrollBar sb = sp.getHorizontalScrollBar();
444                if (sb.isVisible()) 
445                  {
446                    int delta = sb.getBlockIncrement(-1);
447                    sb.setValue(sb.getValue() + delta);
448                  }
449              }
450            }
451    );
452    map.put("scrollEnd", 
453            new AbstractAction("scrollEnd") {
454              public void actionPerformed(ActionEvent event)
455              {
456                JScrollPane sp = (JScrollPane) event.getSource();
457                JScrollBar sb1 = sp.getHorizontalScrollBar();
458                if (sb1.isVisible()) 
459                  {
460                    sb1.setValue(sb1.getMaximum());
461                  }
462                JScrollBar sb2 = sp.getVerticalScrollBar();
463                if (sb2.isVisible()) 
464                  {
465                    sb2.setValue(sb2.getMaximum());
466                  }
467              }
468            }
469    );
470    map.put("unitScrollUp", 
471            new AbstractAction("unitScrollUp") {
472              public void actionPerformed(ActionEvent event)
473              {
474                JScrollPane sp = (JScrollPane) event.getSource();
475                JScrollBar sb = sp.getVerticalScrollBar();
476                if (sb.isVisible()) 
477                  {
478                    int delta = sb.getUnitIncrement(-1);
479                    sb.setValue(sb.getValue() + delta);
480                  }
481              }
482            }
483    );
484    map.put("unitScrollLeft", 
485            new AbstractAction("unitScrollLeft") {
486              public void actionPerformed(ActionEvent event)
487              {
488                JScrollPane sp = (JScrollPane) event.getSource();
489                JScrollBar sb = sp.getHorizontalScrollBar();
490                if (sb.isVisible()) 
491                  {
492                    int delta = sb.getUnitIncrement(-1);
493                    sb.setValue(sb.getValue() + delta);
494                  }
495              }
496            }
497    );
498    map.put("scrollUp", 
499            new AbstractAction("scrollUp") {
500              public void actionPerformed(ActionEvent event)
501              {
502                JScrollPane sp = (JScrollPane) event.getSource();
503                JScrollBar sb = sp.getVerticalScrollBar();
504                if (sb.isVisible()) 
505                  {
506                    int delta = sb.getBlockIncrement(-1);
507                    sb.setValue(sb.getValue() + delta);
508                  }
509              }
510            }
511    );
512    map.put("scrollRight", 
513            new AbstractAction("scrollRight") {
514              public void actionPerformed(ActionEvent event)
515              {
516                JScrollPane sp = (JScrollPane) event.getSource();
517                JScrollBar sb = sp.getHorizontalScrollBar();
518                if (sb.isVisible()) 
519                  {
520                    int delta = sb.getBlockIncrement(1);
521                    sb.setValue(sb.getValue() + delta);
522                  }
523              }
524            }
525    );
526    map.put("scrollHome", 
527            new AbstractAction("scrollHome") {
528              public void actionPerformed(ActionEvent event)
529              {
530                JScrollPane sp = (JScrollPane) event.getSource();
531                JScrollBar sb1 = sp.getHorizontalScrollBar();
532                if (sb1.isVisible()) 
533                  {
534                    sb1.setValue(sb1.getMinimum());
535                  }
536                JScrollBar sb2 = sp.getVerticalScrollBar();
537                if (sb2.isVisible()) 
538                  {
539                    sb2.setValue(sb2.getMinimum());
540                  }
541              }
542            }
543    );
544    map.put("scrollDown", 
545            new AbstractAction("scrollDown") {
546              public void actionPerformed(ActionEvent event)
547              {
548                JScrollPane sp = (JScrollPane) event.getSource();
549                JScrollBar sb = sp.getVerticalScrollBar();
550                if (sb.isVisible()) 
551                  {
552                    int delta = sb.getBlockIncrement(1);
553                    sb.setValue(sb.getValue() + delta);
554                  }
555              }
556            }
557    );
558    map.put("unitScrollDown", 
559            new AbstractAction("unitScrollDown") {
560              public void actionPerformed(ActionEvent event)
561              {
562                JScrollPane sp = (JScrollPane) event.getSource();
563                JScrollBar sb = sp.getVerticalScrollBar();
564                if (sb.isVisible()) 
565                  {
566                    int delta = sb.getUnitIncrement(1);
567                    sb.setValue(sb.getValue() + delta);
568                  }
569              }
570            }
571    );
572    map.put("unitScrollRight", 
573            new AbstractAction("unitScrollRight") {
574              public void actionPerformed(ActionEvent event)
575              {
576                JScrollPane sp = (JScrollPane) event.getSource();
577                JScrollBar sb = sp.getHorizontalScrollBar();
578                if (sb.isVisible()) 
579                  {
580                    int delta = sb.getUnitIncrement(1);
581                    sb.setValue(sb.getValue() + delta);
582                  }
583              }
584            }
585    );
586    return map;
587  }
588  
589  /**
590   * Installs additional keyboard actions on the scrollpane. This is a hook
591   * method provided to subclasses in order to install their own keyboard
592   * actions.
593   *
594   * @param sp the scrollpane to install keyboard actions on
595   */
596  protected void installKeyboardActions(JScrollPane sp)
597  {
598    InputMap keyMap = getInputMap(
599        JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
600    SwingUtilities.replaceUIInputMap(sp, 
601        JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT, keyMap);
602    ActionMap map = getActionMap();
603    SwingUtilities.replaceUIActionMap(sp, map);
604  }
605
606  /**
607   * Uninstalls all keyboard actions from the JScrollPane that have been
608   * installed by {@link #installKeyboardActions}. This is a hook method
609   * provided to subclasses to add their own keyboard actions.
610   *
611   * @param sp the scrollpane to uninstall keyboard actions from
612   */
613  protected void uninstallKeyboardActions(JScrollPane sp)
614  {
615    SwingUtilities.replaceUIActionMap(sp, null);
616    SwingUtilities.replaceUIInputMap(sp, 
617        JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT, null);
618  }
619  
620  /**
621   * Creates and returns the change listener for the horizontal scrollbar.
622   *
623   * @return the change listener for the horizontal scrollbar
624   */
625  protected ChangeListener createHSBChangeListener()
626  {
627    return new HSBChangeListener();
628  }
629
630  /**
631   * Creates and returns the change listener for the vertical scrollbar.
632   *
633   * @return the change listener for the vertical scrollbar
634   */
635  protected ChangeListener createVSBChangeListener()
636  {
637    return new VSBChangeListener();
638  }
639
640  /**
641   * Creates and returns the change listener for the viewport.
642   *
643   * @return the change listener for the viewport
644   */
645  protected ChangeListener createViewportChangeListener()
646  {
647    return new ViewportChangeHandler();
648  }
649
650  /**
651   * Creates and returns the property change listener for the scrollpane.
652   *
653   * @return the property change listener for the scrollpane
654   */
655  protected PropertyChangeListener createPropertyChangeListener()
656  {
657    return new PropertyChangeHandler();
658  }
659
660  /**
661   * Creates and returns the mouse wheel listener for the scrollpane.
662   *
663   * @return the mouse wheel listener for the scrollpane
664   * 
665   * @since 1.4
666   */
667  protected MouseWheelListener createMouseWheelListener()
668  {
669    return new MouseWheelHandler();
670  }
671
672  public void uninstallUI(final JComponent c) 
673  {
674    uninstallDefaults((JScrollPane) c);
675    uninstallListeners(c);
676    installKeyboardActions((JScrollPane) c);
677  }
678
679  /**
680   * Uninstalls all the listeners that have been installed in
681   * {@link #installListeners(JScrollPane)}.
682   *
683   * @param c the scrollpane from which to uninstall the listeners 
684   */
685  protected void uninstallListeners(JComponent c)
686  {
687    JScrollPane sp = (JScrollPane) c;
688    sp.removePropertyChangeListener(spPropertyChangeListener);
689    sp.getHorizontalScrollBar().getModel()
690                               .removeChangeListener(hsbChangeListener);
691    sp.getVerticalScrollBar().getModel()
692                             .removeChangeListener(vsbChangeListener);
693    
694    JViewport v = sp.getViewport();
695    v.removeChangeListener(viewportChangeListener);
696    v.removeContainerListener(containerListener);
697 
698    for (int i = 0; i < v.getComponentCount(); i++)
699      v.getComponent(i).removeMouseWheelListener(mouseWheelListener);
700
701  }
702
703  public Dimension getMinimumSize(JComponent c) 
704  {
705    JScrollPane p = (JScrollPane) c;
706    ScrollPaneLayout sl = (ScrollPaneLayout) p.getLayout();
707    return sl.minimumLayoutSize(c);
708  }
709
710  public void paint(Graphics g, JComponent c)
711  {
712    Border vpBorder = scrollpane.getViewportBorder();
713    if (vpBorder != null)
714      {
715        Rectangle r = scrollpane.getViewportBorderBounds();
716        vpBorder.paintBorder(scrollpane, g, r.x, r.y, r.width, r.height);
717      }
718  }
719
720  /**
721   * Synchronizes the scrollbar and header settings positions and extent
722   * with the viewport's view position and extent.
723   */
724  protected void syncScrollPaneWithViewport()
725  {
726    JViewport vp = scrollpane.getViewport();
727
728    if (vp != null)
729      {
730        Dimension extentSize = vp.getExtentSize();
731        Point viewPos = vp.getViewPosition();
732        Dimension viewSize = vp.getViewSize();
733
734        // Update the vertical scrollbar.
735        JScrollBar vsb = scrollpane.getVerticalScrollBar();
736        if (vsb != null)
737          {
738            int extent = extentSize.height;
739            int max = viewSize.height;
740            int val = Math.max(0, Math.min(viewPos.y, max - extent));
741            vsb.setValues(val, extent, 0, max);
742          }
743
744        // Update the horizontal scrollbar.
745        JScrollBar hsb = scrollpane.getHorizontalScrollBar();
746        if (hsb != null)
747          {
748            int extent = extentSize.width;
749            int max = viewSize.width;
750            int val = Math.max(0, Math.min(viewPos.x, max - extent));
751            hsb.setValues(val, extent, 0, max);
752          }
753
754        // Update the row header.
755        JViewport rowHeader = scrollpane.getRowHeader();
756        if (rowHeader != null)
757          {
758            Point p = new Point(0, viewPos.y);
759            rowHeader.setViewPosition(p);
760          }
761
762        // Update the column header.
763        JViewport colHeader = scrollpane.getColumnHeader();
764        if (colHeader != null)
765          {
766            Point p = new Point(viewPos.x, 0);
767            colHeader.setViewPosition(p);
768          }
769      }
770  }
771
772  /**
773   * Receives notification when the <code>columnHeader</code> property has
774   * changed on the scrollpane.
775   *
776   * @param ev the property change event
777   */
778  protected void updateColumnHeader(PropertyChangeEvent ev)
779  {
780    // TODO: Find out what should be done here. Or is this only a hook?
781  }
782
783  /**
784   * Receives notification when the <code>rowHeader</code> property has changed
785   * on the scrollpane.
786   *
787   * @param ev the property change event
788   */
789  protected void updateRowHeader(PropertyChangeEvent ev)
790  {
791    // TODO: Find out what should be done here. Or is this only a hook?
792  }
793
794  /**
795   * Receives notification when the <code>scrollBarDisplayPolicy</code>
796   * property has changed on the scrollpane.
797   *
798   * @param ev the property change event
799   */
800  protected void updateScrollBarDisplayPolicy(PropertyChangeEvent ev)
801  {
802    scrollpane.revalidate();
803    scrollpane.repaint();
804  }
805
806  /**
807   * Receives notification when the <code>viewport</code> property has changed
808   * on the scrollpane.
809   *
810   * This method sets removes the viewportChangeListener from the old viewport
811   * and adds it to the new viewport.
812   *
813   * @param ev the property change event
814   */
815  protected void updateViewport(PropertyChangeEvent ev)
816  {
817    JViewport oldViewport = (JViewport) ev.getOldValue();
818    oldViewport.removeChangeListener(viewportChangeListener);
819    JViewport newViewport = (JViewport) ev.getNewValue();
820    newViewport.addChangeListener(viewportChangeListener);
821    syncScrollPaneWithViewport();
822  }
823}
824
825
826
827
828
829
830
831
832
833
834
835