package org.ovirt.engine.ui.webadmin.uicommon.model;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import com.google.gwt.cell.client.Cell;
import com.google.gwt.cell.client.CheckboxCell;
import com.google.gwt.cell.client.CompositeCell;
import com.google.gwt.cell.client.FieldUpdater;
import com.google.gwt.cell.client.HasCell;
import com.google.gwt.event.logical.shared.SelectionEvent;
import com.google.gwt.event.logical.shared.SelectionHandler;
import com.google.gwt.safehtml.shared.SafeHtml;
import com.google.gwt.safehtml.shared.SafeHtmlBuilder;
import com.google.gwt.safehtml.shared.SafeHtmlUtils;
import com.google.gwt.user.cellview.client.TextColumn;
import com.google.gwt.view.client.AsyncDataProvider;
import com.google.gwt.view.client.DefaultSelectionEventManager;
import com.google.gwt.view.client.HasData;
import com.google.gwt.view.client.ListDataProvider;
import com.google.gwt.view.client.MultiSelectionModel;
import com.google.gwt.view.client.SelectionChangeEvent;
import com.google.gwt.view.client.SelectionChangeEvent.Handler;
import com.google.gwt.view.client.TreeViewModel;
/**
* A TreeView Model for {@link TreeNodeModel<T>} Nodes
*/
public class ModelListTreeViewModel<T, M extends TreeNodeModel<T, M>> implements TreeViewModel {
private final class NodeSelectionHandler implements SelectionHandler<M> {
private final HasData<M> display;
private NodeSelectionHandler(HasData<M> display) {
this.display = display;
}
@Override
public void onSelection(SelectionEvent<M> event) {
M selectedItem = event.getSelectedItem();
display.getSelectionModel().setSelected(selectedItem,
selectedItem.getSelected());
}
}
private final class CellLabel extends TextColumn<M> {
@Override
public String getValue(M object) {
return object.getName();
}
}
private final class CheckboxColumn implements HasCell<M, Boolean> {
private final CheckboxCell cell = new ExCheckboxCell(true, false);
@Override
public Cell<Boolean> getCell() {
return cell;
}
@Override
public FieldUpdater<M, Boolean> getFieldUpdater() {
return null;
}
@Override
public Boolean getValue(M object) {
return selectionModel.isSelected(object);
}
}
/**
* A {@link CheckboxCell} that can be disabled
*/
private final class ExCheckboxCell extends CheckboxCell {
/**
* An html string representation of a disabled checked input box.
*/
private final SafeHtml INPUT_CHECKED_DISABLED =
SafeHtmlUtils.fromSafeConstant("<input type=\"checkbox\" tabindex=\"-1\" checked disabled/>");
/**
* An html string representation of a disabled unchecked input box.
*/
private final SafeHtml INPUT_UNCHECKED_DISABLED =
SafeHtmlUtils.fromSafeConstant("<input type=\"checkbox\" tabindex=\"-1\" disabled/>");
private final SafeHtml INPUT_CHECKED_NULL_DISABLED =
SafeHtmlUtils.fromSafeConstant("<input type=\"checkbox\" tabindex=\"-1\" checked style=\"-moz-Field ! important; background-color: yellow;\" disabled/>");
private final SafeHtml INPUT_CHECKED_NULL =
SafeHtmlUtils.fromSafeConstant("<input type=\"checkbox\" tabindex=\"-1\" checked style=\"-moz-Field ! important; background-color: yellow;\"/>");
private ExCheckboxCell(boolean dependsOnSelection, boolean handlesSelection) {
super(dependsOnSelection, handlesSelection);
}
@Override
public void render(Context context, Boolean value, SafeHtmlBuilder sb) {
Object key = context.getKey();
// TODO semi-checked checkbox (null value)
// value = ((SimpleSelectionTreeNodeModel) context.getKey()).getIsSelectedNullable();
@SuppressWarnings("unchecked")
M model = (M) key;
if (!model.isEditable()) {
// disabled
Boolean viewData = getViewData(key);
if (viewData != null && viewData.equals(value)) {
clearViewData(key);
viewData = null;
}
// if (value == null) {
// sb.append(INPUT_CHECKED_NULL_DISABLED);
// } else
if (value != null && ((viewData != null) ? viewData : value)) {
sb.append(INPUT_CHECKED_DISABLED);
} else {
sb.append(INPUT_UNCHECKED_DISABLED);
}
} else {
// if (value == null) {
// Boolean viewData = getViewData(key);
// if (viewData != null && viewData.equals(value)) {
// clearViewData(key);
// viewData = null;
// }
// sb.append(INPUT_CHECKED_NULL);
// } else
// enabled
super.render(context, value, sb);
}
}
}
private final AsyncDataProvider<M> asyncTreeDataProvider;
private final List<HasCell<M, ?>> cells = new ArrayList<HasCell<M, ?>>();
private List<M> root;
private final MultiSelectionModel<M> selectionModel = new MultiSelectionModel<M>();
private NodeSelectionHandler nodeSelectionHandler;
public ModelListTreeViewModel() {
cells.add(new CheckboxColumn());
cells.add(new CellLabel());
asyncTreeDataProvider = new AsyncDataProvider<M>() {
@Override
protected void onRangeChanged(HasData<M> display) {
// no-op
}
@Override
protected void updateRowData(final HasData<M> display, int start, List<M> values) {
super.updateRowData(display, start, values);
for (M model : values) {
UpdateSelection(model, display);
}
}
};
// Drive selection
selectionModel.addSelectionChangeHandler(new Handler() {
@Override
public void onSelectionChange(SelectionChangeEvent event) {
Set<M> selectedSet = selectionModel.getSelectedSet();
HashSet<TreeNodeModel> removedSet = new HashSet<TreeNodeModel>();
HashSet<TreeNodeModel> nullSet = new HashSet<TreeNodeModel>();
for (TreeNodeModel<T, M> child : root) {
if (!selectedSet.contains(child) && child.getSelected()) {
selectedSet.remove(child);
removedSet.add(child);
}
for (TreeNodeModel<T, M> child1 : child.getChildren()) {
if (!selectedSet.contains(child1) && child1.getSelected()) {
selectedSet.remove(child1);
removedSet.add(child1);
}
for (TreeNodeModel<T, M> child2 : child1.getChildren()) {
if (!selectedSet.contains(child2) && child2.getSelected()) {
selectedSet.remove(child2);
removedSet.add(child2);
}
}
}
}
for (M m : selectedSet) {
m.setSelected(true);
}
for (TreeNodeModel treeNodeModel : removedSet) {
treeNodeModel.setSelected(false);
}
}
});
}
public void UpdateSelection(M model, final HasData<M> display) {
// Add Selection Listener
if (nodeSelectionHandler == null) {
nodeSelectionHandler = new NodeSelectionHandler(display);
}
model.addSelectionHandler(nodeSelectionHandler);
// show value
display.getSelectionModel().setSelected(model, model.getSelected());
for (int i = 0; i < model.getChildren().size(); i++) {
UpdateSelection(model.getChildren().get(i), display);
}
}
public AsyncDataProvider<M> getAsyncTreeDataProvider() {
return asyncTreeDataProvider;
}
@Override
public <N> NodeInfo<?> getNodeInfo(N value) {
@SuppressWarnings("unchecked")
M model = (M) value;
CompositeCell<M> composite = new CompositeCell<M>(cells);
if (value == null) {
// root node
return new DefaultNodeInfo<M>(asyncTreeDataProvider,
composite,
selectionModel,
DefaultSelectionEventManager.<M> createCheckboxManager(),
null);
} else {
// child nodes
return new DefaultNodeInfo<M>(new ListDataProvider<M>(model.getChildren()),
composite,
selectionModel,
DefaultSelectionEventManager.<M> createCheckboxManager(),
null);
}
}
@Override
public boolean isLeaf(Object value) {
@SuppressWarnings("unchecked")
M model = (M) value;
return model != null && model.getChildren() != null && model.getChildren().size() == 0;
}
/**
* Set the Root list- required to sync the Model Tree correctly
*
* @param arrayList
*/
public void setRoot(List<M> arrayList) {
this.root = arrayList;
}
}