001    /* BasicScrollPaneUI.java
002       Copyright (C) 2002, 2004, 2005, 2006, 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.plaf.basic;
040    
041    import java.awt.Dimension;
042    import java.awt.Graphics;
043    import java.awt.Point;
044    import java.awt.Rectangle;
045    import java.awt.event.ActionEvent;
046    import java.awt.event.ContainerEvent;
047    import java.awt.event.ContainerListener;
048    import java.awt.event.MouseWheelEvent;
049    import java.awt.event.MouseWheelListener;
050    import java.beans.PropertyChangeEvent;
051    import java.beans.PropertyChangeListener;
052    
053    import javax.swing.AbstractAction;
054    import javax.swing.ActionMap;
055    import javax.swing.InputMap;
056    import javax.swing.JComponent;
057    import javax.swing.JScrollBar;
058    import javax.swing.JScrollPane;
059    import javax.swing.JSlider;
060    import javax.swing.JViewport;
061    import javax.swing.LookAndFeel;
062    import javax.swing.ScrollPaneConstants;
063    import javax.swing.ScrollPaneLayout;
064    import javax.swing.SwingUtilities;
065    import javax.swing.UIManager;
066    import javax.swing.border.Border;
067    import javax.swing.event.ChangeEvent;
068    import javax.swing.event.ChangeListener;
069    import javax.swing.plaf.ActionMapUIResource;
070    import javax.swing.plaf.ComponentUI;
071    import javax.swing.plaf.ScrollPaneUI;
072    import javax.swing.plaf.UIResource;
073    
074    /**
075     * A UI delegate for the {@link JScrollPane} component.
076     */
077    public 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