001    /**
002     * Copyright (c) 2009, Piet Blok
003     * All rights reserved.
004     *
005     * Redistribution and use in source and binary forms, with or without
006     * modification, are permitted provided that the following conditions
007     * are met:
008     *
009     *   * Redistributions of source code must retain the above copyright
010     *     notice, this list of conditions and the following disclaimer.
011     *   * Redistributions in binary form must reproduce the above
012     *     copyright notice, this list of conditions and the following
013     *     disclaimer in the documentation and/or other materials provided
014     *     with the distribution.
015     *   * Neither the name of the copyright holder nor the names of the
016     *     contributors may be used to endorse or promote products derived
017     *     from this software without specific prior written permission.
018     *
019     * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
020     * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
021     * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
022     * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
023     * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
024     * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
025     * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
026     * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
027     * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
028     * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
029     * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
030     */
031    
032    package org.pbjar.jxlayer.repaint;
033    
034    import java.awt.Component;
035    import java.awt.Font;
036    import java.awt.event.ActionEvent;
037    import java.io.PrintWriter;
038    import java.io.StringWriter;
039    
040    import javax.swing.AbstractAction;
041    import javax.swing.Action;
042    import javax.swing.JComponent;
043    import javax.swing.JOptionPane;
044    import javax.swing.JTextPane;
045    import javax.swing.RepaintManager;
046    
047    import org.jdesktop.swingx.ForwardingRepaintManager;
048    
049    /**
050     * Utility class that ensures that a correct {@link RepaintManager} is set.
051     * 
052     * @author Piet Blok
053     */
054    public class RepaintManagerUtils {
055    
056        private static class DisplayAction extends AbstractAction {
057    
058            private static final long serialVersionUID = 1L;
059    
060            public DisplayAction() {
061                super("RPM tree");
062            }
063    
064            @Override
065            public void actionPerformed(ActionEvent e) {
066                JComponent c = (JComponent) e.getSource();
067                StringWriter sw = new StringWriter();
068                PrintWriter pw = new PrintWriter(sw);
069                pw.println("The tree for the current RepaintManager:");
070                pw.println();
071                RepaintManager manager = RepaintManager.currentManager(c);
072                appendDelegates(pw, manager);
073                pw.close();
074                String text = sw.toString();
075                JTextPane message = new JTextPane();
076                message.setFont(Font.decode(Font.MONOSPACED));
077                message.setContentType("text/plain");
078                message.setText(text);
079                message.setEditable(false);
080                JOptionPane.showMessageDialog(c, message,
081                        "The RepaintManager tree", JOptionPane.INFORMATION_MESSAGE);
082            }
083    
084            private void appendClass(PrintWriter writer, Object obj) {
085                Class<?> clazz = obj.getClass();
086                String prefix = "Class:   ";
087                while (clazz != null) {
088                    writer.println(prefix + clazz.getName());
089                    clazz = clazz.getSuperclass();
090                    prefix = "Extends: ";
091                }
092            }
093    
094            private void appendDelegates(PrintWriter writer, Object rp) {
095                appendClass(writer, rp);
096                RepaintManager delegate;
097                if (rp instanceof WrappedRepaintManager) {
098                    delegate = ((WrappedRepaintManager) rp).getDelegateManager();
099                } else if (swingX) {
100                    if (rp instanceof ForwardingRepaintManager) {
101                        delegate = ((ForwardingRepaintManager) rp)
102                                .getDelegateManager();
103                    } else {
104                        delegate = null;
105                    }
106                } else {
107                    delegate = null;
108                }
109                if (delegate != null) {
110                    writer.println();
111                    writer.println("Delegate:");
112                    appendDelegates(writer, delegate);
113                }
114            }
115    
116        }
117    
118        /**
119         * Indicates the availability of SwingX on the class path.
120         */
121        private static final boolean swingX = isSwingXAvalable();
122    
123        /**
124         * Create and return an {@link Action} that will display the delegate
125         * structure of the current {@link RepaintManager}.
126         * 
127         * @return an {@link Action} object
128         */
129        public static Action createRPDisplayAction() {
130            return new DisplayAction();
131        }
132    
133        /**
134         * Ensure that a specific {@link RepaintManager} is set according to the
135         * requirements of the {@link RepaintManagerProvider}.
136         * 
137         * @param c
138         *            a component from which the current repaint manager can be
139         *            obtained.
140         * @param provider
141         *            the provider
142         */
143        public static void ensureRepaintManagerSet(Component c,
144                RepaintManagerProvider provider) {
145            ensureImpl(RepaintManager.currentManager(c), provider);
146        }
147    
148        /**
149         * Ensure that a specific {@link RepaintManager} is set according to the
150         * requirements of the {@link RepaintManagerProvider}.
151         * 
152         * @param c
153         *            a component from which the current repaint manager can be
154         *            obtained.
155         * @param provider
156         *            the provider
157         */
158        public static void ensureRepaintManagerSet(JComponent c,
159                RepaintManagerProvider provider) {
160            ensureImpl(RepaintManager.currentManager(c), provider);
161        }
162    
163        private static RepaintManager createManager(
164                Class<? extends RepaintManager> clazz, RepaintManager delegate) {
165            try {
166                RepaintManager newManager = (RepaintManager) clazz.getConstructor(
167                        RepaintManager.class).newInstance(delegate);
168                System.out.println("Created "+ newManager.getClass().getName());
169                return newManager;
170            } catch (Throwable t) {
171                throw new RuntimeException("Cannot instantiate " + clazz.getName(),
172                        t);
173            }
174        }
175    
176        /**
177         * The actual implementation of ensure.
178         * 
179         * @param delegate
180         *            a delegate RepaintManager
181         * @param provider
182         *            the provider that provides for the type and implementation of
183         *            a delegated RepaintManager
184         */
185        private static void ensureImpl(RepaintManager delegate,
186                RepaintManagerProvider provider) {
187            /*
188             * Setup a traversal variable.
189             */
190            RepaintManager manager = delegate;
191    
192            while (!provider.isAdequate(manager.getClass())) {
193                if (swingX) {
194                    if (manager instanceof ForwardingRepaintManager) {
195                        manager = ((ForwardingRepaintManager) manager)
196                                .getDelegateManager();
197                    } else {
198                        RepaintManager.setCurrentManager(createManager(provider
199                                .getForwardingRepaintManagerClass(), delegate));
200                        break;
201                    }
202                } else {
203                    if (manager instanceof WrappedRepaintManager) {
204                        manager = ((WrappedRepaintManager) manager)
205                                .getDelegateManager();
206                    } else {
207                        RepaintManager.setCurrentManager(createManager(provider
208                                .getWrappedRepaintManagerClass(), delegate));
209                        break;
210                    }
211                }
212            }
213        }
214    
215        /**
216         * Detect the availability of the ForwardingRepaintManager class.
217         * 
218         * @return {@code} true if available, {@code false} otherwise
219         */
220        private static boolean isSwingXAvalable() {
221            try {
222                Class<?> clazz = ForwardingRepaintManager.class;
223                System.out.println("SwingX is available");
224                return clazz != null;
225            } catch (Throwable t) {
226                System.out.println("SwingX is not available");
227                return false;
228            }
229        }
230    
231        private RepaintManagerUtils() {
232    
233        }
234    
235    }