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 }