KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > javax > swing > JViewport


1 /*
2  * @(#)JViewport.java 1.114 04/05/18
3  *
4  * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
5  * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
6  */

7
8 package javax.swing;
9
10 import java.awt.*;
11 import java.awt.event.*;
12 import java.awt.image.VolatileImage JavaDoc;
13 import java.awt.peer.ComponentPeer;
14 import java.applet.Applet JavaDoc;
15 import javax.swing.plaf.ViewportUI JavaDoc;
16
17 import javax.swing.event.*;
18 import javax.swing.border.*;
19 import javax.accessibility.*;
20
21
22 import java.io.Serializable JavaDoc;
23
24
25 /**
26  * The "viewport" or "porthole" through which you see the underlying
27  * information. When you scroll, what moves is the viewport. It is like
28  * peering through a camera's viewfinder. Moving the viewfinder upwards
29  * brings new things into view at the top of the picture and loses
30  * things that were at the bottom.
31  * <p>
32  * By default, <code>JViewport</code> is opaque. To change this, use the
33  * <code>setOpaque</code> method.
34  * <p>
35  * <b>NOTE:</b>We have implemented a faster scrolling algorithm that
36  * does not require a buffer to draw in. The algorithm works as follows:
37  * <ol><li>The view and parent view and checked to see if they are
38  * <code>JComponents</code>,
39  * if they aren't, stop and repaint the whole viewport.
40  * <li>If the viewport is obscured by an ancestor, stop and repaint the whole
41  * viewport.
42  * <li>Compute the region that will become visible, if it is as big as
43  * the viewport, stop and repaint the whole view region.
44  * <li>Obtain the ancestor <code>Window</code>'s graphics and
45  * do a <code>copyArea</code> on the scrolled region.
46  * <li>Message the view to repaint the newly visible region.
47  * <li>The next time paint is invoked on the viewport, if the clip region
48  * is smaller than the viewport size a timer is kicked off to repaint the
49  * whole region.
50  * </ol>
51  * In general this approach is much faster. Compared to the backing store
52  * approach this avoids the overhead of maintaining an offscreen buffer and
53  * having to do two <code>copyArea</code>s.
54  * Compared to the non backing store case this
55  * approach will greatly reduce the painted region.
56  * <p>
57  * This approach can cause slower times than the backing store approach
58  * when the viewport is obscured by another window, or partially offscreen.
59  * When another window
60  * obscures the viewport the copyArea will copy garbage and a
61  * paint event will be generated by the system to inform us we need to
62  * paint the newly exposed region. The only way to handle this is to
63  * repaint the whole viewport, which can cause slower performance than the
64  * backing store case. In most applications very rarely will the user be
65  * scrolling while the viewport is obscured by another window or offscreen,
66  * so this optimization is usually worth the performance hit when obscured.
67  * <p>
68  * <strong>Warning:</strong>
69  * Serialized objects of this class will not be compatible with
70  * future Swing releases. The current serialization support is
71  * appropriate for short term storage or RMI between applications running
72  * the same version of Swing. As of 1.4, support for long term storage
73  * of all JavaBeans<sup><font size="-2">TM</font></sup>
74  * has been added to the <code>java.beans</code> package.
75  * Please see {@link java.beans.XMLEncoder}.
76  *
77  * @version 1.114 05/18/04
78  * @author Hans Muller
79  * @author Philip Milne
80  * @see JScrollPane
81  */

82 public class JViewport extends JComponent JavaDoc implements Accessible
83 {
84     /**
85      * @see #getUIClassID
86      * @see #readObject
87      */

88     private static final String JavaDoc uiClassID = "ViewportUI";
89
90     /** Property used to indicate window blitting should not be done.
91      */

92     static final Object JavaDoc EnableWindowBlit = "EnableWindowBlit";
93
94     /**
95      * True when the viewport dimensions have been determined.
96      * The default is false.
97      */

98     protected boolean isViewSizeSet = false;
99
100     /**
101      * The last <code>viewPosition</code> that we've painted, so we know how
102      * much of the backing store image is valid.
103      */

104     protected Point lastPaintPosition = null;
105
106     /**
107      * True when this viewport is maintaining an offscreen image of its
108      * contents, so that some scrolling can take place using fast "bit-blit"
109      * operations instead of by accessing the view object to construct the
110      * display. The default is <code>false</code>.
111      *
112      * @deprecated As of Java 2 platform v1.3
113      * @see #setScrollMode
114      */

115     @Deprecated JavaDoc
116     protected boolean backingStore = false;
117
118     /** The view image used for a backing store. */
119     transient protected Image JavaDoc backingStoreImage = null;
120
121     /**
122      * The <code>scrollUnderway</code> flag is used for components like
123      * <code>JList</code>. When the downarrow key is pressed on a
124      * <code>JList</code> and the selected
125      * cell is the last in the list, the <code>scrollpane</code> autoscrolls.
126      * Here, the old selected cell needs repainting and so we need
127      * a flag to make the viewport do the optimized painting
128      * only when there is an explicit call to
129      * <code>setViewPosition(Point)</code>.
130      * When <code>setBounds</code> is called through other routes,
131      * the flag is off and the view repaints normally. Another approach
132      * would be to remove this from the <code>JViewport</code>
133      * class and have the <code>JList</code> manage this case by using
134      * <code>setBackingStoreEnabled</code>. The default is
135      * <code>false</code>.
136      */

137     protected boolean scrollUnderway = false;
138
139     /*
140      * Listener that is notified each time the view changes size.
141      */

142     private ComponentListener viewListener = null;
143
144     /* Only one <code>ChangeEvent</code> is needed per
145      * <code>JViewport</code> instance since the
146      * event's only (read-only) state is the source property. The source
147      * of events generated here is always "this".
148      */

149     private transient ChangeEvent changeEvent = null;
150
151     /**
152       * Use <code>graphics.copyArea</code> to implement scrolling.
153       * This is the fastest for most applications.
154       *
155       * @see #setScrollMode
156       * @since 1.3
157       */

158     public static final int BLIT_SCROLL_MODE = 1;
159
160     /**
161       * Draws viewport contents into an offscreen image.
162       * This was previously the default mode for <code>JTable</code>.
163       * This mode may offer advantages over "blit mode"
164       * in some cases, but it requires a large chunk of extra RAM.
165       *
166       * @see #setScrollMode
167       * @since 1.3
168       */

169     public static final int BACKINGSTORE_SCROLL_MODE = 2;
170
171     /**
172       * This mode uses the very simple method of redrawing the entire
173       * contents of the scrollpane each time it is scrolled.
174       * This was the default behavior in Swing 1.0 and Swing 1.1.
175       * Either of the other two options will provide better performance
176       * in most cases.
177       *
178       * @see #setScrollMode
179       * @since 1.3
180       */

181     public static final int SIMPLE_SCROLL_MODE = 0;
182
183     /**
184       * @see #setScrollMode
185       * @since 1.3
186       */

187     private int scrollMode = BLIT_SCROLL_MODE;
188
189     //
190
// Window blitting:
191
//
192
// As mentioned in the javadoc when using windowBlit a paint event
193
// will be generated by the system if copyArea copies a non-visible
194
// portion of the view (in other words, it copies garbage). We are
195
// not guaranteed to receive the paint event before other mouse events,
196
// so we can not be sure we haven't already copied garbage a bunch of
197
// times to different parts of the view. For that reason when a blit
198
// happens and the Component is obscured (the check for obscurity
199
// is not supported on all platforms and is checked via ComponentPeer
200
// methods) the ivar repaintAll is set to true. When paint is received
201
// if repaintAll is true (we previously did a blit) it is set to
202
// false, and if the clip region is smaller than the viewport
203
// waitingForRepaint is set to true and a timer is started. When
204
// the timer fires if waitingForRepaint is true, repaint is invoked.
205
// In the mean time, if the view is asked to scroll and waitingForRepaint
206
// is true, a blit will not happen, instead the non-backing store case
207
// of scrolling will happen, which will reset waitingForRepaint.
208
// waitingForRepaint is set to false in paint when the clip rect is
209
// bigger (or equal) to the size of the viewport.
210
// A Timer is used instead of just a repaint as it appeared to offer
211
// better performance.
212

213
214     /**
215      * This is set to true in <code>setViewPosition</code>
216      * if doing a window blit and the viewport is obscured.
217      */

218     private transient boolean repaintAll;
219
220     /**
221      * This is set to true in paint, if <code>repaintAll</code>
222      * is true and the clip rectangle does not match the bounds.
223      * If true, and scrolling happens the
224      * repaint manager is not cleared which then allows for the repaint
225      * previously invoked to succeed.
226      */

227     private transient boolean waitingForRepaint;
228
229     /**
230      * Instead of directly invoking repaint, a <code>Timer</code>
231      * is started and when it fires, repaint is invoked.
232      */

233     private transient Timer JavaDoc repaintTimer;
234
235     /**
236      * Whether or not a valid view has been installed.
237      */

238     private boolean hasHadValidView;
239
240     /** Creates a <code>JViewport</code>. */
241     public JViewport() {
242         super();
243         setLayout(createLayoutManager());
244     setOpaque(true);
245         updateUI();
246     }
247
248
249
250     /**
251      * Returns the L&F object that renders this component.
252      *
253      * @return a <code>ViewportUI</code> object
254      */

255     public ViewportUI JavaDoc getUI() {
256         return (ViewportUI JavaDoc)ui;
257     }
258
259
260     /**
261      * Sets the L&F object that renders this component.
262      *
263      * @param ui the <code>ViewportUI</code> L&F object
264      * @see UIDefaults#getUI
265      * @beaninfo
266      * bound: true
267      * hidden: true
268      * attribute: visualUpdate true
269      * description: The UI object that implements the Component's LookAndFeel.
270      */

271     public void setUI(ViewportUI JavaDoc ui) {
272         super.setUI(ui);
273     }
274
275
276     /**
277      * Resets the UI property to a value from the current look and feel.
278      *
279      * @see JComponent#updateUI
280      */

281     public void updateUI() {
282         setUI((ViewportUI JavaDoc)UIManager.getUI(this));
283     }
284
285
286     /**
287      * Returns a string that specifies the name of the L&F class
288      * that renders this component.
289      *
290      * @return the string "ViewportUI"
291      *
292      * @see JComponent#getUIClassID
293      * @see UIDefaults#getUI
294      */

295     public String JavaDoc getUIClassID() {
296         return uiClassID;
297     }
298
299
300     /**
301      * Sets the <code>JViewport</code>'s one lightweight child,
302      * which can be <code>null</code>.
303      * (Since there is only one child which occupies the entire viewport,
304      * the <code>constraints</code> and <code>index</code>
305      * arguments are ignored.)
306      *
307      * @param child the lightweight <code>child</code> of the viewport
308      * @param constraints the <code>constraints</code> to be respected
309      * @param index the index
310      * @see #setView
311      */

312     protected void addImpl(Component child, Object JavaDoc constraints, int index) {
313       setView(child);
314     }
315
316
317     /**
318      * Removes the <code>Viewport</code>s one lightweight child.
319      *
320      * @see #setView
321      */

322     public void remove(Component child) {
323         child.removeComponentListener(viewListener);
324         super.remove(child);
325     }
326
327
328     /**
329      * Scrolls the view so that <code>Rectangle</code>
330      * within the view becomes visible.
331      * <p>
332      * This attempts to validate the view before scrolling if the
333      * view is currently not valid - <code>isValid</code> returns false.
334      * To avoid excessive validation when the containment hierarchy is
335      * being created this will not validate if one of the ancestors does not
336      * have a peer, or there is no validate root ancestor, or one of the
337      * ancestors is not a <code>Window</code> or <code>Applet</code>.
338      * <p>
339      * Note that this method will not scroll outside of the
340      * valid viewport; for example, if <code>contentRect</code> is larger
341      * than the viewport, scrolling will be confined to the viewport's
342      * bounds.
343      *
344      * @param contentRect the <code>Rectangle</code> to display
345      * @see JComponent#isValidateRoot
346      * @see java.awt.Component#isValid
347      * @see java.awt.Component#getPeer
348      */

349     public void scrollRectToVisible(Rectangle contentRect) {
350         Component view = getView();
351
352         if (view == null) {
353             return;
354         } else {
355         if (!view.isValid()) {
356         // If the view is not valid, validate. scrollRectToVisible
357
// may fail if the view is not valid first, contentRect
358
// could be bigger than invalid size.
359
validateView();
360         }
361             int dx = 0, dy = 0;
362
363             dx = positionAdjustment(getWidth(), contentRect.width, contentRect.x);
364             dy = positionAdjustment(getHeight(), contentRect.height, contentRect.y);
365
366             if (dx != 0 || dy != 0) {
367                 Point viewPosition = getViewPosition();
368         Dimension viewSize = view.getSize();
369         int startX = viewPosition.x;
370         int startY = viewPosition.y;
371         Dimension extent = getExtentSize();
372
373         viewPosition.x -= dx;
374         viewPosition.y -= dy;
375                 // Only constrain the location if the view is valid. If the
376
// the view isn't valid, it typically indicates the view
377
// isn't visible yet and most likely has a bogus size as will
378
// we, and therefore we shouldn't constrain the scrolling
379
if (view.isValid()) {
380                     if (getParent().getComponentOrientation().isLeftToRight()) {
381                         if (viewPosition.x + extent.width > viewSize.width) {
382                             viewPosition.x = Math.max(0, viewSize.width - extent.width);
383                         } else if (viewPosition.x < 0) {
384                             viewPosition.x = 0;
385                         }
386                     } else {
387                         if (extent.width > viewSize.width) {
388                             viewPosition.x = viewSize.width - extent.width;
389                         } else {
390                             viewPosition.x = Math.max(0, Math.min(viewSize.width - extent.width, viewPosition.x));
391             }
392                     }
393                     if (viewPosition.y + extent.height > viewSize.height) {
394                         viewPosition.y = Math.max(0, viewSize.height -
395                                                   extent.height);
396                     }
397                     else if (viewPosition.y < 0) {
398                         viewPosition.y = 0;
399                     }
400                 }
401         if (viewPosition.x != startX || viewPosition.y != startY) {
402             setViewPosition(viewPosition);
403             // NOTE: How JViewport currently works with the
404
// backing store is not foolproof. The sequence of
405
// events when setViewPosition
406
// (scrollRectToVisible) is called is to reset the
407
// views bounds, which causes a repaint on the
408
// visible region and sets an ivar indicating
409
// scrolling (scrollUnderway). When
410
// JViewport.paint is invoked if scrollUnderway is
411
// true, the backing store is blitted. This fails
412
// if between the time setViewPosition is invoked
413
// and paint is received another repaint is queued
414
// indicating part of the view is invalid. There
415
// is no way for JViewport to notice another
416
// repaint has occured and it ends up blitting
417
// what is now a dirty region and the repaint is
418
// never delivered.
419
// It just so happens JTable encounters this
420
// behavior by way of scrollRectToVisible, for
421
// this reason scrollUnderway is set to false
422
// here, which effectively disables the backing
423
// store.
424
scrollUnderway = false;
425         }
426             }
427         }
428     }
429
430     /**
431      * Ascends the <code>Viewport</code>'s parents stopping when
432      * a component is found that returns
433      * <code>true</code> to <code>isValidateRoot</code>.
434      * If all the <code>Component</code>'s parents are visible,
435      * <code>validate</code> will then be invoked on it. The
436      * <code>RepaintManager</code> is then invoked with
437      * <code>removeInvalidComponent</code>. This
438      * is the synchronous version of a <code>revalidate</code>.
439      */

440     private void validateView() {
441         Component validateRoot = null;
442
443     /* Find the first JComponent ancestor of this component whose
444      * isValidateRoot() method returns true.
445      */

446         for(Component c = this; c != null; c = c.getParent()) {
447         if ((c instanceof CellRendererPane JavaDoc) || (c.getPeer() == null)) {
448         return;
449         }
450         if ((c instanceof JComponent JavaDoc) &&
451         (((JComponent JavaDoc)c).isValidateRoot())) {
452         validateRoot = c;
453         break;
454         }
455     }
456
457     // If no validateRoot, nothing to validate from.
458
if (validateRoot == null) {
459         return;
460     }
461
462     // Make sure all ancestors are visible.
463
Component root = null;
464     
465     for(Component c = validateRoot; c != null; c = c.getParent()) {
466             // We don't check isVisible here, otherwise if the component
467
// is contained in something like a JTabbedPane when the
468
// component is made visible again it won't have scrolled
469
// to the correct location.
470
if (c.getPeer() == null) {
471         return;
472         }
473         if ((c instanceof Window) || (c instanceof Applet JavaDoc)) {
474         root = c;
475         break;
476         }
477     }
478
479     // Make sure there is a Window ancestor.
480
if (root == null) {
481         return;
482     }
483
484     // Validate the root.
485
validateRoot.validate();
486
487     // And let the RepaintManager it does not have to validate from
488
// validateRoot anymore.
489
RepaintManager JavaDoc rm = RepaintManager.currentManager(this);
490
491     if (rm != null) {
492         rm.removeInvalidComponent((JComponent JavaDoc)validateRoot);
493     }
494     }
495
496      /* Used by the scrollRectToVisible method to determine the
497       * proper direction and amount to move by. The integer variables are named
498       * width, but this method is applicable to height also. The code assumes that
499       * parentWidth/childWidth are positive and childAt can be negative.
500       */

501     private int positionAdjustment(int parentWidth, int childWidth, int childAt) {
502
503         // +-----+
504
// | --- | No Change
505
// +-----+
506
if (childAt >= 0 && childWidth + childAt <= parentWidth) {
507             return 0;
508         }
509
510         // +-----+
511
// --------- No Change
512
// +-----+
513
if (childAt <= 0 && childWidth + childAt >= parentWidth) {
514             return 0;
515         }
516
517         // +-----+ +-----+
518
// | ---- -> | ----|
519
// +-----+ +-----+
520
if (childAt > 0 && childWidth <= parentWidth) {
521             return -childAt + parentWidth - childWidth;
522         }
523
524         // +-----+ +-----+
525
// | -------- -> |--------
526
// +-----+ +-----+
527
if (childAt >= 0 && childWidth >= parentWidth) {
528             return -childAt;
529         }
530
531         // +-----+ +-----+
532
// ---- | -> |---- |
533
// +-----+ +-----+
534
if (childAt <= 0 && childWidth <= parentWidth) {
535             return -childAt;
536         }
537
538         // +-----+ +-----+
539
//-------- | -> --------|
540
// +-----+ +-----+
541
if (childAt < 0 && childWidth >= parentWidth) {
542             return -childAt + parentWidth - childWidth;
543         }
544
545         return 0;
546     }
547
548
549     /**
550      * The viewport "scrolls" its child (called the "view") by the
551      * normal parent/child clipping (typically the view is moved in
552      * the opposite direction of the scroll). A non-<code>null</code> border,
553      * or non-zero insets, isn't supported, to prevent the geometry
554      * of this component from becoming complex enough to inhibit
555      * subclassing. To create a <code>JViewport</code> with a border,
556      * add it to a <code>JPanel</code> that has a border.
557      * <p>Note: If <code>border</code> is non-<code>null</code>, this
558      * method will throw an exception as borders are not supported on
559      * a <code>JViewPort</code>.
560      *
561      * @param border the <code>Border</code> to set
562      * @exception IllegalArgumentException this method is not implemented
563      */

564     public final void setBorder(Border border) {
565         if (border != null) {
566             throw new IllegalArgumentException JavaDoc("JViewport.setBorder() not supported");
567         }
568     }
569
570
571     /**
572      * Returns the insets (border) dimensions as (0,0,0,0), since borders
573      * are not supported on a <code>JViewport</code>.
574      *
575      * @return a <code>Rectange</code> of zero dimension and zero origin
576      * @see #setBorder
577      */

578     public final Insets getInsets() {
579         return new Insets(0, 0, 0, 0);
580     }
581
582     /**
583      * Returns an <code>Insets</code> object containing this
584      * <code>JViewport</code>s inset values. The passed-in
585      * <code>Insets</code> object will be reinitialized, and
586      * all existing values within this object are overwritten.
587      *
588      * @param insets the <code>Insets</code> object which can be reused
589      * @return this viewports inset values
590      * @see #getInsets
591      * @beaninfo
592      * expert: true
593      */

594     public final Insets getInsets(Insets insets) {
595         insets.left = insets.top = insets.right = insets.bottom = 0;
596         return insets;
597     }
598
599
600     private Graphics getBackingStoreGraphics(Graphics g) {
601         Graphics bsg = backingStoreImage.getGraphics();
602         bsg.setColor(g.getColor());
603         bsg.setFont(g.getFont());
604         bsg.setClip(g.getClipBounds());
605         return bsg;
606     }
607
608
609     private void paintViaBackingStore(Graphics g) {
610         Graphics bsg = getBackingStoreGraphics(g);
611     try {
612         super.paint(bsg);
613         g.drawImage(backingStoreImage, 0, 0, this);
614     } finally {
615         bsg.dispose();
616     }
617     }
618
619     private void paintViaBackingStore(Graphics g, Rectangle oClip) {
620         Graphics bsg = getBackingStoreGraphics(g);
621     try {
622         super.paint(bsg);
623         g.setClip(oClip);
624         g.drawImage(backingStoreImage, 0, 0, this);
625     } finally {
626         bsg.dispose();
627     }
628     }
629
630     /**
631      * The <code>JViewport</code> overrides the default implementation of
632      * this method (in <code>JComponent</code>) to return false.
633      * This ensures
634      * that the drawing machinery will call the <code>Viewport</code>'s
635      * <code>paint</code>
636      * implementation rather than messaging the <code>JViewport</code>'s
637      * children directly.
638      *
639      * @return false
640      */

641     public boolean isOptimizedDrawingEnabled() {
642         return false;
643     }
644
645     /**
646      * Returns true if scroll mode is a BACKINGSTORE_SCROLL_MODE to cause
647      * painting to originate from <code>JViewport</code>, or one of its
648      * ancestors. Otherwise returns false.
649      *
650      * @return true if if scroll mode is a BACKINGSTORE_SCROLL_MODE.
651      * @see JComponent#isPaintingOrigin()
652      */

653     boolean isPaintingOrigin() {
654     if (scrollMode == BACKINGSTORE_SCROLL_MODE) {
655         return true;
656     }
657         return false;
658     }
659
660
661     /**
662      * Only used by the paint method below.
663      */

664     private Point getViewLocation() {
665         Component view = getView();
666         if (view != null) {
667             return view.getLocation();
668         }
669         else {
670             return new Point(0,0);
671         }
672     }
673
674     /**
675      * Depending on whether the <code>backingStore</code> is enabled,
676      * either paint the image through the backing store or paint
677      * just the recently exposed part, using the backing store
678      * to "blit" the remainder.
679      * <blockquote>
680      * The term "blit" is the pronounced version of the PDP-10
681      * BLT (BLock Transfer) instruction, which copied a block of
682      * bits. (In case you were curious.)
683      * </blockquote>
684      *
685      * @param g the <code>Graphics</code> context within which to paint
686      */

687     public void paint(Graphics g)
688     {
689         int width = getWidth();
690         int height = getHeight();
691
692         if ((width <= 0) || (height <= 0)) {
693             return;
694         }
695
696     if (repaintAll) {
697         repaintAll = false;
698         Rectangle clipB = g.getClipBounds();
699         if (clipB.width < getWidth() ||
700         clipB.height < getHeight()) {
701         waitingForRepaint = true;
702         if (repaintTimer == null) {
703             repaintTimer = createRepaintTimer();
704         }
705         repaintTimer.stop();
706         repaintTimer.start();
707         // We really don't need to paint, a future repaint will
708
// take care of it, but if we don't we get an ugly flicker.
709
}
710         else {
711         if (repaintTimer != null) {
712             repaintTimer.stop();
713         }
714         waitingForRepaint = false;
715         }
716     }
717     else if (waitingForRepaint) {
718         // Need a complete repaint before resetting waitingForRepaint
719
Rectangle clipB = g.getClipBounds();
720         if (clipB.width >= getWidth() &&
721         clipB.height >= getHeight()) {
722         waitingForRepaint = false;
723         repaintTimer.stop();
724         }
725     }
726
727         if (!backingStore || isBlitting() || getView() == null) {
728             super.paint(g);
729             lastPaintPosition = getViewLocation();
730             return;
731         }
732
733         // If the view is smaller than the viewport and we are not opaque
734
// (that is, we won't paint our background), we should set the
735
// clip. Otherwise, as the bounds of the view vary, we will
736
// blit garbage into the exposed areas.
737
Rectangle viewBounds = getView().getBounds();
738     if (!isOpaque()) {
739         g.clipRect(0, 0, viewBounds.width, viewBounds.height);
740     }
741
742         if (backingStoreImage == null) {
743             // Backing store is enabled but this is the first call to paint.
744
// Create the backing store, paint it and then copy to g.
745
// The backing store image will be created with the size of
746
// the viewport. We must make sure the clip region is the
747
// same size, otherwise when scrolling the backing image
748
// the region outside of the clipped region will not be painted,
749
// and result in empty areas.
750
backingStoreImage = createImage(width, height);
751         Rectangle clip = g.getClipBounds();
752         if (clip.width != width || clip.height != height) {
753         if (!isOpaque()) {
754             g.setClip(0, 0, Math.min(viewBounds.width, width),
755                   Math.min(viewBounds.height, height));
756         }
757         else {
758             g.setClip(0, 0, width, height);
759         }
760         paintViaBackingStore(g, clip);
761         }
762         else {
763         paintViaBackingStore(g);
764         }
765         }
766         else {
767             if (!scrollUnderway || lastPaintPosition.equals(getViewLocation())) {
768                 // No scrolling happened: repaint required area via backing store.
769
paintViaBackingStore(g);
770             } else {
771                 // The image was scrolled. Manipulate the backing store and flush it to g.
772
Point blitFrom = new Point();
773                 Point blitTo = new Point();
774                 Dimension blitSize = new Dimension();
775                 Rectangle blitPaint = new Rectangle();
776
777                 Point newLocation = getViewLocation();
778                 int dx = newLocation.x - lastPaintPosition.x;
779                 int dy = newLocation.y - lastPaintPosition.y;
780                 boolean canBlit = computeBlit(dx, dy, blitFrom, blitTo, blitSize, blitPaint);
781                 if (!canBlit) {
782                     // The image was either moved diagonally or
783
// moved by more than the image size: paint normally.
784
paintViaBackingStore(g);
785                 } else {
786                     int bdx = blitTo.x - blitFrom.x;
787                     int bdy = blitTo.y - blitFrom.y;
788
789                     // Move the relevant part of the backing store.
790
Rectangle clip = g.getClipBounds();
791             // We don't want to inherit the clip region when copying
792
// bits, if it is inherited it will result in not moving
793
// all of the image resulting in garbage appearing on
794
// the screen.
795
g.setClip(0, 0, width, height);
796                     Graphics bsg = getBackingStoreGraphics(g);
797             try {
798                 bsg.copyArea(blitFrom.x, blitFrom.y, blitSize.width, blitSize.height, bdx, bdy);
799
800             g.setClip(clip.x, clip.y, clip.width, clip.height);
801             // Paint the rest of the view; the part that has just been exposed.
802
Rectangle r = viewBounds.intersection(blitPaint);
803             bsg.setClip(r);
804             super.paint(bsg);
805
806             // Copy whole of the backing store to g.
807
g.drawImage(backingStoreImage, 0, 0, this);
808             } finally {
809                 bsg.dispose();
810             }
811                 }
812             }
813         }
814         lastPaintPosition = getViewLocation();
815         scrollUnderway = false;
816     }
817
818
819     /**
820      * Sets the bounds of this viewport. If the viewport's width
821      * or height has changed, fire a <code>StateChanged</code> event.
822      *
823      * @param x left edge of the origin
824      * @param y top edge of the origin
825      * @param w width in pixels
826      * @param h height in pixels
827      *
828      * @see JComponent#reshape(int, int, int, int)
829      */

830     public void reshape(int x, int y, int w, int h) {
831     boolean sizeChanged = (getWidth() != w) || (getHeight() != h);
832         if (sizeChanged) {
833             backingStoreImage = null;
834         }
835         super.reshape(x, y, w, h);
836     if (sizeChanged) {
837         fireStateChanged();
838     }
839     }
840
841
842     /**
843       * Used to control the method of scrolling the viewport contents.
844       * You may want to change this mode to get maximum performance for your
845       * use case.
846       *
847       * @param mode one of the following values:
848       * <ul>
849       * <li> JViewport.BLIT_SCROLL_MODE
850       * <li> JViewport.BACKINGSTORE_SCROLL_MODE
851       * <li> JViewport.SIMPLE_SCROLL_MODE
852       * </ul>
853       *
854       * @see #BLIT_SCROLL_MODE
855       * @see #BACKINGSTORE_SCROLL_MODE
856       * @see #SIMPLE_SCROLL_MODE
857       *
858       * @beaninfo
859       * bound: false
860       * description: Method of moving contents for incremental scrolls.
861       * enum: BLIT_SCROLL_MODE JViewport.BLIT_SCROLL_MODE
862       * BACKINGSTORE_SCROLL_MODE JViewport.BACKINGSTORE_SCROLL_MODE
863       * SIMPLE_SCROLL_MODE JViewport.SIMPLE_SCROLL_MODE
864       *
865       * @since 1.3
866       */

867     public void setScrollMode(int mode) {
868         scrollMode = mode;
869     if (mode == BACKINGSTORE_SCROLL_MODE) {
870         backingStore = true;
871     } else {
872         backingStore = false;
873     }
874     }
875
876     /**
877       * Returns the current scrolling mode.
878       *
879       * @return the <code>scrollMode</code> property
880       * @see #setScrollMode
881       * @since 1.3
882       */

883     public int getScrollMode() {
884         return scrollMode;
885     }
886
887     /**
888      * Returns <code>true</code> if this viewport is maintaining
889      * an offscreen image of its contents.
890      *
891      * @return <code>true</code> if <code>scrollMode</code> is
892      * <code>BACKINGSTORE_SCROLL_MODE</code>
893      *
894      * @deprecated As of Java 2 platform v1.3, replaced by
895      * <code>getScrollMode()</code>.
896      */

897     @Deprecated JavaDoc
898     public boolean isBackingStoreEnabled() {
899         return scrollMode == BACKINGSTORE_SCROLL_MODE;
900     }
901
902
903     /**
904      * If true if this viewport will maintain an offscreen
905      * image of its contents. The image is used to reduce the cost
906      * of small one dimensional changes to the <code>viewPosition</code>.
907      * Rather than repainting the entire viewport we use
908      * <code>Graphics.copyArea</code> to effect some of the scroll.
909      *
910      * @param enabled if true, maintain an offscreen backing store
911      *
912      * @deprecated As of Java 2 platform v1.3, replaced by
913      * <code>setScrollMode()</code>.
914      */

915     @Deprecated JavaDoc
916     public void setBackingStoreEnabled(boolean enabled) {
917         if (enabled) {
918         setScrollMode(BACKINGSTORE_SCROLL_MODE);
919     } else {
920         setScrollMode(BLIT_SCROLL_MODE);
921     }
922     }
923
924     private final boolean isBlitting() {
925         Component view = getView();
926         return (scrollMode == BLIT_SCROLL_MODE) &&
927            (view instanceof JComponent JavaDoc) && ((JComponent JavaDoc)view).isOpaque();
928     }
929
930
931     /**
932      * Returns the <code>JViewport</code>'s one child or <code>null</code>.
933      *
934      * @return the viewports child, or <code>null</code> if none exists
935      *
936      * @see #setView
937      */

938     public Component getView() {
939         try {
940         return getComponent(0);
941     } catch (ArrayIndexOutOfBoundsException JavaDoc e) {
942         return null;
943     }
944     }
945
946     /**
947      * Sets the <code>JViewport</code>'s one lightweight child
948      * (<code>view</code>), which can be <code>null</code>.
949      *
950      * @param view the viewport's new lightweight child
951      *
952      * @see #getView
953      */

954     public void setView(Component view) {
955
956         /* Remove the viewport's existing children, if any.
957          * Note that removeAll() isn't used here because it
958          * doesn't call remove() (which JViewport overrides).
959          */

960         int n = getComponentCount();
961         for(int i = n - 1; i >= 0; i--) {
962             remove(getComponent(i));
963         }
964
965         isViewSizeSet = false;
966
967         if (view != null) {
968             super.addImpl(view, null, -1);
969             viewListener = createViewListener();
970             view.addComponentListener(viewListener);
971         }
972
973         if (hasHadValidView) {
974             // Only fire a change if a view has been installed.
975
fireStateChanged();
976         }
977         else if (view != null) {
978             hasHadValidView = true;
979         }
980
981     revalidate();
982     repaint();
983     }
984
985
986     /**
987      * If the view's size hasn't been explicitly set, return the
988      * preferred size, otherwise return the view's current size.
989      * If there is no view, return 0,0.
990      *
991      * @return a <code>Dimension</code> object specifying the size of the view
992      */

993     public Dimension getViewSize() {
994         Component view = getView();
995
996         if (view == null) {
997             return new Dimension(0,0);
998         }
999         else if (isViewSizeSet) {
1000            return view.getSize();
1001        }
1002        else {
1003            return view.getPreferredSize();
1004        }
1005    }
1006
1007
1008    /**
1009     * Sets the size of the view. A state changed event will be fired.
1010     *
1011     * @param newSize a <code>Dimension</code> object specifying the new
1012     * size of the view
1013     */

1014    public void setViewSize(Dimension newSize) {
1015        Component view = getView();
1016        if (view != null) {
1017            Dimension oldSize = view.getSize();
1018            if (!newSize.equals(oldSize)) {
1019        // scrollUnderway will be true if this is invoked as the
1020
// result of a validate and setViewPosition was previously
1021
// invoked.
1022
scrollUnderway = false;
1023                view.setSize(newSize);
1024                isViewSizeSet = true;
1025                fireStateChanged();
1026            }
1027        }
1028    }
1029
1030    /**
1031     * Returns the view coordinates that appear in the upper left
1032     * hand corner of the viewport, or 0,0 if there's no view.
1033     *
1034     * @return a <code>Point</code> object giving the upper left coordinates
1035     */

1036    public Point getViewPosition() {
1037        Component view = getView();
1038        if (view != null) {
1039            Point p = view.getLocation();
1040            p.x = -p.x;
1041            p.y = -p.y;
1042            return p;
1043        }
1044        else {
1045            return new Point(0,0);
1046        }
1047    }
1048
1049
1050    /**
1051     * Sets the view coordinates that appear in the upper left
1052     * hand corner of the viewport, does nothing if there's no view.
1053     *
1054     * @param p a <code>Point</code> object giving the upper left coordinates
1055     */

1056    public void setViewPosition(Point p)
1057    {
1058        Component view = getView();
1059        if (view == null) {
1060        return;
1061    }
1062    
1063    int oldX, oldY, x = p.x, y = p.y;
1064
1065    /* Collect the old x,y values for the views location
1066     * and do the song and dance to avoid allocating
1067     * a Rectangle object if we don't have to.
1068     */

1069    if (view instanceof JComponent JavaDoc) {
1070        JComponent JavaDoc c = (JComponent JavaDoc)view;
1071        oldX = c.getX();
1072        oldY = c.getY();
1073    }
1074    else {
1075        Rectangle r = view.getBounds();
1076        oldX = r.x;
1077        oldY = r.y;
1078    }
1079
1080    /* The view scrolls in the opposite direction to mouse
1081     * movement.
1082     */

1083    int newX = -x;
1084    int newY = -y;
1085    
1086    if ((oldX != newX) || (oldY != newY)) {
1087        if (!waitingForRepaint && isBlitting() && canUseWindowBlitter()) {
1088        Graphics g = getGraphics();
1089        flushViewDirtyRegion(g);
1090        // This calls setBounds(), and then repaint().
1091
view.setLocation(newX, newY);
1092        // The cast to JComponent here is valid, if view is not
1093
// a JComponent, isBlitting will return false.
1094
g.setClip(0,0,getWidth(), Math.min(getHeight(),
1095                 ((JComponent JavaDoc)view).getHeight()));
1096                // Repaint the complete component if the blit succeeded
1097
// and needsRepaintAfterBlit returns true.
1098
repaintAll = (windowBlitPaint(g) &&
1099                              needsRepaintAfterBlit());
1100        g.dispose();
1101        RepaintManager JavaDoc rm = RepaintManager.currentManager(this);
1102        rm.markCompletelyClean((JComponent JavaDoc)getParent());
1103        rm.markCompletelyClean(this);
1104        rm.markCompletelyClean((JComponent JavaDoc)view);
1105        }
1106        else {
1107        scrollUnderway = true;
1108        // This calls setBounds(), and then repaint().
1109
view.setLocation(newX, newY);
1110        repaintAll = false;
1111        }
1112        fireStateChanged();
1113    }
1114    }
1115
1116
1117    /**
1118     * Returns a rectangle whose origin is <code>getViewPosition</code>
1119     * and size is <code>getExtentSize</code>.
1120     * This is the visible part of the view, in view coordinates.
1121     *
1122     * @return a <code>Rectangle</code> giving the visible part of
1123     * the view using view coordinates.
1124     */

1125    public Rectangle getViewRect() {
1126        return new Rectangle(getViewPosition(), getExtentSize());
1127    }
1128
1129
1130    /**
1131     * Computes the parameters for a blit where the backing store image
1132     * currently contains <code>oldLoc</code> in the upper left hand corner
1133     * and we're scrolling to <code>newLoc</code>.
1134     * The parameters are modified
1135     * to return the values required for the blit.
1136     *
1137     * @param dx the horizontal delta
1138     * @param dy the vertical delta
1139     * @param blitFrom the <code>Point</code> we're blitting from
1140     * @param blitTo the <code>Point</code> we're blitting to
1141     * @param blitSize the <code>Dimension</code> of the area to blit
1142     * @param blitPaint the area to blit
1143     * @return true if the parameters are modified and we're ready to blit;
1144     * false otherwise
1145     */

1146    protected boolean computeBlit(
1147        int dx,
1148        int dy,
1149        Point blitFrom,
1150        Point blitTo,
1151        Dimension blitSize,
1152        Rectangle blitPaint)
1153    {
1154        int dxAbs = Math.abs(dx);
1155        int dyAbs = Math.abs(dy);
1156        Dimension extentSize = getExtentSize();
1157
1158        if ((dx == 0) && (dy != 0) && (dyAbs < extentSize.height)) {
1159            if (dy < 0) {
1160                blitFrom.y = -dy;
1161                blitTo.y = 0;
1162                blitPaint.y = extentSize.height + dy;
1163            }
1164            else {
1165                blitFrom.y = 0;
1166                blitTo.y = dy;
1167                blitPaint.y = 0;
1168            }
1169
1170            blitPaint.x = blitFrom.x = blitTo.x = 0;
1171
1172            blitSize.width = extentSize.width;
1173            blitSize.height = extentSize.height - dyAbs;
1174
1175            blitPaint.width = extentSize.width;
1176            blitPaint.height = dyAbs;
1177
1178            return true;
1179        }
1180
1181        else if ((dy == 0) && (dx != 0) && (dxAbs < extentSize.width)) {
1182            if (dx < 0) {
1183                blitFrom.x = -dx;
1184                blitTo.x = 0;
1185                blitPaint.x = extentSize.width + dx;
1186            }
1187            else {
1188                blitFrom.x = 0;
1189                blitTo.x = dx;
1190                blitPaint.x = 0;
1191            }
1192
1193            blitPaint.y = blitFrom.y = blitTo.y = 0;
1194
1195            blitSize.width = extentSize.width - dxAbs;
1196            blitSize.height = extentSize.height;
1197
1198            blitPaint.y = 0;
1199            blitPaint.width = dxAbs;
1200            blitPaint.height = extentSize.height;
1201
1202            return true;
1203        }
1204
1205        else {
1206            return false;
1207        }
1208    }
1209
1210
1211    /**
1212     * Returns the size of the visible part of the view in view coordinates.
1213     *
1214     * @return a <code>Dimension</code> object giving the size of the view
1215     */

1216    public Dimension getExtentSize() {
1217        return getSize();
1218    }
1219
1220
1221    /**
1222     * Converts a size in pixel coordinates to view coordinates.
1223     * Subclasses of viewport that support "logical coordinates"
1224     * will override this method.
1225     *
1226     * @param size a <code>Dimension</code> object using pixel coordinates
1227     * @return a <code>Dimension</code> object converted to view coordinates
1228     */

1229    public Dimension toViewCoordinates(Dimension size) {
1230        return new Dimension(size);
1231    }
1232
1233    /**
1234     * Converts a point in pixel coordinates to view coordinates.
1235     * Subclasses of viewport that support "logical coordinates"
1236     * will override this method.
1237     *
1238     * @param p a <code>Point</code> object using pixel coordinates
1239     * @return a <code>Point</code> object converted to view coordinates
1240     */

1241    public Point toViewCoordinates(Point p) {
1242        return new Point(p);
1243    }
1244
1245
1246    /**
1247     * Sets the size of the visible part of the view using view coordinates.
1248     *
1249     * @param newExtent a <code>Dimension</code> object specifying
1250     * the size of the view
1251     */

1252    public void setExtentSize(Dimension newExtent) {
1253        Dimension oldExtent = getExtentSize();
1254        if (!newExtent.equals(oldExtent)) {
1255            setSize(newExtent);
1256            fireStateChanged();
1257        }
1258    }
1259
1260    /**
1261     * A listener for the view.
1262     * <p>
1263     * <strong>Warning:</strong>
1264     * Serialized objects of this class will not be compatible with
1265     * future Swing releases. The current serialization support is
1266     * appropriate for short term storage or RMI between applications running
1267     * the same version of Swing. As of 1.4, support for long term storage
1268     * of all JavaBeans<sup><font size="-2">TM</font></sup>
1269     * has been added to the <code>java.beans</code> package.
1270     * Please see {@link java.beans.XMLEncoder}.
1271     */

1272    protected class ViewListener extends ComponentAdapter implements Serializable JavaDoc
1273    {
1274        public void componentResized(ComponentEvent e) {
1275            fireStateChanged();
1276            revalidate();
1277        }
1278    }
1279
1280    /**
1281     * Creates a listener for the view.
1282     * @return a <code>ViewListener</code>
1283     */

1284    protected ViewListener createViewListener() {
1285        return new ViewListener();
1286    }
1287
1288
1289    /**
1290     * Subclassers can override this to install a different
1291     * layout manager (or <code>null</code>) in the constructor. Returns
1292     * the <code>LayoutManager</code> to install on the <code>JViewport</code>.
1293     *
1294     * @return a <code>LayoutManager</code>
1295     */

1296    protected LayoutManager createLayoutManager() {
1297        return ViewportLayout.SHARED_INSTANCE;
1298    }
1299
1300
1301    /**
1302     * Adds a <code>ChangeListener</code> to the list that is
1303     * notified each time the view's
1304     * size, position, or the viewport's extent size has changed.
1305     *
1306     * @param l the <code>ChangeListener</code> to add
1307     * @see #removeChangeListener
1308     * @see #setViewPosition
1309     * @see #setViewSize
1310     * @see #setExtentSize
1311     */

1312    public void addChangeListener(ChangeListener l) {
1313        listenerList.add(ChangeListener.class, l);
1314    }
1315
1316    /**
1317     * Removes a <code>ChangeListener</code> from the list that's notified each
1318     * time the views size, position, or the viewports extent size
1319     * has changed.
1320     *
1321     * @param l the <code>ChangeListener</code> to remove
1322     * @see #addChangeListener
1323     */

1324    public void removeChangeListener(ChangeListener l) {
1325        listenerList.remove(ChangeListener.class, l);
1326    }
1327
1328    /**
1329     * Returns an array of all the <code>ChangeListener</code>s added
1330     * to this JViewport with addChangeListener().
1331     *
1332     * @return all of the <code>ChangeListener</code>s added or an empty
1333     * array if no listeners have been added
1334     * @since 1.4
1335     */

1336    public ChangeListener[] getChangeListeners() {
1337        return (ChangeListener[])listenerList.getListeners(
1338                ChangeListener.class);
1339    }
1340
1341    /**
1342     * Notifies all <code>ChangeListeners</code> when the views
1343     * size, position, or the viewports extent size has changed.
1344     *
1345     * @see #addChangeListener
1346     * @see #removeChangeListener
1347     * @see EventListenerList
1348     */

1349    protected void fireStateChanged()
1350    {
1351        Object JavaDoc[] listeners = listenerList.getListenerList();
1352        for (int i = listeners.length - 2; i >= 0; i -= 2) {
1353            if (listeners[i] == ChangeListener.class) {
1354                if (changeEvent == null) {
1355                    changeEvent = new ChangeEvent(this);
1356                }
1357                ((ChangeListener)listeners[i + 1]).stateChanged(changeEvent);
1358            }
1359        }
1360    }
1361
1362    /**
1363     * Always repaint in the parents coordinate system to make sure
1364     * only one paint is performed by the <code>RepaintManager</code>.
1365     *
1366     * @param tm maximum time in milliseconds before update
1367     * @param x the <code>x</code> coordinate (pixels over from left)
1368     * @param y the <code>y</code> coordinate (pixels down from top)
1369     * @param w the width
1370     * @param h the height
1371     * @see java.awt.Component#update(java.awt.Graphics)
1372     */

1373    public void repaint(long tm, int x, int y, int w, int h) {
1374        Container parent = getParent();
1375        if(parent != null)
1376            parent.repaint(tm,x+getX(),y+getY(),w,h);
1377        else
1378            super.repaint(tm,x,y,w,h);
1379    }
1380
1381
1382    /**
1383     * Returns a string representation of this <code>JViewport</code>.
1384     * This method
1385     * is intended to be used only for debugging purposes, and the
1386     * content and format of the returned string may vary between
1387     * implementations. The returned string may be empty but may not
1388     * be <code>null</code>.
1389     *
1390     * @return a string representation of this <code>JViewport</code>
1391     */

1392    protected String JavaDoc paramString() {
1393        String JavaDoc isViewSizeSetString = (isViewSizeSet ?
1394                      "true" : "false");
1395        String JavaDoc lastPaintPositionString = (lastPaintPosition != null ?
1396                      lastPaintPosition.toString() : "");
1397        String JavaDoc scrollUnderwayString = (scrollUnderway ?
1398                       "true" : "false");
1399
1400        return super.paramString() +
1401        ",isViewSizeSet=" + isViewSizeSetString +
1402        ",lastPaintPosition=" + lastPaintPositionString +
1403        ",scrollUnderway=" + scrollUnderwayString;
1404    }
1405
1406    //
1407
// Following is used when doBlit is true.
1408
//
1409

1410    /**
1411     * Notifies listeners of a property change. This is subclassed to update
1412     * the <code>windowBlit</code> property.
1413     * (The <code>putClientProperty</code> property is final).
1414     *
1415     * @param propertyName a string containing the property name
1416     * @param oldValue the old value of the property
1417     * @param newValue the new value of the property
1418     */

1419    protected void firePropertyChange(String JavaDoc propertyName, Object JavaDoc oldValue,
1420                      Object JavaDoc newValue) {
1421    super.firePropertyChange(propertyName, oldValue, newValue);
1422    if (propertyName.equals(EnableWindowBlit)) {
1423        if (newValue != null) {
1424            setScrollMode(BLIT_SCROLL_MODE);
1425        } else {
1426            setScrollMode(SIMPLE_SCROLL_MODE);
1427        }
1428    }
1429    }
1430
1431    /**
1432     * Returns true if the component needs to be completely repainted after
1433     * a blit and a paint is received.
1434     */

1435    private boolean needsRepaintAfterBlit() {
1436        // Find the first heavy weight ancestor. isObscured and
1437
// canDetermineObscurity are only appropriate for heavy weights.
1438
Component heavyParent = getParent();
1439
1440    while (heavyParent != null && heavyParent.isLightweight()) {
1441            heavyParent = heavyParent.getParent();
1442        }
1443
1444        if (heavyParent != null) {
1445            ComponentPeer peer = heavyParent.getPeer();
1446
1447            if (peer != null && peer.canDetermineObscurity() &&
1448                                !peer.isObscured()) {
1449                // The peer says we aren't obscured, therefore we can assume
1450
// that we won't later be messaged to paint a portion that
1451
// we tried to blit that wasn't valid.
1452
// It is certainly possible that when we blited we were
1453
// obscured, and by the time this is invoked we aren't, but the
1454
// chances of that happening are pretty slim.
1455
return false;
1456            }
1457        }
1458        return true;
1459    }
1460
1461    private Timer JavaDoc createRepaintTimer() {
1462    Timer JavaDoc timer = new Timer JavaDoc(300, new ActionListener() {
1463        public void actionPerformed(ActionEvent ae) {
1464        // waitingForRepaint will be false if a paint came down
1465
// with the complete clip rect, in which case we don't
1466
// have to cause a repaint.
1467
if (waitingForRepaint) {
1468            repaint();
1469        }
1470        }
1471    });
1472    timer.setRepeats(false);
1473    return timer;
1474    }
1475
1476    /**
1477     * If the repaint manager has a dirty region for the view, the view is
1478     * asked to paint.
1479     *
1480     * @param g the <code>Graphics</code> context within which to paint
1481     */

1482    private void flushViewDirtyRegion(Graphics g) {
1483    RepaintManager JavaDoc rm = RepaintManager.currentManager(this);
1484    JComponent JavaDoc view = (JComponent JavaDoc) getView();
1485    Rectangle dirty;
1486
1487    dirty = rm.getDirtyRegion(view);
1488    if(dirty != null && dirty.width > 0 && dirty.height > 0) {
1489        dirty.x += view.getX();
1490        dirty.y += view.getY();
1491        Rectangle clip = g.getClipBounds();
1492        if (clip == null) {
1493        // Only happens in 1.2
1494
g.setClip(0, 0, getWidth(), getHeight());
1495        }
1496        g.clipRect(dirty.x, dirty.y, dirty.width, dirty.height);
1497            clip = g.getClipBounds();
1498            // Only paint the dirty region if it is visible.
1499
if (clip.width > 0 && clip.height > 0) {
1500                paintView(g);
1501            }
1502    }
1503    }
1504
1505    /**
1506     * Used when blitting.
1507     *
1508     * @param g the <code>Graphics</code> context within which to paint
1509     * @return true if blitting succeeded; otherwise false
1510     */

1511    private boolean windowBlitPaint(Graphics g) {
1512    int width = getWidth();
1513        int height = getHeight();
1514
1515        if ((width == 0) || (height == 0)) {
1516            return false;
1517        }
1518        
1519    boolean retValue;
1520    RepaintManager JavaDoc rm = RepaintManager.currentManager(this);
1521    JComponent JavaDoc view = (JComponent JavaDoc) getView();
1522
1523    if (lastPaintPosition == null ||
1524        lastPaintPosition.equals(getViewLocation())) {
1525        paintView(g);
1526        retValue = false;
1527    } else {
1528        // The image was scrolled. Manipulate the backing store and flush
1529
// it to g.
1530
Point blitFrom = new Point();
1531        Point blitTo = new Point();
1532        Dimension blitSize = new Dimension();
1533        Rectangle blitPaint = new Rectangle();
1534
1535        Point newLocation = getViewLocation();
1536        int dx = newLocation.x - lastPaintPosition.x;
1537        int dy = newLocation.y - lastPaintPosition.y;
1538        boolean canBlit = computeBlit(dx, dy, blitFrom, blitTo, blitSize,
1539                      blitPaint);
1540        if (!canBlit) {
1541        paintView(g);
1542        retValue = false;
1543        } else {
1544        // Prepare the rest of the view; the part that has just been
1545
// exposed.
1546
Rectangle r = view.getBounds().intersection(blitPaint);
1547        r.x -= view.getX();
1548        r.y -= view.getY();
1549
1550        // Attempt to use VolatileImage buffer for maximum performance.
1551
// If for any reason this fails (which should be rare), fallback to
1552
// plain old Image buffer.
1553
//
1554
boolean paintCompleted = false;
1555        Image JavaDoc off = null;
1556        if (rm.useVolatileDoubleBuffer() &&
1557            (off = rm.getVolatileOffscreenBuffer(this,getWidth(),getHeight())) != null) {
1558            VolatileImage JavaDoc vImage = (java.awt.image.VolatileImage JavaDoc)off;
1559                    GraphicsConfiguration gc = view.getGraphicsConfiguration();
1560            for(int i = 0; !paintCompleted && i < RepaintManager.VOLATILE_LOOP_MAX; i++) {
1561                if (vImage.validate(gc) ==
1562                VolatileImage.IMAGE_INCOMPATIBLE)
1563            {
1564                rm.resetVolatileDoubleBuffer(gc);
1565                off = rm.getVolatileOffscreenBuffer(this,getWidth(),getHeight());
1566                vImage = (java.awt.image.VolatileImage JavaDoc)off;
1567            }
1568                blitDoubleBuffered(view, g, r.x, r.y, r.width, r.height,
1569                       blitFrom.x, blitFrom.y, blitTo.x, blitTo.y,
1570                       blitSize.width, blitSize.height, off);
1571                       
1572                paintCompleted = !(vImage.contentsLost());
1573            }
1574        }
1575        if (!paintCompleted) {
1576            off = rm.getOffscreenBuffer(this, getWidth(), getHeight());
1577            blitDoubleBuffered(view, g, r.x, r.y, r.width, r.height,
1578                       blitFrom.x, blitFrom.y, blitTo.x, blitTo.y,
1579                       blitSize.width, blitSize.height, off);
1580            paintCompleted = true;
1581        }
1582        retValue = true;
1583        }
1584    }
1585    lastPaintPosition = getViewLocation();
1586    return retValue;
1587    }
1588
1589    private void blitDoubleBuffered(JComponent JavaDoc view, Graphics g,
1590                    int clipX, int clipY, int clipW, int clipH,
1591                    int blitFromX, int blitFromY, int blitToX, int blitToY,
1592                    int blitW, int blitH, Image JavaDoc off) {
1593    RepaintManager JavaDoc rm = RepaintManager.currentManager(this);
1594    boolean isDBE = rm.isDoubleBufferingEnabled();
1595    int bdx = blitToX - blitFromX;
1596    int bdy = blitToY - blitFromY;
1597
1598    Graphics og = off.getGraphics();
1599    og.translate(-clipX,-clipY);
1600    og.setClip(clipX,clipY,clipW,clipH);
1601    rm.setDoubleBufferingEnabled(false);
1602    view.paint(og);
1603    rm.setDoubleBufferingEnabled(isDBE);
1604
1605    // Move the relevant part of the backing store.
1606
blitWindowGraphics(blitFromX, blitFromY, blitW, blitH, bdx, bdy);
1607                    
1608    clipX += view.getX();
1609    clipY += view.getY();
1610    g.setClip(clipX,clipY,clipW,clipH);
1611    g.drawImage(off,clipX,clipY,null);
1612    og.dispose();
1613    }
1614
1615    /**
1616     * Called to paint the view, usually when <code>blitPaint</code>
1617     * can not blit.
1618     *
1619     * @param g the <code>Graphics</code> context within which to paint
1620     */

1621    private void paintView(Graphics g) {
1622    Rectangle r = g.getClipBounds();
1623    RepaintManager JavaDoc rm = RepaintManager.currentManager(this);
1624    JComponent JavaDoc view = (JComponent JavaDoc) getView();
1625    r.x -= view.getX();
1626    r.y -= view.getY();
1627
1628    // Attempt to use VolatileImage buffer for maximum performance.
1629
// If for any reason this fails (which should be rare), fallback to
1630
// plain old Image buffer.
1631
//
1632
boolean paintCompleted = false;
1633    Image JavaDoc off = null;
1634    if (rm.useVolatileDoubleBuffer() &&
1635        (off = rm.getVolatileOffscreenBuffer(this,r.width,r.height)) != null) {
1636        VolatileImage JavaDoc vImage = (java.awt.image.VolatileImage JavaDoc)off;
1637            GraphicsConfiguration gc = view.getGraphicsConfiguration();
1638        for(int i=0; !paintCompleted && i < RepaintManager.VOLATILE_LOOP_MAX; i++) {
1639        if (vImage.validate(gc) ==
1640            VolatileImage.IMAGE_INCOMPATIBLE)
1641        {
1642            rm.resetVolatileDoubleBuffer(gc);
1643            off = rm.getVolatileOffscreenBuffer(this,getWidth(),getHeight());
1644            vImage = (java.awt.image.VolatileImage JavaDoc)off;
1645        }
1646            paintViewDoubleBuffered(view, g, r.x, r.y, r.width, r.height, off);
1647            paintCompleted = !(vImage.contentsLost());
1648        }
1649    }
1650    if (!paintCompleted) {
1651        off = rm.getOffscreenBuffer(this,r.width,r.height);
1652        paintViewDoubleBuffered(view, g, r.x, r.y, r.width, r.height, off);
1653        paintCompleted = true;
1654    }
1655    }
1656
1657    private void paintViewDoubleBuffered(JComponent JavaDoc view, Graphics g,
1658                     int clipX, int clipY, int clipW, int clipH, Image JavaDoc off) {
1659    RepaintManager JavaDoc rm = RepaintManager.currentManager(this);
1660    boolean isDBE = rm.isDoubleBufferingEnabled();
1661
1662    Graphics og = off.getGraphics();
1663    if (view.getWidth() < clipW) {
1664        og.setColor(getBackground());
1665        og.fillRect(0,0,clipW,clipH);
1666    }
1667    og.translate(-clipX, -clipY);
1668    og.setClip(clipX, clipY, clipW, clipH);
1669    rm.setDoubleBufferingEnabled(false);
1670    view.paint(og);
1671    rm.setDoubleBufferingEnabled(isDBE);
1672    g.drawImage(off, clipX + view.getX(), clipY + view.getY(), null);
1673    og.dispose();
1674    }
1675
1676    /**
1677     * Blits the parent windows graphics from the given region offset
1678     * to <code>ox</code>, <code>oy</code>.
1679     */

1680    private void blitWindowGraphics(int x, int y, int w, int h, int ox,
1681                    int oy) {
1682    Container parent;
1683    for(parent = getParent() ; isLightweightComponent(parent) ;
1684        parent = parent.getParent());
1685    Graphics wg = parent.getGraphics();
1686    Rectangle r = new Rectangle(x,y,w,h);
1687    r = SwingUtilities.convertRectangle(this, r, parent);
1688    wg.copyArea(r.x,r.y,r.width,r.height, ox, oy);
1689    wg.dispose();
1690    }
1691
1692    /**
1693     * Returns true if the viewport is not obscured by one of its ancestors,
1694     * or its ancestors children and if the viewport is showing. Blitting
1695     * when the view isn't showing will work,
1696     * or rather <code>copyArea</code> will work,
1697     * but will not produce the expected behavior.
1698     */

1699    private boolean canUseWindowBlitter() {
1700    if (!isShowing() || (!(getParent() instanceof JComponent JavaDoc) &&
1701                 !(getView() instanceof JComponent JavaDoc))) {
1702        return false;
1703    }
1704        if (isPainting()) {
1705            // We're in the process of painting, don't blit. If we were
1706
// to blit we would draw on top of what we're already drawing,
1707
// so bail.
1708
return false;
1709        }
1710
1711        Rectangle dirtyRegion = RepaintManager.currentManager(this).
1712                                getDirtyRegion((JComponent JavaDoc)getParent());
1713
1714        if (dirtyRegion != null && dirtyRegion.width > 0 &&
1715            dirtyRegion.height > 0) {
1716            // Part of the scrollpane needs to be repainted too, don't blit.
1717
return false;
1718        }
1719
1720    Rectangle clip = new Rectangle(0,0,getWidth(),getHeight());
1721    Rectangle oldClip = new Rectangle();
1722    Rectangle tmp2 = null;
1723    Container parent;
1724    Component lastParent = null;
1725        int x, y, w, h;
1726        
1727    for(parent = this; parent != null && isLightweightComponent(parent); parent = parent.getParent()) {
1728            x = parent.getX();
1729            y = parent.getY();
1730            w = parent.getWidth();
1731            h = parent.getHeight();
1732
1733        oldClip.setBounds(clip);
1734        SwingUtilities.computeIntersection(0, 0, w, h, clip);
1735        if(!clip.equals(oldClip))
1736        return false;
1737
1738        if(lastParent != null && parent instanceof JComponent JavaDoc &&
1739           !((JComponent JavaDoc)parent).isOptimizedDrawingEnabled()) {
1740        Component comps[] = parent.getComponents();
1741        int index = 0;
1742
1743        for(int i = comps.length - 1 ;i >= 0; i--) {
1744            if(comps[i] == lastParent) {
1745            index = i - 1;
1746            break;
1747            }
1748        }
1749
1750        while(index >= 0) {
1751                    tmp2 = comps[index].getBounds(tmp2);
1752                    
1753            if(tmp2.intersects(clip))
1754            return false;
1755            index--;
1756        }
1757        }
1758        clip.x += x;
1759        clip.y += y;
1760        lastParent = parent;
1761    }
1762    if (parent == null) {
1763        // No Window parent.
1764
return false;
1765    }
1766    return true;
1767    }
1768
1769
1770/////////////////
1771
// Accessibility support
1772
////////////////
1773

1774    /**
1775     * Gets the AccessibleContext associated with this JViewport.
1776     * For viewports, the AccessibleContext takes the form of an
1777     * AccessibleJViewport.
1778     * A new AccessibleJViewport instance is created if necessary.
1779     *
1780     * @return an AccessibleJViewport that serves as the
1781     * AccessibleContext of this JViewport
1782     */

1783    public AccessibleContext getAccessibleContext() {
1784        if (accessibleContext == null) {
1785            accessibleContext = new AccessibleJViewport();
1786        }
1787        return accessibleContext;
1788    }
1789
1790    /**
1791     * This class implements accessibility support for the
1792     * <code>JViewport</code> class. It provides an implementation of the
1793     * Java Accessibility API appropriate to viewport user-interface elements.
1794     * <p>
1795     * <strong>Warning:</strong>
1796     * Serialized objects of this class will not be compatible with
1797     * future Swing releases. The current serialization support is
1798     * appropriate for short term storage or RMI between applications running
1799     * the same version of Swing. As of 1.4, support for long term storage
1800     * of all JavaBeans<sup><font size="-2">TM</font></sup>
1801     * has been added to the <code>java.beans</code> package.
1802     * Please see {@link java.beans.XMLEncoder}.
1803     */

1804    protected class AccessibleJViewport extends AccessibleJComponent {
1805        /**
1806         * Get the role of this object.
1807         *
1808         * @return an instance of AccessibleRole describing the role of
1809         * the object
1810         */

1811        public AccessibleRole getAccessibleRole() {
1812            return AccessibleRole.VIEWPORT;
1813        }
1814    } // inner class AccessibleJViewport
1815
}
1816
Popular Tags