001    /*
002     * $Id: WrappingProvider.java 3188 2009-01-20 16:22:37Z kschaefe $
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     */
021    package org.jdesktop.swingx.renderer;
022    
023    import javax.swing.BorderFactory;
024    import javax.swing.Icon;
025    import javax.swing.tree.DefaultMutableTreeNode;
026    
027    import org.jdesktop.swingx.rollover.RolloverRenderer;
028    import 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     * StringValue sv = new StringValue() {
043     * 
044     *     public String getString(Object value) {
045     *         if (value instanceof File) {
046     *             return FileSystemView.getFileSystemView().getSystemDisplayName(
047     *                     (File) value);
048     *         }
049     *         return TO_STRING.getString(value);
050     *     }
051     * 
052     * };
053     * IconValue iv = new IconValue() {
054     * 
055     *     public Icon getIcon(Object value) {
056     *         if (value instanceof File) {
057     *             return FileSystemView.getFileSystemView().getSystemIcon(
058     *                     (File) value);
059     *         }
060     *         return null;
061     *     }
062     * };
063     * TreeCellRenderer r = new DefaultTreeRenderer(iv, sv);
064     * tree.setCellRenderer(r);
065     * treeTable.setTreeCellRenderer(r);
066     * </code></pre>
067     * 
068     * PENDING: ui specific focus rect variation (draw rect around icon) missing
069     * <p>
070     */
071    public class WrappingProvider extends 
072        ComponentProvider<WrappingIconPanel>  implements RolloverRenderer {
073    
074        protected ComponentProvider wrappee;
075        private boolean unwrapUserObject;
076    
077        /**
078         * Instantiates a WrappingProvider with default delegate provider.
079         * 
080         */
081        public WrappingProvider() {
082            this((ComponentProvider) null);
083        }
084    
085        /**
086         * Instantiates a WrappingProvider with default wrappee, configured
087         * to use the wrappeeStringValue. Uses the 
088         * given IconValue to configure the icon. 
089         * 
090         * @param iconValue the IconValue to use for configuring the icon.
091         * @param wrappeeStringValue the StringValue to use in the wrappee.
092         */
093        public WrappingProvider(IconValue iconValue, StringValue wrappeeStringValue) {
094            this(iconValue, wrappeeStringValue, true);
095        }
096    
097        /**
098         * Instantiates a WrappingProvider with default wrappee. Uses the 
099         * given IconValue to configure the icon. 
100         * 
101         * @param iconValue the IconValue to use for configuring the icon.
102         */
103        public WrappingProvider(IconValue iconValue) {
104            this(iconValue, null);
105        }
106       
107        /**
108         * Instantiates a WrappingProvider with default wrappee configured
109         * with the given StringValue. 
110         * 
111         * PENDING: we have a slight semantic glitch compared to super because
112         * the given StringValue is <b>not</b> for use in this provider but for use 
113         * in the wrappee!
114         * 
115         * @param wrappeeStringValue the StringValue to use in the wrappee.
116         */
117        public WrappingProvider(StringValue wrappeeStringValue) {
118            this(null, wrappeeStringValue);
119        }
120    
121        /**
122         * Instantiates a WrappingProvider with the given delegate
123         * provider for the node content. If null, a default 
124         * LabelProvider will be used. 
125         * 
126         * @param delegate the provider to use as delegate
127         */
128        public WrappingProvider(ComponentProvider delegate) {
129            this(delegate, true);
130        }
131        
132        /**
133         * Instantiates a WrappingProvider with the given delegate
134         * provider for the node content and unwrapUserObject property. 
135         * If the delegate is null, a default LabelProvider will be used. 
136         * 
137         * @param delegate the provider to use as delegate
138         * @param unwrapUserObject a flag indicating whether this provider
139         * should auto-unwrap the userObject from the context value. 
140         */
141        public WrappingProvider(ComponentProvider delegate, boolean unwrapUserObject) {
142            this(null, delegate, unwrapUserObject);
143        }
144    
145        /**
146         * Instantiates a WrappingProvider with the given delegate
147         * provider for the node content and unwrapUserObject property. 
148         * If the delegate is null, a default LabelProvider will be used. 
149         * 
150         * @param iv the icon converter to use for this provider
151         * @param delegate the provider to use as delegate
152         * @param unwrapUserObject a flag indicating whether this provider
153         *          should auto-unwrap the userObject from the context value. 
154         */
155        public WrappingProvider(IconValue iv, ComponentProvider delegate, boolean unwrapUserObject) {
156            super(iv != null ? (new MappedValue(null, iv)) : StringValues.EMPTY);
157            setWrappee(delegate);
158            setUnwrapUserObject(unwrapUserObject);
159        }
160        
161        /**
162         * Instantiates a WrappingProvider with the given delegate
163         * provider for the node content and unwrapUserObject property. 
164         * If the delegate is null, a default LabelProvider will be used. 
165         * 
166         * @param iv the icon converter to use for this provider
167         * @param delegateStringValue the StringValue to use in the wrappee.
168         * @param unwrapUserObject a flag indicating whether this provider
169         *          should auto-unwrap the userObject from the context value. 
170         */
171        public WrappingProvider(IconValue iv, StringValue delegateStringValue, boolean unwrapUserObject) {
172            this(iv, (ComponentProvider) null, unwrapUserObject);
173            getWrappee().setStringValue(delegateStringValue);
174        }
175        
176        /**
177         * Sets the given provider as delegate for the node content. 
178         * If the delegate is null, a default LabelProvider is set.<p>
179         * 
180         *  PENDING: rename to setDelegate?
181         *  
182         * @param delegate the provider to use as delegate. 
183         */
184        public void setWrappee(ComponentProvider delegate) {
185            if (delegate == null) {
186                delegate = new LabelProvider();
187            }
188            this.wrappee = delegate;
189        }
190    
191        /**
192         * Returns the delegate provider used to render the node content.
193         * 
194         * @return the provider used for rendering the node content.
195         */
196        public ComponentProvider getWrappee() {
197            return wrappee;
198        }
199        
200        /**
201         * Sets the unwrapUserObject property. If true, this provider 
202         * replaces a context value of type XXNode with its user object before
203         * delegating to the wrappee. Otherwise the value is passed as-is always.<p>
204         * 
205         * The default value is true.
206         * 
207         * @param unwrap
208         * @see #getUnwrapUserObject()
209         */
210        public void setUnwrapUserObject(boolean unwrap) {
211            this.unwrapUserObject = unwrap;
212        }
213        
214        /**
215         * Returns a boolean indicating whether this provider tries to unwrap 
216         * a userObject from a tree/table/node type value before delegating the
217         * context. 
218         * 
219         * @return a flag indicating the auto-unwrap property.
220         * 
221         * @see #setUnwrapUserObject(boolean)
222         */
223        public boolean getUnwrapUserObject() {
224            return unwrapUserObject;
225        }
226        
227        /**
228         * {@inheritDoc} <p>
229         * 
230         * Overridden to comply to contract: returns the string representation as 
231         * provided by the wrappee (as this level has no string rep). Must do the
232         * same unwrapping magic as in configuring the rendering component if the
233         * unwrapUserObject property is true. <p>
234         * 
235         * 
236         * @param value the Object to get a String representation for.
237         * 
238         * @see #setUnwrapUserObject(boolean)
239         * @see #getUnwrappedValue(Object)
240         */
241        @Override
242        public String getString(Object value) {
243            value = getUnwrappedValue(value);
244            return wrappee.getString(value);
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        public void doClick() {
382            if (isEnabled()) {
383                ((RolloverRenderer) wrappee).doClick(); 
384            }
385        }
386    
387        /**
388         * {@inheritDoc}
389         */
390        public boolean isEnabled() {
391            return (wrappee instanceof RolloverRenderer) && 
392               ((RolloverRenderer) wrappee).isEnabled();
393        }
394    
395    
396        
397    }