Source for javax.swing.plaf.basic.BasicScrollPaneUI

   1: /* BasicScrollPaneUI.java
   2:    Copyright (C) 2002, 2004, 2005 Free Software Foundation, Inc.
   3: 
   4: This file is part of GNU Classpath.
   5: 
   6: GNU Classpath is free software; you can redistribute it and/or modify
   7: it under the terms of the GNU General Public License as published by
   8: the Free Software Foundation; either version 2, or (at your option)
   9: any later version.
  10: 
  11: GNU Classpath is distributed in the hope that it will be useful, but
  12: WITHOUT ANY WARRANTY; without even the implied warranty of
  13: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  14: General Public License for more details.
  15: 
  16: You should have received a copy of the GNU General Public License
  17: along with GNU Classpath; see the file COPYING.  If not, write to the
  18: Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
  19: 02110-1301 USA.
  20: 
  21: Linking this library statically or dynamically with other modules is
  22: making a combined work based on this library.  Thus, the terms and
  23: conditions of the GNU General Public License cover the whole
  24: combination.
  25: 
  26: As a special exception, the copyright holders of this library give you
  27: permission to link this library with independent modules to produce an
  28: executable, regardless of the license terms of these independent
  29: modules, and to copy and distribute the resulting executable under
  30: terms of your choice, provided that you also meet, for each linked
  31: independent module, the terms and conditions of the license of that
  32: module.  An independent module is a module which is not derived from
  33: or based on this library.  If you modify this library, you may extend
  34: this exception to your version of the library, but you are not
  35: obligated to do so.  If you do not wish to do so, delete this
  36: exception statement from your version. */
  37: 
  38: 
  39: package javax.swing.plaf.basic;
  40: 
  41: import gnu.classpath.NotImplementedException;
  42: 
  43: import java.awt.Component;
  44: import java.awt.Dimension;
  45: import java.awt.Graphics;
  46: import java.awt.Point;
  47: import java.awt.Rectangle;
  48: import java.awt.event.ContainerEvent;
  49: import java.awt.event.ContainerListener;
  50: import java.awt.event.MouseWheelEvent;
  51: import java.awt.event.MouseWheelListener;
  52: import java.beans.PropertyChangeEvent;
  53: import java.beans.PropertyChangeListener;
  54: 
  55: import javax.swing.JComponent;
  56: import javax.swing.JScrollBar;
  57: import javax.swing.JScrollPane;
  58: import javax.swing.JViewport;
  59: import javax.swing.LookAndFeel;
  60: import javax.swing.ScrollPaneConstants;
  61: import javax.swing.ScrollPaneLayout;
  62: import javax.swing.Scrollable;
  63: import javax.swing.SwingConstants;
  64: import javax.swing.event.ChangeEvent;
  65: import javax.swing.event.ChangeListener;
  66: import javax.swing.plaf.ComponentUI;
  67: import javax.swing.plaf.ScrollPaneUI;
  68: 
  69: public class BasicScrollPaneUI extends ScrollPaneUI
  70:   implements ScrollPaneConstants
  71: {
  72: 
  73:   /**
  74:    * Listens for changes in the state of the horizontal scrollbar's model and
  75:    * updates the scrollpane accordingly.
  76:    *
  77:    * @author Roman Kennke (kennke@aicas.com)
  78:    */
  79:   public class HSBChangeListener implements ChangeListener
  80:   {
  81: 
  82:     /**
  83:      * Receives notification when the state of the horizontal scrollbar
  84:      * model has changed.
  85:      *
  86:      * @param event the change event
  87:      */
  88:     public void stateChanged(ChangeEvent event)
  89:     {
  90:       JScrollBar hsb = scrollpane.getHorizontalScrollBar();
  91:       JViewport vp = scrollpane.getViewport();
  92:       Point viewPosition = vp.getViewPosition();
  93:       int xpos = hsb.getValue();
  94: 
  95:       if (xpos != viewPosition.x)
  96:         {
  97:           viewPosition.x = xpos;
  98:           vp.setViewPosition(viewPosition);
  99:         }
 100: 
 101:       viewPosition.y = 0;
 102:       JViewport columnHeader = scrollpane.getColumnHeader();
 103:       if (columnHeader != null 
 104:           && !columnHeader.getViewPosition().equals(viewPosition))
 105:         columnHeader.setViewPosition(viewPosition);
 106:     }
 107: 
 108:   }
 109: 
 110:   /**
 111:    * Listens for changes in the state of the vertical scrollbar's model and
 112:    * updates the scrollpane accordingly.
 113:    *
 114:    * @author Roman Kennke (kennke@aicas.com)
 115:    */
 116:   public class VSBChangeListener implements ChangeListener
 117:   {
 118: 
 119:     /**
 120:      * Receives notification when the state of the vertical scrollbar
 121:      * model has changed.
 122:      *
 123:      * @param event the change event
 124:      */
 125:     public void stateChanged(ChangeEvent event)
 126:     {
 127:       JScrollBar vsb = scrollpane.getVerticalScrollBar();
 128:       JViewport vp = scrollpane.getViewport();
 129:       Point viewPosition = vp.getViewPosition();
 130:       int ypos = vsb.getValue();
 131:       if (ypos != viewPosition.y)
 132:         {
 133:           viewPosition.y = ypos;
 134:           vp.setViewPosition(viewPosition);
 135:         }
 136: 
 137:       viewPosition.x = 0;
 138:       JViewport rowHeader = scrollpane.getRowHeader();
 139:       if (rowHeader != null 
 140:           && !rowHeader.getViewPosition().equals(viewPosition))
 141:         rowHeader.setViewPosition(viewPosition);
 142:     }
 143:  
 144:   }
 145: 
 146:   /**
 147:    * Listens for changes of the viewport's extent size and updates the
 148:    * scrollpane accordingly.
 149:    *
 150:    * @author Roman Kennke (kennke@aicas.com)
 151:    */
 152:   public class ViewportChangeHandler implements ChangeListener
 153:   {
 154: 
 155:     /**
 156:      * Receives notification when the view's size, position or extent size
 157:      * changes. When the extents size has changed, this method calls
 158:      * {@link BasicScrollPaneUI#syncScrollPaneWithViewport()} to adjust the
 159:      * scrollbars extents as well.
 160:      * 
 161:      * @param event the change event
 162:      */
 163:     public void stateChanged(ChangeEvent event)
 164:     {
 165:       JViewport vp = scrollpane.getViewport();
 166:       JScrollBar hsb = scrollpane.getHorizontalScrollBar();
 167:       JScrollBar vsb = scrollpane.getVerticalScrollBar();
 168:       syncScrollPaneWithViewport();
 169:     }
 170: 
 171:   }
 172: 
 173:   /**
 174:    * Listens for property changes on the scrollpane and update the view
 175:    * accordingly.
 176:    *
 177:    * @author Roman Kennke (kennke@aicas.com)
 178:    */
 179:   public class PropertyChangeHandler implements PropertyChangeListener
 180:   {
 181: 
 182:     /**
 183:      * Receives notification when any of the scrollpane's bound property
 184:      * changes. This method calls the appropriate update method on the
 185:      * <code>ScrollBarUI</code>.
 186:      *
 187:      * @param e the property change event
 188:      *
 189:      * @see BasicScrollPaneUI#updateColumnHeader(PropertyChangeEvent)
 190:      * @see BasicScrollPaneUI#updateRowHeader(PropertyChangeEvent)
 191:      * @see BasicScrollPaneUI#updateScrollBarDisplayPolicy(PropertyChangeEvent)
 192:      * @see BasicScrollPaneUI#updateViewport(PropertyChangeEvent)
 193:      */
 194:     public void propertyChange(PropertyChangeEvent e)
 195:     {
 196:       String propName = e.getPropertyName();
 197:       if (propName.equals("viewport"))
 198:         updateViewport(e);
 199:       else if (propName.equals("rowHeader"))
 200:         updateRowHeader(e);
 201:       else if (propName.equals("columnHeader"))
 202:         updateColumnHeader(e);
 203:       else if (propName.equals("horizontalScrollBarPolicy")
 204:           || e.getPropertyName().equals("verticalScrollBarPolicy"))
 205:         updateScrollBarDisplayPolicy(e);
 206:       else if (propName.equals("verticalScrollBar"))
 207:         {
 208:           JScrollBar oldSb = (JScrollBar) e.getOldValue();
 209:           oldSb.getModel().removeChangeListener(vsbChangeListener);
 210:           JScrollBar newSb = (JScrollBar) e.getNewValue();
 211:           newSb.getModel().addChangeListener(vsbChangeListener);
 212:         }
 213:       else if (propName.equals("horizontalScrollBar"))
 214:         {
 215:           JScrollBar oldSb = (JScrollBar) e.getOldValue();
 216:           oldSb.getModel().removeChangeListener(hsbChangeListener);
 217:           JScrollBar newSb = (JScrollBar) e.getNewValue();
 218:           newSb.getModel().addChangeListener(hsbChangeListener);
 219:         }
 220:     }
 221: 
 222:   }
 223: 
 224:   /**
 225:    * Listens for mouse wheel events and update the scrollpane accordingly.
 226:    *
 227:    * @author Roman Kennke (kennke@aicas.com)
 228:    *
 229:    * @since 1.4
 230:    */
 231:   protected class MouseWheelHandler implements MouseWheelListener
 232:   {
 233:     /**
 234:      * Use to compute the visible rectangle.
 235:      */
 236:     final Rectangle rect = new Rectangle();
 237: 
 238:     /**
 239:      * Scroll with the mouse whell.
 240:      * 
 241:      * @author Audrius Meskauskas (audriusa@Bioinformatics.org)
 242:      */
 243:     public void mouseWheelMoved(MouseWheelEvent e)
 244:     {
 245:       if (scrollpane.getViewport().getComponentCount() == 0)
 246:         return;
 247: 
 248:       Component target = scrollpane.getViewport().getComponent(0);
 249:       JScrollBar bar = scrollpane.getVerticalScrollBar();
 250:       Scrollable scrollable = (target instanceof Scrollable) ? (Scrollable) target
 251:                                                             : null;
 252: 
 253:       boolean tracksHeight = scrollable != null
 254:                              && scrollable.getScrollableTracksViewportHeight();
 255:       int wheel = e.getWheelRotation() * ROWS_PER_WHEEL_CLICK;
 256:       int delta;
 257: 
 258:       // If possible, scroll vertically.
 259:       if (bar != null && ! tracksHeight)
 260:         {
 261:           if (scrollable != null)
 262:             {
 263:               bounds(target);
 264:               delta = scrollable.getScrollableUnitIncrement(
 265:                 rect, SwingConstants.VERTICAL, wheel);
 266:             }
 267:           else
 268:             {
 269:               // Scroll non scrollables.
 270:               delta = wheel * SCROLL_NON_SCROLLABLES;
 271:             }
 272:           scroll(bar, delta);
 273:         }
 274:       // If not, try to scroll horizontally
 275:       else
 276:         {
 277:           bar = scrollpane.getHorizontalScrollBar();
 278:           boolean tracksWidth = scrollable != null
 279:                                 && scrollable.getScrollableTracksViewportWidth();
 280: 
 281:           if (bar != null && ! tracksWidth)
 282:             {
 283:               if (scrollable != null)
 284:                 {
 285:                   bounds(target);
 286:                   delta = scrollable.getScrollableUnitIncrement(
 287:                      rect, SwingConstants.HORIZONTAL, wheel);
 288:                 }
 289:               else
 290:                 {
 291:                   // Scroll non scrollables.
 292:                   delta = wheel * SCROLL_NON_SCROLLABLES;
 293:                 }
 294:               scroll(bar, delta);
 295:             }
 296:         }
 297:     }
 298:     
 299:     /**
 300:      * Place the component bounds into rect. The x and y values 
 301:      * need to be reversed.
 302:      * 
 303:      * @param target the target being scrolled
 304:      */
 305:     final void bounds(Component target)
 306:     {
 307:       // Viewport bounds, translated by the scroll bar positions.
 308:       target.getParent().getBounds(rect);
 309:       rect.x = getValue(scrollpane.getHorizontalScrollBar());
 310:       rect.y = getValue(scrollpane.getVerticalScrollBar());
 311:     }
 312:     
 313:     /**
 314:      * Get the scroll bar value or null if there is no such scroll bar.
 315:      */
 316:     final int getValue(JScrollBar bar)
 317:     {
 318:       return bar != null ? bar.getValue() : 0;
 319:     }
 320:     
 321:     /**
 322:      * Scroll the given distance.
 323:      * 
 324:      * @param bar the scrollbar to scroll
 325:      * @param delta the distance
 326:      */
 327:     final void scroll(JScrollBar bar, int delta)
 328:     {
 329:       int y = bar.getValue() + delta;
 330: 
 331:       if (y < bar.getMinimum())
 332:         y = bar.getMinimum();
 333:       if (y > bar.getMaximum())
 334:         y = bar.getMaximum();
 335: 
 336:       bar.setValue(y);
 337:     }
 338:   }
 339:   
 340:   /**
 341:    * Adds/removes the mouse wheel listener when the component is added/removed
 342:    * to/from the scroll pane view port.
 343:    * 
 344:    * @author Audrius Meskauskas (audriusa@bioinformatics.org)
 345:    */
 346:   class ViewportContainerListener implements ContainerListener
 347:   {
 348:     /**
 349:      * Add the mouse wheel listener, allowing to scroll with the mouse.
 350:      */
 351:     public void componentAdded(ContainerEvent e)
 352:     {
 353:       e.getChild().addMouseWheelListener(mouseWheelListener);
 354:     }
 355:     
 356:     /**
 357:      * Remove the mouse wheel listener.
 358:      */
 359:     public void componentRemoved(ContainerEvent e)
 360:     {
 361:       e.getChild().removeMouseWheelListener(mouseWheelListener);
 362:     }
 363:   }
 364:   
 365:   /**
 366:    * The number of pixels by that we should scroll the content that does
 367:    * not implement Scrollable.
 368:    */
 369:   static int SCROLL_NON_SCROLLABLES = 10;
 370:   
 371:   /**
 372:    * The number of rows to scroll per mouse wheel click. From impression,
 373:    * Sun seems using the value 3.
 374:    */
 375:   static int ROWS_PER_WHEEL_CLICK = 3;     
 376: 
 377:   /** The Scrollpane for which the UI is provided by this class. */
 378:   protected JScrollPane scrollpane;
 379: 
 380:   /**
 381:    * The horizontal scrollbar listener.
 382:    */
 383:   protected ChangeListener hsbChangeListener;
 384: 
 385:   /**
 386:    * The vertical scrollbar listener.
 387:    */
 388:   protected ChangeListener vsbChangeListener;
 389: 
 390:   /**
 391:    * The viewport listener.
 392:    */
 393:   protected ChangeListener viewportChangeListener;
 394: 
 395:   /**
 396:    * The scrollpane property change listener.
 397:    */
 398:   protected PropertyChangeListener spPropertyChangeListener;
 399: 
 400:   /**
 401:    * The mousewheel listener for the scrollpane.
 402:    */
 403:   MouseWheelListener mouseWheelListener;
 404:   
 405:   /**
 406:    * The listener to add and remove the mouse wheel listener to/from
 407:    * the component container.
 408:    */
 409:   ContainerListener containerListener;
 410: 
 411:   public static ComponentUI createUI(final JComponent c) 
 412:   {
 413:     return new BasicScrollPaneUI();
 414:   }
 415: 
 416:   protected void installDefaults(JScrollPane p)
 417:   {
 418:     scrollpane = p;
 419:     LookAndFeel.installColorsAndFont(p, "ScrollPane.background",
 420:                                      "ScrollPane.foreground",
 421:                                      "ScrollPane.font");
 422:     LookAndFeel.installBorder(p, "ScrollPane.border");
 423:     p.setOpaque(true);
 424:   }
 425: 
 426:   protected void uninstallDefaults(JScrollPane p)
 427:   {
 428:     p.setForeground(null);
 429:     p.setBackground(null);
 430:     p.setFont(null);
 431:     p.setBorder(null);
 432:     scrollpane = null;
 433:   }
 434:     
 435:   public void installUI(final JComponent c) 
 436:   {
 437:     super.installUI(c);
 438:     installDefaults((JScrollPane) c);
 439:     installListeners((JScrollPane) c);
 440:     installKeyboardActions((JScrollPane) c);
 441:   }
 442: 
 443:   /**
 444:    * Installs the listeners on the scrollbars, the viewport and the scrollpane.
 445:    *
 446:    * @param sp the scrollpane on which to install the listeners
 447:    */
 448:   protected void installListeners(JScrollPane sp)
 449:   {
 450:     if (spPropertyChangeListener == null)
 451:       spPropertyChangeListener = createPropertyChangeListener();
 452:     sp.addPropertyChangeListener(spPropertyChangeListener);
 453: 
 454:     if (hsbChangeListener == null)
 455:       hsbChangeListener = createHSBChangeListener();
 456:     sp.getHorizontalScrollBar().getModel().addChangeListener(hsbChangeListener);
 457:     
 458:     if (vsbChangeListener == null)
 459:       vsbChangeListener = createVSBChangeListener();
 460:     sp.getVerticalScrollBar().getModel().addChangeListener(vsbChangeListener);
 461: 
 462:     if (viewportChangeListener == null)
 463:       viewportChangeListener = createViewportChangeListener();
 464:     
 465:     if (mouseWheelListener == null)
 466:       mouseWheelListener = createMouseWheelListener();
 467:     
 468:     if (containerListener == null)
 469:       containerListener = new ViewportContainerListener();
 470:     
 471:     JViewport v = sp.getViewport();
 472:     v.addChangeListener(viewportChangeListener);
 473:     v.addContainerListener(containerListener);
 474:     
 475:     // Add mouse wheel listeners to the componets that are probably already
 476:     // in the view port.
 477:     for (int i = 0; i < v.getComponentCount(); i++)
 478:       v.getComponent(i).addMouseWheelListener(mouseWheelListener);
 479:   }
 480: 
 481:   /**
 482:    * Installs additional keyboard actions on the scrollpane. This is a hook
 483:    * method provided to subclasses in order to install their own keyboard
 484:    * actions.
 485:    *
 486:    * @param sp the scrollpane to install keyboard actions on
 487:    */
 488:   protected void installKeyboardActions(JScrollPane sp)
 489:     throws NotImplementedException
 490:   {
 491:     // TODO: Is this only a hook method or should we actually do something
 492:     // here? If the latter, than figure out what and implement this.
 493:   }
 494: 
 495:   /**
 496:    * Creates and returns the change listener for the horizontal scrollbar.
 497:    *
 498:    * @return the change listener for the horizontal scrollbar
 499:    */
 500:   protected ChangeListener createHSBChangeListener()
 501:   {
 502:     return new HSBChangeListener();
 503:   }
 504: 
 505:   /**
 506:    * Creates and returns the change listener for the vertical scrollbar.
 507:    *
 508:    * @return the change listener for the vertical scrollbar
 509:    */
 510:   protected ChangeListener createVSBChangeListener()
 511:   {
 512:     return new VSBChangeListener();
 513:   }
 514: 
 515:   /**
 516:    * Creates and returns the change listener for the viewport.
 517:    *
 518:    * @return the change listener for the viewport
 519:    */
 520:   protected ChangeListener createViewportChangeListener()
 521:   {
 522:     return new ViewportChangeHandler();
 523:   }
 524: 
 525:   /**
 526:    * Creates and returns the property change listener for the scrollpane.
 527:    *
 528:    * @return the property change listener for the scrollpane
 529:    */
 530:   protected PropertyChangeListener createPropertyChangeListener()
 531:   {
 532:     return new PropertyChangeHandler();
 533:   }
 534: 
 535:   /**
 536:    * Creates and returns the mouse wheel listener for the scrollpane.
 537:    *
 538:    * @return the mouse wheel listener for the scrollpane
 539:    */
 540:   protected MouseWheelListener createMouseWheelListener()
 541:   {
 542:     return new MouseWheelHandler();
 543:   }
 544: 
 545:   public void uninstallUI(final JComponent c) 
 546:   {
 547:     super.uninstallUI(c);
 548:     this.uninstallDefaults((JScrollPane)c);
 549:     uninstallListeners((JScrollPane) c);
 550:     installKeyboardActions((JScrollPane) c);
 551:   }
 552: 
 553:   /**
 554:    * Uninstalls all the listeners that have been installed in
 555:    * {@link #installListeners(JScrollPane)}.
 556:    *
 557:    * @param c the scrollpane from which to uninstall the listeners 
 558:    */
 559:   protected void uninstallListeners(JComponent c)
 560:   {
 561:     JScrollPane sp = (JScrollPane) c;
 562:     sp.removePropertyChangeListener(spPropertyChangeListener);
 563:     sp.getHorizontalScrollBar().getModel()
 564:                                .removeChangeListener(hsbChangeListener);
 565:     sp.getVerticalScrollBar().getModel()
 566:                              .removeChangeListener(vsbChangeListener);
 567:     
 568:     JViewport v = sp.getViewport();
 569:     v.removeChangeListener(viewportChangeListener);
 570:     v.removeContainerListener(containerListener);
 571:  
 572:     for (int i = 0; i < v.getComponentCount(); i++)
 573:       v.getComponent(i).removeMouseWheelListener(mouseWheelListener);
 574: 
 575:   }
 576: 
 577:   /**
 578:    * Uninstalls all keyboard actions from the JScrollPane that have been
 579:    * installed by {@link #installKeyboardActions}. This is a hook method
 580:    * provided to subclasses to add their own keyboard actions.
 581:    *
 582:    * @param sp the scrollpane to uninstall keyboard actions from
 583:    */
 584:   protected void uninstallKeyboardActions(JScrollPane sp)
 585:     throws NotImplementedException
 586:   {
 587:     // TODO: Is this only a hook method or should we actually do something
 588:     // here? If the latter, than figure out what and implement this.
 589:   }
 590: 
 591:   public Dimension getMinimumSize(JComponent c) 
 592:   {
 593:     JScrollPane p = (JScrollPane ) c;
 594:     ScrollPaneLayout sl = (ScrollPaneLayout) p.getLayout();
 595:     return sl.minimumLayoutSize(c);
 596:   }
 597: 
 598:   public void paint(Graphics g, JComponent c)
 599:   {      
 600:     // do nothing; the normal painting-of-children algorithm, along with
 601:     // ScrollPaneLayout, does all the relevant work.
 602:   }
 603: 
 604:   /**
 605:    * Synchronizes the scrollbars with the viewport's extents.
 606:    */
 607:   protected void syncScrollPaneWithViewport()
 608:   {
 609:     JViewport vp = scrollpane.getViewport();
 610: 
 611:     // Update the horizontal scrollbar.
 612:     JScrollBar hsb = scrollpane.getHorizontalScrollBar();
 613:     hsb.setMaximum(vp.getViewSize().width);
 614:     hsb.setValue(vp.getViewPosition().x);
 615:     hsb.setVisibleAmount(vp.getExtentSize().width);
 616:     
 617:     // Update the vertical scrollbar.
 618:     JScrollBar vsb = scrollpane.getVerticalScrollBar();
 619:     vsb.setMaximum(vp.getViewSize().height);
 620:     vsb.setValue(vp.getViewPosition().y);
 621:     vsb.setVisibleAmount(vp.getExtentSize().height);
 622:   }
 623: 
 624:   /**
 625:    * Receives notification when the <code>columnHeader</code> property has
 626:    * changed on the scrollpane.
 627:    *
 628:    * @param ev the property change event
 629:    */
 630:   protected void updateColumnHeader(PropertyChangeEvent ev)
 631:   {
 632:     // TODO: Find out what should be done here. Or is this only a hook?
 633:   }
 634: 
 635:   /**
 636:    * Receives notification when the <code>rowHeader</code> property has changed
 637:    * on the scrollpane.
 638:    *
 639:    * @param ev the property change event
 640:    */
 641:   protected void updateRowHeader(PropertyChangeEvent ev)
 642:   {
 643:     // TODO: Find out what should be done here. Or is this only a hook?
 644:   }
 645: 
 646:   /**
 647:    * Receives notification when the <code>scrollBarDisplayPolicy</code>
 648:    * property has changed on the scrollpane.
 649:    *
 650:    * @param ev the property change event
 651:    */
 652:   protected void updateScrollBarDisplayPolicy(PropertyChangeEvent ev)
 653:   {
 654:     // TODO: Find out what should be done here. Or is this only a hook?
 655:   }
 656: 
 657:   /**
 658:    * Receives notification when the <code>viewport</code> property has changed
 659:    * on the scrollpane.
 660:    *
 661:    * This method sets removes the viewportChangeListener from the old viewport
 662:    * and adds it to the new viewport.
 663:    *
 664:    * @param ev the property change event
 665:    */
 666:   protected void updateViewport(PropertyChangeEvent ev)
 667:   {
 668:     JViewport oldViewport = (JViewport) ev.getOldValue();
 669:     oldViewport.removeChangeListener(viewportChangeListener);
 670:     JViewport newViewport = (JViewport) ev.getNewValue();
 671:     newViewport.addChangeListener(viewportChangeListener);
 672:     syncScrollPaneWithViewport();
 673:   }
 674: }
 675: 
 676: 
 677: 
 678: 
 679: 
 680: 
 681: 
 682: 
 683: 
 684: 
 685: