/* *************************************************************************
IT Mill Toolkit
Development of Browser User Interfaces Made Easy
Copyright (C) 2000-2006 IT Mill Ltd
*************************************************************************
This product is distributed under commercial license that can be found
from the product package on license.pdf. Use of this product might
require purchasing a commercial license from IT Mill Ltd. For guidelines
on usage, see licensing-guidelines.html
*************************************************************************
For more information, contact:
IT Mill Ltd phone: +358 2 4802 7180
Ruukinkatu 2-4 fax: +358 2 4802 7181
20540, Turku email: info@itmill.com
Finland company www: www.itmill.com
Primary source for information and releases: www.itmill.com
********************************************************************** */
package com.itmill.toolkit.ui;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import com.itmill.toolkit.data.Container;
import com.itmill.toolkit.data.Property;
import com.itmill.toolkit.terminal.PaintException;
import com.itmill.toolkit.terminal.PaintTarget;
import com.itmill.toolkit.terminal.Resource;
/**
*
* A class representing a selection of items the user has selected in a UI. The
* set of choices is presented as a set of {@link com.itmill.toolkit.data.Item}s
* in a {@link com.itmill.toolkit.data.Container}.
*
*
*
* A Select
component may be in single- or multiselect mode.
* Multiselect mode means that more than one item can be selected
* simultaneously.
*
*
* @author IT Mill Ltd.
* @version
* @VERSION@
* @since 3.0
*/
public class Select extends AbstractSelect implements AbstractSelect.Filtering {
/**
* Holds value of property pageLength. 0 disables paging.
*/
protected int pageLength = 15;
// 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);
}
if (isNullSelectionAllowed()) {
target.addAttribute("nullselect", 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(); }
*/
List options = getFilteredOptions();
if (options.size() > this.pageLength) {
int first = this.currentPage * this.pageLength;
int last = first + this.pageLength;
if (options.size() < last) {
last = options.size();
}
options = options.subList(first, last);
}
Iterator i = options.iterator();
// Paints the available selection options from data source
target.startTag("options");
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());
if (this.filteredOptions != null) {
target.addAttribute("totalMatches", this.filteredOptions.size());
}
// 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
}
}
}
}
// 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;
}
}