From 0f494df92022c454b807fa87cf66eed91ef0956c Mon Sep 17 00:00:00 2001
From: Marc Englund
- * 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.
- *
- * The value of the select is the selected item id. If the select is in
- * multiselect-mode, the value is a set of selected item keys. In
- * multiselect mode all collections of id:s can be assigned.
- *
- * The value of the select is the selected item id. If the select is in
- * multiselect-mode, the value is a set of selected item keys. In
- * multiselect mode all collections of id:s can be assigned.
- *
- * The mode can be one of the following ones:
- * String
representation is
- * used as caption.
- */
- public static final int ITEM_CAPTION_MODE_ID = 0;
- /**
- * Item caption mode: Item's String
representation is used as
- * caption.
- */
- public static final int ITEM_CAPTION_MODE_ITEM = 1;
- /**
- * Item caption mode: Index of the item is used as caption. The index mode
- * can only be used with the containers implementing the
- * {@link com.itmill.toolkit.data.Container.Indexed} interface.
- */
- public static final int ITEM_CAPTION_MODE_INDEX = 2;
- /**
- * Item caption mode: If an Item has a caption it's used, if not, Item's
- * ID's String
representation is used as caption. This is
- * the default.
- */
- public static final int ITEM_CAPTION_MODE_EXPLICIT_DEFAULTS_ID = 3;
- /**
- * Item caption mode: Captions must be explicitly specified.
- */
- public static final int ITEM_CAPTION_MODE_EXPLICIT = 4;
- /**
- * Item caption mode: Only icons are shown, captions are hidden.
- */
- public static final int ITEM_CAPTION_MODE_ICON_ONLY = 5;
- /**
- * Item caption mode: Item captions are read from property specified with
- * setItemCaptionPropertyId
.
- */
- public static final int ITEM_CAPTION_MODE_PROPERTY = 6;
-
- /**
- * Interface for option filtering, used to filter options based on user
- * entered value. The value is matched to the item caption.
- * FILTERINGMODE_OFF
(0) turns the filtering off.
- * FILTERINGMODE_STARTSWITH
(1) matches from the start of the
- * caption. FILTERINGMODE_CONTAINS
(1) matches anywhere in
- * the caption.
- */
- public interface Filtering {
- public static final int FILTERINGMODE_OFF = 0;
- public static final int FILTERINGMODE_STARTSWITH = 1;
- public static final int FILTERINGMODE_CONTAINS = 2;
-
- /**
- * Sets the option filtering mode.
- *
- * @param filteringMode
- * the filtering mode to use
- */
- public void setFilteringMode(int filteringMode);
-
- /**
- * Gets the current filtering mode.
- *
- * @return the filtering mode in use
- */
- public int getFilteringMode();
-
- }
-
- /**
- * Is the select in multiselect mode?
- */
- private boolean multiSelect = false;
-
- /**
- * Select options.
- */
- protected Container items;
-
- /**
- * Is the user allowed to add new options?
- */
- private boolean allowNewOptions;
-
- /**
- * Keymapper used to map key values.
- */
- protected KeyMapper itemIdMapper = new KeyMapper();
-
- /**
- * Item icons.
- */
- private final HashMap itemIcons = new HashMap();
-
- /**
- * Item captions.
- */
- private final HashMap itemCaptions = new HashMap();
-
- /**
- * Item caption mode.
- */
- private int itemCaptionMode = ITEM_CAPTION_MODE_EXPLICIT_DEFAULTS_ID;
-
- /**
- * Item caption source property id.
- */
- private Object itemCaptionPropertyId = null;
-
- /**
- * Item icon source property id.
- */
- private Object itemIconPropertyId = null;
-
- /**
- * List of property set change event listeners.
- */
- private LinkedList propertySetEventListeners = null;
-
- /**
- * List of item set change event listeners.
- */
- private LinkedList itemSetEventListeners = null;
-
- /**
- * Item id that represents null selection of this select.
- *
- * getValue
and
- * setValue
methods must be compatible with this type: one
- * can safely cast getValue
to given type and pass any
- * variable assignable to this type as a parameter to setValue
.
- *
- * @return the Type of the property.
- */
- public Class getType() {
- if (isMultiSelect()) {
- return Set.class;
- } else {
- return Object.class;
- }
- }
-
- /**
- * Gets the selected item id or in multiselect mode a set of selected ids.
- *
- * @see com.itmill.toolkit.ui.AbstractField#getValue()
- */
- public Object getValue() {
- Object retValue = super.getValue();
-
- if (isMultiSelect()) {
-
- // If the return value is not a set
- if (retValue == null) {
- return new HashSet();
- }
- if (retValue instanceof Set) {
- return Collections.unmodifiableSet((Set) retValue);
- } else if (retValue instanceof Collection) {
- return new HashSet((Collection) retValue);
- } else {
- Set s = new HashSet();
- if (this.items.containsId(retValue)) {
- s.add(retValue);
- }
- return s;
- }
-
- } else {
- return retValue;
- }
- }
-
- /**
- * Sets the visible value of the property.
- *
- * 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 ITEM_CAPTION_MODE_ID:
- caption = itemId.toString();
- break;
-
- case ITEM_CAPTION_MODE_INDEX:
- try {
- caption = String.valueOf(((Container.Indexed) this.items)
- .indexOfId(itemId));
- } catch (ClassCastException ignored) {
- }
- break;
-
- case ITEM_CAPTION_MODE_ITEM:
- Item i = getItem(itemId);
- if (i != null) {
- caption = i.toString();
- }
- break;
-
- case ITEM_CAPTION_MODE_EXPLICIT:
- caption = (String) this.itemCaptions.get(itemId);
- break;
-
- case ITEM_CAPTION_MODE_EXPLICIT_DEFAULTS_ID:
- caption = (String) this.itemCaptions.get(itemId);
- if (caption == null) {
- caption = itemId.toString();
- }
- break;
-
- case ITEM_CAPTION_MODE_PROPERTY:
- Property p = getContainerProperty(itemId,
- getItemCaptionPropertyId());
- if (p != null) {
- caption = p.toString();
- }
- break;
- }
-
- // All items must have some captions
- return caption != null ? caption : "";
- }
-
- /**
- * Sets the icon for an item.
- *
- * @param itemId
- * the id of the item to be assigned an icon.
- * @param icon
- * the New icon.
- */
- public void setItemIcon(Object itemId, Resource icon) {
- if (itemId != null) {
- if (icon == null) {
- this.itemIcons.remove(itemId);
- } else {
- this.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) {
- Resource explicit = (Resource) this.itemIcons.get(itemId);
- if (explicit != null) {
- return explicit;
- }
-
- if (getItemIconPropertyId() == null) {
- return null;
- }
-
- Property ip = getContainerProperty(itemId, getItemIconPropertyId());
- if (ip == null) {
- return null;
- }
- Object icon = ip.getValue();
- if (icon instanceof Resource) {
- return (Resource) icon;
- }
-
- return null;
- }
-
- /**
- * Sets the item caption mode.
- *
- *
- *
- * The 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
.ITEM_CAPTION_MODE_EXPLICIT_DEFAULTS_ID
is the default
- * 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
.ITEM_CAPTION_MODE_EXPLICIT_DEFAULTS_ID
is the default
- * mode.
- *
- *
- * @return the One of the modes listed above.
- */
- public int getItemCaptionMode() {
- return this.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
.
- *
- * 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) { - this.itemCaptionPropertyId = propertyId; - setItemCaptionMode(ITEM_CAPTION_MODE_PROPERTY); - requestRepaint(); - } else { - this.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 this.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 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 - *
. - * - * @param propertyId - * the Id of the property that specifies icons for items. - */ - public void setItemIconPropertyId(Object propertyId) { - if ((propertyId != null) - && Resource.class.isAssignableFrom(getType(propertyId))) { - this.itemIconPropertyId = propertyId; - requestRepaint(); - } else { - this.itemIconPropertyId = null; - } - } - - /** - * 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 this.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 { - 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 tem to be selected. - * @see #getNullSelectionItemId() - * @see #setNullSelectionItemId(Object) - * - */ - public void select(Object itemId) { - if (!isSelected(itemId) && this.items.containsId(itemId)) { - if (isMultiSelect()) { - Set s = new HashSet((Set) getValue()); - s.add(itemId); - setValue(s); - } else if (itemId.equals(getNullSelectionItemId())) { - setValue(null); - } else { - setValue(itemId); - } - } - } - - /** - * Unselects an item. - * - * @param itemId - * the Item to be unselected. - * @see #getNullSelectionItemId() - * @see #setNullSelectionItemId(Object) - * - */ - public void unselect(Object itemId) { - if (isSelected(itemId)) { - if (isMultiSelect()) { - 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.itmill.toolkit.data.Container.PropertySetChangeListener#containerPropertySetChange(com.itmill.toolkit.data.Container.PropertySetChangeEvent) - */ - public void containerPropertySetChange( - Container.PropertySetChangeEvent event) { - firePropertySetChange(); - } - - /** - * Adds a new Property set change listener for this Container. - * - * @see com.itmill.toolkit.data.Container.PropertySetChangeNotifier#addListener(com.itmill.toolkit.data.Container.PropertySetChangeListener) - */ - public void addListener(Container.PropertySetChangeListener listener) { - if (this.propertySetEventListeners == null) { - this.propertySetEventListeners = new LinkedList(); - } - this.propertySetEventListeners.add(listener); - } - - /** - * Removes a previously registered Property set change listener. - * - * @see com.itmill.toolkit.data.Container.PropertySetChangeNotifier#removeListener(com.itmill.toolkit.data.Container.PropertySetChangeListener) - */ - public void removeListener(Container.PropertySetChangeListener listener) { - if (this.propertySetEventListeners != null) { - this.propertySetEventListeners.remove(listener); - if (this.propertySetEventListeners.isEmpty()) { - this.propertySetEventListeners = null; - } - } - } - - /** - * Adds an Item set change listener for the object. - * - * @see com.itmill.toolkit.data.Container.ItemSetChangeNotifier#addListener(com.itmill.toolkit.data.Container.ItemSetChangeListener) - */ - public void addListener(Container.ItemSetChangeListener listener) { - if (this.itemSetEventListeners == null) { - this.itemSetEventListeners = new LinkedList(); - } - this.itemSetEventListeners.add(listener); - } - - /** - * Removes the Item set change listener from the object. - * - * @see com.itmill.toolkit.data.Container.ItemSetChangeNotifier#removeListener(com.itmill.toolkit.data.Container.ItemSetChangeListener) - */ - public void removeListener(Container.ItemSetChangeListener listener) { - if (this.itemSetEventListeners != null) { - this.itemSetEventListeners.remove(listener); - if (this.itemSetEventListeners.isEmpty()) { - this.itemSetEventListeners = null; - } - } - } - - /** - * Lets the listener know a Containers Item set has changed. - * - * @see com.itmill.toolkit.data.Container.ItemSetChangeListener#containerItemSetChange(com.itmill.toolkit.data.Container.ItemSetChangeEvent) - */ - public void containerItemSetChange(Container.ItemSetChangeEvent event) { - // Clears the item id mapping table - this.itemIdMapper.removeAll(); - - // Notify all listeners - fireItemSetChange(); - } - - /** - * Fires the property set change event. - */ - protected void firePropertySetChange() { - if (this.propertySetEventListeners != null - && !this.propertySetEventListeners.isEmpty()) { - Container.PropertySetChangeEvent event = new PropertySetChangeEvent(); - Object[] listeners = this.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 (this.itemSetEventListeners != null - && !this.itemSetEventListeners.isEmpty()) { - Container.ItemSetChangeEvent event = new ItemSetChangeEvent(); - Object[] listeners = this.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 Container.ItemSetChangeEvent { - - /** - * Gets the Property where the event occurred. - * - * @see com.itmill.toolkit.data.Container.ItemSetChangeEvent#getContainer() - */ - public Container getContainer() { - return AbstractSelect.this; - } - - } - - /** - * Implementation of property set change event. - */ - private class PropertySetChangeEvent implements - Container.PropertySetChangeEvent { - - /** - * Retrieves the Container whose contents have been modified. - * - * @see com.itmill.toolkit.data.Container.PropertySetChangeEvent#getContainer() - */ - public Container getContainer() { - return AbstractSelect.this; - } - - } - - /** - * Allow of disallow empty selection. 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) {
- this.nullSelectionAllowed = nullSelectionAllowed;
- }
-
- /**
- * Checks if null empty selection is allowed.
- *
- * @return whether or not empty selection is allowed
- * @see #setNullSelectionAllowed(boolean)
- */
- public boolean isNullSelectionAllowed() {
- return this.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 final Object getNullSelectionItemId() { - return this.nullSelectionItemId; - } - - /** - * Sets the item id that represents null value of this select. - * - *- * Data interface does not support nulls as item ids. Selecting the item - * idetified 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.itmill.toolkit.ui.AbstractField#attach() - */ - public void attach() { - super.attach(); - } - - /** - * Detaches the component from application. - * - * @see com.itmill.toolkit.ui.AbstractComponent#detach() - */ - public void detach() { - super.detach(); - } + Container, Container.Viewer, Container.PropertySetChangeListener, + Container.PropertySetChangeNotifier, Container.ItemSetChangeNotifier, + Container.ItemSetChangeListener { + + /** + * Item caption mode: Item's ID'sString
representation is
+ * used as caption.
+ */
+ public static final int ITEM_CAPTION_MODE_ID = 0;
+ /**
+ * Item caption mode: Item's String
representation is used as
+ * caption.
+ */
+ public static final int ITEM_CAPTION_MODE_ITEM = 1;
+ /**
+ * Item caption mode: Index of the item is used as caption. The index mode
+ * can only be used with the containers implementing the
+ * {@link com.itmill.toolkit.data.Container.Indexed} interface.
+ */
+ public static final int ITEM_CAPTION_MODE_INDEX = 2;
+ /**
+ * Item caption mode: If an Item has a caption it's used, if not, Item's
+ * ID's String
representation is used as caption. This is
+ * the default.
+ */
+ public static final int ITEM_CAPTION_MODE_EXPLICIT_DEFAULTS_ID = 3;
+ /**
+ * Item caption mode: Captions must be explicitly specified.
+ */
+ public static final int ITEM_CAPTION_MODE_EXPLICIT = 4;
+ /**
+ * Item caption mode: Only icons are shown, captions are hidden.
+ */
+ public static final int ITEM_CAPTION_MODE_ICON_ONLY = 5;
+ /**
+ * Item caption mode: Item captions are read from property specified with
+ * setItemCaptionPropertyId
.
+ */
+ public static final int ITEM_CAPTION_MODE_PROPERTY = 6;
+
+ /**
+ * Interface for option filtering, used to filter options based on user
+ * entered value. The value is matched to the item caption.
+ * FILTERINGMODE_OFF
(0) turns the filtering off.
+ * FILTERINGMODE_STARTSWITH
(1) matches from the start of the
+ * caption. FILTERINGMODE_CONTAINS
(1) matches anywhere in
+ * the caption.
+ */
+ public interface Filtering {
+ public static final int FILTERINGMODE_OFF = 0;
+ public static final int FILTERINGMODE_STARTSWITH = 1;
+ public static final int FILTERINGMODE_CONTAINS = 2;
+
+ /**
+ * Sets the option filtering mode.
+ *
+ * @param filteringMode
+ * the filtering mode to use
+ */
+ public void setFilteringMode(int filteringMode);
+
+ /**
+ * Gets the current filtering mode.
+ *
+ * @return the filtering mode in use
+ */
+ public int getFilteringMode();
+
+ }
+
+ /**
+ * Is the select in multiselect mode?
+ */
+ private boolean multiSelect = false;
+
+ /**
+ * Select options.
+ */
+ protected Container items;
+
+ /**
+ * Is the user allowed to add new options?
+ */
+ private boolean allowNewOptions;
+
+ /**
+ * Keymapper used to map key values.
+ */
+ protected KeyMapper itemIdMapper = new KeyMapper();
+
+ /**
+ * Item icons.
+ */
+ private final HashMap itemIcons = new HashMap();
+
+ /**
+ * Item captions.
+ */
+ private final HashMap itemCaptions = new HashMap();
+
+ /**
+ * Item caption mode.
+ */
+ private int itemCaptionMode = ITEM_CAPTION_MODE_EXPLICIT_DEFAULTS_ID;
+
+ /**
+ * Item caption source property id.
+ */
+ private Object itemCaptionPropertyId = null;
+
+ /**
+ * Item icon source property id.
+ */
+ private Object itemIconPropertyId = null;
+
+ /**
+ * List of property set change event listeners.
+ */
+ private LinkedList propertySetEventListeners = null;
+
+ /**
+ * List of item set change event listeners.
+ */
+ private LinkedList itemSetEventListeners = null;
+
+ /**
+ * Item id that represents null selection 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. + *
+ */ + private Object nullSelectionItemId = null; + + // Null (empty) selection is enabled by default + private boolean nullSelectionAllowed = true; + + /* Constructors ********************************************************* */ + + /** + * Creates an empty Select. The caption is not used. + */ + public AbstractSelect() { + setContainerDataSource(new IndexedContainer()); + } + + /** + * Creates an empty Select with caption. + */ + public AbstractSelect(String caption) { + setContainerDataSource(new IndexedContainer()); + setCaption(caption); + } + + /** + * Creates a new select that is connected to a data-source. + * + * @param caption + * the Caption of the component. + * @param dataSource + * the Container datasource to be selected from by this + * select. + */ + public AbstractSelect(String caption, Container dataSource) { + setCaption(caption); + setContainerDataSource(dataSource); + } + + /** + * Creates a new select that is filled from a collection of option values. + * + * @param caption + * the Caption of this field. + * @param options + * the Collection containing the options. + */ + public AbstractSelect(String caption, Collection options) { + + // Creates the options container and add given options to it + Container c = new IndexedContainer(); + if (options != null) { + for (Iterator i = options.iterator(); i.hasNext();) { + c.addItem(i.next()); + } + } + + setCaption(caption); + setContainerDataSource(c); + } + + /* Component methods **************************************************** */ + + /** + * Paints the content of this component. + * + * @param target + * the Paint Event. + * @throws PaintException + * if the paint operation failed. + */ + public void paintContent(PaintTarget target) throws PaintException { + + // Paints field properties + super.paintContent(target); + + // Paints select attributes + if (isMultiSelect()) { + target.addAttribute("selectmode", "multi"); + } + if (isNewItemsAllowed()) { + target.addAttribute("allownewitem", true); + } + if (isNullSelectionAllowed()) { + target.addAttribute("nullselect", true); + if (getNullSelectionItemId() != null) { + target.addAttribute("nullselectitem", true); + } + } + + // Constructs selected keys array + String[] selectedKeys; + if (isMultiSelect()) { + selectedKeys = new String[((Set) getValue()).size()]; + } else { + selectedKeys = new String[(getValue() == null + && getNullSelectionItemId() == null ? 0 : 1)]; + } + + // == + // Paints the options and create array of selected id keys + target.startTag("options"); + int keyIndex = 0; + // Support for external null selection item id + Collection ids = getItemIds(); + if (isNullSelectionAllowed() && getNullSelectionItemId() != null + && !ids.contains(getNullSelectionItemId())) { + // Gets the option attribute values + Object id = getNullSelectionItemId(); + String key = itemIdMapper.key(id); + String caption = getItemCaption(id); + Resource icon = getItemIcon(id); + // Paints option + target.startTag("so"); + if (icon != null) { + target.addAttribute("icon", icon); + } + target.addAttribute("caption", caption); + target.addAttribute("nullselection", true); + target.addAttribute("key", key); + if (isSelected(id)) { + target.addAttribute("selected", true); + selectedKeys[keyIndex++] = key; + } + target.endTag("so"); + } + + Iterator i = getItemIds().iterator(); + // Paints the available selection options from data source + while (i.hasNext()) { + // Gets the option attribute values + Object id = i.next(); + if (!isNullSelectionAllowed() && id != null + && id.equals(getNullSelectionItemId())) { + // Remove item if it's the null selection item but null + // selection is not allowed + continue; + } + String key = itemIdMapper.key(id); + String caption = getItemCaption(id); + Resource icon = getItemIcon(id); // Paints the option + target.startTag("so"); + if (icon != null) { + target.addAttribute("icon", icon); + } + target.addAttribute("caption", caption); + if (id != null && id.equals(getNullSelectionItemId())) { + target.addAttribute("nullselection", true); + } + target.addAttribute("key", key); + if (isSelected(id) && keyIndex < selectedKeys.length) { + target.addAttribute("selected", true); + selectedKeys[keyIndex++] = key; + } + target.endTag("so"); + } + target.endTag("options"); + // == + + // Paint variables + target.addVariable(this, "selected", selectedKeys); + if (isNewItemsAllowed()) { + target.addVariable(this, "newitem", ""); + } + + } + + /** + * Invoked when the value of a variable has changed. + * + * @see com.itmill.toolkit.ui.AbstractComponent#changeVariables(java.lang.Object, + * java.util.Map) + */ + public void changeVariables(Object source, Map variables) { + // Try to set the property value + + // New option entered (and it is allowed) + String newitem = (String) variables.get("newitem"); + if (newitem != null && newitem.length() > 0) { + + // Checks for readonly + if (isReadOnly()) { + throw new Property.ReadOnlyException(); + } + + // Adds new option + if (addItem(newitem) != null) { + + // Sets the caption property, if used + if (getItemCaptionPropertyId() != null) { + try { + getContainerProperty(newitem, + getItemCaptionPropertyId()).setValue(newitem); + } catch (Property.ConversionException ignored) { + // The conversion exception is safely ignored, the + // caption is + // just missing + } + } + } + } + + // Selection change + if (variables.containsKey("selected")) { + String[] ka = (String[]) variables.get("selected"); + + // Multiselect mode + if (isMultiSelect()) { + + // TODO Optimize by adding repaintNotNeeded when applicable + + // Converts the key-array to id-set + LinkedList s = new LinkedList(); + for (int i = 0; i < ka.length; i++) { + Object id = itemIdMapper.get(ka[i]); + if (!isNullSelectionAllowed() + && (id == null || id == getNullSelectionItemId())) { + // skip empty selection if nullselection is not allowed + requestRepaint(); + } else if (id != null && containsId(id)) { + s.add(id); + } else if (itemIdMapper.isNewIdKey(ka[i]) + && newitem != null && newitem.length() > 0) { + s.add(newitem); + } + } + + if (!isNullSelectionAllowed() && s.size() < 1) { + // empty selection not allowed, keep old value + requestRepaint(); + return; + } + + // Limits the deselection to the set of visible items + // (non-visible items can not be deselected) + Collection visible = getVisibleItemIds(); + if (visible != null) { + Set newsel = (Set) getValue(); + if (newsel == null) { + newsel = new HashSet(); + } else { + newsel = new HashSet(newsel); + } + newsel.removeAll(visible); + newsel.addAll(s); + setValue(newsel, true); + } + } else { + // Single select mode + if (!isNullSelectionAllowed() + && (ka.length == 0 || ka[0] == null || ka[0] == getNullSelectionItemId())) { + requestRepaint(); + return; + } + if (ka.length == 0) { + // Allows deselection only if the deselected item is + // visible + Object current = getValue(); + Collection visible = getVisibleItemIds(); + if (visible != null && visible.contains(current)) { + setValue(null, true); + } + } else { + Object id = itemIdMapper.get(ka[0]); + if (!isNullSelectionAllowed() && id == null) { + requestRepaint(); + } else if (id != null + && id.equals(getNullSelectionItemId())) { + setValue(null, true); + } else if (itemIdMapper.isNewIdKey(ka[0])) { + setValue(newitem); + } else { + setValue(id, true); + } + } + } + } + } + + /** + * Gets the component UIDL tag. + * + * @return the Component UIDL tag as string. + */ + public String getTag() { + return "select"; + } + + /** + * Gets the visible item ids. In Select, this returns list of all item ids, + * but can be overriden in subclasses if they paint only part of the items + * to the terminal or null if no items is visible. + */ + public Collection getVisibleItemIds() { + if (isVisible()) { + return getItemIds(); + } + return null; + } + + /* Property methods ***************************************************** */ + + /** + * Returns the type of the property.getValue
and
+ * setValue
methods must be compatible with this type: one
+ * can safely cast getValue
to given type and pass any
+ * variable assignable to this type as a parameter to setValue
.
+ *
+ * @return the Type of the property.
+ */
+ public Class getType() {
+ if (isMultiSelect()) {
+ return Set.class;
+ } else {
+ return Object.class;
+ }
+ }
+
+ /**
+ * Gets the selected item id or in multiselect mode a set of selected ids.
+ *
+ * @see com.itmill.toolkit.ui.AbstractField#getValue()
+ */
+ public Object getValue() {
+ Object retValue = super.getValue();
+
+ if (isMultiSelect()) {
+
+ // If the return value is not a set
+ if (retValue == null) {
+ return new HashSet();
+ }
+ if (retValue instanceof Set) {
+ return Collections.unmodifiableSet((Set) retValue);
+ } else if (retValue instanceof Collection) {
+ return new HashSet((Collection) retValue);
+ } else {
+ Set s = new HashSet();
+ if (items.containsId(retValue)) {
+ s.add(retValue);
+ }
+ return s;
+ }
+
+ } else {
+ return retValue;
+ }
+ }
+
+ /**
+ * Sets the visible value of the property.
+ *
+ * + * The value of the select is the selected item id. If the select is in + * multiselect-mode, the value is a set of selected item keys. In + * multiselect mode all collections of id:s can be assigned. + *
+ * + * @param newValue + * the New selected item or collection of selected items. + * @see com.itmill.toolkit.ui.AbstractField#setValue(java.lang.Object) + */ + public void setValue(Object newValue) throws Property.ReadOnlyException, + Property.ConversionException { + setValue(newValue, false); + } + + /** + * Sets the visible value of the property. + * + *+ * The value of the select is the selected item id. If the select is in + * multiselect-mode, the value is a set of selected item keys. In + * multiselect mode all collections of id:s can be assigned. + *
+ * + * @param newValue + * the New selected item or collection of selected items. + * @param repaintIsNotNeeded + * True if caller is sure that repaint is not needed. + * @see com.itmill.toolkit.ui.AbstractField#setValue(java.lang.Object, + * java.lang.Boolean) + */ + protected void setValue(Object newValue, boolean repaintIsNotNeeded) + throws Property.ReadOnlyException, Property.ConversionException { + + if (isMultiSelect()) { + if (newValue == null) { + super.setValue(new HashSet(), repaintIsNotNeeded); + } else if (Collection.class.isAssignableFrom(newValue.getClass())) { + super.setValue(new HashSet((Collection) newValue), + repaintIsNotNeeded); + } + } else if (newValue == null || items.containsId(newValue)) { + super.setValue(newValue, repaintIsNotNeeded); + } + } + + /* Container methods **************************************************** */ + + /** + * Gets the item from the container with given id. If the container does not + * contain the requested item, null is returned. + * + * @param itemId + * the item id. + * @return the item from the container. + */ + public Item getItem(Object itemId) { + return items.getItem(itemId); + } + + /** + * Gets the item Id collection from the container. + * + * @return the Collection of item ids. + */ + public Collection getItemIds() { + return items.getItemIds(); + } + + /** + * Gets the property Id collection from the container. + * + * @return the Collection of property ids. + */ + public Collection getContainerPropertyIds() { + return items.getContainerPropertyIds(); + } + + /** + * Gets the property type. + * + * @param propertyId + * the Id identifying the property. + * @see com.itmill.toolkit.data.Container#getType(java.lang.Object) + */ + public Class getType(Object propertyId) { + return items.getType(propertyId); + } + + /* + * Gets the number of items in the container. + * + * @return the Number of items in the container. + * + * @see com.itmill.toolkit.data.Container#size() + */ + public int size() { + return items.size(); + } + + /** + * Tests, if the collection contains an item with given id. + * + * @param itemId + * the Id the of item to be tested. + */ + public boolean containsId(Object itemId) { + if (itemId != null) { + return items.containsId(itemId); + } else { + return false; + } + } + + /** + * Gets the Property identified by the given itemId and propertyId from the + * Container + * + * @see com.itmill.toolkit.data.Container#getContainerProperty(Object, + * Object) + */ + public Property getContainerProperty(Object itemId, Object propertyId) { + return items.getContainerProperty(itemId, propertyId); + } + + /* Container.Managed methods ******************************************** */ + + /** + * Adds the new property to all items. Adds a property with given id, type + * and default value to all items in the container. + * + * This functionality is optional. If the function is unsupported, it always + * returns false. + * + * @return True if the operation succeeded. + * @see com.itmill.toolkit.data.Container#addContainerProperty(java.lang.Object, + * java.lang.Class, java.lang.Object) + */ + public boolean addContainerProperty(Object propertyId, Class type, + Object defaultValue) throws UnsupportedOperationException { + + boolean retval = items.addContainerProperty(propertyId, type, + defaultValue); + if (retval && !(items instanceof Container.PropertySetChangeNotifier)) { + firePropertySetChange(); + } + return retval; + } + + /** + * Removes all items from the container. + * + * This functionality is optional. If the function is unsupported, it always + * returns false. + * + * @return True if the operation succeeded. + * @see com.itmill.toolkit.data.Container#removeAllItems() + */ + public boolean removeAllItems() throws UnsupportedOperationException { + + boolean retval = items.removeAllItems(); + itemIdMapper.removeAll(); + if (retval) { + setValue(null); + if (!(items instanceof Container.ItemSetChangeNotifier)) { + fireItemSetChange(); + } + } + return retval; + } + + /** + * Creates a new item into container with container managed id. The id of + * the created new item is returned. The item can be fetched with getItem() + * method. if the creation fails, null is returned. + * + * @return the Id of the created item or null in case of failure. + * @see com.itmill.toolkit.data.Container#addItem() + */ + public Object addItem() throws UnsupportedOperationException { + + Object retval = items.addItem(); + if (retval != null + && !(items instanceof Container.ItemSetChangeNotifier)) { + fireItemSetChange(); + } + return retval; + } + + /** + * Create a new item into container. The created new item is returned and + * ready for setting property values. if the creation fails, null is + * returned. In case the container already contains the item, null is + * returned. + * + * This functionality is optional. If the function is unsupported, it always + * returns null. + * + * @param itemId + * the Identification of the item to be created. + * @return the Created item with the given id, or null in case of failure. + * @see com.itmill.toolkit.data.Container#addItem(java.lang.Object) + */ + public Item addItem(Object itemId) throws UnsupportedOperationException { + + Item retval = items.addItem(itemId); + if (retval != null + && !(items instanceof Container.ItemSetChangeNotifier)) { + fireItemSetChange(); + } + return retval; + } + + /** + * Removes the item identified by Id from the container. This functionality + * is optional. If the function is not implemented, the functions allways + * returns false. + * + * @return True if the operation succeeded. + * @see com.itmill.toolkit.data.Container#removeItem(java.lang.Object) + */ + public boolean removeItem(Object itemId) + throws UnsupportedOperationException { + + unselect(itemId); + boolean retval = items.removeItem(itemId); + itemIdMapper.remove(itemId); + if (retval && !(items instanceof Container.ItemSetChangeNotifier)) { + fireItemSetChange(); + } + return retval; + } + + /** + * Removes the property from all items. Removes a property with given id + * from all the items in the container. + * + * This functionality is optional. If the function is unsupported, it always + * returns false. + * + * @return True if the operation succeeded. + * @see com.itmill.toolkit.data.Container#removeContainerProperty(java.lang.Object) + */ + public boolean removeContainerProperty(Object propertyId) + throws UnsupportedOperationException { + + boolean retval = items.removeContainerProperty(propertyId); + if (retval && !(items instanceof Container.PropertySetChangeNotifier)) { + firePropertySetChange(); + } + return retval; + } + + /* Container.Viewer methods ********************************************* */ + + /** + * Sets the container as data-source for viewing. + * + * @param newDataSource + * the new data source. + */ + public void setContainerDataSource(Container newDataSource) { + if (newDataSource == null) { + newDataSource = new IndexedContainer(); + } + + if (items != newDataSource) { + + // Removes listeners from the old datasource + if (items != null) { + try { + ((Container.ItemSetChangeNotifier) items) + .removeListener(this); + } catch (ClassCastException ignored) { + // Ignored + } + try { + ((Container.PropertySetChangeNotifier) items) + .removeListener(this); + } catch (ClassCastException ignored) { + // Ignored + } + } + + // Assigns new data source + items = newDataSource; + + // Clears itemIdMapper also + itemIdMapper.removeAll(); + + // Adds listeners + if (items != null) { + try { + ((Container.ItemSetChangeNotifier) items).addListener(this); + } catch (ClassCastException ignored) { + // Ignored + } + try { + ((Container.PropertySetChangeNotifier) items) + .addListener(this); + } catch (ClassCastException ignored) { + // Ignored + } + } + + // TODO: This should be conditional + fireValueChange(false); + } + } + + /** + * Gets the viewing data-source container. + * + * @see com.itmill.toolkit.data.Container.Viewer#getContainerDataSource() + */ + public Container getContainerDataSource() { + return items; + } + + /* Select attributes **************************************************** */ + + /** + * Is the select in multiselect mode? In multiselect mode + * + * @return the Value of property multiSelect. + */ + public boolean isMultiSelect() { + return multiSelect; + } + + /** + * Sets the multiselect mode. Setting multiselect mode false may loose + * selection information: if selected items set contains one or more + * selected items, only one of the selected items is kept as selected. + * + * @param multiSelect + * the New value of property multiSelect. + */ + public void setMultiSelect(boolean multiSelect) { + if (multiSelect && getNullSelectionItemId() != null) { + throw new IllegalStateException( + "Multiselect and NullSelectionItemId can not be set at the same time."); + } + if (multiSelect != this.multiSelect) { + + // Selection before mode change + Object oldValue = getValue(); + + this.multiSelect = multiSelect; + + // Convert the value type + if (multiSelect) { + Set s = new HashSet(); + if (oldValue != null) { + s.add(oldValue); + } + setValue(s); + } else { + 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. No that data-source must allow adding new items (it must + * implement Container.Managed). + * + * @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. SeesetItemCaptionMode()
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 ITEM_CAPTION_MODE_ID:
+ caption = itemId.toString();
+ break;
+
+ case ITEM_CAPTION_MODE_INDEX:
+ try {
+ caption = String.valueOf(((Container.Indexed) items)
+ .indexOfId(itemId));
+ } catch (ClassCastException ignored) {
+ }
+ break;
+
+ case ITEM_CAPTION_MODE_ITEM:
+ Item i = getItem(itemId);
+ if (i != null) {
+ caption = i.toString();
+ }
+ break;
+
+ case ITEM_CAPTION_MODE_EXPLICIT:
+ caption = (String) itemCaptions.get(itemId);
+ break;
+
+ case ITEM_CAPTION_MODE_EXPLICIT_DEFAULTS_ID:
+ caption = (String) itemCaptions.get(itemId);
+ if (caption == null) {
+ caption = itemId.toString();
+ }
+ break;
+
+ case ITEM_CAPTION_MODE_PROPERTY:
+ Property p = getContainerProperty(itemId,
+ getItemCaptionPropertyId());
+ if (p != null) {
+ caption = p.toString();
+ }
+ break;
+ }
+
+ // All items must have some captions
+ return caption != null ? caption : "";
+ }
+
+ /**
+ * Sets the icon for an item.
+ *
+ * @param itemId
+ * the id of the item to be assigned an icon.
+ * @param icon
+ * the New icon.
+ */
+ 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) {
+ Resource explicit = (Resource) itemIcons.get(itemId);
+ if (explicit != null) {
+ return explicit;
+ }
+
+ if (getItemIconPropertyId() == null) {
+ return null;
+ }
+
+ Property ip = getContainerProperty(itemId, getItemIconPropertyId());
+ if (ip == null) {
+ return null;
+ }
+ 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
.ITEM_CAPTION_MODE_EXPLICIT_DEFAULTS_ID
is the default
+ * mode.
+ *
+ *
+ * @param mode
+ * the One of the modes listed above.
+ */
+ public void setItemCaptionMode(int mode) {
+ if (ITEM_CAPTION_MODE_ID <= mode && mode <= ITEM_CAPTION_MODE_PROPERTY) {
+ 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
.ITEM_CAPTION_MODE_EXPLICIT_DEFAULTS_ID
is the default
+ * mode.
+ *
+ *
+ * @return the One of the modes listed above.
+ */
+ public int 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
.
+ *
+ * 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 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 + *
. + * + * @param propertyId + * the Id of the property that specifies icons for items. + */ + public void setItemIconPropertyId(Object propertyId) { + if ((propertyId != null) + && Resource.class.isAssignableFrom(getType(propertyId))) { + itemIconPropertyId = propertyId; + requestRepaint(); + } else { + itemIconPropertyId = null; + } + } + + /** + * 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 { + 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 tem to be selected. + * @see #getNullSelectionItemId() + * @see #setNullSelectionItemId(Object) + * + */ + public void select(Object itemId) { + if (!isSelected(itemId) && items.containsId(itemId)) { + if (isMultiSelect()) { + Set s = new HashSet((Set) getValue()); + s.add(itemId); + setValue(s); + } else if (itemId.equals(getNullSelectionItemId())) { + setValue(null); + } else { + setValue(itemId); + } + } + } + + /** + * Unselects an item. + * + * @param itemId + * the Item to be unselected. + * @see #getNullSelectionItemId() + * @see #setNullSelectionItemId(Object) + * + */ + public void unselect(Object itemId) { + if (isSelected(itemId)) { + if (isMultiSelect()) { + 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.itmill.toolkit.data.Container.PropertySetChangeListener#containerPropertySetChange(com.itmill.toolkit.data.Container.PropertySetChangeEvent) + */ + public void containerPropertySetChange( + Container.PropertySetChangeEvent event) { + firePropertySetChange(); + } + + /** + * Adds a new Property set change listener for this Container. + * + * @see com.itmill.toolkit.data.Container.PropertySetChangeNotifier#addListener(com.itmill.toolkit.data.Container.PropertySetChangeListener) + */ + public void addListener(Container.PropertySetChangeListener listener) { + if (propertySetEventListeners == null) { + propertySetEventListeners = new LinkedList(); + } + propertySetEventListeners.add(listener); + } + + /** + * Removes a previously registered Property set change listener. + * + * @see com.itmill.toolkit.data.Container.PropertySetChangeNotifier#removeListener(com.itmill.toolkit.data.Container.PropertySetChangeListener) + */ + 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.itmill.toolkit.data.Container.ItemSetChangeNotifier#addListener(com.itmill.toolkit.data.Container.ItemSetChangeListener) + */ + public void addListener(Container.ItemSetChangeListener listener) { + if (itemSetEventListeners == null) { + itemSetEventListeners = new LinkedList(); + } + itemSetEventListeners.add(listener); + } + + /** + * Removes the Item set change listener from the object. + * + * @see com.itmill.toolkit.data.Container.ItemSetChangeNotifier#removeListener(com.itmill.toolkit.data.Container.ItemSetChangeListener) + */ + public void removeListener(Container.ItemSetChangeListener listener) { + if (itemSetEventListeners != null) { + itemSetEventListeners.remove(listener); + if (itemSetEventListeners.isEmpty()) { + itemSetEventListeners = null; + } + } + } + + /** + * Lets the listener know a Containers Item set has changed. + * + * @see com.itmill.toolkit.data.Container.ItemSetChangeListener#containerItemSetChange(com.itmill.toolkit.data.Container.ItemSetChangeEvent) + */ + 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()) { + Container.PropertySetChangeEvent event = new PropertySetChangeEvent(); + 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()) { + Container.ItemSetChangeEvent event = new ItemSetChangeEvent(); + 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 Container.ItemSetChangeEvent { + + /** + * Gets the Property where the event occurred. + * + * @see com.itmill.toolkit.data.Container.ItemSetChangeEvent#getContainer() + */ + public Container getContainer() { + return AbstractSelect.this; + } + + } + + /** + * Implementation of property set change event. + */ + private class PropertySetChangeEvent implements + Container.PropertySetChangeEvent { + + /** + * Retrieves the Container whose contents have been modified. + * + * @see com.itmill.toolkit.data.Container.PropertySetChangeEvent#getContainer() + */ + public Container getContainer() { + return AbstractSelect.this; + } + + } + + /** + * Allow of disallow empty selection. 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) {
+ this.nullSelectionAllowed = nullSelectionAllowed;
+ }
+
+ /**
+ * Checks if null empty selection is allowed.
+ *
+ * @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 final 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 + * idetified 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.itmill.toolkit.ui.AbstractField#attach() + */ + public void attach() { + super.attach(); + } + + /** + * Detaches the component from application. + * + * @see com.itmill.toolkit.ui.AbstractComponent#detach() + */ + public void detach() { + super.detach(); + } } diff --git a/src/com/itmill/toolkit/ui/Select.java b/src/com/itmill/toolkit/ui/Select.java index 562dd30109..12f8757096 100644 --- a/src/com/itmill/toolkit/ui/Select.java +++ b/src/com/itmill/toolkit/ui/Select.java @@ -62,360 +62,363 @@ import com.itmill.toolkit.terminal.Resource; */ public class Select extends AbstractSelect implements AbstractSelect.Filtering { - /** - * Holds value of property pageLength. 0 disables paging. - */ - protected int pageLength = 10; - - // current page when the user is 'paging' trough options - private int currentPage; - - private int filteringMode = FILTERINGMODE_STARTSWITH; - - private String filterstring; - private String prevfilterstring; - private List filteredOptions; - - /* Constructors ********************************************************* */ - - /* Component methods **************************************************** */ - - public Select() { - super(); - } - - public Select(String caption, Collection options) { - super(caption, options); - } - - public Select(String caption, Container dataSource) { - super(caption, dataSource); - } - - public Select(String caption) { - super(caption); - } - - /** - * Paints the content of this component. - * - * @param target - * the Paint Event. - * @throws PaintException - * if the paint operation failed. - */ - public void paintContent(PaintTarget target) throws PaintException { - // Focus control id - if (getFocusableId() > 0) { - target.addAttribute("focusid", getFocusableId()); - } - - // The tab ordering number - if (getTabIndex() > 0) { - target.addAttribute("tabindex", getTabIndex()); - } - - // If the field is modified, but not committed, set modified attribute - if (isModified()) { - target.addAttribute("modified", true); - } - - // Adds the required attribute - if (isRequired()) { - target.addAttribute("required", true); - } - - // Paints select attributes - if (isMultiSelect()) { - target.addAttribute("selectmode", "multi"); - } - if (isNewItemsAllowed()) { - target.addAttribute("allownewitem", true); - } - - boolean needNullSelectOption = false; - if (isNullSelectionAllowed()) { - target.addAttribute("nullselect", true); - needNullSelectOption = getNullSelectionItemId() == null; - } - - // Constructs selected keys array - String[] selectedKeys; - if (isMultiSelect()) { - selectedKeys = new String[((Set) getValue()).size()]; - } else { - selectedKeys = new String[(getValue() == null - && getNullSelectionItemId() == null ? 0 : 1)]; - } - - target.addAttribute("filteringmode", getFilteringMode()); - - // Paints the options and create array of selected id keys - // TODO Also use conventional rendering if lazy loading is not supported - // by terminal - int keyIndex = 0; - - /* - * if (!isLazyLoading()) { // Support for external null selection item - * id Collection ids = getItemIds(); if (getNullSelectionItemId() != - * null && (!ids.contains(getNullSelectionItemId()))) { // Gets the - * option attribute values Object id = getNullSelectionItemId(); String - * key = this.itemIdMapper.key(id); String caption = getItemCaption(id); - * Resource icon = getItemIcon(id); // Paints option - * target.startTag("so"); if (icon != null) { - * target.addAttribute("icon", icon); } target.addAttribute("caption", - * caption); target.addAttribute("nullselection", true); - * target.addAttribute("key", key); if (isSelected(id)) { - * target.addAttribute("selected", true); selectedKeys[keyIndex++] = - * key; } target.endTag("so"); } } - */ - /* - * Iterator i; if (this.filterstring != null) { i = - * this.optionFilter.filter(this.filterstring, - * this.lazyLoadingPageLength, this.page).iterator(); - * target.addAttribute("totalMatches", this.optionFilter - * .getMatchCount()); } else { i = getItemIds().iterator(); } - */ - target.startTag("options"); - - boolean paintNullSelection = needNullSelectOption - && (currentPage == 0 && (filterstring == null - || filterstring.equals("") || filterstring.equals("-"))); - - if (paintNullSelection) { - target.startTag("so"); - target.addAttribute("caption", "-"); - target.addAttribute("key", ""); - target.endTag("so"); - } - - List options = getFilteredOptions(); - if (options.size() > this.pageLength) { - int first = this.currentPage * this.pageLength; - int last = first + this.pageLength; - if(needNullSelectOption) { - if(currentPage > 0) { - first--; - } - last--; - } - if (options.size() < last) { - last = options.size(); - } - options = options.subList(first, last); - } - Iterator i = options.iterator(); - // Paints the available selection options from data source - - while (i.hasNext()) { - - // Gets the option attribute values - Object id = i.next(); - String key = this.itemIdMapper.key(id); - String caption = getItemCaption(id); - Resource icon = getItemIcon(id); - - // Paints the option - target.startTag("so"); - if (icon != null) { - target.addAttribute("icon", icon); - } - target.addAttribute("caption", caption); - if (id != null && id.equals(getNullSelectionItemId())) { - target.addAttribute("nullselection", true); - } - target.addAttribute("key", key); - if (isSelected(id) && keyIndex < selectedKeys.length) { - target.addAttribute("selected", true); - selectedKeys[keyIndex++] = key; - } - target.endTag("so"); - } - target.endTag("options"); - - target.addAttribute("totalitems", size() - + (needNullSelectOption ? 1 : 0)); - if (this.filteredOptions != null) { - target.addAttribute("totalMatches", this.filteredOptions.size() - + (needNullSelectOption ? 1 : 0)); - } - - // Paint variables - target.addVariable(this, "selected", selectedKeys); - if (isNewItemsAllowed()) { - target.addVariable(this, "newitem", ""); - } - - target.addVariable(this, "filter", this.filterstring); - target.addVariable(this, "page", this.currentPage); - - } - - protected List getFilteredOptions() { - if (this.filterstring == null || this.filterstring.equals("") - || this.filteringMode == FILTERINGMODE_OFF) { - this.filteredOptions = new LinkedList(getItemIds()); - return this.filteredOptions; - } - - if (this.filterstring.equals(this.prevfilterstring)) { - return this.filteredOptions; - } - - Collection items; - if (prevfilterstring != null - && filterstring.startsWith(this.prevfilterstring)) { - items = filteredOptions; - } else { - items = getItemIds(); - } - prevfilterstring = filterstring; - - this.filteredOptions = new LinkedList(); - for (Iterator it = items.iterator(); it.hasNext();) { - Object itemId = it.next(); - String caption = getItemCaption(itemId); - if (caption == null || caption.equals("")) { - continue; - } - switch (this.filteringMode) { - case FILTERINGMODE_CONTAINS: - if (caption.indexOf(this.filterstring) > -1) { - this.filteredOptions.add(itemId); - } - break; - case FILTERINGMODE_STARTSWITH: - default: - if (caption.startsWith(this.filterstring)) { - this.filteredOptions.add(itemId); - } - break; - } - } - - return this.filteredOptions; - } - - /** - * Invoked when the value of a variable has changed. - * - * @see com.itmill.toolkit.ui.AbstractComponent#changeVariables(java.lang.Object, - * java.util.Map) - */ - public void changeVariables(Object source, Map variables) { - String newFilter; - if ((newFilter = (String) variables.get("filter")) != null) { - // this is a filter request - this.currentPage = ((Integer) variables.get("page")).intValue(); - this.filterstring = newFilter; - requestRepaint(); - return; - } - - // Try to set the property value - - // New option entered (and it is allowed) - String newitem = (String) variables.get("newitem"); - if (newitem != null && newitem.length() > 0) { - - // Checks for readonly - if (isReadOnly()) { - throw new Property.ReadOnlyException(); - } - - // Adds new option - if (addItem(newitem) != null) { - - // Sets the caption property, if used - if (getItemCaptionPropertyId() != null) { - try { - getContainerProperty(newitem, - getItemCaptionPropertyId()).setValue(newitem); - } catch (Property.ConversionException ignored) { - // The conversion exception is safely ignored, the - // caption is - // just missing - } - } - setValue(newitem); - } - } - - // Selection change - if (variables.containsKey("selected")) { - String[] ka = (String[]) variables.get("selected"); - - // Multiselect mode - if (isMultiSelect()) { - - // TODO Optimize by adding repaintNotNeeded whan applicaple - - // Converts the key-array to id-set - LinkedList s = new LinkedList(); - for (int i = 0; i < ka.length; i++) { - Object id = this.itemIdMapper.get(ka[i]); - if (id != null && containsId(id)) { - s.add(id); - } else if (this.itemIdMapper.isNewIdKey(ka[i]) - && newitem != null && newitem.length() > 0) { - s.add(newitem); - } - } - - // Limits the deselection to the set of visible items - // (non-visible items can not be deselected) - Collection visible = getVisibleItemIds(); - if (visible != null) { - Set newsel = (Set) getValue(); - if (newsel == null) { - newsel = new HashSet(); - } else { - newsel = new HashSet(newsel); - } - newsel.removeAll(visible); - newsel.addAll(s); - setValue(newsel, true); - } - } - - // Single select mode - else { - if (ka.length == 0) { - - // Allows deselection only if the deselected item is visible - Object current = getValue(); - Collection visible = getVisibleItemIds(); - if (visible != null && visible.contains(current)) { - setValue(null, true); - } - } else { - Object id = this.itemIdMapper.get(ka[0]); - if (id != null && id.equals(getNullSelectionItemId())) { - setValue(null, true); - } else if (this.itemIdMapper.isNewIdKey(ka[0])) { - setValue(newitem); - } else { - setValue(id, true); - } - } - } - } - } - - /** - * Gets the component UIDL tag. - * - * @return the Component UIDL tag as string. - */ - public String getTag() { - return "select"; - } - - public void setFilteringMode(int filteringMode) { - this.filteringMode = filteringMode; - } - - public int getFilteringMode() { - return this.filteringMode; - } + /** + * Holds value of property pageLength. 0 disables paging. + */ + protected int pageLength = 10; + + // current page when the user is 'paging' trough options + private int currentPage; + + private int filteringMode = FILTERINGMODE_STARTSWITH; + + private String filterstring; + private String prevfilterstring; + private List filteredOptions; + + /* Constructors ********************************************************* */ + + /* Component methods **************************************************** */ + + public Select() { + super(); + } + + public Select(String caption, Collection options) { + super(caption, options); + } + + public Select(String caption, Container dataSource) { + super(caption, dataSource); + } + + public Select(String caption) { + super(caption); + } + + /** + * Paints the content of this component. + * + * @param target + * the Paint Event. + * @throws PaintException + * if the paint operation failed. + */ + public void paintContent(PaintTarget target) throws PaintException { + // Focus control id + if (getFocusableId() > 0) { + target.addAttribute("focusid", getFocusableId()); + } + + // The tab ordering number + if (getTabIndex() > 0) { + target.addAttribute("tabindex", getTabIndex()); + } + + // If the field is modified, but not committed, set modified attribute + if (isModified()) { + target.addAttribute("modified", true); + } + + // Adds the required attribute + if (isRequired()) { + target.addAttribute("required", true); + } + + // Paints select attributes + if (isMultiSelect()) { + target.addAttribute("selectmode", "multi"); + } + if (isNewItemsAllowed()) { + target.addAttribute("allownewitem", true); + } + + boolean needNullSelectOption = false; + if (isNullSelectionAllowed()) { + target.addAttribute("nullselect", true); + needNullSelectOption = (getNullSelectionItemId() == null); + if (!needNullSelectOption) { + target.addAttribute("nullselectitem", true); + } + } + + // Constructs selected keys array + String[] selectedKeys; + if (isMultiSelect()) { + selectedKeys = new String[((Set) getValue()).size()]; + } else { + selectedKeys = new String[(getValue() == null + && getNullSelectionItemId() == null ? 0 : 1)]; + } + + target.addAttribute("filteringmode", getFilteringMode()); + + // Paints the options and create array of selected id keys + // TODO Also use conventional rendering if lazy loading is not supported + // by terminal + int keyIndex = 0; + + /* + * if (!isLazyLoading()) { // Support for external null selection item + * id Collection ids = getItemIds(); if (getNullSelectionItemId() != + * null && (!ids.contains(getNullSelectionItemId()))) { // Gets the + * option attribute values Object id = getNullSelectionItemId(); String + * key = this.itemIdMapper.key(id); String caption = getItemCaption(id); + * Resource icon = getItemIcon(id); // Paints option + * target.startTag("so"); if (icon != null) { + * target.addAttribute("icon", icon); } target.addAttribute("caption", + * caption); target.addAttribute("nullselection", true); + * target.addAttribute("key", key); if (isSelected(id)) { + * target.addAttribute("selected", true); selectedKeys[keyIndex++] = + * key; } target.endTag("so"); } } + */ + /* + * Iterator i; if (this.filterstring != null) { i = + * this.optionFilter.filter(this.filterstring, + * this.lazyLoadingPageLength, this.page).iterator(); + * target.addAttribute("totalMatches", this.optionFilter + * .getMatchCount()); } else { i = getItemIds().iterator(); } + */ + target.startTag("options"); + + boolean paintNullSelection = needNullSelectOption + && (currentPage == 0 && (filterstring == null + || filterstring.equals("") || filterstring.equals("-"))); + + if (paintNullSelection) { + target.startTag("so"); + target.addAttribute("caption", "-"); + target.addAttribute("key", ""); + target.endTag("so"); + } + + List options = getFilteredOptions(); + if (options.size() > pageLength) { + int first = currentPage * pageLength; + int last = first + pageLength; + if (needNullSelectOption) { + if (currentPage > 0) { + first--; + } + last--; + } + if (options.size() < last) { + last = options.size(); + } + options = options.subList(first, last); + } + Iterator i = options.iterator(); + // Paints the available selection options from data source + + while (i.hasNext()) { + + // Gets the option attribute values + Object id = i.next(); + String key = itemIdMapper.key(id); + String caption = getItemCaption(id); + Resource icon = getItemIcon(id); + + // Paints the option + target.startTag("so"); + if (icon != null) { + target.addAttribute("icon", icon); + } + target.addAttribute("caption", caption); + if (id != null && id.equals(getNullSelectionItemId())) { + target.addAttribute("nullselection", true); + } + target.addAttribute("key", key); + if (isSelected(id) && keyIndex < selectedKeys.length) { + target.addAttribute("selected", true); + selectedKeys[keyIndex++] = key; + } + target.endTag("so"); + } + target.endTag("options"); + + target.addAttribute("totalitems", size() + + (needNullSelectOption ? 1 : 0)); + if (filteredOptions != null) { + target.addAttribute("totalMatches", filteredOptions.size() + + (needNullSelectOption ? 1 : 0)); + } + + // Paint variables + target.addVariable(this, "selected", selectedKeys); + if (isNewItemsAllowed()) { + target.addVariable(this, "newitem", ""); + } + + target.addVariable(this, "filter", filterstring); + target.addVariable(this, "page", currentPage); + + } + + protected List getFilteredOptions() { + if (filterstring == null || filterstring.equals("") + || filteringMode == FILTERINGMODE_OFF) { + filteredOptions = new LinkedList(getItemIds()); + return filteredOptions; + } + + if (filterstring.equals(prevfilterstring)) { + return filteredOptions; + } + + Collection items; + if (prevfilterstring != null + && filterstring.startsWith(prevfilterstring)) { + items = filteredOptions; + } else { + items = getItemIds(); + } + prevfilterstring = filterstring; + + filteredOptions = new LinkedList(); + for (Iterator it = items.iterator(); it.hasNext();) { + Object itemId = it.next(); + String caption = getItemCaption(itemId); + if (caption == null || caption.equals("")) { + continue; + } + switch (filteringMode) { + case FILTERINGMODE_CONTAINS: + if (caption.indexOf(filterstring) > -1) { + filteredOptions.add(itemId); + } + break; + case FILTERINGMODE_STARTSWITH: + default: + if (caption.startsWith(filterstring)) { + filteredOptions.add(itemId); + } + break; + } + } + + return filteredOptions; + } + + /** + * Invoked when the value of a variable has changed. + * + * @see com.itmill.toolkit.ui.AbstractComponent#changeVariables(java.lang.Object, + * java.util.Map) + */ + public void changeVariables(Object source, Map variables) { + String newFilter; + if ((newFilter = (String) variables.get("filter")) != null) { + // this is a filter request + currentPage = ((Integer) variables.get("page")).intValue(); + filterstring = newFilter; + requestRepaint(); + return; + } + + // Try to set the property value + + // New option entered (and it is allowed) + String newitem = (String) variables.get("newitem"); + if (newitem != null && newitem.length() > 0) { + + // Checks for readonly + if (isReadOnly()) { + throw new Property.ReadOnlyException(); + } + + // Adds new option + if (addItem(newitem) != null) { + + // Sets the caption property, if used + if (getItemCaptionPropertyId() != null) { + try { + getContainerProperty(newitem, + getItemCaptionPropertyId()).setValue(newitem); + } catch (Property.ConversionException ignored) { + // The conversion exception is safely ignored, the + // caption is + // just missing + } + } + setValue(newitem); + } + } + + // Selection change + if (variables.containsKey("selected")) { + String[] ka = (String[]) variables.get("selected"); + + // Multiselect mode + if (isMultiSelect()) { + + // TODO Optimize by adding repaintNotNeeded whan applicaple + + // Converts the key-array to id-set + LinkedList s = new LinkedList(); + for (int i = 0; i < ka.length; i++) { + Object id = itemIdMapper.get(ka[i]); + if (id != null && containsId(id)) { + s.add(id); + } else if (itemIdMapper.isNewIdKey(ka[i]) + && newitem != null && newitem.length() > 0) { + s.add(newitem); + } + } + + // Limits the deselection to the set of visible items + // (non-visible items can not be deselected) + Collection visible = getVisibleItemIds(); + if (visible != null) { + Set newsel = (Set) getValue(); + if (newsel == null) { + newsel = new HashSet(); + } else { + newsel = new HashSet(newsel); + } + newsel.removeAll(visible); + newsel.addAll(s); + setValue(newsel, true); + } + } + + // Single select mode + else { + if (ka.length == 0) { + + // Allows deselection only if the deselected item is visible + Object current = getValue(); + Collection visible = getVisibleItemIds(); + if (visible != null && visible.contains(current)) { + setValue(null, true); + } + } else { + Object id = itemIdMapper.get(ka[0]); + if (id != null && id.equals(getNullSelectionItemId())) { + setValue(null, true); + } else if (itemIdMapper.isNewIdKey(ka[0])) { + setValue(newitem); + } else { + setValue(id, true); + } + } + } + } + } + + /** + * Gets the component UIDL tag. + * + * @return the Component UIDL tag as string. + */ + public String getTag() { + return "select"; + } + + public void setFilteringMode(int filteringMode) { + this.filteringMode = filteringMode; + } + + public int getFilteringMode() { + return filteringMode; + } } -- 2.39.5