001/*
002 * $Id: WrappingProvider.java 3927 2011-02-22 16:34:11Z kleopatra $
003 *
004 * Copyright 2007 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 */
021package org.jdesktop.swingx.renderer;
022
023import javax.swing.BorderFactory;
024import javax.swing.Icon;
025import javax.swing.tree.DefaultMutableTreeNode;
026
027import org.jdesktop.swingx.rollover.RolloverRenderer;
028import org.jdesktop.swingx.treetable.TreeTableNode;
029
030
031/**
032 * Wrapping ComponentProvider for usage in tree rendering. Handles the icon
033 * itself, delegates the node content to the wrappee. Value-based icon and
034 * content mapping can be configured by custom <code>IconValue</code>s and
035 * <b>StringValue</b>, respectively.
036 * <p>
037 * 
038 * An example of how to configure a file tree by using the system icons and
039 * display names
040 * 
041 * <pre><code>
042 * TreeCellRenderer r = new DefaultTreeRenderer(
043 *         IconValues.FILE_ICON, StringValues.FILE_NAME);
044 * tree.setCellRenderer(r);
045 * treeTable.setTreeCellRenderer(r);
046 * </code></pre>
047 * 
048 * PENDING: ui specific focus rect variation (draw rect around icon) missing
049 * <p>
050 */
051public class WrappingProvider extends 
052    ComponentProvider<WrappingIconPanel>  implements RolloverRenderer {
053
054    protected ComponentProvider<?> wrappee;
055    private boolean unwrapUserObject;
056
057    /**
058     * Instantiates a WrappingProvider with default delegate provider.
059     * 
060     */
061    public WrappingProvider() {
062        this((ComponentProvider<?>) null);
063    }
064
065    /**
066     * Instantiates a WrappingProvider with default wrappee, configured
067     * to use the wrappeeStringValue. Uses the 
068     * given IconValue to configure the icon. 
069     * 
070     * @param iconValue the IconValue to use for configuring the icon.
071     * @param wrappeeStringValue the StringValue to use in the wrappee.
072     */
073    public WrappingProvider(IconValue iconValue, StringValue wrappeeStringValue) {
074        this(iconValue, wrappeeStringValue, true);
075    }
076
077    /**
078     * Instantiates a WrappingProvider with default wrappee. Uses the 
079     * given IconValue to configure the icon. 
080     * 
081     * @param iconValue the IconValue to use for configuring the icon.
082     */
083    public WrappingProvider(IconValue iconValue) {
084        this(iconValue, null);
085    }
086   
087    /**
088     * Instantiates a WrappingProvider with default wrappee configured
089     * with the given StringValue. 
090     * 
091     * PENDING: we have a slight semantic glitch compared to super because
092     * the given StringValue is <b>not</b> for use in this provider but for use 
093     * in the wrappee!
094     * 
095     * @param wrappeeStringValue the StringValue to use in the wrappee.
096     */
097    public WrappingProvider(StringValue wrappeeStringValue) {
098        this(null, wrappeeStringValue);
099    }
100
101    /**
102     * Instantiates a WrappingProvider with the given delegate
103     * provider for the node content. If null, a default 
104     * LabelProvider will be used. 
105     * 
106     * @param delegate the provider to use as delegate
107     */
108    public WrappingProvider(ComponentProvider<?> delegate) {
109        this(delegate, true);
110    }
111    
112    /**
113     * Instantiates a WrappingProvider with the given delegate
114     * provider for the node content and unwrapUserObject property. 
115     * If the delegate is null, a default LabelProvider will be used. 
116     * 
117     * @param delegate the provider to use as delegate
118     * @param unwrapUserObject a flag indicating whether this provider
119     * should auto-unwrap the userObject from the context value. 
120     */
121    public WrappingProvider(ComponentProvider<?> delegate, boolean unwrapUserObject) {
122        this(null, delegate, unwrapUserObject);
123    }
124
125    /**
126     * Instantiates a WrappingProvider with the given delegate
127     * provider for the node content and unwrapUserObject property. 
128     * If the delegate is null, a default LabelProvider will be used. 
129     * 
130     * @param iv the icon converter to use for this provider
131     * @param delegate the provider to use as delegate
132     * @param unwrapUserObject a flag indicating whether this provider
133     *          should auto-unwrap the userObject from the context value. 
134     */
135    public WrappingProvider(IconValue iv, ComponentProvider<?> delegate, boolean unwrapUserObject) {
136        super(iv != null ? (new MappedValue(null, iv)) : StringValues.EMPTY);
137        setWrappee(delegate);
138        setUnwrapUserObject(unwrapUserObject);
139    }
140    
141    /**
142     * Instantiates a WrappingProvider with the given delegate
143     * provider for the node content and unwrapUserObject property. 
144     * If the delegate is null, a default LabelProvider will be used. 
145     * 
146     * @param iv the icon converter to use for this provider
147     * @param delegateStringValue the StringValue to use in the wrappee.
148     * @param unwrapUserObject a flag indicating whether this provider
149     *          should auto-unwrap the userObject from the context value. 
150     */
151    public WrappingProvider(IconValue iv, StringValue delegateStringValue, boolean unwrapUserObject) {
152        this(iv, (ComponentProvider<?>) null, unwrapUserObject);
153        getWrappee().setStringValue(delegateStringValue);
154    }
155    
156    /**
157     * Sets the given provider as delegate for the node content. 
158     * If the delegate is null, a default LabelProvider is set.<p>
159     * 
160     *  PENDING: rename to setDelegate?
161     *  
162     * @param delegate the provider to use as delegate. 
163     */
164    public void setWrappee(ComponentProvider<?> delegate) {
165        if (delegate == null) {
166            delegate = new LabelProvider();
167        }
168        this.wrappee = delegate;
169    }
170
171    /**
172     * Returns the delegate provider used to render the node content.
173     * 
174     * @return the provider used for rendering the node content.
175     */
176    public ComponentProvider<?> getWrappee() {
177        return wrappee;
178    }
179    
180    /**
181     * Sets the unwrapUserObject property. If true, this provider 
182     * replaces a context value of type XXNode with its user object before
183     * delegating to the wrappee. Otherwise the value is passed as-is always.<p>
184     * 
185     * The default value is true.
186     * 
187     * @param unwrap
188     * @see #getUnwrapUserObject()
189     */
190    public void setUnwrapUserObject(boolean unwrap) {
191        this.unwrapUserObject = unwrap;
192    }
193    
194    /**
195     * Returns a boolean indicating whether this provider tries to unwrap 
196     * a userObject from a tree/table/node type value before delegating the
197     * context. 
198     * 
199     * @return a flag indicating the auto-unwrap property.
200     * 
201     * @see #setUnwrapUserObject(boolean)
202     */
203    public boolean getUnwrapUserObject() {
204        return unwrapUserObject;
205    }
206    
207    /**
208     * {@inheritDoc} <p>
209     * 
210     * Overridden to comply to contract: returns the string representation as 
211     * provided by the wrappee (as this level has no string rep). Must do the
212     * same unwrapping magic as in configuring the rendering component if the
213     * unwrapUserObject property is true. <p>
214     * 
215     * 
216     * @param value the Object to get a String representation for.
217     * 
218     * @see #setUnwrapUserObject(boolean)
219     * @see #getUnwrappedValue(Object)
220     */
221    @Override
222    public String getString(Object value) {
223        value = getUnwrappedValue(value);
224        return wrappee.getString(value);
225    }
226
227    /**
228     * Sets a boolean indicating whether or not the main component's opacity
229     * should be applied to the Icon region.<p>
230     * 
231     * The default value is false. This covers the main use case in a JTree.
232     * 
233     * @param extendsComponentOpacity
234     */
235    public void setExtendsComponentOpacity(boolean extendsComponentOpacity) {
236        rendererComponent.setExtendsComponentOpacity(extendsComponentOpacity);
237        
238    }
239    /**
240     * @return the extendsComponentOpacity
241     */
242    public boolean getExtendsComponentOpacity() {
243        return rendererComponent.getExtendsComponentOpacity();
244    }
245    
246
247    /**
248     * Returns the value as it should be passed to the delegate. If the unwrapUserObject
249     * property is true, tries return a userObject as appropriate for the value type.
250     * Returns the given value itself, ff the property is false or the type does 
251     * not support the notion of userObject<p>
252     * 
253     * Here: unwraps userObject of DefaultMutableTreeNode and TreeTableNode.<p>
254     * 
255     * @param value the value to possibly unwrap
256     * @return the userObject if the value has an appropriate type and the 
257     *   unwrapUserObject property is true, otherwise returns the value unchanged.
258     *   
259     * @see #setUnwrapUserObject(boolean)
260     * @see #getString(Object)
261     * @see #getRendererComponent(CellContext)  
262     */
263    protected Object getUnwrappedValue(Object value) {
264        if (!getUnwrapUserObject()) return value;
265        if (value instanceof DefaultMutableTreeNode) {
266            value = ((DefaultMutableTreeNode) value).getUserObject();
267        } else if (value instanceof TreeTableNode) {
268            TreeTableNode node = (TreeTableNode) value;
269            value = node.getUserObject();
270        }
271        return value;
272    }
273
274    /**
275     * {@inheritDoc}
276     */
277    @Override
278    public WrappingIconPanel getRendererComponent(CellContext context) {
279        if (context != null) {
280            rendererComponent.setComponent(wrappee.rendererComponent);
281            Object oldValue = adjustContextValue(context);
282            // PENDING JW: sequence of config?
283            // A - first wrappee, then this allows to override configure/format methods
284            // of this class and overrule the wrappee
285            // B - first this, then wrappee allows overrule by overriding getRendererComp
286            // would take control from wrappee (f.i. Hyperlink foreground)
287            super.getRendererComponent(context);
288            wrappee.getRendererComponent(context);
289            restoreContextValue(context, oldValue);
290            return rendererComponent;
291        }
292        // PENDING JW: Findbugs barking [NP] Load of known null value
293        // probably can move the return rendererComponent from the if
294        // to here (the contract is to return the comp as-is if the
295        // context is null) - so we can do it here instead of delegating
296        // to super?
297        return super.getRendererComponent(context);
298    }
299
300    /**
301     * Restores the context value to the old value.
302     * 
303     * @param context the CellContext to restore.
304     * @param oldValue the value to restore the context to.
305     */
306    protected void restoreContextValue(CellContext context, Object oldValue) {
307        context.replaceValue(oldValue);
308    }
309
310    /**
311     * Replace the context's value with the userobject if the value is a type
312     * supporting the notion of userObject and this provider's unwrapUserObject
313     * property is true. Otherwise does nothing.<p>
314     * 
315     * Subclasses may override but must guarantee to return the original 
316     * value for restoring. 
317     * 
318     * @param context the context to adjust
319     * @return the old context value
320     * 
321     * @see #setUnwrapUserObject(boolean)
322     * @see #getString(Object)
323     */
324    protected Object adjustContextValue(CellContext context) {
325        Object oldValue = context.getValue();
326        if (getUnwrapUserObject()) {
327            context.replaceValue(getUnwrappedValue(oldValue));
328        }
329        return oldValue;
330    }
331
332    @Override
333    protected void configureState(CellContext context) {
334        rendererComponent.setBorder(BorderFactory.createEmptyBorder());
335    }
336
337//    /**
338//     * @return
339//     */
340//    private boolean isBorderAroundIcon() {
341//        return Boolean.TRUE.equals(UIManager.get("Tree.drawsFocusBorderAroundIcon"));
342//    }
343
344    @Override
345    protected WrappingIconPanel createRendererComponent() {
346        return new WrappingIconPanel();
347    }
348
349    /**
350     * {@inheritDoc} <p>
351     * 
352     * Here: implemented to set the icon.
353     */
354    @Override
355    protected void format(CellContext context) {
356        rendererComponent.setIcon(getValueAsIcon(context));
357    }
358
359    /**
360     * {@inheritDoc} <p>
361     * 
362     * Overridden to fallback to the default icons supplied by the 
363     * context if super returns null.
364     *   
365     */
366    @Override
367    protected Icon getValueAsIcon(CellContext context) {
368        Icon icon = super.getValueAsIcon(context);
369        if (icon == null) {
370            return context.getIcon();
371        }
372        return IconValue.NULL_ICON == icon ? null : icon;
373    }
374    
375    //----------------- implement RolloverController
376    
377
378    /**
379     * {@inheritDoc}
380     */
381    @Override
382    public void doClick() {
383        if (isEnabled()) {
384            ((RolloverRenderer) wrappee).doClick(); 
385        }
386    }
387
388    /**
389     * {@inheritDoc}
390     */
391    @Override
392    public boolean isEnabled() {
393        return (wrappee instanceof RolloverRenderer) && 
394           ((RolloverRenderer) wrappee).isEnabled();
395    }
396
397
398    
399}