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 java.awt.Dimension;
  42: import java.awt.Graphics;
  43: import java.awt.Point;
  44: import java.awt.event.MouseWheelEvent;
  45: import java.awt.event.MouseWheelListener;
  46: import java.beans.PropertyChangeEvent;
  47: import java.beans.PropertyChangeListener;
  48: 
  49: import javax.swing.JComponent;
  50: import javax.swing.JScrollBar;
  51: import javax.swing.JScrollPane;
  52: import javax.swing.JViewport;
  53: import javax.swing.LookAndFeel;
  54: import javax.swing.ScrollPaneConstants;
  55: import javax.swing.ScrollPaneLayout;
  56: import javax.swing.event.ChangeEvent;
  57: import javax.swing.event.ChangeListener;
  58: import javax.swing.plaf.ComponentUI;
  59: import javax.swing.plaf.ScrollPaneUI;
  60: 
  61: public class BasicScrollPaneUI extends ScrollPaneUI
  62:   implements ScrollPaneConstants
  63: {
  64: 
  65:   /**
  66:    * Listens for changes in the state of the horizontal scrollbar's model and
  67:    * updates the scrollpane accordingly.
  68:    *
  69:    * @author Roman Kennke (kennke@aicas.com)
  70:    */
  71:   public class HSBChangeListener implements ChangeListener
  72:   {
  73: 
  74:     /**
  75:      * Receives notification when the state of the horizontal scrollbar
  76:      * model has changed.
  77:      *
  78:      * @param event the change event
  79:      */
  80:     public void stateChanged(ChangeEvent event)
  81:     {
  82:       JScrollBar hsb = scrollpane.getHorizontalScrollBar();
  83:       JViewport vp = scrollpane.getViewport();
  84:       Point viewPosition = vp.getViewPosition();
  85:       int xpos = hsb.getValue();
  86: 
  87:       if (xpos != viewPosition.x)
  88:         {
  89:           viewPosition.x = xpos;
  90:           vp.setViewPosition(viewPosition);
  91:         }
  92: 
  93:       viewPosition.y = 0;
  94:       JViewport columnHeader = scrollpane.getColumnHeader();
  95:       if (columnHeader != null 
  96:           && !columnHeader.getViewPosition().equals(viewPosition))
  97:         columnHeader.setViewPosition(viewPosition);
  98:     }
  99: 
 100:   }
 101: 
 102:   /**
 103:    * Listens for changes in the state of the vertical scrollbar's model and
 104:    * updates the scrollpane accordingly.
 105:    *
 106:    * @author Roman Kennke (kennke@aicas.com)
 107:    */
 108:   public class VSBChangeListener implements ChangeListener
 109:   {
 110: 
 111:     /**
 112:      * Receives notification when the state of the vertical scrollbar
 113:      * model has changed.
 114:      *
 115:      * @param event the change event
 116:      */
 117:     public void stateChanged(ChangeEvent event)
 118:     {
 119:       JScrollBar vsb = scrollpane.getVerticalScrollBar();
 120:       JViewport vp = scrollpane.getViewport();
 121:       Point viewPosition = vp.getViewPosition();
 122:       int ypos = vsb.getValue();
 123:       if (ypos != viewPosition.y)
 124:         {
 125:           viewPosition.y = ypos;
 126:           vp.setViewPosition(viewPosition);
 127:         }
 128: 
 129:       viewPosition.x = 0;
 130:       JViewport rowHeader = scrollpane.getRowHeader();
 131:       if (rowHeader != null 
 132:           && !rowHeader.getViewPosition().equals(viewPosition))
 133:         rowHeader.setViewPosition(viewPosition);
 134:     }
 135:  
 136:   }
 137: 
 138:   /**
 139:    * Listens for changes of the viewport's extent size and updates the
 140:    * scrollpane accordingly.
 141:    *
 142:    * @author Roman Kennke (kennke@aicas.com)
 143:    */
 144:   public class ViewportChangeHandler implements ChangeListener
 145:   {
 146: 
 147:     /**
 148:      * Receives notification when the view's size, position or extent size
 149:      * changes. When the extents size has changed, this method calls
 150:      * {@link BasicScrollPaneUI#syncScrollPaneWithViewport()} to adjust the
 151:      * scrollbars extents as well.
 152:      * 
 153:      * @param event the change event
 154:      */
 155:     public void stateChanged(ChangeEvent event)
 156:     {
 157:       JViewport vp = scrollpane.getViewport();
 158:       JScrollBar hsb = scrollpane.getHorizontalScrollBar();
 159:       JScrollBar vsb = scrollpane.getVerticalScrollBar();
 160:       syncScrollPaneWithViewport();
 161:     }
 162: 
 163:   }
 164: 
 165:   /**
 166:    * Listens for property changes on the scrollpane and update the view
 167:    * accordingly.
 168:    *
 169:    * @author Roman Kennke (kennke@aicas.com)
 170:    */
 171:   public class PropertyChangeHandler implements PropertyChangeListener
 172:   {
 173: 
 174:     /**
 175:      * Receives notification when any of the scrollpane's bound property
 176:      * changes. This method calls the appropriate update method on the
 177:      * <code>ScrollBarUI</code>.
 178:      *
 179:      * @param e the property change event
 180:      *
 181:      * @see BasicScrollPaneUI#updateColumnHeader(PropertyChangeEvent)
 182:      * @see BasicScrollPaneUI#updateRowHeader(PropertyChangeEvent)
 183:      * @see BasicScrollPaneUI#updateScrollBarDisplayPolicy(PropertyChangeEvent)
 184:      * @see BasicScrollPaneUI#updateViewport(PropertyChangeEvent)
 185:      */
 186:     public void propertyChange(PropertyChangeEvent e)
 187:     {
 188:       String propName = e.getPropertyName();
 189:       if (propName.equals("viewport"))
 190:         updateViewport(e);
 191:       else if (propName.equals("rowHeader"))
 192:         updateRowHeader(e);
 193:       else if (propName.equals("columnHeader"))
 194:         updateColumnHeader(e);
 195:       else if (propName.equals("horizontalScrollBarPolicy")
 196:           || e.getPropertyName().equals("verticalScrollBarPolicy"))
 197:         updateScrollBarDisplayPolicy(e);
 198:       else if (propName.equals("verticalScrollBar"))
 199:         {
 200:           JScrollBar oldSb = (JScrollBar) e.getOldValue();
 201:           oldSb.getModel().removeChangeListener(vsbChangeListener);
 202:           JScrollBar newSb = (JScrollBar) e.getNewValue();
 203:           newSb.getModel().addChangeListener(vsbChangeListener);
 204:         }
 205:       else if (propName.equals("horizontalScrollBar"))
 206:         {
 207:           JScrollBar oldSb = (JScrollBar) e.getOldValue();
 208:           oldSb.getModel().removeChangeListener(hsbChangeListener);
 209:           JScrollBar newSb = (JScrollBar) e.getNewValue();
 210:           newSb.getModel().addChangeListener(hsbChangeListener);
 211:         }
 212:     }
 213: 
 214:   }
 215: 
 216:   /**
 217:    * Listens for mouse wheel events and update the scrollpane accordingly.
 218:    *
 219:    * @author Roman Kennke (kennke@aicas.com)
 220:    *
 221:    * @since 1.4
 222:    */
 223:   protected class MouseWheelHandler implements MouseWheelListener
 224:   {
 225: 
 226:     /**
 227:      * Receives notification whenever the mouse wheel is moved.
 228:      *
 229:      * @param event the mouse wheel event
 230:      */
 231:     public void mouseWheelMoved(MouseWheelEvent event)
 232:     {
 233:       // TODO: Implement this properly.
 234:     }
 235: 
 236:   }
 237: 
 238:   /** The Scrollpane for which the UI is provided by this class. */
 239:   protected JScrollPane scrollpane;
 240: 
 241:   /**
 242:    * The horizontal scrollbar listener.
 243:    */
 244:   protected ChangeListener hsbChangeListener;
 245: 
 246:   /**
 247:    * The vertical scrollbar listener.
 248:    */
 249:   protected ChangeListener vsbChangeListener;
 250: 
 251:   /**
 252:    * The viewport listener.
 253:    */
 254:   protected ChangeListener viewportChangeListener;
 255: 
 256:   /**
 257:    * The scrollpane property change listener.
 258:    */
 259:   protected PropertyChangeListener spPropertyChangeListener;
 260: 
 261:   /**
 262:    * The mousewheel listener for the scrollpane.
 263:    */
 264:   MouseWheelListener mouseWheelListener;
 265: 
 266:   public static ComponentUI createUI(final JComponent c) 
 267:   {
 268:     return new BasicScrollPaneUI();
 269:   }
 270: 
 271:   protected void installDefaults(JScrollPane p)
 272:   {
 273:     scrollpane = p;
 274:     LookAndFeel.installColorsAndFont(p, "ScrollPane.background",
 275:                                      "ScrollPane.foreground",
 276:                                      "ScrollPane.font");
 277:     LookAndFeel.installBorder(p, "ScrollPane.border");
 278:     p.setOpaque(true);
 279:   }
 280: 
 281:   protected void uninstallDefaults(JScrollPane p)
 282:   {
 283:     p.setForeground(null);
 284:     p.setBackground(null);
 285:     p.setFont(null);
 286:     p.setBorder(null);
 287:     scrollpane = null;
 288:   }
 289:     
 290:   public void installUI(final JComponent c) 
 291:   {
 292:     super.installUI(c);
 293:     installDefaults((JScrollPane) c);
 294:     installListeners((JScrollPane) c);
 295:     installKeyboardActions((JScrollPane) c);
 296:   }
 297: 
 298:   /**
 299:    * Installs the listeners on the scrollbars, the viewport and the scrollpane.
 300:    *
 301:    * @param sp the scrollpane on which to install the listeners
 302:    */
 303:   protected void installListeners(JScrollPane sp)
 304:   {
 305:     if (spPropertyChangeListener == null)
 306:       spPropertyChangeListener = createPropertyChangeListener();
 307:     sp.addPropertyChangeListener(spPropertyChangeListener);
 308: 
 309:     if (hsbChangeListener == null)
 310:       hsbChangeListener = createHSBChangeListener();
 311:     sp.getHorizontalScrollBar().getModel().addChangeListener(hsbChangeListener);
 312:     
 313:     if (vsbChangeListener == null)
 314:       vsbChangeListener = createVSBChangeListener();
 315:     sp.getVerticalScrollBar().getModel().addChangeListener(vsbChangeListener);
 316: 
 317:     if (viewportChangeListener == null)
 318:       viewportChangeListener = createViewportChangeListener();
 319:     sp.getViewport().addChangeListener(viewportChangeListener);
 320: 
 321:     if (mouseWheelListener == null)
 322:       mouseWheelListener = createMouseWheelListener();
 323:     sp.addMouseWheelListener(mouseWheelListener);
 324:   }
 325: 
 326:   /**
 327:    * Installs additional keyboard actions on the scrollpane. This is a hook
 328:    * method provided to subclasses in order to install their own keyboard
 329:    * actions.
 330:    *
 331:    * @param sp the scrollpane to install keyboard actions on
 332:    */
 333:   protected void installKeyboardActions(JScrollPane sp)
 334:   {
 335:     // TODO: Is this only a hook method or should we actually do something
 336:     // here? If the latter, than figure out what and implement this.
 337:   }
 338: 
 339:   /**
 340:    * Creates and returns the change listener for the horizontal scrollbar.
 341:    *
 342:    * @return the change listener for the horizontal scrollbar
 343:    */
 344:   protected ChangeListener createHSBChangeListener()
 345:   {
 346:     return new HSBChangeListener();
 347:   }
 348: 
 349:   /**
 350:    * Creates and returns the change listener for the vertical scrollbar.
 351:    *
 352:    * @return the change listener for the vertical scrollbar
 353:    */
 354:   protected ChangeListener createVSBChangeListener()
 355:   {
 356:     return new VSBChangeListener();
 357:   }
 358: 
 359:   /**
 360:    * Creates and returns the change listener for the viewport.
 361:    *
 362:    * @return the change listener for the viewport
 363:    */
 364:   protected ChangeListener createViewportChangeListener()
 365:   {
 366:     return new ViewportChangeHandler();
 367:   }
 368: 
 369:   /**
 370:    * Creates and returns the property change listener for the scrollpane.
 371:    *
 372:    * @return the property change listener for the scrollpane
 373:    */
 374:   protected PropertyChangeListener createPropertyChangeListener()
 375:   {
 376:     return new PropertyChangeHandler();
 377:   }
 378: 
 379:   /**
 380:    * Creates and returns the mouse wheel listener for the scrollpane.
 381:    *
 382:    * @return the mouse wheel listener for the scrollpane
 383:    */
 384:   protected MouseWheelListener createMouseWheelListener()
 385:   {
 386:     return new MouseWheelHandler();
 387:   }
 388: 
 389:   public void uninstallUI(final JComponent c) 
 390:   {
 391:     super.uninstallUI(c);
 392:     this.uninstallDefaults((JScrollPane)c);
 393:     uninstallListeners((JScrollPane) c);
 394:     installKeyboardActions((JScrollPane) c);
 395:   }
 396: 
 397:   /**
 398:    * Uninstalls all the listeners that have been installed in
 399:    * {@link #installListeners(JScrollPane)}.
 400:    *
 401:    * @param c the scrollpane from which to uninstall the listeners 
 402:    */
 403:   protected void uninstallListeners(JComponent c)
 404:   {
 405:     JScrollPane sp = (JScrollPane) c;
 406:     sp.removePropertyChangeListener(spPropertyChangeListener);
 407:     sp.getHorizontalScrollBar().getModel()
 408:                                .removeChangeListener(hsbChangeListener);
 409:     sp.getVerticalScrollBar().getModel()
 410:                              .removeChangeListener(vsbChangeListener);
 411:     sp.getViewport().removeChangeListener(viewportChangeListener);
 412:     sp.removeMouseWheelListener(mouseWheelListener);
 413:   }
 414: 
 415:   /**
 416:    * Uninstalls all keyboard actions from the JScrollPane that have been
 417:    * installed by {@link #installKeyboardActions}. This is a hook method
 418:    * provided to subclasses to add their own keyboard actions.
 419:    *
 420:    * @param sp the scrollpane to uninstall keyboard actions from
 421:    */
 422:   protected void uninstallKeyboardActions(JScrollPane sp)
 423:   {
 424:     // TODO: Is this only a hook method or should we actually do something
 425:     // here? If the latter, than figure out what and implement this.
 426:   }
 427: 
 428:   public Dimension getMinimumSize(JComponent c) 
 429:   {
 430:     JScrollPane p = (JScrollPane ) c;
 431:     ScrollPaneLayout sl = (ScrollPaneLayout) p.getLayout();
 432:     return sl.minimumLayoutSize(c);
 433:   }
 434: 
 435:   public void paint(Graphics g, JComponent c)
 436:   {      
 437:     // do nothing; the normal painting-of-children algorithm, along with
 438:     // ScrollPaneLayout, does all the relevant work.
 439:   }
 440: 
 441:   /**
 442:    * Synchronizes the scrollbars with the viewport's extents.
 443:    */
 444:   protected void syncScrollPaneWithViewport()
 445:   {
 446:     JViewport vp = scrollpane.getViewport();
 447: 
 448:     // Update the horizontal scrollbar.
 449:     JScrollBar hsb = scrollpane.getHorizontalScrollBar();
 450:     hsb.setMaximum(vp.getViewSize().width);
 451:     hsb.setValue(vp.getViewPosition().x);
 452:     hsb.setVisibleAmount(vp.getExtentSize().width);
 453:     
 454:     // Update the vertical scrollbar.
 455:     JScrollBar vsb = scrollpane.getVerticalScrollBar();
 456:     vsb.setMaximum(vp.getViewSize().height);
 457:     vsb.setValue(vp.getViewPosition().y);
 458:     vsb.setVisibleAmount(vp.getExtentSize().height);
 459:   }
 460: 
 461:   /**
 462:    * Receives notification when the <code>columnHeader</code> property has
 463:    * changed on the scrollpane.
 464:    *
 465:    * @param ev the property change event
 466:    */
 467:   protected void updateColumnHeader(PropertyChangeEvent ev)
 468:   {
 469:     // TODO: Find out what should be done here. Or is this only a hook?
 470:   }
 471: 
 472:   /**
 473:    * Receives notification when the <code>rowHeader</code> property has changed
 474:    * on the scrollpane.
 475:    *
 476:    * @param ev the property change event
 477:    */
 478:   protected void updateRowHeader(PropertyChangeEvent ev)
 479:   {
 480:     // TODO: Find out what should be done here. Or is this only a hook?
 481:   }
 482: 
 483:   /**
 484:    * Receives notification when the <code>scrollBarDisplayPolicy</code>
 485:    * property has changed on the scrollpane.
 486:    *
 487:    * @param ev the property change event
 488:    */
 489:   protected void updateScrollBarDisplayPolicy(PropertyChangeEvent ev)
 490:   {
 491:     // TODO: Find out what should be done here. Or is this only a hook?
 492:   }
 493: 
 494:   /**
 495:    * Receives notification when the <code>viewport</code> property has changed
 496:    * on the scrollpane.
 497:    *
 498:    * This method sets removes the viewportChangeListener from the old viewport
 499:    * and adds it to the new viewport.
 500:    *
 501:    * @param ev the property change event
 502:    */
 503:   protected void updateViewport(PropertyChangeEvent ev)
 504:   {
 505:     JViewport oldViewport = (JViewport) ev.getOldValue();
 506:     oldViewport.removeChangeListener(viewportChangeListener);
 507:     JViewport newViewport = (JViewport) ev.getNewValue();
 508:     newViewport.addChangeListener(viewportChangeListener);
 509:     syncScrollPaneWithViewport();
 510:   }
 511: }
 512: 
 513: 
 514: 
 515: 
 516: 
 517: 
 518: 
 519: 
 520: 
 521: 
 522: