001/*
002 * $Id$
003 *
004 * Copyright 2009 Sun Microsystems, Inc., 4150 Network Circle,
005 * Santa Clara, California 95054, U.S.A. All rights reserved.
006 *
007 * This library is free software; you can redistribute it and/or
008 * modify it under the terms of the GNU Lesser General Public
009 * License as published by the Free Software Foundation; either
010 * version 2.1 of the License, or (at your option) any later version.
011 * 
012 * This library is distributed in the hope that it will be useful,
013 * but WITHOUT ANY WARRANTY; without even the implied warranty of
014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
015 * Lesser General Public License for more details.
016 * 
017 * You should have received a copy of the GNU Lesser General Public
018 * License along with this library; if not, write to the Free Software
019 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
020 *
021 */
022package org.jdesktop.swingx.plaf.synth;
023
024import java.awt.Graphics;
025import java.awt.Rectangle;
026import java.beans.PropertyChangeEvent;
027
028import javax.swing.JComponent;
029import javax.swing.LookAndFeel;
030import javax.swing.UIManager;
031import javax.swing.plaf.synth.ColorType;
032import javax.swing.plaf.synth.Region;
033import javax.swing.plaf.synth.SynthConstants;
034import javax.swing.plaf.synth.SynthContext;
035import javax.swing.plaf.synth.SynthLookAndFeel;
036import javax.swing.plaf.synth.SynthPainter;
037import javax.swing.plaf.synth.SynthStyle;
038
039/**
040 * Utility class as stand-in for package private synth utility methods.
041 * 
042 * @author Jeanette Winzenburg
043 */
044public class SynthUtils {
045
046//----------------------- context-related
047    
048    /**
049     * Used to avoid null painter checks everywhere.
050     */
051    private static SynthPainter NULL_PAINTER = new SynthPainter() {};
052    
053    /**
054     * Returns a SynthContext with the specified values. 
055     *
056     * @param component JComponent
057     * @param region Identifies the portion of the JComponent
058     * @param style Style associated with the component
059     * @param state State of the component as defined in SynthConstants.
060     * @return a SynthContext with the specified values.
061     * 
062     * @throws NullPointerException if component, region of style is null.
063     * 
064     */
065    public static SynthContext getContext(JComponent c, Region region, SynthStyle style, int state) {
066        return new SynthContext(c, region, style, state);
067    }
068
069    /**
070     * @param context
071     * @param style
072     * @return
073     */
074    public static SynthContext getContext(SynthContext context, SynthStyle style) {
075        if (context.getStyle().equals(style)) return context;
076        return getContext(context.getComponent(), context.getRegion(), style, context.getComponentState());
077    }
078    /**
079     * Returns a context with the given component state and all other fields same as input context.
080     * 
081     * @param context the context, must not be null 
082     * @param state the component state.
083     * @return a context with the given component state and other fields as inpu context.
084     */
085    public static SynthContext getContext(SynthContext context, int state) {
086        if (context.getComponentState() == state) return context;
087        return getContext(context.getComponent(), context.getRegion(), context.getStyle(), state);
088    }
089    
090    /**
091     * Returns a SynthPainter from the context's style. Fall-back to default if 
092     * none available.
093     * 
094     * @param context SynthContext containing the style, must not be null.
095     * @return a SynthPainter from the context's style, or a default if null.
096     */
097    public static SynthPainter getPainter(SynthContext context) {
098        SynthPainter painter = context.getStyle().getPainter(context);
099        return painter != null ? painter : NULL_PAINTER;
100    }
101    
102//------------------- style-related
103    
104    /**
105     * Returns true if the Style should be updated in response to the
106     * specified PropertyChangeEvent. This forwards to
107     * <code>shouldUpdateStyleOnAncestorChanged</code> as necessary.
108     */
109    public static boolean shouldUpdateStyle(PropertyChangeEvent event) {
110        String eName = event.getPropertyName();
111        if ("name" == eName) {
112            // Always update on a name change
113            return true;
114        }
115        else if ("componentOrientation" == eName) {
116            // Always update on a component orientation change
117            return true;
118        }
119        else if ("ancestor" == eName && event.getNewValue() != null) {
120            // Only update on an ancestor change when getting a valid
121            // parent and the LookAndFeel wants this.
122            LookAndFeel laf = UIManager.getLookAndFeel();
123            return (laf instanceof SynthLookAndFeel &&
124                    ((SynthLookAndFeel)laf).
125                     shouldUpdateStyleOnAncestorChanged());
126        }
127        // Note: The following two nimbus based overrides should be refactored
128        // to be in the Nimbus LAF. Due to constraints in an update release,
129        // we couldn't actually provide the public API necessary to allow
130        // NimbusLookAndFeel (a subclass of SynthLookAndFeel) to provide its
131        // own rules for shouldUpdateStyle.
132        else if ("Nimbus.Overrides" == eName) {
133            // Always update when the Nimbus.Overrides client property has
134            // been changed
135            return true;
136        }
137        else if ("Nimbus.Overrides.InheritDefaults" == eName) {
138            // Always update when the Nimbus.Overrides.InheritDefaults
139            // client property has changed
140            return true;
141        }
142        else if ("JComponent.sizeVariant" == eName) {
143            // Always update when the JComponent.sizeVariant
144            // client property has changed
145            return true;
146        }
147        return false;
148    }
149    
150//--------------- component related
151    
152    public static int getComponentState(JComponent c) {
153        if (c.isEnabled()) {
154            if (c.isFocusOwner()) {
155                return SynthConstants.ENABLED | SynthConstants.FOCUSED;
156            }
157            return SynthConstants.ENABLED;
158        }
159        return SynthConstants.DISABLED;
160    }
161
162    // ---------------- divers ...
163
164    /**
165     * A convenience method that handles painting of the background. All SynthUI
166     * implementations should override update and invoke this method.
167     * 
168     * @param context must not be null
169     * @param g must not be null
170     */
171    public static void update(SynthContext context, Graphics g) {
172        update(context, g, null);
173    }
174    
175    /**
176     * A convenience method that handles painting of the background. All SynthUI
177     * implementations should override update and invoke this method.
178     * 
179     * @param context must not be null
180     * @param g must not be null
181     * @param the bounds to fill, may be null to indicate the complete size
182     */
183    public static void update(SynthContext context, Graphics g, Rectangle bounds) {
184        JComponent c = context.getComponent();
185        SynthStyle style = context.getStyle();
186        int x, y, width, height;
187
188        if (bounds == null) {
189            x = 0;
190            y = 0;
191            width = c.getWidth();
192            height = c.getHeight();
193        } else {
194            x = bounds.x;
195            y = bounds.y;
196            width = bounds.width;
197            height = bounds.height;
198        }
199
200        // Fill in the background, if necessary.
201        boolean subregion = context.getRegion().isSubregion();
202        if ((subregion && style.isOpaque(context))
203                || (!subregion && c.isOpaque())) {
204            g.setColor(style.getColor(context, ColorType.BACKGROUND));
205            g.fillRect(x, y, width, height);
206        }
207    }
208
209}
210