History

My first initial commit with all of mine experimental changes.

Dmitry Kabak (userdima committed 13 years ago

Commit d37b88d1 parent 3f5b46618352 files · +2598093 −-8690Browse files

Showing the changes to java/pkg/javax/swing/RepaintManager.java (1 of 18352 files). Show all

Unified · Split

java/pkg/javax/swing/RepaintManager.java+1631−0
@@ -0,0 +1,1631 @@
1/*
2 * Copyright (c) 1997, 2011, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25package javax.swing;
26
27
28import java.awt.*;
29import java.awt.event.*;
30import java.awt.peer.ComponentPeer;
31import java.awt.peer.ContainerPeer;
32import java.awt.image.VolatileImage;
33import java.security.AccessController;
34import java.util.*;
35import java.applet.*;
36
37import sun.awt.AWTAccessor;
38import sun.awt.AppContext;
39import sun.awt.DisplayChangedListener;
40import sun.awt.SunToolkit;
41import sun.java2d.SunGraphicsEnvironment;
42import sun.security.action.GetPropertyAction;
43
44import com.sun.java.swing.SwingUtilities3;
45
46/**
47 * This class manages repaint requests, allowing the number
48 * of repaints to be minimized, for example by collapsing multiple
49 * requests into a single repaint for members of a component tree.
50 * <p>
51 * As of 1.6 <code>RepaintManager</code> handles repaint requests
52 * for Swing's top level components (<code>JApplet</code>,
53 * <code>JWindow</code>, <code>JFrame</code> and <code>JDialog</code>).
54 * Any calls to <code>repaint</code> on one of these will call into the
55 * appropriate <code>addDirtyRegion</code> method.
56 *
57 * @author Arnaud Weber
58 */
59public class RepaintManager
60{
61 /**
62 * Whether or not the RepaintManager should handle paint requests
63 * for top levels.
64 */
65 static final boolean HANDLE_TOP_LEVEL_PAINT;
66
67 private static final short BUFFER_STRATEGY_NOT_SPECIFIED = 0;
68 private static final short BUFFER_STRATEGY_SPECIFIED_ON = 1;
69 private static final short BUFFER_STRATEGY_SPECIFIED_OFF = 2;
70
71 private static final short BUFFER_STRATEGY_TYPE;
72
73 /**
74 * Maps from GraphicsConfiguration to VolatileImage.
75 */
76 private Map<GraphicsConfiguration,VolatileImage> volatileMap = new
77 HashMap<GraphicsConfiguration,VolatileImage>(1);
78
79 //
80 // As of 1.6 Swing handles scheduling of paint events from native code.
81 // That is, SwingPaintEventDispatcher is invoked on the toolkit thread,
82 // which in turn invokes nativeAddDirtyRegion. Because this is invoked
83 // from the native thread we can not invoke any public methods and so
84 // we introduce these added maps. So, any time nativeAddDirtyRegion is
85 // invoked the region is added to hwDirtyComponents and a work request
86 // is scheduled. When the work request is processed all entries in
87 // this map are pushed to the real map (dirtyComponents) and then
88 // painted with the rest of the components.
89 //
90 private Map<Container,Rectangle> hwDirtyComponents;
91
92 private Map<Component,Rectangle> dirtyComponents;
93 private Map<Component,Rectangle> tmpDirtyComponents;
94 private java.util.List<Component> invalidComponents;
95
96 // List of Runnables that need to be processed before painting from AWT.
97 private java.util.List<Runnable> runnableList;
98
99 boolean doubleBufferingEnabled = true;
100
101 private Dimension doubleBufferMaxSize;
102
103 // Support for both the standard and volatile offscreen buffers exists to
104 // provide backwards compatibility for the [rare] programs which may be
105 // calling getOffScreenBuffer() and not expecting to get a VolatileImage.
106 // Swing internally is migrating to use *only* the volatile image buffer.
107
108 // Support for standard offscreen buffer
109 //
110 DoubleBufferInfo standardDoubleBuffer;
111
112 /**
113 * Object responsible for hanlding core paint functionality.
114 */
115 private PaintManager paintManager;
116
117 private static final Object repaintManagerKey = RepaintManager.class;
118
119 // Whether or not a VolatileImage should be used for double-buffered painting
120 static boolean volatileImageBufferEnabled = true;
121 /**
122 * Value of the system property awt.nativeDoubleBuffering.
123 */
124 private static boolean nativeDoubleBuffering;
125
126 // The maximum number of times Swing will attempt to use the VolatileImage
127 // buffer during a paint operation.
128 private static final int VOLATILE_LOOP_MAX = 2;
129
130 /**
131 * Number of <code>beginPaint</code> that have been invoked.
132 */
133 private int paintDepth = 0;
134
135 /**
136 * Type of buffer strategy to use. Will be one of the BUFFER_STRATEGY_
137 * constants.
138 */
139 private short bufferStrategyType;
140
141 //
142 // BufferStrategyPaintManager has the unique characteristic that it
143 // must deal with the buffer being lost while painting to it. For
144 // example, if we paint a component and show it and the buffer has
145 // become lost we must repaint the whole window. To deal with that
146 // the PaintManager calls into repaintRoot, and if we're still in
147 // the process of painting the repaintRoot field is set to the JRootPane
148 // and after the current JComponent.paintImmediately call finishes
149 // paintImmediately will be invoked on the repaintRoot. In this
150 // way we don't try to show garbage to the screen.
151 //
152 /**
153 * True if we're in the process of painting the dirty regions. This is
154 * set to true in <code>paintDirtyRegions</code>.
155 */
156 private boolean painting;
157 /**
158 * If the PaintManager calls into repaintRoot during painting this field
159 * will be set to the root.
160 */
161 private JComponent repaintRoot;
162
163 /**
164 * The Thread that has initiated painting. If null it
165 * indicates painting is not currently in progress.
166 */
167 private Thread paintThread;
168
169 /**
170 * Runnable used to process all repaint/revalidate requests.
171 */
172 private final ProcessingRunnable processingRunnable;
173
174
175 static {
176 volatileImageBufferEnabled = "true".equals(AccessController.
177 doPrivileged(new GetPropertyAction(
178 "swing.volatileImageBufferEnabled", "true")));
179 boolean headless = GraphicsEnvironment.isHeadless();
180 if (volatileImageBufferEnabled && headless) {
181 volatileImageBufferEnabled = false;
182 }
183 nativeDoubleBuffering = "true".equals(AccessController.doPrivileged(
184 new GetPropertyAction("awt.nativeDoubleBuffering")));
185 String bs = AccessController.doPrivileged(
186 new GetPropertyAction("swing.bufferPerWindow"));
187 if (headless) {
188 BUFFER_STRATEGY_TYPE = BUFFER_STRATEGY_SPECIFIED_OFF;
189 }
190 else if (bs == null) {
191 BUFFER_STRATEGY_TYPE = BUFFER_STRATEGY_NOT_SPECIFIED;
192 }
193 else if ("true".equals(bs)) {
194 BUFFER_STRATEGY_TYPE = BUFFER_STRATEGY_SPECIFIED_ON;
195 }
196 else {
197 BUFFER_STRATEGY_TYPE = BUFFER_STRATEGY_SPECIFIED_OFF;
198 }
199 HANDLE_TOP_LEVEL_PAINT = "true".equals(AccessController.doPrivileged(
200 new GetPropertyAction("swing.handleTopLevelPaint", "true")));
201 GraphicsEnvironment ge = GraphicsEnvironment.
202 getLocalGraphicsEnvironment();
203 if (ge instanceof SunGraphicsEnvironment) {
204 ((SunGraphicsEnvironment)ge).addDisplayChangedListener(
205 new DisplayChangedHandler());
206 }
207 }
208
209 /**
210 * Return the RepaintManager for the calling thread given a Component.
211 *
212 * @param c a Component -- unused in the default implementation, but could
213 * be used by an overridden version to return a different RepaintManager
214 * depending on the Component
215 * @return the RepaintManager object
216 */
217 public static RepaintManager currentManager(Component c) {
218 // Note: DisplayChangedRunnable passes in null as the component, so if
219 // component is ever used to determine the current
220 // RepaintManager, DisplayChangedRunnable will need to be modified
221 // accordingly.
222 return currentManager(AppContext.getAppContext());
223 }
224
225 /**
226 * Returns the RepaintManager for the specified AppContext. If
227 * a RepaintManager has not been created for the specified
228 * AppContext this will return null.
229 */
230 static RepaintManager currentManager(AppContext appContext) {
231 RepaintManager rm = (RepaintManager)appContext.get(repaintManagerKey);
232 if (rm == null) {
233 rm = new RepaintManager(BUFFER_STRATEGY_TYPE);
234 appContext.put(repaintManagerKey, rm);
235 }
236 return rm;
237 }
238
239 /**
240 * Return the RepaintManager for the calling thread given a JComponent.
241 * <p>
242 * Note: This method exists for backward binary compatibility with earlier
243 * versions of the Swing library. It simply returns the result returned by
244 * {@link #currentManager(Component)}.
245 *
246 * @param c a JComponent -- unused
247 * @return the RepaintManager object
248 */
249 public static RepaintManager currentManager(JComponent c) {
250 return currentManager((Component)c);
251 }
252
253
254 /**
255 * Set the RepaintManager that should be used for the calling
256 * thread. <b>aRepaintManager</b> will become the current RepaintManager
257 * for the calling thread's thread group.
258 * @param aRepaintManager the RepaintManager object to use
259 */
260 public static void setCurrentManager(RepaintManager aRepaintManager) {
261 if (aRepaintManager != null) {
262 SwingUtilities.appContextPut(repaintManagerKey, aRepaintManager);
263 } else {
264 SwingUtilities.appContextRemove(repaintManagerKey);
265 }
266 }
267
268 /**
269 * Create a new RepaintManager instance. You rarely call this constructor.
270 * directly. To get the default RepaintManager, use
271 * RepaintManager.currentManager(JComponent) (normally "this").
272 */
273 public RepaintManager() {
274 // Because we can't know what a subclass is doing with the
275 // volatile image we immediately punt in subclasses. If this
276 // poses a problem we'll need a more sophisticated detection algorithm,
277 // or API.
278 this(BUFFER_STRATEGY_SPECIFIED_OFF);
279 }
280
281 private RepaintManager(short bufferStrategyType) {
282 // If native doublebuffering is being used, do NOT use
283 // Swing doublebuffering.
284 doubleBufferingEnabled = !nativeDoubleBuffering;
285 synchronized(this) {
286 dirtyComponents = new IdentityHashMap<Component,Rectangle>();
287 tmpDirtyComponents = new IdentityHashMap<Component,Rectangle>();
288 this.bufferStrategyType = bufferStrategyType;
289 hwDirtyComponents = new IdentityHashMap<Container,Rectangle>();
290 }
291 processingRunnable = new ProcessingRunnable();
292 }
293
294 private void displayChanged() {
295 clearImages();
296 }
297
298 /**
299 * Mark the component as in need of layout and queue a runnable
300 * for the event dispatching thread that will validate the components
301 * first isValidateRoot() ancestor.
302 *
303 * @see JComponent#isValidateRoot
304 * @see #removeInvalidComponent
305 */
306 public synchronized void addInvalidComponent(JComponent invalidComponent)
307 {
308 RepaintManager delegate = getDelegate(invalidComponent);
309 if (delegate != null) {
310 delegate.addInvalidComponent(invalidComponent);
311 return;
312 }
313 Component validateRoot =
314 SwingUtilities.getValidateRoot(invalidComponent, true);
315
316 if (validateRoot == null) {
317 return;
318 }
319
320 /* Lazily create the invalidateComponents vector and add the
321 * validateRoot if it's not there already. If this validateRoot
322 * is already in the vector, we're done.
323 */
324 if (invalidComponents == null) {
325 invalidComponents = new ArrayList<Component>();
326 }
327 else {
328 int n = invalidComponents.size();
329 for(int i = 0; i < n; i++) {
330 if(validateRoot == invalidComponents.get(i)) {
331 return;
332 }
333 }
334 }
335 invalidComponents.add(validateRoot);
336
337 // Queue a Runnable to invoke paintDirtyRegions and
338 // validateInvalidComponents.
339 scheduleProcessingRunnable();
340 }
341
342
343 /**
344 * Remove a component from the list of invalid components.
345 *
346 * @see #addInvalidComponent
347 */
348 public synchronized void removeInvalidComponent(JComponent component) {
349 RepaintManager delegate = getDelegate(component);
350 if (delegate != null) {
351 delegate.removeInvalidComponent(component);
352 return;
353 }
354 if(invalidComponents != null) {
355 int index = invalidComponents.indexOf(component);
356 if(index != -1) {
357 invalidComponents.remove(index);
358 }
359 }
360 }
361
362
363 /**
364 * Add a component in the list of components that should be refreshed.
365 * If <i>c</i> already has a dirty region, the rectangle <i>(x,y,w,h)</i>
366 * will be unioned with the region that should be redrawn.
367 *
368 * @see JComponent#repaint
369 */
370 private void addDirtyRegion0(Container c, int x, int y, int w, int h) {
371 /* Special cases we don't have to bother with.
372 */
373 if ((w <= 0) || (h <= 0) || (c == null)) {
374 return;
375 }
376
377 if ((c.getWidth() <= 0) || (c.getHeight() <= 0)) {
378 return;
379 }
380
381 if (extendDirtyRegion(c, x, y, w, h)) {
382 // Component was already marked as dirty, region has been
383 // extended, no need to continue.
384 return;
385 }
386
387 /* Make sure that c and all it ancestors (up to an Applet or
388 * Window) are visible. This loop has the same effect as
389 * checking c.isShowing() (and note that it's still possible
390 * that c is completely obscured by an opaque ancestor in
391 * the specified rectangle).
392 */
393 Component root = null;
394
395 // Note: We can't synchronize around this, Frame.getExtendedState
396 // is synchronized so that if we were to synchronize around this
397 // it could lead to the possibility of getting locks out
398 // of order and deadlocking.
399 for (Container p = c; p != null; p = p.getParent()) {
400 if (!p.isVisible() || (p.getPeer() == null)) {
401 return;
402 }
403 if ((p instanceof Window) || (p instanceof Applet)) {
404 // Iconified frames are still visible!
405 if (p instanceof Frame &&
406 (((Frame)p).getExtendedState() & Frame.ICONIFIED) ==
407 Frame.ICONIFIED) {
408 return;
409 }
410 root = p;
411 break;
412 }
413 }
414
415 if (root == null) return;
416
417 synchronized(this) {
418 if (extendDirtyRegion(c, x, y, w, h)) {
419 // In between last check and this check another thread
420 // queued up runnable, can bail here.
421 return;
422 }
423 dirtyComponents.put(c, new Rectangle(x, y, w, h));
424 }
425
426 // Queue a Runnable to invoke paintDirtyRegions and
427 // validateInvalidComponents.
428 scheduleProcessingRunnable();
429 }
430
431 /**
432 * Add a component in the list of components that should be refreshed.
433 * If <i>c</i> already has a dirty region, the rectangle <i>(x,y,w,h)</i>
434 * will be unioned with the region that should be redrawn.
435 *
436 * @param c Component to repaint, null results in nothing happening.
437 * @param x X coordinate of the region to repaint
438 * @param y Y coordinate of the region to repaint
439 * @param w Width of the region to repaint
440 * @param h Height of the region to repaint
441 * @see JComponent#repaint
442 */
443 public void addDirtyRegion(JComponent c, int x, int y, int w, int h)
444 {
445 RepaintManager delegate = getDelegate(c);
446 if (delegate != null) {
447 delegate.addDirtyRegion(c, x, y, w, h);
448 return;
449 }
450 addDirtyRegion0(c, x, y, w, h);
451 }
452
453 /**
454 * Adds <code>window</code> to the list of <code>Component</code>s that
455 * need to be repainted.
456 *
457 * @param window Window to repaint, null results in nothing happening.
458 * @param x X coordinate of the region to repaint
459 * @param y Y coordinate of the region to repaint
460 * @param w Width of the region to repaint
461 * @param h Height of the region to repaint
462 * @see JFrame#repaint
463 * @see JWindow#repaint
464 * @see JDialog#repaint
465 * @since 1.6
466 */
467 public void addDirtyRegion(Window window, int x, int y, int w, int h) {
468 addDirtyRegion0(window, x, y, w, h);
469 }
470
471 /**
472 * Adds <code>applet</code> to the list of <code>Component</code>s that
473 * need to be repainted.
474 *
475 * @param applet Applet to repaint, null results in nothing happening.
476 * @param x X coordinate of the region to repaint
477 * @param y Y coordinate of the region to repaint
478 * @param w Width of the region to repaint
479 * @param h Height of the region to repaint
480 * @see JApplet#repaint
481 * @since 1.6
482 */
483 public void addDirtyRegion(Applet applet, int x, int y, int w, int h) {
484 addDirtyRegion0(applet, x, y, w, h);
485 }
486
487 void scheduleHeavyWeightPaints() {
488 Map<Container,Rectangle> hws;
489
490 synchronized(this) {
491 if (hwDirtyComponents.size() == 0) {
492 return;
493 }
494 hws = hwDirtyComponents;
495 hwDirtyComponents = new IdentityHashMap<Container,Rectangle>();
496 }
497 for (Container hw : hws.keySet()) {
498 Rectangle dirty = hws.get(hw);
499 if (hw instanceof Window) {
500 addDirtyRegion((Window)hw, dirty.x, dirty.y,
501 dirty.width, dirty.height);
502 }
503 else if (hw instanceof Applet) {
504 addDirtyRegion((Applet)hw, dirty.x, dirty.y,
505 dirty.width, dirty.height);
506 }
507 else { // SwingHeavyWeight
508 addDirtyRegion0(hw, dirty.x, dirty.y,
509 dirty.width, dirty.height);
510 }
511 }
512 }
513
514 //
515 // This is called from the toolkit thread when a native expose is
516 // received.
517 //
518 void nativeAddDirtyRegion(AppContext appContext, Container c,
519 int x, int y, int w, int h) {
520 if (w > 0 && h > 0) {
521 synchronized(this) {
522 Rectangle dirty = hwDirtyComponents.get(c);
523 if (dirty == null) {
524 hwDirtyComponents.put(c, new Rectangle(x, y, w, h));
525 }
526 else {
527 hwDirtyComponents.put(c, SwingUtilities.computeUnion(
528 x, y, w, h, dirty));
529 }
530 }
531 scheduleProcessingRunnable(appContext);
532 }
533 }
534
535 //
536 // This is called from the toolkit thread when awt needs to run a
537 // Runnable before we paint.
538 //
539 void nativeQueueSurfaceDataRunnable(AppContext appContext, Component c,
540 Runnable r) {
541 synchronized(this) {
542 if (runnableList == null) {
543 runnableList = new LinkedList<Runnable>();
544 }
545 runnableList.add(r);
546 }
547 scheduleProcessingRunnable(appContext);
548 }
549
550 /**
551 * Extends the dirty region for the specified component to include
552 * the new region.
553 *
554 * @return false if <code>c</code> is not yet marked dirty.
555 */
556 private synchronized boolean extendDirtyRegion(
557 Component c, int x, int y, int w, int h) {
558 Rectangle r = dirtyComponents.get(c);
559 if (r != null) {
560 // A non-null r implies c is already marked as dirty,
561 // and that the parent is valid. Therefore we can
562 // just union the rect and bail.
563 SwingUtilities.computeUnion(x, y, w, h, r);
564 return true;
565 }
566 return false;
567 }
568
569 /** Return the current dirty region for a component.
570 * Return an empty rectangle if the component is not
571 * dirty.
572 */
573 public Rectangle getDirtyRegion(JComponent aComponent) {
574 RepaintManager delegate = getDelegate(aComponent);
575 if (delegate != null) {
576 return delegate.getDirtyRegion(aComponent);
577 }
578 Rectangle r;
579 synchronized(this) {
580 r = dirtyComponents.get(aComponent);
581 }
582 if(r == null)
583 return new Rectangle(0,0,0,0);
584 else
585 return new Rectangle(r);
586 }
587
588 /**
589 * Mark a component completely dirty. <b>aComponent</b> will be
590 * completely painted during the next paintDirtyRegions() call.
591 */
592 public void markCompletelyDirty(JComponent aComponent) {
593 RepaintManager delegate = getDelegate(aComponent);
594 if (delegate != null) {
595 delegate.markCompletelyDirty(aComponent);
596 return;
597 }
598 addDirtyRegion(aComponent,0,0,Integer.MAX_VALUE,Integer.MAX_VALUE);
599 }
600
601 /**
602 * Mark a component completely clean. <b>aComponent</b> will not
603 * get painted during the next paintDirtyRegions() call.
604 */
605 public void markCompletelyClean(JComponent aComponent) {
606 RepaintManager delegate = getDelegate(aComponent);
607 if (delegate != null) {
608 delegate.markCompletelyClean(aComponent);
609 return;
610 }
611 synchronized(this) {
612 dirtyComponents.remove(aComponent);
613 }
614 }
615
616 /**
617 * Convenience method that returns true if <b>aComponent</b> will be completely
618 * painted during the next paintDirtyRegions(). If computing dirty regions is
619 * expensive for your component, use this method and avoid computing dirty region
620 * if it return true.
621 */
622 public boolean isCompletelyDirty(JComponent aComponent) {
623 RepaintManager delegate = getDelegate(aComponent);
624 if (delegate != null) {
625 return delegate.isCompletelyDirty(aComponent);
626 }
627 Rectangle r;
628
629 r = getDirtyRegion(aComponent);
630 if(r.width == Integer.MAX_VALUE &&
631 r.height == Integer.MAX_VALUE)
632 return true;
633 else
634 return false;
635 }
636
637
638 /**
639 * Validate all of the components that have been marked invalid.
640 * @see #addInvalidComponent
641 */
642 public void validateInvalidComponents() {
643 java.util.List<Component> ic;
644 synchronized(this) {
645 if(invalidComponents == null) {
646 return;
647 }
648 ic = invalidComponents;
649 invalidComponents = null;
650 }
651 int n = ic.size();
652 for(int i = 0; i < n; i++) {
653 ic.get(i).validate();
654 }
655 }
656
657
658 /**
659 * This is invoked to process paint requests. It's needed
660 * for backward compatability in so far as RepaintManager would previously
661 * not see paint requests for top levels, so, we have to make sure
662 * a subclass correctly paints any dirty top levels.
663 */
664 private void prePaintDirtyRegions() {
665 Map<Component,Rectangle> dirtyComponents;
666 java.util.List<Runnable> runnableList;
667 synchronized(this) {
668 dirtyComponents = this.dirtyComponents;
669 runnableList = this.runnableList;
670 this.runnableList = null;
671 }
672 if (runnableList != null) {
673 for (Runnable runnable : runnableList) {
674 runnable.run();
675 }
676 }
677 paintDirtyRegions();
678 if (dirtyComponents.size() > 0) {
679 // This'll only happen if a subclass isn't correctly dealing
680 // with toplevels.
681 paintDirtyRegions(dirtyComponents);
682 }
683 }
684
685 private void updateWindows(Map<Component,Rectangle> dirtyComponents) {
686 Toolkit toolkit = Toolkit.getDefaultToolkit();
687 if (!(toolkit instanceof SunToolkit &&
688 ((SunToolkit)toolkit).needUpdateWindow()))
689 {
690 return;
691 }
692
693 Set<Window> windows = new HashSet<Window>();
694 Set<Component> dirtyComps = dirtyComponents.keySet();
695 for (Iterator<Component> it = dirtyComps.iterator(); it.hasNext();) {
696 Component dirty = it.next();
697 Window window = dirty instanceof Window ?
698 (Window)dirty :
699 SwingUtilities.getWindowAncestor(dirty);
700 if (window != null &&
701 !window.isOpaque())
702 {
703 windows.add(window);
704 }
705 }
706
707 for (Window window : windows) {
708 AWTAccessor.getWindowAccessor().updateWindow(window);
709 }
710 }
711
712 boolean isPainting() {
713 return painting;
714 }
715
716 /**
717 * Paint all of the components that have been marked dirty.
718 *
719 * @see #addDirtyRegion
720 */
721 public void paintDirtyRegions() {
722 synchronized(this) { // swap for thread safety
723 Map<Component,Rectangle> tmp = tmpDirtyComponents;
724 tmpDirtyComponents = dirtyComponents;
725 dirtyComponents = tmp;
726 dirtyComponents.clear();
727 }
728 paintDirtyRegions(tmpDirtyComponents);
729 }
730
731 private void paintDirtyRegions(Map<Component,Rectangle>
732 tmpDirtyComponents){
733 int i, count;
734 java.util.List<Component> roots;
735 Component dirtyComponent;
736
737 count = tmpDirtyComponents.size();
738 if (count == 0) {
739 return;
740 }
741
742 Rectangle rect;
743 int localBoundsX = 0;
744 int localBoundsY = 0;
745 int localBoundsH;
746 int localBoundsW;
747 Enumeration keys;
748
749 roots = new ArrayList<Component>(count);
750
751 for (Component dirty : tmpDirtyComponents.keySet()) {
752 collectDirtyComponents(tmpDirtyComponents, dirty, roots);
753 }
754
755 count = roots.size();
756 painting = true;
757 try {
758 for(i=0 ; i < count ; i++) {
759 dirtyComponent = roots.get(i);
760 rect = tmpDirtyComponents.get(dirtyComponent);
761 localBoundsH = dirtyComponent.getHeight();
762 localBoundsW = dirtyComponent.getWidth();
763
764 SwingUtilities.computeIntersection(localBoundsX,
765 localBoundsY,
766 localBoundsW,
767 localBoundsH,
768 rect);
769 if (dirtyComponent instanceof JComponent) {
770 ((JComponent)dirtyComponent).paintImmediately(
771 rect.x,rect.y,rect.width, rect.height);
772 }
773 else if (dirtyComponent.isShowing()) {
774 Graphics g = JComponent.safelyGetGraphics(
775 dirtyComponent, dirtyComponent);
776 // If the Graphics goes away, it means someone disposed of
777 // the window, don't do anything.
778 if (g != null) {
779 g.setClip(rect.x, rect.y, rect.width, rect.height);
780 try {
781 dirtyComponent.paint(g);
782 } finally {
783 g.dispose();
784 }
785 }
786 }
787 // If the repaintRoot has been set, service it now and
788 // remove any components that are children of repaintRoot.
789 if (repaintRoot != null) {
790 adjustRoots(repaintRoot, roots, i + 1);
791 count = roots.size();
792 paintManager.isRepaintingRoot = true;
793 repaintRoot.paintImmediately(0, 0, repaintRoot.getWidth(),
794 repaintRoot.getHeight());
795 paintManager.isRepaintingRoot = false;
796 // Only service repaintRoot once.
797 repaintRoot = null;
798 }
799 }
800 } finally {
801 painting = false;
802 }
803
804 updateWindows(tmpDirtyComponents);
805
806 tmpDirtyComponents.clear();
807 }
808
809
810 /**
811 * Removes any components from roots that are children of
812 * root.
813 */
814 private void adjustRoots(JComponent root,
815 java.util.List<Component> roots, int index) {
816 for (int i = roots.size() - 1; i >= index; i--) {
817 Component c = roots.get(i);
818 for(;;) {
819 if (c == root || c == null || !(c instanceof JComponent)) {
820 break;
821 }
822 c = c.getParent();
823 }
824 if (c == root) {
825 roots.remove(i);
826 }
827 }
828 }
829
830 Rectangle tmp = new Rectangle();
831
832 void collectDirtyComponents(Map<Component,Rectangle> dirtyComponents,
833 Component dirtyComponent,
834 java.util.List<Component> roots) {
835 int dx, dy, rootDx, rootDy;
836 Component component, rootDirtyComponent,parent;
837 Rectangle cBounds;
838
839 // Find the highest parent which is dirty. When we get out of this
840 // rootDx and rootDy will contain the translation from the
841 // rootDirtyComponent's coordinate system to the coordinates of the
842 // original dirty component. The tmp Rect is also used to compute the
843 // visible portion of the dirtyRect.
844
845 component = rootDirtyComponent = dirtyComponent;
846
847 int x = dirtyComponent.getX();
848 int y = dirtyComponent.getY();
849 int w = dirtyComponent.getWidth();
850 int h = dirtyComponent.getHeight();
851
852 dx = rootDx = 0;
853 dy = rootDy = 0;
854 tmp.setBounds(dirtyComponents.get(dirtyComponent));
855
856 // System.out.println("Collect dirty component for bound " + tmp +
857 // "component bounds is " + cBounds);;
858 SwingUtilities.computeIntersection(0,0,w,h,tmp);
859
860 if (tmp.isEmpty()) {
861 // System.out.println("Empty 1");
862 return;
863 }
864
865 for(;;) {
866 if(!(component instanceof JComponent))
867 break;
868
869 parent = component.getParent();
870 if(parent == null)
871 break;
872
873 component = parent;
874
875 dx += x;
876 dy += y;
877 tmp.setLocation(tmp.x + x, tmp.y + y);
878
879 x = component.getX();
880 y = component.getY();
881 w = component.getWidth();
882 h = component.getHeight();
883 tmp = SwingUtilities.computeIntersection(0,0,w,h,tmp);
884
885 if (tmp.isEmpty()) {
886 // System.out.println("Empty 2");
887 return;
888 }
889
890 if (dirtyComponents.get(component) != null) {
891 rootDirtyComponent = component;
892 rootDx = dx;
893 rootDy = dy;
894 }
895 }
896
897 if (dirtyComponent != rootDirtyComponent) {
898 Rectangle r;
899 tmp.setLocation(tmp.x + rootDx - dx,
900 tmp.y + rootDy - dy);
901 r = dirtyComponents.get(rootDirtyComponent);
902 SwingUtilities.computeUnion(tmp.x,tmp.y,tmp.width,tmp.height,r);
903 }
904
905 // If we haven't seen this root before, then we need to add it to the
906 // list of root dirty Views.
907
908 if (!roots.contains(rootDirtyComponent))
909 roots.add(rootDirtyComponent);
910 }
911
912
913 /**
914 * Returns a string that displays and identifies this
915 * object's properties.
916 *
917 * @return a String representation of this object
918 */
919 public synchronized String toString() {
920 StringBuffer sb = new StringBuffer();
921 if(dirtyComponents != null)
922 sb.append("" + dirtyComponents);
923 return sb.toString();
924 }
925
926
927 /**
928 * Return the offscreen buffer that should be used as a double buffer with
929 * the component <code>c</code>.
930 * By default there is a double buffer per RepaintManager.
931 * The buffer might be smaller than <code>(proposedWidth,proposedHeight)</code>
932 * This happens when the maximum double buffer size as been set for the receiving
933 * repaint manager.
934 */
935 public Image getOffscreenBuffer(Component c,int proposedWidth,int proposedHeight) {
936 RepaintManager delegate = getDelegate(c);
937 if (delegate != null) {
938 return delegate.getOffscreenBuffer(c, proposedWidth, proposedHeight);
939 }
940 return _getOffscreenBuffer(c, proposedWidth, proposedHeight);
941 }
942
943 /**
944 * Return a volatile offscreen buffer that should be used as a
945 * double buffer with the specified component <code>c</code>.
946 * The image returned will be an instance of VolatileImage, or null
947 * if a VolatileImage object could not be instantiated.
948 * This buffer might be smaller than <code>(proposedWidth,proposedHeight)</code>.
949 * This happens when the maximum double buffer size has been set for this
950 * repaint manager.
951 *
952 * @see java.awt.image.VolatileImage
953 * @since 1.4
954 */
955 public Image getVolatileOffscreenBuffer(Component c,
956 int proposedWidth,int proposedHeight) {
957 RepaintManager delegate = getDelegate(c);
958 if (delegate != null) {
959 return delegate.getVolatileOffscreenBuffer(c, proposedWidth,
960 proposedHeight);
961 }
962
963 // If the window is non-opaque, it's double-buffered at peer's level
964 Window w = (c instanceof Window) ? (Window)c : SwingUtilities.getWindowAncestor(c);
965 if (!w.isOpaque()) {
966 Toolkit tk = Toolkit.getDefaultToolkit();
967 if ((tk instanceof SunToolkit) && (((SunToolkit)tk).needUpdateWindow())) {
968 return null;
969 }
970 }
971
972 GraphicsConfiguration config = c.getGraphicsConfiguration();
973 if (config == null) {
974 config = GraphicsEnvironment.getLocalGraphicsEnvironment().
975 getDefaultScreenDevice().getDefaultConfiguration();
976 }
977 Dimension maxSize = getDoubleBufferMaximumSize();
978 int width = proposedWidth < 1 ? 1 :
979 (proposedWidth > maxSize.width? maxSize.width : proposedWidth);
980 int height = proposedHeight < 1 ? 1 :
981 (proposedHeight > maxSize.height? maxSize.height : proposedHeight);
982 VolatileImage image = volatileMap.get(config);
983 if (image == null || image.getWidth() < width ||
984 image.getHeight() < height) {
985 if (image != null) {
986 image.flush();
987 }
988 image = config.createCompatibleVolatileImage(width, height);
989 volatileMap.put(config, image);
990 }
991 return image;
992 }
993
994 private Image _getOffscreenBuffer(Component c, int proposedWidth, int proposedHeight) {
995 Dimension maxSize = getDoubleBufferMaximumSize();
996 DoubleBufferInfo doubleBuffer;
997 int width, height;
998
999 // If the window is non-opaque, it's double-buffered at peer's level
1000 Window w = (c instanceof Window) ? (Window)c : SwingUtilities.getWindowAncestor(c);
1001 if (!w.isOpaque()) {
1002 Toolkit tk = Toolkit.getDefaultToolkit();
1003 if ((tk instanceof SunToolkit) && (((SunToolkit)tk).needUpdateWindow())) {
1004 return null;
1005 }
1006 }
1007
1008 if (standardDoubleBuffer == null) {
1009 standardDoubleBuffer = new DoubleBufferInfo();
1010 }
1011 doubleBuffer = standardDoubleBuffer;
1012
1013 width = proposedWidth < 1? 1 :
1014 (proposedWidth > maxSize.width? maxSize.width : proposedWidth);
1015 height = proposedHeight < 1? 1 :
1016 (proposedHeight > maxSize.height? maxSize.height : proposedHeight);
1017
1018 if (doubleBuffer.needsReset || (doubleBuffer.image != null &&
1019 (doubleBuffer.size.width < width ||
1020 doubleBuffer.size.height < height))) {
1021 doubleBuffer.needsReset = false;
1022 if (doubleBuffer.image != null) {
1023 doubleBuffer.image.flush();
1024 doubleBuffer.image = null;
1025 }
1026 width = Math.max(doubleBuffer.size.width, width);
1027 height = Math.max(doubleBuffer.size.height, height);
1028 }
1029
1030 Image result = doubleBuffer.image;
1031
1032 if (doubleBuffer.image == null) {
1033 result = c.createImage(width , height);
1034 doubleBuffer.size = new Dimension(width, height);
1035 if (c instanceof JComponent) {
1036 ((JComponent)c).setCreatedDoubleBuffer(true);
1037 doubleBuffer.image = result;
1038 }
1039 // JComponent will inform us when it is no longer valid
1040 // (via removeNotify) we have no such hook to other components,
1041 // therefore we don't keep a ref to the Component
1042 // (indirectly through the Image) by stashing the image.
1043 }
1044 return result;
1045 }
1046
1047
1048 /** Set the maximum double buffer size. **/
1049 public void setDoubleBufferMaximumSize(Dimension d) {
1050 doubleBufferMaxSize = d;
1051 if (doubleBufferMaxSize == null) {
1052 clearImages();
1053 } else {
1054 clearImages(d.width, d.height);
1055 }
1056 }
1057
1058 private void clearImages() {
1059 clearImages(0, 0);
1060 }
1061
1062 private void clearImages(int width, int height) {
1063 if (standardDoubleBuffer != null && standardDoubleBuffer.image != null) {
1064 if (standardDoubleBuffer.image.getWidth(null) > width ||
1065 standardDoubleBuffer.image.getHeight(null) > height) {
1066 standardDoubleBuffer.image.flush();
1067 standardDoubleBuffer.image = null;
1068 }
1069 }
1070 // Clear out the VolatileImages
1071 Iterator gcs = volatileMap.keySet().iterator();
1072 while (gcs.hasNext()) {
1073 GraphicsConfiguration gc = (GraphicsConfiguration)gcs.next();
1074 VolatileImage image = volatileMap.get(gc);
1075 if (image.getWidth() > width || image.getHeight() > height) {
1076 image.flush();
1077 gcs.remove();
1078 }
1079 }
1080 }
1081
1082 /**
1083 * Returns the maximum double buffer size.
1084 *
1085 * @return a Dimension object representing the maximum size
1086 */
1087 public Dimension getDoubleBufferMaximumSize() {
1088 if (doubleBufferMaxSize == null) {
1089 try {
1090 Rectangle virtualBounds = new Rectangle();
1091 GraphicsEnvironment ge = GraphicsEnvironment.
1092 getLocalGraphicsEnvironment();
1093 for (GraphicsDevice gd : ge.getScreenDevices()) {
1094 GraphicsConfiguration gc = gd.getDefaultConfiguration();
1095 virtualBounds = virtualBounds.union(gc.getBounds());
1096 }
1097 doubleBufferMaxSize = new Dimension(virtualBounds.width,
1098 virtualBounds.height);
1099 } catch (HeadlessException e) {
1100 doubleBufferMaxSize = new Dimension(Integer.MAX_VALUE, Integer.MAX_VALUE);
1101 }
1102 }
1103 return doubleBufferMaxSize;
1104 }
1105
1106 /**
1107 * Enables or disables double buffering in this RepaintManager.
1108 * CAUTION: The default value for this property is set for optimal
1109 * paint performance on the given platform and it is not recommended
1110 * that programs modify this property directly.
1111 *
1112 * @param aFlag true to activate double buffering
1113 * @see #isDoubleBufferingEnabled
1114 */
1115 public void setDoubleBufferingEnabled(boolean aFlag) {
1116 doubleBufferingEnabled = aFlag;
1117 PaintManager paintManager = getPaintManager();
1118 if (!aFlag && paintManager.getClass() != PaintManager.class) {
1119 setPaintManager(new PaintManager());
1120 }
1121 }
1122
1123 /**
1124 * Returns true if this RepaintManager is double buffered.
1125 * The default value for this property may vary from platform
1126 * to platform. On platforms where native double buffering
1127 * is supported in the AWT, the default value will be <code>false</code>
1128 * to avoid unnecessary buffering in Swing.
1129 * On platforms where native double buffering is not supported,
1130 * the default value will be <code>true</code>.
1131 *
1132 * @return true if this object is double buffered
1133 */
1134 public boolean isDoubleBufferingEnabled() {
1135 return doubleBufferingEnabled;
1136 }
1137
1138 /**
1139 * This resets the double buffer. Actually, it marks the double buffer
1140 * as invalid, the double buffer will then be recreated on the next
1141 * invocation of getOffscreenBuffer.
1142 */
1143 void resetDoubleBuffer() {
1144 if (standardDoubleBuffer != null) {
1145 standardDoubleBuffer.needsReset = true;
1146 }
1147 }
1148
1149 /**
1150 * This resets the volatile double buffer.
1151 */
1152 void resetVolatileDoubleBuffer(GraphicsConfiguration gc) {
1153 Image image = volatileMap.remove(gc);
1154 if (image != null) {
1155 image.flush();
1156 }
1157 }
1158
1159 /**
1160 * Returns true if we should use the <code>Image</code> returned
1161 * from <code>getVolatileOffscreenBuffer</code> to do double buffering.
1162 */
1163 boolean useVolatileDoubleBuffer() {
1164 return volatileImageBufferEnabled;
1165 }
1166
1167 /**
1168 * Returns true if the current thread is the thread painting. This
1169 * will return false if no threads are painting.
1170 */
1171 private synchronized boolean isPaintingThread() {
1172 return (Thread.currentThread() == paintThread);
1173 }
1174 //
1175 // Paint methods. You very, VERY rarely need to invoke these.
1176 // They are invoked directly from JComponent's painting code and
1177 // when painting happens outside the normal flow: DefaultDesktopManager
1178 // and JViewport. If you end up needing these methods in other places be
1179 // careful that you don't get stuck in a paint loop.
1180 //
1181
1182 /**
1183 * Paints a region of a component
1184 *
1185 * @param paintingComponent Component to paint
1186 * @param bufferComponent Component to obtain buffer for
1187 * @param g Graphics to paint to
1188 * @param x X-coordinate
1189 * @param y Y-coordinate
1190 * @param w Width
1191 * @param h Height
1192 */
1193 void paint(JComponent paintingComponent,
1194 JComponent bufferComponent, Graphics g,
1195 int x, int y, int w, int h) {
1196 PaintManager paintManager = getPaintManager();
1197 if (!isPaintingThread()) {
1198 // We're painting to two threads at once. PaintManager deals
1199 // with this a bit better than BufferStrategyPaintManager, use
1200 // it to avoid possible exceptions/corruption.
1201 if (paintManager.getClass() != PaintManager.class) {
1202 paintManager = new PaintManager();
1203 paintManager.repaintManager = this;
1204 }
1205 }
1206 if (!paintManager.paint(paintingComponent, bufferComponent, g,
1207 x, y, w, h)) {
1208 g.setClip(x, y, w, h);
1209 paintingComponent.paintToOffscreen(g, x, y, w, h, x + w, y + h);
1210 }
1211 }
1212
1213 /**
1214 * Does a copy area on the specified region.
1215 *
1216 * @param clip Whether or not the copyArea needs to be clipped to the
1217 * Component's bounds.
1218 */
1219 void copyArea(JComponent c, Graphics g, int x, int y, int w, int h,
1220 int deltaX, int deltaY, boolean clip) {
1221 getPaintManager().copyArea(c, g, x, y, w, h, deltaX, deltaY, clip);
1222 }
1223
1224 /**
1225 * Invoked prior to any paint/copyArea method calls. This will
1226 * be followed by an invocation of <code>endPaint</code>.
1227 * <b>WARNING</b>: Callers of this method need to wrap the call
1228 * in a <code>try/finally</code>, otherwise if an exception is thrown
1229 * during the course of painting the RepaintManager may
1230 * be left in a state in which the screen is not updated, eg:
1231 * <pre>
1232 * repaintManager.beginPaint();
1233 * try {
1234 * repaintManager.paint(...);
1235 * } finally {
1236 * repaintManager.endPaint();
1237 * }
1238 * </pre>
1239 */
1240 void beginPaint() {
1241 boolean multiThreadedPaint = false;
1242 int paintDepth;
1243 Thread currentThread = Thread.currentThread();
1244 synchronized(this) {
1245 paintDepth = this.paintDepth;
1246 if (paintThread == null || currentThread == paintThread) {
1247 paintThread = currentThread;
1248 this.paintDepth++;
1249 } else {
1250 multiThreadedPaint = true;
1251 }
1252 }
1253 if (!multiThreadedPaint && paintDepth == 0) {
1254 getPaintManager().beginPaint();
1255 }
1256 }
1257
1258 /**
1259 * Invoked after <code>beginPaint</code> has been invoked.
1260 */
1261 void endPaint() {
1262 if (isPaintingThread()) {
1263 PaintManager paintManager = null;
1264 synchronized(this) {
1265 if (--paintDepth == 0) {
1266 paintManager = getPaintManager();
1267 }
1268 }
1269 if (paintManager != null) {
1270 paintManager.endPaint();
1271 synchronized(this) {
1272 paintThread = null;
1273 }
1274 }
1275 }
1276 }
1277
1278 /**
1279 * If possible this will show a previously rendered portion of
1280 * a Component. If successful, this will return true, otherwise false.
1281 * <p>
1282 * WARNING: This method is invoked from the native toolkit thread, be
1283 * very careful as to what methods this invokes!
1284 */
1285 boolean show(Container c, int x, int y, int w, int h) {
1286 return getPaintManager().show(c, x, y, w, h);
1287 }
1288
1289 /**
1290 * Invoked when the doubleBuffered or useTrueDoubleBuffering
1291 * properties of a JRootPane change. This may come in on any thread.
1292 */
1293 void doubleBufferingChanged(JRootPane rootPane) {
1294 getPaintManager().doubleBufferingChanged(rootPane);
1295 }
1296
1297 /**
1298 * Sets the <code>PaintManager</code> that is used to handle all
1299 * double buffered painting.
1300 *
1301 * @param paintManager The PaintManager to use. Passing in null indicates
1302 * the fallback PaintManager should be used.
1303 */
1304 void setPaintManager(PaintManager paintManager) {
1305 if (paintManager == null) {
1306 paintManager = new PaintManager();
1307 }
1308 PaintManager oldPaintManager;
1309 synchronized(this) {
1310 oldPaintManager = this.paintManager;
1311 this.paintManager = paintManager;
1312 paintManager.repaintManager = this;
1313 }
1314 if (oldPaintManager != null) {
1315 oldPaintManager.dispose();
1316 }
1317 }
1318
1319 private synchronized PaintManager getPaintManager() {
1320 if (paintManager == null) {
1321 PaintManager paintManager = null;
1322 if (doubleBufferingEnabled && !nativeDoubleBuffering) {
1323 switch (bufferStrategyType) {
1324 case BUFFER_STRATEGY_NOT_SPECIFIED:
1325 Toolkit tk = Toolkit.getDefaultToolkit();
1326 if (tk instanceof SunToolkit) {
1327 SunToolkit stk = (SunToolkit) tk;
1328 if (stk.useBufferPerWindow()) {
1329 paintManager = new BufferStrategyPaintManager();
1330 }
1331 }
1332 break;
1333 case BUFFER_STRATEGY_SPECIFIED_ON:
1334 paintManager = new BufferStrategyPaintManager();
1335 break;
1336 default:
1337 break;
1338 }
1339 }
1340 // null case handled in setPaintManager
1341 setPaintManager(paintManager);
1342 }
1343 return paintManager;
1344 }
1345
1346 private void scheduleProcessingRunnable() {
1347 scheduleProcessingRunnable(AppContext.getAppContext());
1348 }
1349
1350 private void scheduleProcessingRunnable(AppContext context) {
1351 if (processingRunnable.markPending()) {
1352 Toolkit tk = Toolkit.getDefaultToolkit();
1353 if (tk instanceof SunToolkit) {
1354 SunToolkit.getSystemEventQueueImplPP(context).
1355 postEvent(new InvocationEvent(Toolkit.getDefaultToolkit(),
1356 processingRunnable));
1357 } else {
1358 Toolkit.getDefaultToolkit().getSystemEventQueue().
1359 postEvent(new InvocationEvent(Toolkit.getDefaultToolkit(),
1360 processingRunnable));
1361 }
1362 }
1363 }
1364
1365
1366 /**
1367 * PaintManager is used to handle all double buffered painting for
1368 * Swing. Subclasses should call back into the JComponent method
1369 * <code>paintToOffscreen</code> to handle the actual painting.
1370 */
1371 static class PaintManager {
1372 /**
1373 * RepaintManager the PaintManager has been installed on.
1374 */
1375 protected RepaintManager repaintManager;
1376 boolean isRepaintingRoot;
1377
1378 /**
1379 * Paints a region of a component
1380 *
1381 * @param paintingComponent Component to paint
1382 * @param bufferComponent Component to obtain buffer for
1383 * @param g Graphics to paint to
1384 * @param x X-coordinate
1385 * @param y Y-coordinate
1386 * @param w Width
1387 * @param h Height
1388 * @return true if painting was successful.
1389 */
1390 public boolean paint(JComponent paintingComponent,
1391 JComponent bufferComponent, Graphics g,
1392 int x, int y, int w, int h) {
1393 // First attempt to use VolatileImage buffer for performance.
1394 // If this fails (which should rarely occur), fallback to a
1395 // standard Image buffer.
1396 boolean paintCompleted = false;
1397 Image offscreen;
1398 if (repaintManager.useVolatileDoubleBuffer() &&
1399 (offscreen = getValidImage(repaintManager.
1400 getVolatileOffscreenBuffer(bufferComponent, w, h))) != null) {
1401 VolatileImage vImage = (java.awt.image.VolatileImage)offscreen;
1402 GraphicsConfiguration gc = bufferComponent.
1403 getGraphicsConfiguration();
1404 for (int i = 0; !paintCompleted &&
1405 i < RepaintManager.VOLATILE_LOOP_MAX; i++) {
1406 if (vImage.validate(gc) ==
1407 VolatileImage.IMAGE_INCOMPATIBLE) {
1408 repaintManager.resetVolatileDoubleBuffer(gc);
1409 offscreen = repaintManager.getVolatileOffscreenBuffer(
1410 bufferComponent,w, h);
1411 vImage = (java.awt.image.VolatileImage)offscreen;
1412 }
1413 paintDoubleBuffered(paintingComponent, vImage, g, x, y,
1414 w, h);
1415 paintCompleted = !vImage.contentsLost();
1416 }
1417 }
1418 // VolatileImage painting loop failed, fallback to regular
1419 // offscreen buffer
1420 if (!paintCompleted && (offscreen = getValidImage(
1421 repaintManager.getOffscreenBuffer(
1422 bufferComponent, w, h))) != null) {
1423 paintDoubleBuffered(paintingComponent, offscreen, g, x, y, w,
1424 h);
1425 paintCompleted = true;
1426 }
1427 return paintCompleted;
1428 }
1429
1430 /**
1431 * Does a copy area on the specified region.
1432 */
1433 public void copyArea(JComponent c, Graphics g, int x, int y, int w,
1434 int h, int deltaX, int deltaY, boolean clip) {
1435 g.copyArea(x, y, w, h, deltaX, deltaY);
1436 }
1437
1438 /**
1439 * Invoked prior to any calls to paint or copyArea.
1440 */
1441 public void beginPaint() {
1442 }
1443
1444 /**
1445 * Invoked to indicate painting has been completed.
1446 */
1447 public void endPaint() {
1448 }
1449
1450 /**
1451 * Shows a region of a previously rendered component. This
1452 * will return true if successful, false otherwise. The default
1453 * implementation returns false.
1454 */
1455 public boolean show(Container c, int x, int y, int w, int h) {
1456 return false;
1457 }
1458
1459 /**
1460 * Invoked when the doubleBuffered or useTrueDoubleBuffering
1461 * properties of a JRootPane change. This may come in on any thread.
1462 */
1463 public void doubleBufferingChanged(JRootPane rootPane) {
1464 }
1465
1466 /**
1467 * Paints a portion of a component to an offscreen buffer.
1468 */
1469 protected void paintDoubleBuffered(JComponent c, Image image,
1470 Graphics g, int clipX, int clipY,
1471 int clipW, int clipH) {
1472 Graphics osg = image.getGraphics();
1473 int bw = Math.min(clipW, image.getWidth(null));
1474 int bh = Math.min(clipH, image.getHeight(null));
1475 int x,y,maxx,maxy;
1476
1477 try {
1478 for(x = clipX, maxx = clipX+clipW; x < maxx ; x += bw ) {
1479 for(y=clipY, maxy = clipY + clipH; y < maxy ; y += bh) {
1480 osg.translate(-x, -y);
1481 osg.setClip(x,y,bw,bh);
1482 c.paintToOffscreen(osg, x, y, bw, bh, maxx, maxy);
1483 g.setClip(x, y, bw, bh);
1484 g.drawImage(image, x, y, c);
1485 osg.translate(x, y);
1486 }
1487 }
1488 } finally {
1489 osg.dispose();
1490 }
1491 }
1492
1493 /**
1494 * If <code>image</code> is non-null with a positive size it
1495 * is returned, otherwise null is returned.
1496 */
1497 private Image getValidImage(Image image) {
1498 if (image != null && image.getWidth(null) > 0 &&
1499 image.getHeight(null) > 0) {
1500 return image;
1501 }
1502 return null;
1503 }
1504
1505 /**
1506 * Schedules a repaint for the specified component. This differs
1507 * from <code>root.repaint</code> in that if the RepaintManager is
1508 * currently processing paint requests it'll process this request
1509 * with the current set of requests.
1510 */
1511 protected void repaintRoot(JComponent root) {
1512 assert (repaintManager.repaintRoot == null);
1513 if (repaintManager.painting) {
1514 repaintManager.repaintRoot = root;
1515 }
1516 else {
1517 root.repaint();
1518 }
1519 }
1520
1521 /**
1522 * Returns true if the component being painted is the root component
1523 * that was previously passed to <code>repaintRoot</code>.
1524 */
1525 protected boolean isRepaintingRoot() {
1526 return isRepaintingRoot;
1527 }
1528
1529 /**
1530 * Cleans up any state. After invoked the PaintManager will no
1531 * longer be used anymore.
1532 */
1533 protected void dispose() {
1534 }
1535 }
1536
1537
1538 private class DoubleBufferInfo {
1539 public Image image;
1540 public Dimension size;
1541 public boolean needsReset = false;
1542 }
1543
1544
1545 /**
1546 * Listener installed to detect display changes. When display changes,
1547 * schedules a callback to notify all RepaintManagers of the display
1548 * changes. Only one DisplayChangedHandler is ever installed. The
1549 * singleton instance will schedule notification for all AppContexts.
1550 */
1551 private static final class DisplayChangedHandler implements
1552 DisplayChangedListener {
1553 public void displayChanged() {
1554 scheduleDisplayChanges();
1555 }
1556
1557 public void paletteChanged() {
1558 }
1559
1560 private void scheduleDisplayChanges() {
1561 // To avoid threading problems, we notify each RepaintManager
1562 // on the thread it was created on.
1563 for (Object c : AppContext.getAppContexts()) {
1564 AppContext context = (AppContext) c;
1565 synchronized(context) {
1566 if (!context.isDisposed()) {
1567 EventQueue eventQueue = (EventQueue)context.get(
1568 AppContext.EVENT_QUEUE_KEY);
1569 if (eventQueue != null) {
1570 eventQueue.postEvent(new InvocationEvent(
1571 Toolkit.getDefaultToolkit(),
1572 new DisplayChangedRunnable()));
1573 }
1574 }
1575 }
1576 }
1577 }
1578 }
1579
1580
1581 private static final class DisplayChangedRunnable implements Runnable {
1582 public void run() {
1583 RepaintManager.currentManager((JComponent)null).displayChanged();
1584 }
1585 }
1586
1587
1588 /**
1589 * Runnable used to process all repaint/revalidate requests.
1590 */
1591 private final class ProcessingRunnable implements Runnable {
1592 // If true, we're wainting on the EventQueue.
1593 private boolean pending;
1594
1595 /**
1596 * Marks this processing runnable as pending. If this was not
1597 * already marked as pending, true is returned.
1598 */
1599 public synchronized boolean markPending() {
1600 if (!pending) {
1601 pending = true;
1602 return true;
1603 }
1604 return false;
1605 }
1606
1607 public void run() {
1608 synchronized (this) {
1609 pending = false;
1610 }
1611 // First pass, flush any heavy paint events into real paint
1612 // events. If there are pending heavy weight requests this will
1613 // result in q'ing this request up one more time. As
1614 // long as no other requests come in between now and the time
1615 // the second one is processed nothing will happen. This is not
1616 // ideal, but the logic needed to suppress the second request is
1617 // more headache than it's worth.
1618 scheduleHeavyWeightPaints();
1619 // Do the actual validation and painting.
1620 validateInvalidComponents();
1621 prePaintDirtyRegions();
1622 }
1623 }
1624 private RepaintManager getDelegate(Component c) {
1625 RepaintManager delegate = SwingUtilities3.getDelegateRepaintManager(c);
1626 if (this == delegate) {
1627 delegate = null;
1628 }
1629 return delegate;
1630 }
1631}