/*******************************************************************************
* Copyright (c) 2010-2014 SAP AG and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* SAP AG - initial API and implementation
*******************************************************************************/
package org.eclipse.skalli.view.component;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.UUID;
import org.apache.commons.lang.StringUtils;
import org.eclipse.skalli.commons.UUIDList;
import org.eclipse.skalli.model.Project;
import org.eclipse.skalli.model.PropertyName;
import org.eclipse.skalli.services.entity.EntityServices;
import org.eclipse.skalli.services.project.ProjectService;
import com.vaadin.data.Item;
import com.vaadin.data.Validator.InvalidValueException;
import com.vaadin.data.util.IndexedContainer;
import com.vaadin.ui.AbstractSelect;
import com.vaadin.ui.AbstractSelect.Filtering;
import com.vaadin.ui.Button;
import com.vaadin.ui.Button.ClickEvent;
import com.vaadin.ui.ComboBox;
import com.vaadin.ui.HorizontalLayout;
import com.vaadin.ui.Select;
import com.vaadin.ui.VerticalLayout;
public class MultiComboBox extends CustomField {
private static final long serialVersionUID = -2946220818606365985L;
private static final String STYLE_LAYOUT = "multicombobox-layout";
private static final String STYLE_LINE_LAYOUT = "multicombobox-line";
private static final String STYLE_BUTTON = "multicombobox-btn";
private UUIDList values;
private VerticalLayout layout;
private List<ComboBoxElement> comboBoxEntries;
private String description;
private String inputPrompt;
private boolean readOnly;
private int columns;
private int maxSize;
private static class ComboBoxElement {
public ComboBoxElement(ComboBox comboBox) {
this.comboBox = comboBox;
comboBox.setItemCaptionPropertyId(ProjectDataSource.PROPERTY_DISPLAYNAME);
comboBox.setItemCaptionMode(AbstractSelect.ITEM_CAPTION_MODE_PROPERTY);
comboBox.setFilteringMode(Filtering.FILTERINGMODE_CONTAINS);
comboBox.setImmediate(true);
comboBox.setNewItemsAllowed(false);
comboBox.setNullSelectionAllowed(true);
}
ComboBox comboBox;
Button removeButton;
}
public MultiComboBox(String caption, UUIDList values) {
if (values == null) {
throw new IllegalArgumentException("argument 'values' must not be null");
}
setCaption(caption);
this.values = values;
this.maxSize = Integer.MAX_VALUE;
init(values);
layout = new VerticalLayout();
layout.setStyleName(STYLE_LAYOUT);
renderComboBoxes();
setCompositionRoot(layout);
}
private void renderComboBoxes() {
int size = comboBoxEntries.size();
int last = size - 1;
for (int i = 0; i <= last; ++i) {
ComboBoxElement comboBoxEntry = comboBoxEntries.get(i);
HorizontalLayout horLayout = new HorizontalLayout();
horLayout.setStyleName(STYLE_LINE_LAYOUT);
ComboBox comboBox = comboBoxEntry.comboBox;
if (comboBox.getValue() == null) {
comboBox.setEnabled(!readOnly);
} else {
comboBox.setReadOnly(readOnly);
}
horLayout.addComponent(comboBox);
if (!readOnly) {
if (size > 1) {
Button b = createRemoveButton();
comboBoxEntry.removeButton = b;
horLayout.addComponent(b);
}
if (size < maxSize && i == last) {
horLayout.addComponent(createAddButton());
}
}
layout.addComponent(horLayout);
}
}
private void init(UUIDList values) {
comboBoxEntries = new ArrayList<ComboBoxElement>();
if (values != null && !values.isEmpty()) {
for (UUID value : values) {
ComboBox comboBox = createComboBox(value);
comboBoxEntries.add(new ComboBoxElement(comboBox));
}
} else {
ComboBox comboBox = createComboBox(null);
comboBoxEntries.add(new ComboBoxElement(comboBox));
}
}
private ComboBox createComboBox(UUID uuid) {
ComboBox comboBox = new ComboBox(null, new ProjectDataSource());
comboBox.setImmediate(true);
if (description != null) {
comboBox.setDescription(description);
}
if (inputPrompt != null) {
comboBox.setInputPrompt(inputPrompt);
}
if (uuid != null) {
ProjectService projectService = ((ProjectService)EntityServices.getByEntityClass(Project.class));
Project project = projectService.getByUUID(uuid);
comboBox.select(project);
}
return comboBox;
}
public void setColumns(int columns) {
this.columns = columns;
for (ComboBoxElement entry : comboBoxEntries) {
entry.comboBox.setWidth(columns, Select.UNITS_EM);
}
}
public void setMaxSize(int maxSize) {
this.maxSize = maxSize <=0 ? Integer.MAX_VALUE : maxSize;
requestRepaint();
}
private Button createAddButton() {
Button b = new Button("Add");
b.setStyleName(Button.STYLE_LINK);
b.addStyleName(STYLE_BUTTON);
b.setDescription("Add another entry");
b.setEnabled(!readOnly);
b.addListener(new Button.ClickListener() {
@Override
public void buttonClick(ClickEvent event) {
ComboBox cb = createComboBox(null);
cb.setWidth(columns, Select.UNITS_EM);
comboBoxEntries.add(new ComboBoxElement(cb));
layout.removeAllComponents();
renderComboBoxes();
}
});
return b;
}
private Button createRemoveButton() {
Button b = new Button("Remove");
b.setStyleName(Button.STYLE_LINK);
b.addStyleName(STYLE_BUTTON);
b.setDescription("Remove this entry");
b.setEnabled(!readOnly);
b.addListener(new Button.ClickListener() {
@Override
public void buttonClick(ClickEvent event) {
Button b = event.getButton();
Iterator<ComboBoxElement> it = comboBoxEntries.iterator();
while (it.hasNext()) {
ComboBoxElement element = it.next();
if (element.removeButton == b) {
it.remove();
break;
}
}
layout.removeAllComponents();
renderComboBoxes();
}
});
return b;
}
public void setInputPrompt(String inputPrompt) {
this.inputPrompt = inputPrompt;
for (ComboBoxElement element : comboBoxEntries) {
element.comboBox.setInputPrompt(inputPrompt);
}
}
@Override
public void setDescription(String description) {
this.description = description;
for (ComboBoxElement element : comboBoxEntries) {
element.comboBox.setDescription(description);
}
}
@Override
public Object getValue() {
UUIDList uuid = new UUIDList(comboBoxEntries.size());
copyValues(uuid);
return uuid;
}
@Override
public void commit() throws SourceException, InvalidValueException {
values.clear();
copyValues(values);
}
private void copyValues(UUIDList values) {
for (ComboBoxElement entry : comboBoxEntries) {
Project project = (Project) entry.comboBox.getValue();
if (project == null) {
return;
}
UUID value = project.getUuid();
if (StringUtils.isNotBlank(value.toString())) {
values.add(value);
}
}
}
@Override
public Class<?> getType() {
return Collection.class;
}
private static class ProjectDataSource extends IndexedContainer {
private static final long serialVersionUID = 8192530422436522676L;
@PropertyName(position = 0)
public static final Object PROPERTY_DISPLAYNAME = "displayName";
public ProjectDataSource() {
super();
addContainerProperty(Project.PROPERTY_NAME, String.class, null);
addContainerProperty(Project.PROPERTY_PROJECTID, String.class, null);
addContainerProperty(PROPERTY_DISPLAYNAME, String.class, null);
ProjectService projectService = ((ProjectService)EntityServices.getByEntityClass(Project.class));
List<Project> projects = projectService.getAll();
for (Project project : projects) {
addItem(project);
}
sort(new Object[] { Project.PROPERTY_NAME, Project.PROPERTY_PROJECTID }, new boolean[] { true, true });
}
private Item addItem(Project project) {
// item key = project instance
Item item = getItem(project);
if (item == null) {
item = super.addItem(project); // IndexedContainer#addItem return null, if entry already exists!!!
}
if (item != null) {
String projectId = project.getProjectId();
String name = project.getName();
item.getItemProperty(Project.PROPERTY_NAME).setValue(name);
item.getItemProperty(Project.PROPERTY_PROJECTID).setValue(projectId);
item.getItemProperty(PROPERTY_DISPLAYNAME).setValue(name + " <" + projectId + ">");
}
return item;
}
}
@Override
public void setReadOnly(boolean readOnly) {
this.readOnly = readOnly;
layout.removeAllComponents();
renderComboBoxes();
}
@Override
public boolean isReadOnly() {
return readOnly;
}
}