blob: da92306a3c8ad0cd2e9e2488d988f4baea40c00c [file] [log] [blame]
/**
*
* Copyright (c) 2011, 2016 - Loetz GmbH&Co.KG (69115 Heidelberg, Germany)
*
* 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:
* Christophe Loetz (Loetz GmbH&Co.KG) - initial implementation
*
*/
package org.eclipse.osbp.xtext.table.common;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import com.vaadin.data.Container;
import com.vaadin.data.Property;
import com.vaadin.ui.AbstractSelect;
import com.vaadin.ui.CheckBox;
import com.vaadin.ui.Component;
import com.vaadin.ui.CustomTable;
import com.vaadin.ui.TextField;
public class CheckboxSelectionCellSetFilterTable extends CellSetFilterTable {
public enum UserSelectionState {
SINGLE {
@Override
protected String headerString() { return ""; }
@Override
protected UserSelectionState nextState() { return SINGLE; }
},
NONE {
@Override
protected String headerString() { return "[X]"; }
@Override
protected UserSelectionState nextState() { return ALL; }
},
SOME {
@Override
protected String headerString() { return "[X]"; }
@Override
protected UserSelectionState nextState() { return ALL; }
},
ALL {
@Override
protected String headerString() { return "[-]"; }
@Override
protected UserSelectionState nextState() { return NONE; }
};
abstract protected String headerString();
abstract protected UserSelectionState nextState();
}
private static final long serialVersionUID = -5042607921306226333L;
private static final Object CHECKBOX_COLUMN_ID = new Integer(-99999);
private static final int CHECKBOX_COLUMN_WIDTH = 64;
/**
* This map keeps track of all the created and attached checkboxes, by ItemId
*/
protected Map<Object, CheckBox> itemIdToCheckbox = new HashMap<Object, CheckBox>();
/**
* This is the last selection value of the table. We need to keep track of what *was* selected,
* so that we can "deselect" the checkboxes.
*/
private Object lastSelectionValue;
private UserSelectionState userSelectionState = UserSelectionState.SOME;
/**
* A simple flag to stop a circular event
*/
private boolean ignorePropertyChangeEventInCheckBoxListener;
@SuppressWarnings("deprecation")
public CheckboxSelectionCellSetFilterTable() {
super();
addGeneratedColumn(CHECKBOX_COLUMN_ID, new ColumnGenerator() {
private static final long serialVersionUID = -1241374338743135797L;
@Override
public Object generateCell(final CustomTable source, final Object itemId, Object columnId) {
final boolean selected = isItemIdSelected(source, itemId);
final CheckBox checkBox = new CheckBox("", selected);
checkBox.addValueChangeListener(new Property.ValueChangeListener() {
private static final long serialVersionUID = -7317483363411043081L;
@Override
public void valueChange(Property.ValueChangeEvent valueChangeEvent) {
// Don't react to the event if we're being changed from the table-value-change event
if (!ignorePropertyChangeEventInCheckBoxListener) {
Boolean selected = (Boolean) valueChangeEvent.getProperty().getValue();
if (selected) {
source.select(itemId);
}
else {
source.unselect(itemId);
}
setUserSelectionState(UserSelectionState.SOME);
}
}
});
itemIdToCheckbox.put(itemId, checkBox);
if (selected) {
source.select(itemId);
}
// Let's keep track of the checkboxes
checkBox.addAttachListener(new AttachListener() {
private static final long serialVersionUID = 2477921773100973217L;
@Override
public void attach(AttachEvent event) {
itemIdToCheckbox.put(itemId, checkBox);
}
});
checkBox.addDetachListener(new DetachListener() {
private static final long serialVersionUID = -2093323551404903130L;
@Override
public void detach(DetachEvent event) {
itemIdToCheckbox.remove(itemId);
}
});
checkBox.setWidth(CHECKBOX_COLUMN_WIDTH+"px");
return checkBox;
}
});
setFilterFieldVisible(CHECKBOX_COLUMN_ID, true);
addListener(new HeaderClickListener() {
private static final long serialVersionUID = 1238308428039209109L;
@Override
public void headerClick(HeaderClickEvent event) {
if (CHECKBOX_COLUMN_ID.equals(event.getPropertyId())) {
setUserSelectionState(userSelectionState.nextState());
}
}
});
//setColumnHeader(CHECKBOX_COLUMN_ID, userSelectionState.headerString());
setUserSelectionState(UserSelectionState.SOME);
this.lastSelectionValue = getValue();
this.ignorePropertyChangeEventInCheckBoxListener = false;
/*
* When the table value - i.e. the selection - changes, make sure
* any attached checkboxes are updated
*/
addValueChangeListener(new Property.ValueChangeListener() {
private static final long serialVersionUID = 6017328233818014479L;
@Override
public void valueChange(Property.ValueChangeEvent event) {
ignorePropertyChangeEventInCheckBoxListener = true;
Object newSelectionValue = event.getProperty().getValue();
/* Deselect all of the old checkboxes, then reselect all of the new ones.
If we wanted to be really smart, you could work out which checkboxes you wanted to change.
I don't care about being smart right now */
setCheckBoxes(lastSelectionValue, false);
setCheckBoxes(newSelectionValue, true);
lastSelectionValue = newSelectionValue;
ignorePropertyChangeEventInCheckBoxListener = false;
}
});
try {
setColumnWidth(CHECKBOX_COLUMN_ID, CHECKBOX_COLUMN_WIDTH);
}
catch (Exception e) {}
}
@Override
public void setMultiSelect(boolean multiSelect) {
if (multiSelect) {
setUserSelectionState(UserSelectionState.SOME);
}
else {
setUserSelectionState(UserSelectionState.SINGLE);
}
super.setMultiSelect(multiSelect);
}
private void setUserSelectionState(UserSelectionState nextState) {
switch (nextState) {
case NONE:
setCheckBoxes(itemIdToCheckbox.keySet(), false);
break;
case ALL:
setCheckBoxes(itemIdToCheckbox.keySet(), true);
break;
case SOME:
break;
case SINGLE:
break;
}
userSelectionState = nextState;
setColumnHeader(CHECKBOX_COLUMN_ID, userSelectionState.headerString());
}
@Override
public void setContainerDataSource(Container newDataSource, Collection<?> visibleIds) {
// force selection column to be the first
if ((visibleIds.size() > 1) && (visibleIds.iterator().next() instanceof Integer)) {
Set<Integer> sortedVisibleIds = new TreeSet<Integer>();
for (Object item : visibleIds) {
sortedVisibleIds.add((Integer) item);
}
visibleIds = sortedVisibleIds;
}
super.setContainerDataSource(newDataSource, visibleIds);
try {
setColumnWidth(CHECKBOX_COLUMN_ID, CHECKBOX_COLUMN_WIDTH);
}
catch (Exception e) {}
updateCheckboxColumnFilterLabel();
}
protected void updateCheckboxColumnFilterLabel() {
Map<Object, Component> columnIdToFilterMap = getColumnIdToFilterMap();
Component c = null;
if (columnIdToFilterMap != null) {
c = columnIdToFilterMap.get(CHECKBOX_COLUMN_ID);
}
if (c instanceof TextField) {
String caption = "";
if (getContainerDataSource() instanceof CellSetIndexedContainer) {
CellSetIndexedContainer dataSource = (CellSetIndexedContainer) getContainerDataSource();
int allItems = dataSource.getAllItemIds().size();
//int filteredItems = dataSource.getFilteredItemIds() == null ? 0 : dataSource.getFilteredItemIds().size();
int selected = 0;
Object value = getValue();
if (value instanceof Collection) {
selected = ((Collection) value).size();
}
else if (value != null) {
selected = 1;
}
caption = selected+"/"+allItems;
}
((TextField) c).setInputPrompt(caption);
c.setEnabled(false);
setFilterFieldVisible(CHECKBOX_COLUMN_ID, true);
}
}
/**
* Set the value of all of the checkboxes
* @param itemIdOrIds
* @param value
*/
private void setCheckBoxes(Object itemIdOrIds, boolean value) {
if (itemIdOrIds instanceof Collection) {
Collection ids = (Collection) itemIdOrIds;
for (Object id : ids) {
setCheckBox(id, value);
}
}
else {
setCheckBox(itemIdOrIds, value);
}
}
/**
* Set the value of the given checkbox
* @param id
* @param value
*/
public void setCheckBox(Object id, boolean value) {
CheckBox checkBox = itemIdToCheckbox.get(id);
if (checkBox != null) {
checkBox.setValue(value);
updateCheckboxColumnFilterLabel();
}
}
/**
* Is the given itemId selected ?
* @param select
* @param itemId
* @return
*/
protected boolean isItemIdSelected(AbstractSelect select, Object itemId) {
switch (userSelectionState) {
case NONE:
return false;
case ALL:
return true;
case SOME:
case SINGLE:
Object value = select.getValue();
if (itemId == null || value == null) {
return false;
}
if (select.isMultiSelect()) {
return ((Collection) value).contains(itemId);
}
return itemId.equals(value);
}
return false;
}
@Override
public String getColumnHeader(Object propertyId) {
if (getColumnHeaderMode() == ColumnHeaderMode.HIDDEN) {
return null;
}
if (propertyId == CHECKBOX_COLUMN_ID) {
return userSelectionState.headerString();
}
return super.getColumnHeader(propertyId);
}
}