package org.ovirt.engine.ui.common.widget.editor; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.List; import org.ovirt.engine.ui.uicompat.external.StringUtils; import com.google.gwt.core.client.Scheduler; import com.google.gwt.dom.client.Element; import com.google.gwt.event.logical.shared.ValueChangeEvent; import com.google.gwt.safehtml.shared.SafeHtmlUtils; import com.google.gwt.text.shared.Renderer; /** * See ListModelMultipleSelectListBoxEditor for an explanation of how to use the ListBox * @see ListModelMultipleSelectListBoxEditor */ public class ListModelMultipleSelectListBox<T> extends ListModelListBox<List<T>> implements TakesListValue<T> { /** * The editor. */ private TakesConstrainedValueListEditor<T> editor; /** * The current selected list of typed values. */ protected final List<T> selectedList = new ArrayList<>(); protected List<T> lastValues; /** * Constructor * @param renderer The rendered used to render the values. */ public ListModelMultipleSelectListBox(Renderer<List<T>> renderer) { super(renderer); this.isMultiSelect = true; } @Override protected void setValue(List<T> values, boolean fireEvents, boolean fromClick) { // Prevent potential event loops, as well as optimize away multiple set values to the same value. if (values != null && lastValues != null && lastValues.equals(values)) { return; } if (fromClick) { // Click event can only be one value; T value = values.get(0); if (selectedList.contains(value)) { // Can only remove 1 at a time, so if we get more than 1 we shouldn't remove them. selectedList.remove(value); } else { // Shouldn't get a null value, but in case we do, ignore it. if (value != null) { selectedList.add(value); } } } else { // Received a list of selections from some code, we should clear the selected items, and replace them // with the ones received. selectedList.clear(); if (values != null) { for (T value: values) { if (value != null) { selectedList.add(value); } } } } listPanel.setSelected(selectedList); updateCurrentValue(selectedList, fireEvents); } private void updateCurrentValue(final List<T> value, boolean fireEvents) { lastValues = value; List<String> renderedValues = new ArrayList<>(); for (T val: value) { renderedValues.add(getRenderer().render(Arrays.asList(val))); } String renderedValue = StringUtils.join(renderedValues, ","); //$NON-NLS-1$ if (StringUtils.isEmpty(renderedValue)) { renderedValue = NBSP; } else { renderedValue = SafeHtmlUtils.htmlEscape(renderedValue); } ((Element)dropdownButton.getElement().getChild(0)).setInnerHTML(renderedValue); dropdownButton.setTitle(renderedValue); Scheduler.get().scheduleDeferred(() -> listPanel.setSelected(value)); if (fireEvents) { Scheduler.get().scheduleDeferred(() -> { ValueChangeEvent.fire(ListModelMultipleSelectListBox.this, selectedItems()); // Clear the value so we don't have any leaks lastValues = null; }); } else { // Clear the value so we don't have any leaks lastValues = null; } } @Override public void setListValue(List<T> value) { setValue(value, true); } @Override public TakesConstrainedValueListEditor<T> asEditor() { if (editor == null) { editor = TakesConstrainedValueListEditor.ofList(this, this, this); } return editor; } @Override public void setAcceptableValues(Collection<List<T>> newValues) { if (newValues == null) { newValues = Collections.emptyList(); } // Set a value in the super class, so calling setAcceptableValues doesn't add a null value and // potentially NPE if the renderer doesn't take kindly to getting a null value passed to it. List<T> val = newValues.isEmpty() ? Collections.<T>emptyList() : newValues.iterator().next(); //Clone selection. List<T> currentSelected = new ArrayList<>(); for (T value: selectedItems()) { if (val.contains(value)) { currentSelected.add(value); } } selectedList.clear(); // Populate the list box. super.setAcceptableValues(newValues); setValue(currentSelected, false); } /** * Getter for the list of selected items. * @return The list of typed selected items. */ public List<T> selectedItems() { return selectedList; } }