From 645545f63d8744677ee6159c19da5351be0e93d8 Mon Sep 17 00:00:00 2001 From: Joonas Lehtinen Date: Wed, 31 Jan 2007 18:54:58 +0000 Subject: [PATCH] Integrated filter select features. Fixed #41 svn changeset:393/svn branch:toolkit --- .../toolkit/demo/features/FeatureSelect.java | 50 +- .../toolkit/demo/features/PropertyPanel.java | 30 +- src/com/itmill/toolkit/ui/Select.java | 1125 +++++++++++------ src/com/itmill/toolkit/ui/Table.java | 11 +- src/com/itmill/toolkit/ui/Tree.java | 9 + 5 files changed, 814 insertions(+), 411 deletions(-) diff --git a/src/com/itmill/toolkit/demo/features/FeatureSelect.java b/src/com/itmill/toolkit/demo/features/FeatureSelect.java index 721b833503..463cb06b11 100644 --- a/src/com/itmill/toolkit/demo/features/FeatureSelect.java +++ b/src/com/itmill/toolkit/demo/features/FeatureSelect.java @@ -32,6 +32,45 @@ import com.itmill.toolkit.ui.*; public class FeatureSelect extends Feature { + private static final String[] firstnames = + new String[] { + "John", + "Mary", + "Joe", + "Sarah", + "Jeff", + "Jane", + "Peter", + "Marc", + "Robert", + "Paula", + "Lenny", + "Kenny", + "Nathan", + "Nicole", + "Laura", + "JosĀŽ", + "Josie", + "Linus" }; + private static final String[] lastnames = + new String[] { + "Torvalds", + "Smith", + "Adams", + "Black", + "Wilson", + "Richards", + "Thompson", + "McGoff", + "Halas", + "Jones", + "Beck", + "Sheridan", + "Picard", + "Hill", + "Fielding", + "Einstein" }; + public FeatureSelect() { super(); } @@ -42,11 +81,10 @@ public class FeatureSelect extends Feature { // Example panel Panel show = new Panel("Select component"); - Select s = new Select("Select Car"); - s.addItem("Audi"); - s.addItem("BMW"); - s.addItem("Chrysler"); - s.addItem("Volvo"); + Select s = new Select("Select Person"); + for (int i=0; i<1000; i++) + s.addItem(firstnames[(int) (Math.random() * (firstnames.length-1))] + " " + + lastnames[(int) (Math.random() * (lastnames.length-1))]); show.addComponent(s); l.addComponent(show); @@ -62,7 +100,7 @@ public class FeatureSelect extends Feature { .getItemProperty(themes.getItemCaptionPropertyId()) .setValue("twincol"); l.addComponent(p); - + return l; } diff --git a/src/com/itmill/toolkit/demo/features/PropertyPanel.java b/src/com/itmill/toolkit/demo/features/PropertyPanel.java index ade6effe54..e7f6099617 100644 --- a/src/com/itmill/toolkit/demo/features/PropertyPanel.java +++ b/src/com/itmill/toolkit/demo/features/PropertyPanel.java @@ -38,6 +38,7 @@ import java.util.Iterator; import java.util.LinkedList; import com.itmill.toolkit.data.*; +import com.itmill.toolkit.data.Property.ValueChangeListener; import com.itmill.toolkit.data.util.*; import com.itmill.toolkit.terminal.*; import com.itmill.toolkit.ui.*; @@ -300,7 +301,7 @@ public class PropertyPanel private void addSelectProperties() { Form set = createBeanPropertySet( - new String[] { "multiSelect", "newItemsAllowed" }); + new String[] { "newItemsAllowed", "lazyLoading" ,"multiSelect"}); addProperties("Select Properties", set); set.getField("multiSelect").setDescription( @@ -309,9 +310,19 @@ public class PropertyPanel "Select component (but not Tree or Table) can allow the user to directly " + "add new items to set of options. The new items are constrained to be " + "strings and thus feature only applies to simple lists."); + Button ll = (Button) set.getField("lazyLoading"); + ll.setDescription("In Ajax rendering mode select supports lazy loading and filtering of options."); + ll.addListener((ValueChangeListener)this); + ll.setImmediate(true); + if (((Boolean)ll.getValue()).booleanValue()) { + set.getField("multiSelect").setVisible(false); + set.getField("newItemsAllowed").setVisible(false); + } if (objectToConfigure instanceof Tree - || objectToConfigure instanceof Table) - set.removeItemProperty("newItemsAllowed"); + || objectToConfigure instanceof Table) { + set.removeItemProperty("newItemsAllowed"); + set.removeItemProperty("lazyLoading"); + } } /** Field special properties */ @@ -415,6 +426,19 @@ public class PropertyPanel addComponent.setValue(null); } + } else if (event.getProperty() == getField("lazyLoading")) { + boolean newValue = ((Boolean)event.getProperty().getValue()).booleanValue(); + Field multiselect = getField("multiSelect"); + Field newitems = getField("newItemsAllowed"); + if (newValue) { + newitems.setValue(Boolean.FALSE); + newitems.setVisible(false); + multiselect.setValue(Boolean.FALSE); + multiselect.setVisible(false); + } else { + newitems.setVisible(true); + multiselect.setVisible(true); + } } } diff --git a/src/com/itmill/toolkit/ui/Select.java b/src/com/itmill/toolkit/ui/Select.java index ac1b5221ba..dc8a41e7cc 100644 --- a/src/com/itmill/toolkit/ui/Select.java +++ b/src/com/itmill/toolkit/ui/Select.java @@ -1,33 +1,42 @@ /* ************************************************************************* - IT Mill Toolkit + IT Mill Toolkit - Development of Browser User Interfaces Made Easy + Development of Browser User Interfaces Made Easy - Copyright (C) 2000-2006 IT Mill Ltd - - ************************************************************************* + 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 + 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 + ************************************************************************* + + 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.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.OutputStreamWriter; +import java.io.UnsupportedEncodingException; +import java.net.URL; +import java.net.URLEncoder; +import java.nio.charset.Charset; +import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashSet; @@ -41,66 +50,75 @@ import com.itmill.toolkit.data.Container; import com.itmill.toolkit.data.Item; import com.itmill.toolkit.data.Property; import com.itmill.toolkit.data.util.IndexedContainer; +import com.itmill.toolkit.terminal.DownloadStream; import com.itmill.toolkit.terminal.KeyMapper; 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}.

+import com.itmill.toolkit.terminal.URIHandler; + +/** + *

+ * 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. + *

+ * A Select component may be in single- or multiselect mode. * Multiselect mode means that more than one item can be selected - * simultaneously.

- * + * simultaneously. + *

+ * * @author IT Mill Ltd. - * @version @VERSION@ + * @version + * @VERSION@ * @since 3.0 */ -public class Select - extends AbstractField - implements - Container, - Container.Viewer, - Container.PropertySetChangeListener, - Container.PropertySetChangeNotifier, - Container.ItemSetChangeNotifier, +public class Select extends AbstractField implements Container, + Container.Viewer, Container.PropertySetChangeListener, + Container.PropertySetChangeNotifier, Container.ItemSetChangeNotifier, Container.ItemSetChangeListener { - /** Item caption mode: Item's ID's String representation - * is used as caption. + /** + * Item caption mode: Item's ID's 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. + /** + * 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 + /** + * 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. + /** + * 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. + /** + * 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. + /** + * 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. + /** + * Item caption mode: Item captions are read from property specified with + * setItemCaptionPropertyId. */ public static final int ITEM_CAPTION_MODE_PROPERTY = 6; @@ -137,44 +155,66 @@ public class Select /** List of item set change event listeners */ private LinkedList itemSetEventListeners = null; - /** Item id that represents null selection of this select. + /** + * Item id that represents null selection 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.

+ *

+ * 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. + *

*/ private Object nullSelectionItemId = null; + /** Mechanism for streaming select options outside of the UIDL. + * + * By default streaming is not enabled and this is null. Streaming can + * be enabled with setOptionsLoadingLazy(true). + * + */ + private OptionsStream optionsStream = null; + /* Constructors ********************************************************* */ - /** Creates an empty Select. - * The caption is not used. + /** + * Creates an empty Select. The caption is not used. */ public Select() { setContainerDataSource(new IndexedContainer()); } - /** Creates an empty Select with caption. + /** + * Creates an empty Select with caption. */ public Select(String caption) { setContainerDataSource(new IndexedContainer()); setCaption(caption); } - /** Creates a new select wthat is connected to a data-source. - * @param dataSource Container datasource to be selected from by this select. - * @param caption Caption of the component. - * @param selected Selected item id or null, if none selected. + /** + * Creates a new select wthat is connected to a data-source. + * + * @param dataSource + * Container datasource to be selected from by this select. + * @param caption + * Caption of the component. + * @param selected + * Selected item id or null, if none selected. */ public Select(String caption, Container dataSource) { setCaption(caption); setContainerDataSource(dataSource); } - /** Creates a new select that is filled from a collection of option values. - * @param caption Caption of this field. - * @param options Collection containing the options. - * @param selected Selected option or null, if none selected. + /** + * Creates a new select that is filled from a collection of option values. + * + * @param caption + * Caption of this field. + * @param options + * Collection containing the options. + * @param selected + * Selected option or null, if none selected. */ public Select(String caption, Collection options) { @@ -190,9 +230,13 @@ public class Select /* Component methods **************************************************** */ - /** Paint the content of this component. - * @param event PaintEvent. - * @throws PaintException The paint operation failed. + /** + * Paint the content of this component. + * + * @param event + * PaintEvent. + * @throws PaintException + * The paint operation failed. */ public void paintContent(PaintTarget target) throws PaintException { @@ -205,65 +249,78 @@ public class Select if (isNewItemsAllowed()) target.addAttribute("allownewitem", true); - // Paint options and create array of selected id keys + // Construct selected keys array String[] selectedKeys; if (isMultiSelect()) selectedKeys = new String[((Set) getValue()).size()]; else - selectedKeys = - new String[( - getValue() == null - && getNullSelectionItemId() == null ? 0 : 1)]; - int keyIndex = 0; + selectedKeys = new String[(getValue() == null + && getNullSelectionItemId() == null ? 0 : 1)]; + String[] selectedCaptions; + selectedCaptions = new String[selectedKeys.length]; + + // Paint options and create array of selected id keys target.startTag("options"); - - // Support for external null selection item id - Collection ids = getItemIds(); - if (getNullSelectionItemId() != null - && (!ids.contains(getNullSelectionItemId()))) { - - // Get the option attribute values - Object id = getNullSelectionItemId(); - String key = itemIdMapper.key(id); - String caption = getItemCaption(id); - Resource icon = getItemIcon(id); - - // Paint 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; + if (!isLazyLoading()) { + int keyIndex = 0; + + // Support for external null selection item id + Collection ids = getItemIds(); + if (getNullSelectionItemId() != null + && (!ids.contains(getNullSelectionItemId()))) { + + // Get the option attribute values + Object id = getNullSelectionItemId(); + String key = itemIdMapper.key(id); + String caption = getItemCaption(id); + Resource icon = getItemIcon(id); + + // Paint 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"); } - target.endTag("so"); - } - // Paint available selection options from data source - for (Iterator i = getItemIds().iterator(); i.hasNext();) { - - // Get the option attribute values - Object id = i.next(); - String key = itemIdMapper.key(id); - String caption = getItemCaption(id); - Resource icon = getItemIcon(id); - - // Paint 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; + // Paint available selection options from data source + for (Iterator i = getItemIds().iterator(); i.hasNext();) { + + // Get the option attribute values + Object id = i.next(); + String key = itemIdMapper.key(id); + String caption = getItemCaption(id); + Resource icon = getItemIcon(id); + + // Paint 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("so"); + } else { + + // Lazy options loading + target.addAttribute("loadfrom", getApplication().getURL().toString() + + optionsStream.uri); + target.addAttribute("total", (getItemIds() != null) ? getItemIds() + .size() : 0); + target.addAttribute("initial", optionsStream.getJSON(20,0,"")); + target.addAttribute("selectedValue", toString() == null ? "" : toString()); } target.endTag("options"); @@ -273,9 +330,12 @@ public class Select target.addVariable(this, "newitem", ""); } - /** Invoked when the value of a variable has changed. - * @param event Variable change event containing the information about - * the changed variable. + /** + * Invoked when the value of a variable has changed. + * + * @param event + * Variable change event containing the information about the + * changed variable. */ public void changeVariables(Object source, Map variables) { @@ -295,13 +355,12 @@ public class Select // Set the caption property, if used if (getItemCaptionPropertyId() != null) try { - getContainerProperty( - newitem, - getItemCaptionPropertyId()).setValue( - newitem); + getContainerProperty(newitem, + getItemCaptionPropertyId()).setValue(newitem); } catch (Property.ConversionException ignored) { - // The conversion exception is safely ignored, the caption is - // just missing + // The conversion exception is safely ignored, the + // caption is + // just missing } } } @@ -319,9 +378,7 @@ public class Select Object id = itemIdMapper.get(ka[i]); if (id != null && containsId(id)) s.add(id); - else if ( - itemIdMapper.isNewIdKey(ka[i]) - && newitem != null + else if (itemIdMapper.isNewIdKey(ka[i]) && newitem != null && newitem.length() > 0) s.add(newitem); } @@ -363,15 +420,18 @@ public class Select } } - /** Get component UIDL tag. + /** + * Get component UIDL tag. + * * @return Component UIDL tag as string. */ public String getTag() { return "select"; } - /** Get 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 + /** + * Get 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() { @@ -382,10 +442,12 @@ public class Select /* Property methods ***************************************************** */ - /** Return the type of the property. - * getValue and setValue functions 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. getValue and setValue functions 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 type Type of the property. */ public Class getType() { @@ -395,7 +457,8 @@ public class Select return Object.class; } - /** Get the selected item id or in multiselect mode a set of selected ids. + /** + * Get the selected item id or in multiselect mode a set of selected ids. */ public Object getValue() { Object retValue = super.getValue(); @@ -406,9 +469,9 @@ public class Select if (retValue == null) return new HashSet(); if (retValue instanceof Set) { - return Collections.unmodifiableSet((Set) retValue); + return Collections.unmodifiableSet((Set) retValue); } else if (retValue instanceof Collection) { - return new HashSet((Collection)retValue); + return new HashSet((Collection) retValue); } else { Set s = new HashSet(); if (items.containsId(retValue)) @@ -420,16 +483,20 @@ public class Select return retValue; } - /** Set 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.

+ /** + * Set the visible value of the property. * - * @param newValue New selected item or collection of selected items. + *

+ * 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 + * New selected item or collection of selected items. */ - public void setValue(Object newValue) - throws Property.ReadOnlyException, Property.ConversionException { + public void setValue(Object newValue) throws Property.ReadOnlyException, + Property.ConversionException { if (isMultiSelect()) { if (newValue == null) @@ -442,43 +509,56 @@ public class Select /* Container methods **************************************************** */ - /** Get the item from the container with given id. - * If the container does not contain the requested item, null is returned. + /** + * Get the item from the container with given id. If the container does not + * contain the requested item, null is returned. */ public Item getItem(Object itemId) { return items.getItem(itemId); } - /** Get item Id collection from the container. + /** + * Get item Id collection from the container. + * * @return Collection of item ids. */ public Collection getItemIds() { return items.getItemIds(); } - /** Get property Id collection from the container. + /** + * Get property Id collection from the container. + * * @return Collection of property ids. */ public Collection getContainerPropertyIds() { return items.getContainerPropertyIds(); } - /** Get property type. - * @param id Id identifying the of the property. + /** + * Get property type. + * + * @param id + * Id identifying the of the property. */ public Class getType(Object propertyId) { return items.getType(propertyId); } - /** Get the number of items in the container. + /** + * Get the number of items in the container. + * * @return Number of items in the container. */ public int size() { return items.size(); } - /** Test, if the collection contains an item with given id. - * @param itemId Id the of item to be tested. + /** + * Test, if the collection contains an item with given id. + * + * @param itemId + * Id the of item to be tested. */ public boolean containsId(Object itemId) { if (itemId != null) @@ -488,7 +568,8 @@ public class Select } /** - * @see com.itmill.toolkit.data.Container#getContainerProperty(Object, Object) + * @see com.itmill.toolkit.data.Container#getContainerProperty(Object, + * Object) */ public Property getContainerProperty(Object itemId, Object propertyId) { return items.getContainerProperty(itemId, propertyId); @@ -496,35 +577,32 @@ public class Select /* Container.Managed methods ******************************************** */ - /** Add new property to all items. - * Adds a property with given id, type and default value to all items - * in the container. - * + /** + * Add 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 iff the operation succeeded. */ - public boolean addContainerProperty( - Object propertyId, - Class type, - Object defaultValue) - throws UnsupportedOperationException { - - boolean retval = - items.addContainerProperty(propertyId, type, defaultValue); - if (retval - && !(items instanceof Container.PropertySetChangeNotifier)) { + 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; } - /** Remove all items from the container. - * + /** + * Remove all items from the container. + * * This functionality is optional. If the function is unsupported, it always * returns false. - * + * * @return True iff the operation succeeded. */ public boolean removeAllItems() throws UnsupportedOperationException { @@ -539,50 +617,53 @@ public class Select return retval; } - /** Create 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. - * + /** + * Create 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 Id of the created item or null in case of failure. */ public Object addItem() throws UnsupportedOperationException { Object retval = items.addItem(); if (retval != null - && !(items instanceof Container.ItemSetChangeNotifier)) + && !(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. - * + /** + * 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 Identification of the item to be created. + * + * @param itemId + * Identification of the item to be created. * @return Created item with the given id, or null in case of failure. */ public Item addItem(Object itemId) throws UnsupportedOperationException { Item retval = items.addItem(itemId); if (retval != null - && !(items instanceof Container.ItemSetChangeNotifier)) + && !(items instanceof Container.ItemSetChangeNotifier)) fireItemSetChange(); return retval; } - /** Remove item identified by Id from the container. - * This functionality is optional. If the function is not implemented, - * the functions allways returns false. - * + /** + * Remove item identified by Id from the container. This functionality is + * optional. If the function is not implemented, the functions allways + * returns false. + * * @return True iff the operation succeeded. */ public boolean removeItem(Object itemId) - throws UnsupportedOperationException { + throws UnsupportedOperationException { unselect(itemId); boolean retval = items.removeItem(itemId); @@ -592,16 +673,17 @@ public class Select return retval; } - /** Remove property from all items. - * Removes a property with given id from all the items in the container. - * + /** + * Remove 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 iff the operation succeeded. */ public boolean removeContainerProperty(Object propertyId) - throws UnsupportedOperationException { + throws UnsupportedOperationException { boolean retval = items.removeContainerProperty(propertyId); if (retval && !(items instanceof Container.PropertySetChangeNotifier)) @@ -611,7 +693,7 @@ public class Select /* Container.Viewer methods ********************************************* */ - /** Set the container as data-source for viewing. */ + /** Set the container as data-source for viewing. */ public void setContainerDataSource(Container newDataSource) { if (newDataSource == null) newDataSource = new IndexedContainer(); @@ -621,18 +703,14 @@ public class Select // Remove listeners from the old datasource if (items != null) { try { - ((Container.ItemSetChangeNotifier) items).removeListener( - (Container.ItemSetChangeListener) this); + ((Container.ItemSetChangeNotifier) items) + .removeListener((Container.ItemSetChangeListener) this); } catch (ClassCastException ignored) { // Ignored } try { - ( - ( - Container - .PropertySetChangeNotifier) items) - .removeListener( - (Container.PropertySetChangeListener) this); + ((Container.PropertySetChangeNotifier) items) + .removeListener((Container.PropertySetChangeListener) this); } catch (ClassCastException ignored) { // Ignored } @@ -643,53 +721,56 @@ public class Select // Clear itemIdMapper also this.itemIdMapper.removeAll(); - + // Add listeners if (items != null) { try { - ((Container.ItemSetChangeNotifier) items).addListener( - (Container.ItemSetChangeListener) this); + ((Container.ItemSetChangeNotifier) items) + .addListener((Container.ItemSetChangeListener) this); } catch (ClassCastException ignored) { // Ignored } try { - ((Container.PropertySetChangeNotifier) items).addListener( - (Container.PropertySetChangeListener) this); + ((Container.PropertySetChangeNotifier) items) + .addListener((Container.PropertySetChangeListener) this); } catch (ClassCastException ignored) { // Ignored } } - //TODO: This should be conditional + // TODO: This should be conditional fireValueChange(); } } - /** Get viewing data-source container. */ + /** Get viewing data-source container. */ public Container getContainerDataSource() { return items; } /* Select attributes **************************************************** */ - /** Is the select in multiselect mode? In multiselect mode + /** + * Is the select in multiselect mode? In multiselect mode + * * @return Value of property multiSelect. */ public boolean isMultiSelect() { return this.multiSelect; } - /** Set 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 New value of property multiSelect. + /** + * Set 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 + * New value of property multiSelect. */ public void setMultiSelect(boolean multiSelect) { if (multiSelect != this.multiSelect) { - // Selection before mode change + // Selection before mode change Object oldValue = getValue(); this.multiSelect = multiSelect; @@ -706,7 +787,7 @@ public class Select setValue(null); else - // Set the single select to contain only the first + // Set the single select to contain only the first // selected value in the multiselect setValue(s.iterator().next()); } @@ -715,10 +796,12 @@ public class Select } } - /** 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). + /** + * 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 iff additions are allowed. */ public boolean isNewItemsAllowed() { @@ -726,8 +809,11 @@ public class Select return this.allowNewOptions; } - /** Enable or disable possibility to add new options by the user. - * @param allowNewOptions New value of property allowNewOptions. + /** + * Enable or disable possibility to add new options by the user. + * + * @param allowNewOptions + * New value of property allowNewOptions. */ public void setNewItemsAllowed(boolean allowNewOptions) { @@ -740,11 +826,14 @@ public class Select } } - /** 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 New caption. + /** + * 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 + * New caption. */ public void setItemCaption(Object itemId, String caption) { if (itemId != null) { @@ -753,11 +842,13 @@ public class Select } } - /** Get 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. + /** + * Get 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 caption for specified item. */ public String getItemCaption(Object itemId) { @@ -770,51 +861,53 @@ public class Select switch (getItemCaptionMode()) { - case ITEM_CAPTION_MODE_ID : - caption = itemId.toString(); - break; + 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; + 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 : ""; } - /** Set icon for an item. - * - * @param itemId The id of the item to be assigned an icon. - * @param icon New icon. + /** + * Set icon for an item. + * + * @param itemId + * The id of the item to be assigned an icon. + * @param icon + * New icon. */ public void setItemIcon(Object itemId, Resource icon) { if (itemId != null) { @@ -826,9 +919,11 @@ public class Select } } - /** Get the item icon. - * - * @param itemId The id of the item to be assigned an icon. + /** + * Get the item icon. + * + * @param itemId + * The id of the item to be assigned an icon. * @return Icon for the item or null, if not specified. */ public Resource getItemIcon(Object itemId) { @@ -849,82 +944,89 @@ public class Select return null; } - /** Set the item caption mode. - * - *

The mode can be one of the following ones: + /** + * Set the item caption mode. + * + *

+ * The mode can be one of the following ones: *

* The ITEM_CAPTION_MODE_EXPLICIT_DEFAULTS_ID is the default * mode. *

- * - * @param mode One of the modes listed above. + * + * @param mode + * One of the modes listed above. */ public void setItemCaptionMode(int mode) { - if (ITEM_CAPTION_MODE_ID <= mode - && mode <= ITEM_CAPTION_MODE_PROPERTY) { + if (ITEM_CAPTION_MODE_ID <= mode && mode <= ITEM_CAPTION_MODE_PROPERTY) { itemCaptionMode = mode; requestRepaint(); } } - /** Get the item caption mode. - * - *

The mode can be one of the following ones: + /** + * Get the item caption mode. + * + *

+ * The mode can be one of the following ones: *

* The ITEM_CAPTION_MODE_EXPLICIT_DEFAULTS_ID is the default * mode. *

- * + * * @return One of the modes listed above. */ public int getItemCaptionMode() { return itemCaptionMode; } - /** Set the item caption property. + /** + * Set 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 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

. + *

+ * Setting the property id to null disables this feature. The id is null by + * default + *

. * */ public void setItemCaptionPropertyId(Object propertyId) { @@ -940,7 +1042,8 @@ public class Select } } - /** Get the item caption property. + /** + * Get the item caption property. * * @return Id of the property used as item caption source. */ @@ -948,41 +1051,55 @@ public class Select return itemCaptionPropertyId; } - /** Set the item icon property. + /** + * Set 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.

+ *

+ * 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 that the icons set with setItemIcon - * function override the icons from the property.

+ *

+ * Note that 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

. + *

+ * Setting the property id to null disables this feature. The id is null by + * default + *

. * - * @param propertyId Id of the property that specifies icons for - * items. + * @param propertyId + * Id of the property that specifies icons for items. */ public void setItemIconPropertyId(Object propertyId) { if ((propertyId != null) - && Resource.class.isAssignableFrom(getType(propertyId))) { + && Resource.class.isAssignableFrom(getType(propertyId))) { itemIconPropertyId = propertyId; requestRepaint(); } else itemIconPropertyId = null; } - /** Get the item icon property. + /** + * Get 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.

+ *

+ * 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 that the icons set with setItemIcon - * function override the icons from the property.

+ *

+ * Note that 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

. + *

+ * Setting the property id to null disables this feature. The id is null by + * default + *

. * * @return Id of the property containing the item icons. */ @@ -990,15 +1107,19 @@ public class Select return itemIconPropertyId; } - /** Test if an item is selected + /** + * Test 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.

+ *

+ * In single select mode testing selection status of the item identified by + * {@link #getNullSelectionItemId()} returns true if the value of the + * property is null. + *

* * @see #getNullSelectionItemId() * @see #setNullSelectionItemId(Object) - * @param itemId Id the of the item to be tested + * @param itemId + * Id the of the item to be tested */ public boolean isSelected(Object itemId) { if (itemId == null) @@ -1007,20 +1128,23 @@ public class Select return ((Set) getValue()).contains(itemId); else { Object value = getValue(); - return itemId.equals( - value == null ? getNullSelectionItemId() : value); + return itemId.equals(value == null ? getNullSelectionItemId() + : value); } } - /** Select an item. + /** + * Select an item. * - *

In single select mode selecting item identified - * by {@link #getNullSelectionItemId()} sets the value of the - * property to null.

+ *

+ * In single select mode selecting item identified by + * {@link #getNullSelectionItemId()} sets the value of the property to null. + *

* * @see #getNullSelectionItemId() * @see #setNullSelectionItemId(Object) - * @param itemId Item to be selected. + * @param itemId + * Item to be selected. */ public void select(Object itemId) { if (!isSelected(itemId) && items.containsId(itemId)) { @@ -1035,11 +1159,13 @@ public class Select } } - /** Unselect an item. + /** + * Unselect an item. * * @see #getNullSelectionItemId() * @see #setNullSelectionItemId(Object) - * @param itemId Item to be unselected. + * @param itemId + * Item to be unselected. */ public void unselect(Object itemId) { if (isSelected(itemId)) { @@ -1056,7 +1182,7 @@ public class Select * @see com.itmill.toolkit.data.Container.PropertySetChangeListener#containerPropertySetChange(com.itmill.toolkit.data.Container.PropertySetChangeEvent) */ public void containerPropertySetChange( - Container.PropertySetChangeEvent event) { + Container.PropertySetChangeEvent event) { firePropertySetChange(); } @@ -1106,7 +1232,7 @@ public class Select public void containerItemSetChange(Container.ItemSetChangeEvent event) { // Clear item id mapping table this.itemIdMapper.removeAll(); - + // Notify all listeners fireItemSetChange(); } @@ -1114,34 +1240,24 @@ public class Select /** Fire property set change event */ protected void firePropertySetChange() { if (propertySetEventListeners != null - && !propertySetEventListeners.isEmpty()) { - Container.PropertySetChangeEvent event = - new PropertySetChangeEvent(); + && !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); + ((Container.PropertySetChangeListener) listeners[i]) + .containerPropertySetChange(event); } requestRepaint(); } /** Fire item set change event */ protected void fireItemSetChange() { - if (itemSetEventListeners != null - && !itemSetEventListeners.isEmpty()) { + 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); + ((Container.ItemSetChangeListener) listeners[i]) + .containerItemSetChange(event); } requestRepaint(); } @@ -1159,8 +1275,8 @@ public class Select } /** Implementation of property set change event */ - private class PropertySetChangeEvent - implements Container.PropertySetChangeEvent { + private class PropertySetChangeEvent implements + Container.PropertySetChangeEvent { /** * @see com.itmill.toolkit.data.Container.PropertySetChangeEvent#getContainer() @@ -1170,12 +1286,17 @@ public class Select } } - /** Returns the item id that represents null value of this select in single select mode. + + /** + * 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 + * idetified by this id is the same as selecting no items at all. This + * setting only affects the single select mode. + *

* - *

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.

- * @return Object Null value item id. * @see #setNullSelectionItemId(Object) * @see #isSelected(Object) @@ -1185,13 +1306,17 @@ public class Select return nullSelectionItemId; } - /** Sets the item id that represents null value of this select. + /** + * 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. + *

* - *

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 nullPropertyValueContainerItemId The nullPropertyValueContainerItemId to set + * @param nullPropertyValueContainerItemId + * The nullPropertyValueContainerItemId to set * @see #getNullSelectionItemId() * @see #isSelected(Object) * @see #select(Object) @@ -1199,4 +1324,202 @@ public class Select public void setNullSelectionItemId(Object nullSelectionItemId) { this.nullSelectionItemId = nullSelectionItemId; } + + // TODO javadoc + public boolean isLazyLoading() { + return optionsStream != null; + } + + // TODO javadoc + // TODO What to do when terminal does not support lazy loading? + public void setLazyLoading(boolean useLazyLoading) { + if (useLazyLoading != isLazyLoading()) { + if (useLazyLoading) { + optionsStream = new OptionsStream(); + if (getApplication() != null) + getWindow().addURIHandler(optionsStream); + } else { + if (getApplication() != null) + getWindow().removeURIHandler(optionsStream); + optionsStream = null; + } + + requestRepaint(); + } + } + + /** + * @see org.millstone.base.ui.Component#attach() + */ + public void attach() { + super.attach(); + if (optionsStream != null) { + getWindow().removeURIHandler(optionsStream); + getWindow().addURIHandler(optionsStream); + } + } + + /** + * @see org.millstone.base.ui.Component#detach() + */ + public void detach() { + if (optionsStream != null) + getWindow().removeURIHandler(optionsStream); + super.detach(); + } + + private class OptionsStream implements URIHandler { + + private String currentFilter = ""; + + private ArrayList filteredItemsBuffer = null; + + private String uri = "selectOptionsStream" + + (long) (Math.random() * 1000000000000000000L); + + public DownloadStream handleURI(URL context, String relativeUri) { + + if (!"".equals(uri)) { + DownloadStream ds = null; + + if (relativeUri.indexOf(uri + "/feedMoreItems/") != -1) { // this + // feed visible items + int i = 0; + String index = relativeUri.substring(relativeUri + .lastIndexOf("/") + 1); + try { + i = Integer.parseInt(index); + } catch (NumberFormatException e) { + // ignore + } + // TODO Req size + ds = createDownloadStream(13,i,""); + return ds; + + } else if (relativeUri.indexOf(uri) != -1) { + + // TODO support '/' character in prefix. + // read prefix + String prefix = relativeUri.substring(relativeUri + .lastIndexOf("/") + 1); + // TODO Req size + ds = createDownloadStream(13,0,prefix.trim()); + return ds; + } + } + return null; + } + + /** + * Creates DownloadStream for response + * + * @param visibleitems + * Items to be return + * @return new DownloadStream + */ + public DownloadStream createDownloadStream(int size, int first, String filter) { + + ByteArrayOutputStream os = new ByteArrayOutputStream(); + OutputStreamWriter osw = new OutputStreamWriter(os, Charset + .forName("utf-8")); + + // JSONObject json = createJSONObject(visibleitems); + String json = getJSON( size, first, filter); + try { + osw.write(json); + osw.flush(); + os.flush(); + } catch (IOException e) { + e.printStackTrace(); + } + DownloadStream ds = new DownloadStream(new ByteArrayInputStream(os + .toByteArray()), "text/plain;charset=utf-8", "options.js"); + return ds; + } + + /** + * Update visible items by given prefix. + * + * @param prefix + * Filter prefix + * @return All item ids filtered by given prefix. + */ + public ArrayList filterContent(String prefix) { + // prefix MUST be in lowercase + if ("".equals(prefix)) { + this.filteredItemsBuffer = new ArrayList(getItemIds()); + return this.filteredItemsBuffer; + + } else if (items != null) { + // all items will be iterated and tested. + // SLOW when there are lot of items. + // TODO Should we add + this.filteredItemsBuffer = new ArrayList(); + for (Iterator iter = items.getItemIds().iterator(); iter + .hasNext();) { + Object id = iter.next(); + + Item item = getItem(id); + String test = ""; + if (getItemCaptionMode() == ITEM_CAPTION_MODE_PROPERTY) + test = item.getItemProperty(getItemCaptionPropertyId()) + .getValue().toString().trim(); + else + test = String.valueOf(id); + + if (test.toLowerCase().startsWith(prefix)) { + this.filteredItemsBuffer.add(id); + } + } + } + + return this.filteredItemsBuffer; + } + + private void addToJSONArray(StringBuffer json, ArrayList values) { + for (int i = 0; i < values.size(); i++) + json.append((i > 0 ? "," : "") + '"' + values.get(i).toString() + + '"'); + } + + private String getJSON(int size, int first, String filter) { + + // Refilter options, if needed + if (!currentFilter.equals(filter) || filteredItemsBuffer == null) { + filteredItemsBuffer = filterContent(filter); + currentFilter = filter; + } + + // Create list of shown options + ArrayList keys = new ArrayList(); + ArrayList values = new ArrayList(); + + for (int i=first; i