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+ */
25+package javax.swing;
26+
27+
28+import java.awt.*;
29+import java.awt.event.*;
30+import java.awt.peer.ComponentPeer;
31+import java.awt.peer.ContainerPeer;
32+import java.awt.image.VolatileImage;
33+import java.security.AccessController;
34+import java.util.*;
35+import java.applet.*;
36+
37+import sun.awt.AWTAccessor;
38+import sun.awt.AppContext;
39+import sun.awt.DisplayChangedListener;
40+import sun.awt.SunToolkit;
41+import sun.java2d.SunGraphicsEnvironment;
42+import sun.security.action.GetPropertyAction;
43+
44+import 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+ */
59+public 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+}