Source for javax.swing.JViewport

   1: /* JViewport.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;
  40: 
  41: import java.awt.Component;
  42: import java.awt.Dimension;
  43: import java.awt.Graphics;
  44: import java.awt.Insets;
  45: import java.awt.LayoutManager;
  46: import java.awt.Point;
  47: import java.awt.Rectangle;
  48: import java.awt.event.ComponentAdapter;
  49: import java.awt.event.ComponentEvent;
  50: import java.io.Serializable;
  51: 
  52: import javax.swing.border.Border;
  53: import javax.swing.event.ChangeEvent;
  54: import javax.swing.event.ChangeListener;
  55: import javax.swing.plaf.ViewportUI;
  56: 
  57: /**
  58:  *  
  59:  * <pre>
  60:  *                                                     _
  61:  *   +-------------------------------+    ...........Y1 \
  62:  *   |  view                         |                .  \
  63:  *   |  (this component's child)     |                .   > VY
  64:  *   |                               |                .  / = Y2-Y1
  65:  *   |         +------------------------------+  ....Y2_/
  66:  *   |         | viewport            |        |       .
  67:  *   |         | (this component)    |        |       .
  68:  *   |         |                     |        |       .
  69:  *   |         |                     |        |       .
  70:  *   |         |                     |        |       .
  71:  *   |         |                     |        |       .
  72:  *   |         +------------------------------+  ....Y3
  73:  *   |                               |                .
  74:  *   |         .                     |        .       .
  75:  *   |         .                     |        .       .
  76:  *   +---------.---------------------+    ...........Y4
  77:  *   .         .                     .        .
  78:  *   .         .                     .        .
  79:  *   .         .                     .        .
  80:  *   X1.......X2.....................X3.......X4
  81:  *   \____  ___/
  82:  *        \/
  83:  *        VX = X2-X1
  84:  *</pre>
  85:  *  
  86:  * <p>A viewport is, like all swing components, located at some position in
  87:  * the swing component tree; that location is exactly the same as any other
  88:  * components: the viewport's "bounds".</p>
  89:  *
  90:  * <p>But in terms of drawing its child, the viewport thinks of itself as
  91:  * covering a particular position <em>of the view's coordinate space</em>.
  92:  * For example, the {@link #getViewPosition} method returns
  93:  * the position <code>(VX,VY)</code> shown above, which is an position in
  94:  * "view space", even though this is <em>implemented</em> by positioning
  95:  * the underlying child at position <code>(-VX,-VY)</code></p>
  96:  *
  97:  */
  98: public class JViewport extends JComponent
  99: {
 100: 
 101:   /**
 102:    * A {@link java.awt.event.ComponentListener} that listens for
 103:    * changes of the view's size. This class forbids changes of the view
 104:    * component's size that would exceed the viewport's size.
 105:    */
 106:   protected class ViewListener
 107:     extends ComponentAdapter
 108:     implements Serializable
 109:   {
 110:     private static final long serialVersionUID = -2812489404285958070L;
 111: 
 112:     /**
 113:      * Creates a new instance of ViewListener.
 114:      */
 115:     protected ViewListener()
 116:     {
 117:     }
 118: 
 119:     /**
 120:      * Receives notification when a component (in this case: the view
 121:      * component) changes it's size.
 122:      *
 123:      * @param ev the ComponentEvent describing the change
 124:      */
 125:     public void componentResized(ComponentEvent ev)
 126:     {
 127:       // According to some tests that I did with Sun's implementation
 128:       // this class is supposed to make sure that the view component
 129:       // is not resized to a larger size than the viewport.
 130:       // This is not documented anywhere. What I did is: I subclassed JViewport
 131:       // and ViewListener and 'disabled' the componentResized method by
 132:       // overriding it and not calling super.componentResized().
 133:       // When this method is disabled I can set the size on the view component
 134:       // normally, when it is enabled, it gets immediatly resized back,
 135:       // after a resize attempt that would exceed the Viewport's size.
 136:       Component comp = ev.getComponent();
 137:       Dimension newSize = comp.getSize();
 138:       Dimension viewportSize = getSize();
 139:       boolean revert = false;
 140:       if (newSize.width > viewportSize.width)
 141:         {
 142:           newSize.width = viewportSize.width;
 143:           revert = true;
 144:         }
 145:       if (newSize.height > viewportSize.height)
 146:         {
 147:           newSize.height = viewportSize.height;
 148:           revert = true;
 149:         }
 150:       if (revert == true)
 151:         comp.setSize(newSize);
 152:     }
 153:   }
 154: 
 155:   private static final long serialVersionUID = -6925142919680527970L;
 156:   
 157:   public static final int SIMPLE_SCROLL_MODE = 0;
 158:   public static final int BLIT_SCROLL_MODE = 1;
 159:   public static final int BACKINGSTORE_SCROLL_MODE = 2;
 160: 
 161:   ChangeEvent changeEvent = new ChangeEvent(this);
 162: 
 163:   int scrollMode;
 164: 
 165:   protected boolean scrollUnderway;
 166:   protected boolean isViewSizeSet;
 167: 
 168:   /** 
 169:    * The width and height of the Viewport's area in terms of view
 170:    * coordinates.  Typically this will be the same as the width and height
 171:    * of the viewport's bounds, unless the viewport transforms units of
 172:    * width and height, which it may do, for example if it magnifies or
 173:    * rotates its view.
 174:    *
 175:    * @see #toViewCoordinates
 176:    */
 177:   Dimension extentSize;
 178: 
 179:   /**
 180:    * The width and height of the view in its own coordinate space.
 181:    */
 182: 
 183:   Dimension viewSize;
 184: 
 185:   Point lastPaintPosition;
 186: 
 187:   /**
 188:    * The ViewListener instance.
 189:    */
 190:   ViewListener viewListener;
 191: 
 192:   public JViewport()
 193:   {
 194:     setOpaque(true);
 195:     setScrollMode(BLIT_SCROLL_MODE);
 196:     setLayout(createLayoutManager());
 197:     updateUI();
 198:   }
 199: 
 200:   public Dimension getExtentSize()
 201:   {
 202:     if (extentSize == null)
 203:       return toViewCoordinates(getSize());
 204:     else
 205:       return extentSize;
 206:   }
 207: 
 208:   public Dimension toViewCoordinates(Dimension size)
 209:   {
 210:     return size;
 211:   }
 212: 
 213:   public Point toViewCoordinates(Point p)
 214:   {
 215:     Point pos = getViewPosition();
 216:     return new Point(p.x + pos.x,
 217:                      p.y + pos.y);
 218:   }
 219: 
 220:   public void setExtentSize(Dimension newSize)
 221:   {
 222:     extentSize = newSize;
 223:     fireStateChanged();
 224:   }
 225: 
 226:   /**
 227:    * Returns the viewSize when set, or the preferred size of the set
 228:    * Component view.  If no viewSize and no Component view is set an
 229:    * empty Dimension is returned.
 230:    */
 231:   public Dimension getViewSize()
 232:   {
 233:     if (isViewSizeSet)
 234:       return viewSize;
 235:     else
 236:       {
 237:     Component view = getView();
 238:     if (view != null)
 239:       return view.getPreferredSize();
 240:     else
 241:       return new Dimension();
 242:       }
 243:   }
 244: 
 245: 
 246:   public void setViewSize(Dimension newSize)
 247:   {
 248:     viewSize = newSize;
 249:     Component view = getView();
 250:     if (view != null)
 251:       view.setSize(viewSize);
 252:     isViewSizeSet = true;
 253:     fireStateChanged();
 254:   }
 255: 
 256:   /**
 257:    * Get the viewport's position in view space. Despite confusing name,
 258:    * this really does return the viewport's (0,0) position in view space,
 259:    * not the view's position.
 260:    */
 261: 
 262:   public Point getViewPosition()
 263:   {
 264:     Component view = getView();
 265:     if (view == null)
 266:       return new Point(0,0);
 267:     else
 268:       {
 269:         Point p = view.getLocation();
 270:         p.x = -p.x;
 271:         p.y = -p.y;
 272:         return p;
 273:       }
 274:   }
 275: 
 276:   public void setViewPosition(Point p)
 277:   {
 278:     Component view = getView();
 279:     if (view != null)
 280:       {
 281:         Point q = new Point(-p.x, -p.y);
 282:         view.setLocation(q);
 283:         fireStateChanged();
 284:       }
 285:   }
 286: 
 287:   public Rectangle getViewRect()
 288:   {
 289:     return new Rectangle(getViewPosition(), 
 290:                          getExtentSize());
 291:   }
 292: 
 293:   /**
 294:    * @deprecated 1.4
 295:    */
 296:   public boolean isBackingStoreEnabled()
 297:   {
 298:     return scrollMode == BACKINGSTORE_SCROLL_MODE;
 299:   }
 300: 
 301:   /**
 302:    * @deprecated 1.4
 303:    */
 304:   public void setBackingStoreEnabled(boolean b)
 305:   {
 306:     if (b && scrollMode != BACKINGSTORE_SCROLL_MODE)
 307:       {
 308:         scrollMode = BACKINGSTORE_SCROLL_MODE;
 309:         fireStateChanged();
 310:       }
 311:   }
 312: 
 313:   public void setScrollMode(int mode)
 314:   {
 315:     scrollMode = mode;
 316:     fireStateChanged();
 317:   }
 318: 
 319:   public int getScrollMode()
 320:   {
 321:     return scrollMode;
 322:   }
 323: 
 324:   public Component getView()
 325:   {
 326:     if (getComponentCount() == 0)
 327:       return null;
 328:   
 329:     return getComponents()[0];
 330:   }
 331: 
 332:   public void setView(Component v)
 333:   {
 334:     while (getComponentCount() > 0)
 335:       {
 336:         if (viewListener != null)
 337:           getView().removeComponentListener(viewListener);
 338:         remove(0);
 339:       }
 340: 
 341:     if (v != null)
 342:       {
 343:         if (viewListener == null)
 344:           viewListener = createViewListener();
 345:         v.addComponentListener(viewListener);
 346:         add(v);
 347:         fireStateChanged();
 348:       }
 349:   }
 350: 
 351:   public void revalidate()
 352:   {
 353:     fireStateChanged();
 354:     super.revalidate();
 355:   }
 356: 
 357:   public void reshape(int x, int y, int w, int h)
 358:   {
 359:     boolean changed = 
 360:       (x != getX()) 
 361:       || (y != getY()) 
 362:       || (w != getWidth())
 363:       || (h != getHeight());
 364:     super.reshape(x, y, w, h);
 365:     if (changed)
 366:       fireStateChanged();
 367:   }
 368:     
 369:   protected void addImpl(Component comp, Object constraints, int index)
 370:   {
 371:     if (getComponentCount() > 0)
 372:       remove(getComponents()[0]);
 373:     
 374:     super.addImpl(comp, constraints, index);
 375:   }
 376: 
 377:   public final Insets getInsets() 
 378:   {
 379:     return new Insets(0,0,0,0);
 380:   }
 381: 
 382:   public final Insets getInsets(Insets insets)
 383:   {
 384:     if (insets == null)
 385:       return getInsets();
 386:     insets.top = 0;
 387:     insets.bottom = 0;
 388:     insets.left = 0;
 389:     insets.right = 0;
 390:     return insets;
 391:   }
 392:     
 393:   public boolean isOptimizedDrawingEnabled()
 394:   {
 395:     return false;
 396:   }
 397: 
 398:   public void paint(Graphics g)
 399:   {
 400:     paintComponent(g);
 401:   }
 402: 
 403:   public void addChangeListener(ChangeListener listener)
 404:   {
 405:     listenerList.add(ChangeListener.class, listener);
 406:   }
 407: 
 408:   public void removeChangeListener(ChangeListener listener)
 409:   {
 410:     listenerList.remove(ChangeListener.class, listener);
 411:   }
 412: 
 413:   public ChangeListener[] getChangeListeners() 
 414:   {
 415:     return (ChangeListener[]) getListeners(ChangeListener.class);
 416:   }
 417: 
 418:   protected void fireStateChanged()
 419:   {
 420:     ChangeListener[] listeners = getChangeListeners();
 421:     for (int i = 0; i < listeners.length; ++i)
 422:       listeners[i].stateChanged(changeEvent);
 423:   }
 424: 
 425:   /**
 426:    * This method returns the String ID of the UI class of  Separator.
 427:    *
 428:    * @return The UI class' String ID.
 429:    */
 430:   public String getUIClassID()
 431:   {
 432:     return "ViewportUI";
 433:   }
 434: 
 435:   /**
 436:    * This method resets the UI used to the Look and Feel defaults..
 437:    */
 438:   public void updateUI()
 439:   {
 440:     setUI((ViewportUI) UIManager.getUI(this));
 441:   }            
 442: 
 443:   /**
 444:    * This method returns the viewport's UI delegate.
 445:    *
 446:    * @return The viewport's UI delegate.
 447:    */
 448:   public ViewportUI getUI()
 449:   {
 450:     return (ViewportUI) ui;
 451:   }
 452: 
 453:   /**
 454:    * This method sets the viewport's UI delegate.
 455:    *
 456:    * @param ui The viewport's UI delegate.
 457:    */
 458:   public void setUI(ViewportUI ui)
 459:   {
 460:     super.setUI(ui);
 461:   }
 462: 
 463:   public final void setBorder(Border border)
 464:   {
 465:     if (border != null)
 466:       throw new IllegalArgumentException();
 467:   }
 468: 
 469:   /**
 470:    * Creates a {@link ViewListener} that is supposed to listen for
 471:    * size changes on the view component.
 472:    *
 473:    * @return a ViewListener instance
 474:    */
 475:   protected ViewListener createViewListener()
 476:   {
 477:     return new ViewListener();
 478:   }
 479: 
 480:   /**
 481:    * Creates the LayoutManager that is used for this viewport. Override
 482:    * this method if you want to use a custom LayoutManager.
 483:    *
 484:    * @return a LayoutManager to use for this viewport
 485:    */
 486:   protected LayoutManager createLayoutManager()
 487:   {
 488:     return new ViewportLayout();
 489:   }
 490: 
 491:   /**
 492:    * Scrolls the view so that contentRect becomes visible.
 493:    *
 494:    * @param contentRect the rectangle to make visible within the view
 495:    */
 496:   public void scrollRectToVisible(Rectangle contentRect)
 497:   {
 498:     Point pos = getViewPosition();
 499:     Rectangle viewBounds = getView().getBounds();
 500:     Rectangle portBounds = getBounds();
 501:     
 502:     // FIXME: should validate the view if it is not valid, however
 503:     // this may cause excessive validation when the containment
 504:     // hierarchy is being created.
 505:     
 506:     // if contentRect is larger than the portBounds, center the view
 507:     if (contentRect.height > portBounds.height || 
 508:         contentRect.width > portBounds.width)
 509:       {
 510:         setViewPosition(new Point(contentRect.x, contentRect.y));
 511:         return;
 512:       }
 513:     
 514:     // Y-DIRECTION
 515:     if (contentRect.y < -viewBounds.y)
 516:       setViewPosition(new Point(pos.x, contentRect.y));
 517:     else if (contentRect.y + contentRect.height > 
 518:              -viewBounds.y + portBounds.height)
 519:       setViewPosition (new Point(pos.x, contentRect.y - 
 520:                                  (portBounds.height - contentRect.height)));
 521:     
 522:     // X-DIRECTION
 523:     pos = getViewPosition();
 524:     if (contentRect.x < -viewBounds.x)
 525:       setViewPosition(new Point(contentRect.x, pos.y));
 526:     else if (contentRect.x + contentRect.width > 
 527:              -viewBounds.x + portBounds.width)
 528:       setViewPosition (new Point(contentRect.x - 
 529:                                  (portBounds.width - contentRect.width), pos.y));
 530:   }
 531: }