();
if (oldValue != null) {
s.add(oldValue);
}
setValue(s);
} else {
final Set> s = (Set>) oldValue;
if (s == null || s.isEmpty()) {
setValue(null);
} else {
// Set the single select to contain only the first
// selected value in the multiselect
setValue(s.iterator().next());
}
}
requestRepaint();
}
}
/**
* Does the select allow adding new options by the user. If true, the new
* options can be added to the Container. The text entered by the user is
* used as id. Note that data-source must allow adding new items.
*
* @return True if additions are allowed.
*/
public boolean isNewItemsAllowed() {
return allowNewOptions;
}
/**
* Enables or disables possibility to add new options by the user.
*
* @param allowNewOptions
* the New value of property allowNewOptions.
*/
public void setNewItemsAllowed(boolean allowNewOptions) {
// Only handle change requests
if (this.allowNewOptions != allowNewOptions) {
this.allowNewOptions = allowNewOptions;
requestRepaint();
}
}
/**
* Override the caption of an item. Setting caption explicitly overrides id,
* item and index captions.
*
* @param itemId
* the id of the item to be recaptioned.
* @param caption
* the New caption.
*/
public void setItemCaption(Object itemId, String caption) {
if (itemId != null) {
itemCaptions.put(itemId, caption);
requestRepaint();
}
}
/**
* Gets the caption of an item. The caption is generated as specified by the
* item caption mode. See setItemCaptionMode()
for more
* details.
*
* @param itemId
* the id of the item to be queried.
* @return the caption for specified item.
*/
public String getItemCaption(Object itemId) {
// Null items can not be found
if (itemId == null) {
return null;
}
String caption = null;
switch (getItemCaptionMode()) {
case ID:
caption = itemId.toString();
break;
case INDEX:
if (items instanceof Container.Indexed) {
caption = String.valueOf(((Container.Indexed) items)
.indexOfId(itemId));
} else {
caption = "ERROR: Container is not indexed";
}
break;
case ITEM:
final Item i = getItem(itemId);
if (i != null) {
caption = i.toString();
}
break;
case EXPLICIT:
caption = itemCaptions.get(itemId);
break;
case EXPLICIT_DEFAULTS_ID:
caption = itemCaptions.get(itemId);
if (caption == null) {
caption = itemId.toString();
}
break;
case PROPERTY:
final Property> p = getContainerProperty(itemId,
getItemCaptionPropertyId());
if (p != null) {
Object value = p.getValue();
if (value != null) {
caption = value.toString();
}
}
break;
}
// All items must have some captions
return caption != null ? caption : "";
}
/**
* Sets tqhe icon for an item.
*
* @param itemId
* the id of the item to be assigned an icon.
* @param icon
* the icon to use or null.
*/
public void setItemIcon(Object itemId, Resource icon) {
if (itemId != null) {
if (icon == null) {
itemIcons.remove(itemId);
} else {
itemIcons.put(itemId, icon);
}
requestRepaint();
}
}
/**
* Gets the item icon.
*
* @param itemId
* the id of the item to be assigned an icon.
* @return the icon for the item or null, if not specified.
*/
public Resource getItemIcon(Object itemId) {
final Resource explicit = itemIcons.get(itemId);
if (explicit != null) {
return explicit;
}
if (getItemIconPropertyId() == null) {
return null;
}
final Property> ip = getContainerProperty(itemId,
getItemIconPropertyId());
if (ip == null) {
return null;
}
final Object icon = ip.getValue();
if (icon instanceof Resource) {
return (Resource) icon;
}
return null;
}
/**
* Sets the item caption mode.
*
*
* The mode can be one of the following ones:
*
* ITEM_CAPTION_MODE_EXPLICIT_DEFAULTS_ID
: Items
* Id-objects toString
is used as item caption. If caption is
* explicitly specified, it overrides the id-caption.
* ITEM_CAPTION_MODE_ID
: Items Id-objects
* toString
is used as item caption.
* ITEM_CAPTION_MODE_ITEM
: Item-objects
* toString
is used as item caption.
* ITEM_CAPTION_MODE_INDEX
: The index of the item is used
* as item caption. The index mode can only be used with the containers
* implementing Container.Indexed
interface.
* ITEM_CAPTION_MODE_EXPLICIT
: The item captions must be
* explicitly specified.
* ITEM_CAPTION_MODE_PROPERTY
: The item captions are read
* from property, that must be specified with
* setItemCaptionPropertyId
.
*
* The ITEM_CAPTION_MODE_EXPLICIT_DEFAULTS_ID
is the default
* mode.
*
*
* @param mode
* the One of the modes listed above.
*/
public void setItemCaptionMode(ItemCaptionMode mode) {
if (mode != null) {
itemCaptionMode = mode;
requestRepaint();
}
}
/**
* Gets the item caption mode.
*
*
* The mode can be one of the following ones:
*
* ITEM_CAPTION_MODE_EXPLICIT_DEFAULTS_ID
: Items
* Id-objects toString
is used as item caption. If caption is
* explicitly specified, it overrides the id-caption.
* ITEM_CAPTION_MODE_ID
: Items Id-objects
* toString
is used as item caption.
* ITEM_CAPTION_MODE_ITEM
: Item-objects
* toString
is used as item caption.
* ITEM_CAPTION_MODE_INDEX
: The index of the item is used
* as item caption. The index mode can only be used with the containers
* implementing Container.Indexed
interface.
* ITEM_CAPTION_MODE_EXPLICIT
: The item captions must be
* explicitly specified.
* ITEM_CAPTION_MODE_PROPERTY
: The item captions are read
* from property, that must be specified with
* setItemCaptionPropertyId
.
*
* The ITEM_CAPTION_MODE_EXPLICIT_DEFAULTS_ID
is the default
* mode.
*
*
* @return the One of the modes listed above.
*/
public ItemCaptionMode getItemCaptionMode() {
return itemCaptionMode;
}
/**
* Sets the item caption property.
*
*
* Setting the id to a existing property implicitly sets the item caption
* mode to ITEM_CAPTION_MODE_PROPERTY
. If the object is in
* ITEM_CAPTION_MODE_PROPERTY
mode, setting caption property id
* null resets the item caption mode to
* ITEM_CAPTION_EXPLICIT_DEFAULTS_ID
.
*
*
* Note that the type of the property used for caption must be String
*
*
* Setting the property id to null disables this feature. The id is null by
* default
*
* .
*
* @param propertyId
* the id of the property.
*
*/
public void setItemCaptionPropertyId(Object propertyId) {
if (propertyId != null) {
itemCaptionPropertyId = propertyId;
setItemCaptionMode(ITEM_CAPTION_MODE_PROPERTY);
requestRepaint();
} else {
itemCaptionPropertyId = null;
if (getItemCaptionMode() == ITEM_CAPTION_MODE_PROPERTY) {
setItemCaptionMode(ITEM_CAPTION_MODE_EXPLICIT_DEFAULTS_ID);
}
requestRepaint();
}
}
/**
* Gets the item caption property.
*
* @return the Id of the property used as item caption source.
*/
public Object getItemCaptionPropertyId() {
return itemCaptionPropertyId;
}
/**
* Sets the item icon property.
*
*
* If the property id is set to a valid value, each item is given an icon
* got from the given property of the items. The type of the property must
* be assignable to Resource.
*
*
*
* Note : The icons set with setItemIcon
function override the
* icons from the property.
*
*
*
* Setting the property id to null disables this feature. The id is null by
* default
*
* .
*
* @param propertyId
* the id of the property that specifies icons for items or null
* @throws IllegalArgumentException
* If the propertyId is not in the container or is not of a
* valid type
*/
public void setItemIconPropertyId(Object propertyId)
throws IllegalArgumentException {
if (propertyId == null) {
itemIconPropertyId = null;
} else if (!getContainerPropertyIds().contains(propertyId)) {
throw new IllegalArgumentException(
"Property id not found in the container");
} else if (Resource.class.isAssignableFrom(getType(propertyId))) {
itemIconPropertyId = propertyId;
} else {
throw new IllegalArgumentException(
"Property type must be assignable to Resource");
}
requestRepaint();
}
/**
* Gets the item icon property.
*
*
* If the property id is set to a valid value, each item is given an icon
* got from the given property of the items. The type of the property must
* be assignable to Icon.
*
*
*
* Note : The icons set with setItemIcon
function override the
* icons from the property.
*
*
*
* Setting the property id to null disables this feature. The id is null by
* default
*
* .
*
* @return the Id of the property containing the item icons.
*/
public Object getItemIconPropertyId() {
return itemIconPropertyId;
}
/**
* Tests if an item is selected.
*
*
* In single select mode testing selection status of the item identified by
* {@link #getNullSelectionItemId()} returns true if the value of the
* property is null.
*
*
* @param itemId
* the Id the of the item to be tested.
* @see #getNullSelectionItemId()
* @see #setNullSelectionItemId(Object)
*
*/
public boolean isSelected(Object itemId) {
if (itemId == null) {
return false;
}
if (isMultiSelect()) {
return ((Set>) getValue()).contains(itemId);
} else {
final Object value = getValue();
return itemId.equals(value == null ? getNullSelectionItemId()
: value);
}
}
/**
* Selects an item.
*
*
* In single select mode selecting item identified by
* {@link #getNullSelectionItemId()} sets the value of the property to null.
*
*
* @param itemId
* the identifier of Item to be selected.
* @see #getNullSelectionItemId()
* @see #setNullSelectionItemId(Object)
*
*/
public void select(Object itemId) {
if (!isMultiSelect()) {
setValue(itemId);
} else if (!isSelected(itemId) && itemId != null
&& items.containsId(itemId)) {
final Set s = new HashSet((Set>) getValue());
s.add(itemId);
setValue(s);
}
}
/**
* Unselects an item.
*
* @param itemId
* the identifier of the Item to be unselected.
* @see #getNullSelectionItemId()
* @see #setNullSelectionItemId(Object)
*
*/
public void unselect(Object itemId) {
if (isSelected(itemId)) {
if (isMultiSelect()) {
final Set s = new HashSet((Set>) getValue());
s.remove(itemId);
setValue(s);
} else {
setValue(null);
}
}
}
/**
* Notifies this listener that the Containers contents has changed.
*
* @see com.vaadin.data.Container.PropertySetChangeListener#containerPropertySetChange(com.vaadin.data.Container.PropertySetChangeEvent)
*/
@Override
public void containerPropertySetChange(
Container.PropertySetChangeEvent event) {
firePropertySetChange();
}
/**
* Adds a new Property set change listener for this Container.
*
* @see com.vaadin.data.Container.PropertySetChangeNotifier#addListener(com.vaadin.data.Container.PropertySetChangeListener)
*/
@Override
public void addListener(Container.PropertySetChangeListener listener) {
if (propertySetEventListeners == null) {
propertySetEventListeners = new LinkedHashSet();
}
propertySetEventListeners.add(listener);
}
/**
* Removes a previously registered Property set change listener.
*
* @see com.vaadin.data.Container.PropertySetChangeNotifier#removeListener(com.vaadin.data.Container.PropertySetChangeListener)
*/
@Override
public void removeListener(Container.PropertySetChangeListener listener) {
if (propertySetEventListeners != null) {
propertySetEventListeners.remove(listener);
if (propertySetEventListeners.isEmpty()) {
propertySetEventListeners = null;
}
}
}
/**
* Adds an Item set change listener for the object.
*
* @see com.vaadin.data.Container.ItemSetChangeNotifier#addListener(com.vaadin.data.Container.ItemSetChangeListener)
*/
@Override
public void addListener(Container.ItemSetChangeListener listener) {
if (itemSetEventListeners == null) {
itemSetEventListeners = new LinkedHashSet();
}
itemSetEventListeners.add(listener);
}
/**
* Removes the Item set change listener from the object.
*
* @see com.vaadin.data.Container.ItemSetChangeNotifier#removeListener(com.vaadin.data.Container.ItemSetChangeListener)
*/
@Override
public void removeListener(Container.ItemSetChangeListener listener) {
if (itemSetEventListeners != null) {
itemSetEventListeners.remove(listener);
if (itemSetEventListeners.isEmpty()) {
itemSetEventListeners = null;
}
}
}
@Override
public Collection> getListeners(Class> eventType) {
if (Container.ItemSetChangeEvent.class.isAssignableFrom(eventType)) {
if (itemSetEventListeners == null) {
return Collections.EMPTY_LIST;
} else {
return Collections
.unmodifiableCollection(itemSetEventListeners);
}
} else if (Container.PropertySetChangeEvent.class
.isAssignableFrom(eventType)) {
if (propertySetEventListeners == null) {
return Collections.EMPTY_LIST;
} else {
return Collections
.unmodifiableCollection(propertySetEventListeners);
}
}
return super.getListeners(eventType);
}
/**
* Lets the listener know a Containers Item set has changed.
*
* @see com.vaadin.data.Container.ItemSetChangeListener#containerItemSetChange(com.vaadin.data.Container.ItemSetChangeEvent)
*/
@Override
public void containerItemSetChange(Container.ItemSetChangeEvent event) {
// Clears the item id mapping table
itemIdMapper.removeAll();
// Notify all listeners
fireItemSetChange();
}
/**
* Fires the property set change event.
*/
protected void firePropertySetChange() {
if (propertySetEventListeners != null
&& !propertySetEventListeners.isEmpty()) {
final Container.PropertySetChangeEvent event = new PropertySetChangeEvent();
final Object[] listeners = propertySetEventListeners.toArray();
for (int i = 0; i < listeners.length; i++) {
((Container.PropertySetChangeListener) listeners[i])
.containerPropertySetChange(event);
}
}
requestRepaint();
}
/**
* Fires the item set change event.
*/
protected void fireItemSetChange() {
if (itemSetEventListeners != null && !itemSetEventListeners.isEmpty()) {
final Container.ItemSetChangeEvent event = new ItemSetChangeEvent();
final Object[] listeners = itemSetEventListeners.toArray();
for (int i = 0; i < listeners.length; i++) {
((Container.ItemSetChangeListener) listeners[i])
.containerItemSetChange(event);
}
}
requestRepaint();
}
/**
* Implementation of item set change event.
*/
private class ItemSetChangeEvent implements Serializable,
Container.ItemSetChangeEvent {
/**
* Gets the Property where the event occurred.
*
* @see com.vaadin.data.Container.ItemSetChangeEvent#getContainer()
*/
@Override
public Container getContainer() {
return AbstractSelect.this;
}
}
/**
* Implementation of property set change event.
*/
private class PropertySetChangeEvent implements
Container.PropertySetChangeEvent, Serializable {
/**
* Retrieves the Container whose contents have been modified.
*
* @see com.vaadin.data.Container.PropertySetChangeEvent#getContainer()
*/
@Override
public Container getContainer() {
return AbstractSelect.this;
}
}
/**
* For multi-selectable fields, also an empty collection of values is
* considered to be an empty field.
*
* @see AbstractField#isEmpty().
*/
@Override
protected boolean isEmpty() {
if (!multiSelect) {
return super.isEmpty();
} else {
Object value = getValue();
return super.isEmpty()
|| (value instanceof Collection && ((Collection>) value)
.isEmpty());
}
}
/**
* Allow or disallow empty selection by the user. If the select is in
* single-select mode, you can make an item represent the empty selection by
* calling setNullSelectionItemId()
. This way you can for
* instance set an icon and caption for the null selection item.
*
* @param nullSelectionAllowed
* whether or not to allow empty selection
* @see #setNullSelectionItemId(Object)
* @see #isNullSelectionAllowed()
*/
public void setNullSelectionAllowed(boolean nullSelectionAllowed) {
if (nullSelectionAllowed != this.nullSelectionAllowed) {
this.nullSelectionAllowed = nullSelectionAllowed;
requestRepaint();
}
}
/**
* Checks if null empty selection is allowed by the user.
*
* @return whether or not empty selection is allowed
* @see #setNullSelectionAllowed(boolean)
*/
public boolean isNullSelectionAllowed() {
return nullSelectionAllowed;
}
/**
* Returns the item id that represents null value of this select in single
* select mode.
*
*
* Data interface does not support nulls as item ids. Selecting the item
* identified by this id is the same as selecting no items at all. This
* setting only affects the single select mode.
*
*
* @return the Object Null value item id.
* @see #setNullSelectionItemId(Object)
* @see #isSelected(Object)
* @see #select(Object)
*/
public Object getNullSelectionItemId() {
return nullSelectionItemId;
}
/**
* Sets the item id that represents null value of this select.
*
*
* Data interface does not support nulls as item ids. Selecting the item
* identified by this id is the same as selecting no items at all. This
* setting only affects the single select mode.
*
*
* @param nullSelectionItemId
* the nullSelectionItemId to set.
* @see #getNullSelectionItemId()
* @see #isSelected(Object)
* @see #select(Object)
*/
public void setNullSelectionItemId(Object nullSelectionItemId) {
if (nullSelectionItemId != null && isMultiSelect()) {
throw new IllegalStateException(
"Multiselect and NullSelectionItemId can not be set at the same time.");
}
this.nullSelectionItemId = nullSelectionItemId;
}
/**
* Notifies the component that it is connected to an application.
*
* @see com.vaadin.ui.AbstractField#attach()
*/
@Override
public void attach() {
super.attach();
}
/**
* Detaches the component from application.
*
* @see com.vaadin.ui.AbstractComponent#detach()
*/
@Override
public void detach() {
getCaptionChangeListener().clear();
super.detach();
}
// Caption change listener
protected CaptionChangeListener getCaptionChangeListener() {
if (captionChangeListener == null) {
captionChangeListener = new CaptionChangeListener();
}
return captionChangeListener;
}
/**
* This is a listener helper for Item and Property changes that should cause
* a repaint. It should be attached to all items that are displayed, and the
* default implementation does this in paintContent(). Especially
* "lazyloading" components should take care to add and remove listeners as
* appropriate. Call addNotifierForItem() for each painted item (and
* remember to clear).
*
* NOTE: singleton, use getCaptionChangeListener().
*
*/
protected class CaptionChangeListener implements
Item.PropertySetChangeListener, Property.ValueChangeListener {
// TODO clean this up - type is either Item.PropertySetChangeNotifier or
// Property.ValueChangeNotifier
HashSet captionChangeNotifiers = new HashSet();
public void addNotifierForItem(Object itemId) {
switch (getItemCaptionMode()) {
case ITEM:
final Item i = getItem(itemId);
if (i == null) {
return;
}
if (i instanceof Item.PropertySetChangeNotifier) {
((Item.PropertySetChangeNotifier) i)
.addListener(getCaptionChangeListener());
captionChangeNotifiers.add(i);
}
Collection> pids = i.getItemPropertyIds();
if (pids != null) {
for (Iterator> it = pids.iterator(); it.hasNext();) {
Property> p = i.getItemProperty(it.next());
if (p != null
&& p instanceof Property.ValueChangeNotifier) {
((Property.ValueChangeNotifier) p)
.addListener(getCaptionChangeListener());
captionChangeNotifiers.add(p);
}
}
}
break;
case PROPERTY:
final Property> p = getContainerProperty(itemId,
getItemCaptionPropertyId());
if (p != null && p instanceof Property.ValueChangeNotifier) {
((Property.ValueChangeNotifier) p)
.addListener(getCaptionChangeListener());
captionChangeNotifiers.add(p);
}
break;
}
}
public void clear() {
for (Iterator it = captionChangeNotifiers.iterator(); it
.hasNext();) {
Object notifier = it.next();
if (notifier instanceof Item.PropertySetChangeNotifier) {
((Item.PropertySetChangeNotifier) notifier)
.removeListener(getCaptionChangeListener());
} else {
((Property.ValueChangeNotifier) notifier)
.removeListener(getCaptionChangeListener());
}
}
captionChangeNotifiers.clear();
}
@Override
public void valueChange(com.vaadin.data.Property.ValueChangeEvent event) {
requestRepaint();
}
@Override
public void itemPropertySetChange(
com.vaadin.data.Item.PropertySetChangeEvent event) {
requestRepaint();
}
}
/**
* Criterion which accepts a drop only if the drop target is (one of) the
* given Item identifier(s). Criterion can be used only on a drop targets
* that extends AbstractSelect like {@link Table} and {@link Tree}. The
* target and identifiers of valid Items are given in constructor.
*
* @since 6.3
*/
public static class TargetItemIs extends AbstractItemSetCriterion {
/**
* @param select
* the select implementation that is used as a drop target
* @param itemId
* the identifier(s) that are valid drop locations
*/
public TargetItemIs(AbstractSelect select, Object... itemId) {
super(select, itemId);
}
@Override
public boolean accept(DragAndDropEvent dragEvent) {
AbstractSelectTargetDetails dropTargetData = (AbstractSelectTargetDetails) dragEvent
.getTargetDetails();
if (dropTargetData.getTarget() != select) {
return false;
}
return itemIds.contains(dropTargetData.getItemIdOver());
}
}
/**
* Abstract helper class to implement item id based criterion.
*
* Note, inner class used not to open itemIdMapper for public access.
*
* @since 6.3
*
*/
private static abstract class AbstractItemSetCriterion extends
ClientSideCriterion {
protected final Collection itemIds = new HashSet();
protected AbstractSelect select;
public AbstractItemSetCriterion(AbstractSelect select, Object... itemId) {
if (itemIds == null || select == null) {
throw new IllegalArgumentException(
"Accepted item identifiers must be accepted.");
}
Collections.addAll(itemIds, itemId);
this.select = select;
}
@Override
public void paintContent(PaintTarget target) throws PaintException {
super.paintContent(target);
String[] keys = new String[itemIds.size()];
int i = 0;
for (Object itemId : itemIds) {
String key = select.itemIdMapper.key(itemId);
keys[i++] = key;
}
target.addAttribute("keys", keys);
target.addAttribute("s", select);
}
}
/**
* This criterion accepts a only a {@link Transferable} that contains given
* Item (practically its identifier) from a specific AbstractSelect.
*
* @since 6.3
*/
public static class AcceptItem extends AbstractItemSetCriterion {
/**
* @param select
* the select from which the item id's are checked
* @param itemId
* the item identifier(s) of the select that are accepted
*/
public AcceptItem(AbstractSelect select, Object... itemId) {
super(select, itemId);
}
@Override
public boolean accept(DragAndDropEvent dragEvent) {
DataBoundTransferable transferable = (DataBoundTransferable) dragEvent
.getTransferable();
if (transferable.getSourceComponent() != select) {
return false;
}
return itemIds.contains(transferable.getItemId());
}
/**
* A simple accept criterion which ensures that {@link Transferable}
* contains an {@link Item} (or actually its identifier). In other words
* the criterion check that drag is coming from a {@link Container} like
* {@link Tree} or {@link Table}.
*/
public static final ClientSideCriterion ALL = new ContainsDataFlavor(
"itemId");
}
/**
* TargetDetails implementation for subclasses of {@link AbstractSelect}
* that implement {@link DropTarget}.
*
* @since 6.3
*/
public class AbstractSelectTargetDetails extends TargetDetailsImpl {
/**
* The item id over which the drag event happened.
*/
protected Object idOver;
/**
* Constructor that automatically converts itemIdOver key to
* corresponding item Id
*
*/
protected AbstractSelectTargetDetails(Map rawVariables) {
super(rawVariables, (DropTarget) AbstractSelect.this);
// eagar fetch itemid, mapper may be emptied
String keyover = (String) getData("itemIdOver");
if (keyover != null) {
idOver = itemIdMapper.get(keyover);
}
}
/**
* If the drag operation is currently over an {@link Item}, this method
* returns the identifier of that {@link Item}.
*
*/
public Object getItemIdOver() {
return idOver;
}
/**
* Returns a detailed vertical location where the drop happened on Item.
*/
public VerticalDropLocation getDropLocation() {
String detail = (String) getData("detail");
if (detail == null) {
return null;
}
return VerticalDropLocation.valueOf(detail);
}
}
/**
* An accept criterion to accept drops only on a specific vertical location
* of an item.
*
* This accept criterion is currently usable in Tree and Table
* implementations.
*/
public static class VerticalLocationIs extends TargetDetailIs {
public static VerticalLocationIs TOP = new VerticalLocationIs(
VerticalDropLocation.TOP);
public static VerticalLocationIs BOTTOM = new VerticalLocationIs(
VerticalDropLocation.BOTTOM);
public static VerticalLocationIs MIDDLE = new VerticalLocationIs(
VerticalDropLocation.MIDDLE);
private VerticalLocationIs(VerticalDropLocation l) {
super("detail", l.name());
}
}
/**
* Implement this interface and pass it to Tree.setItemDescriptionGenerator
* or Table.setItemDescriptionGenerator to generate mouse over descriptions
* ("tooltips") for the rows and cells in Table or for the items in Tree.
*/
public interface ItemDescriptionGenerator extends Serializable {
/**
* Called by Table when a cell (and row) is painted or a item is painted
* in Tree
*
* @param source
* The source of the generator, the Tree or Table the
* generator is attached to
* @param itemId
* The itemId of the painted cell
* @param propertyId
* The propertyId of the cell, null when getting row
* description
* @return The description or "tooltip" of the item.
*/
public String generateDescription(Component source, Object itemId,
Object propertyId);
}
}