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.plaf.ext.transform;
033    
034    import java.awt.Point;
035    import java.awt.Rectangle;
036    import java.lang.reflect.Field;
037    import java.lang.reflect.Method;
038    
039    import javax.swing.JComponent;
040    import javax.swing.RepaintManager;
041    import javax.swing.SwingUtilities;
042    
043    import org.jdesktop.jxlayer.JXLayer;
044    import org.jdesktop.jxlayer.plaf.LayerUI;
045    import org.pbjar.jxlayer.plaf.ext.TransformUI;
046    
047    /**
048     * To avoid duplicate code, this class implements the actual logic for
049     * {@link TransformRPMSwingX} and {@link TransformRPMFallBack}.
050     * 
051     * @author Piet Blok
052     */
053    public class TransformRPMImpl {
054    
055        /**
056         * A flag, indicating whether or not a very dirty initialization on created
057         * {@link RepaintManager}s must be performed.
058         * 
059         * @see #hackInitialization(RepaintManager, RepaintManager)
060         */
061        public static boolean hack = false;
062    
063        /**
064         * Searches upwards in the component hierarchy for a {@link JXLayer}
065         * ancestor with an enabled {@link TransformUI}.
066         * <p>
067         * If found, the dirty rectangle is transformed to a rectangle targeted at
068         * that {@link JXLayer} and the argument manager's
069         * {@link RepaintManager#addDirtyRegion(JComponent, int, int, int, int)} is
070         * invoked. {@code true} is returned.
071         * </p>
072         * <p>
073         * Else, (@code false} is returned.
074         * </p>
075         * 
076         * @param aComponent
077         *            a component
078         * @param x
079         *            the X of the dirty region
080         * @param y
081         *            the Y of the dirty region
082         * @param w
083         *            the width of the dirty region
084         * @param h
085         *            the height of the dirty region
086         * @param manager
087         *            the current {@link RepaintManager}
088         * @return {@code true} if the call is delegated to the manager with a
089         *         transformed rectangle, {@code false} otherwise
090         */
091        @SuppressWarnings("unchecked")
092        public static boolean addDirtyRegion(JComponent aComponent, int x, int y,
093                int w, int h, RepaintManager manager) {
094            if (aComponent.isShowing()) {
095                JXLayer<?> layer = findJXLayer(aComponent);
096                if (layer != null) {
097                    TransformUI ui = (TransformUI) layer.getUI();
098                    Point point = aComponent.getLocationOnScreen();
099                    SwingUtilities.convertPointFromScreen(point, layer);
100                    Rectangle transformPortRegion = ui.transform(new Rectangle(x
101                            + point.x, y + point.y, w, h),
102                            (JXLayer<JComponent>) layer);
103                    manager.addDirtyRegion((JXLayer<?>) layer,
104                            transformPortRegion.x, transformPortRegion.y,
105                            transformPortRegion.width, transformPortRegion.height);
106                    return true;
107                }
108            }
109            return false;
110        }
111    
112        /**
113         * If {@link #hack} is {@code true}, the private fields {@code paintManager}
114         * and {@code bufferStrategyType} are copied via reflection from the source
115         * manager into the destination manager.
116         * 
117         * @param sourceManager
118         * @param destinationManager
119         */
120        public static void hackInitialization(RepaintManager sourceManager,
121                RepaintManager destinationManager) {
122            if (hack) {
123                Class<RepaintManager> rpmClass = RepaintManager.class;
124                try {
125    
126                    Field fieldBufferStrategyType = rpmClass
127                            .getDeclaredField("bufferStrategyType");
128                    Field fieldPaintManager = rpmClass
129                            .getDeclaredField("paintManager");
130                    Method methodGetPaintManager = rpmClass
131                            .getDeclaredMethod("getPaintManager");
132    
133                    fieldBufferStrategyType.setAccessible(true);
134                    fieldPaintManager.setAccessible(true);
135                    methodGetPaintManager.setAccessible(true);
136    
137                    Object paintManager = methodGetPaintManager
138                            .invoke(sourceManager);
139                    short bufferStrategyType = (Short) fieldBufferStrategyType
140                            .get(sourceManager);
141    
142                    fieldBufferStrategyType.set(destinationManager,
143                            bufferStrategyType);
144                    fieldPaintManager.set(destinationManager, paintManager);
145    
146                    fieldBufferStrategyType.setAccessible(false);
147                    fieldPaintManager.setAccessible(false);
148                    methodGetPaintManager.setAccessible(false);
149    
150                    System.out.println("Copied paintManager of type: "
151                            + paintManager.getClass().getName());
152                    switch (bufferStrategyType) {
153                    case (0):
154                        System.out.println("Copied bufferStrategyType "
155                                + bufferStrategyType
156                                + ": BUFFER_STRATEGY_NOT_SPECIFIED");
157                        break;
158                    case (1):
159                        System.out.println("Copied bufferStrategyType "
160                                + bufferStrategyType
161                                + ": BUFFER_STRATEGY_SPECIFIED_ON");
162                        break;
163                    case (2):
164                        System.out.println("Copied bufferStrategyType "
165                                + bufferStrategyType
166                                + ": BUFFER_STRATEGY_SPECIFIED_OFF");
167                        break;
168                    default:
169                        System.out.println("Copied bufferStrategyType "
170                                + bufferStrategyType + ": ???");
171                    }
172                } catch (Throwable t) {
173                    t.printStackTrace(System.out);
174                }
175            }
176        }
177    
178        /**
179         * Find the first ancestor {@link JXLayer} with an enabled
180         * {@link TransformUI}.
181         * 
182         * @param aComponent
183         *            some component
184         * @return a {@link JXLayer} instance or {@code null}
185         */
186        private static JXLayer<?> findJXLayer(JComponent aComponent) {
187    
188            JXLayer<?> layer = (JXLayer<?>) SwingUtilities.getAncestorOfClass(
189                    JXLayer.class, aComponent);
190            if (layer != null) {
191                LayerUI<?> ui = ((JXLayer<?>) layer).getUI();
192                if (ui instanceof TransformUI) {
193                    if (ui.isEnabled()) {
194                        return layer;
195                    }
196                }
197                return findJXLayer(layer);
198            }
199            return null;
200        }
201    
202        private TransformRPMImpl() {
203        }
204    }