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 }