package org.ovirt.engine.ui.webadmin.widget.editor;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
import org.ovirt.engine.ui.webadmin.gin.ClientGinjectorProvider;
import org.ovirt.engine.ui.webadmin.widget.table.column.EnumColumn;
import org.ovirt.engine.ui.webadmin.widget.table.column.RadioboxCell;
import com.google.gwt.core.client.GWT;
import com.google.gwt.editor.client.LeafValueEditor;
import com.google.gwt.event.dom.client.KeyDownEvent;
import com.google.gwt.event.dom.client.KeyDownHandler;
import com.google.gwt.event.dom.client.KeyPressEvent;
import com.google.gwt.event.dom.client.KeyPressHandler;
import com.google.gwt.event.dom.client.KeyUpEvent;
import com.google.gwt.event.dom.client.KeyUpHandler;
import com.google.gwt.event.logical.shared.HasValueChangeHandlers;
import com.google.gwt.event.logical.shared.ValueChangeEvent;
import com.google.gwt.event.logical.shared.ValueChangeHandler;
import com.google.gwt.event.shared.EventBus;
import com.google.gwt.event.shared.GwtEvent;
import com.google.gwt.event.shared.HandlerRegistration;
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.CellTable;
import com.google.gwt.user.cellview.client.Column;
import com.google.gwt.user.cellview.client.RowStyles;
import com.google.gwt.user.client.TakesValue;
import com.google.gwt.user.client.ui.HasEnabled;
import com.google.gwt.view.client.ListDataProvider;
import com.google.gwt.view.client.SelectionChangeEvent;
import com.google.gwt.view.client.SelectionChangeEvent.Handler;
import com.google.gwt.view.client.SingleSelectionModel;
/**
* An Editor that shows an enum as a Radio Group, with one Radio button for each enum value.<BR>
* Each enum radio button may be disabled separately.
*
* @param <E>
* The enum type
*/
public class EnumRadioEditor<E extends Enum<E>> implements EditorWidget<E, LeafValueEditor<E>>, TakesValue<E>, HasValueChangeHandlers<E> {
public interface EnumRadioCellTableResources extends CellTable.Resources {
interface TableStyle extends CellTable.Style {
/**
* Applied to disabled rows
*/
String cellTableDisabledRow();
}
@Override
@Source({ CellTable.Style.DEFAULT_CSS, "org/ovirt/engine/ui/webadmin/css/EnumRadioCellTable.css" })
TableStyle cellTableStyle();
}
/**
* A {@link CellTable} that shows a radio button for each enum value.<BR>
* Each enum radio button may be disabled separately.
*
* @param <E>
* The enum type
*/
public static class EnumRadioCellTable<E extends Enum<E>> extends CellTable<E> implements HasEnabled {
/**
* A {@link RadioboxCell} that can be disabled
*/
private static class ExRadioboxCell<E extends Enum<E>> extends RadioboxCell {
/**
* An html string representation of a checked disabled input box.
*/
private static final SafeHtml INPUT_CHECKED_DISABLED =
SafeHtmlUtils.fromSafeConstant("<input type=\"radio\" tabindex=\"-1\" checked disabled/>");
/**
* An html string representation of an unchecked disabled input box.
*/
private static final SafeHtml INPUT_UNCHECKED_DISABLED =
SafeHtmlUtils.fromSafeConstant("<input type=\"radio\" tabindex=\"-1\" disabled/>");
private final Set<E> disabledSet;
public ExRadioboxCell(boolean dependsOnSelection, boolean handlesSelection, Set<E> disabledSet) {
super(dependsOnSelection, handlesSelection);
this.disabledSet = disabledSet;
}
@Override
public void render(com.google.gwt.cell.client.Cell.Context context, Boolean value, SafeHtmlBuilder sb) {
// Get the view data.
Object key = context.getKey();
E keyValue = (E) key;
if (disabledSet.contains(keyValue)) {
// disabled
Boolean viewData = getViewData(key);
if (viewData != null && viewData.equals(value)) {
clearViewData(key);
viewData = null;
}
if (value != null && ((viewData != null) ? viewData : value)) {
sb.append(INPUT_CHECKED_DISABLED);
} else {
sb.append(INPUT_UNCHECKED_DISABLED);
}
} else {
// enabled
super.render(context, value, sb);
}
}
}
/**
* holds the enum values that are disabled
*/
private final Set<E> disabledSet;
private final EnumRadioCellTableResources resources;
public EnumRadioCellTable(Class<E> enumClass, EnumRadioCellTableResources resources) {
super(15, resources, new ListDataProvider<E>(Arrays.asList(enumClass.getEnumConstants())));
this.resources = resources;
disabledSet = new HashSet<E>();
RowStyles<E> rowStyles = new RowStyles<E>() {
@Override
public String getStyleNames(E row, int rowIndex) {
return disabledSet.contains(row) ? EnumRadioCellTable.this.resources.cellTableStyle()
.cellTableDisabledRow() : null;
}
};
setRowStyles(rowStyles);
// Radio Column
Column<E, Boolean> radioColumn = new Column<E, Boolean>(
new ExRadioboxCell<E>(true, false, disabledSet)) {
@Override
public Boolean getValue(E object) {
return getSelectionModel().isSelected(object);
}
};
// Text Column
EnumColumn<E, E> nameColumn = new EnumColumn<E, E>() {
@Override
protected E getRawValue(E object) {
return object;
}
};
addColumn(radioColumn);
addColumn(nameColumn);
setRowData(Arrays.asList(enumClass.getEnumConstants()));
}
@Override
public boolean isEnabled() {
for (E item : getVisibleItems()) {
if (isEnabled(item)) {
return true;
}
}
return false;
}
/**
* Returns whether a specific enum value is enabled
*/
public boolean isEnabled(E value) {
return true;
}
@Override
public void setEnabled(boolean enabled) {
for (E item : getVisibleItems()) {
setEnabled(item, enabled);
}
}
/**
* Enables a specific enum value
*/
public void setEnabled(E value, boolean enabled) {
if (enabled) {
disabledSet.remove(value);
} else {
disabledSet.add(value);
}
}
protected Set<E> getDisabledSet() {
return disabledSet;
}
}
private final EventBus bus;
private TakesValueWithChangeHandlersEditor<E> editor;
private final EnumRadioCellTable<E> peer;
private final SingleSelectionModel<E> selectionModel;
/**
* Create a new EnumRadioEditor, for the provided enum class
*
* @param enumClass
* the enum class
*/
public EnumRadioEditor(Class<E> enumClass) {
bus = ClientGinjectorProvider.instance().getEventBus();
peer =
new EnumRadioCellTable<E>(enumClass,
GWT.<EnumRadioCellTableResources> create(EnumRadioCellTableResources.class));
// Selection Model
selectionModel = new SingleSelectionModel<E>() {
@Override
public void setSelected(E object, boolean selected) {
// enforce disabled set
if (peer.getDisabledSet().contains(object)) {
return;
} else {
super.setSelected(object, selected);
}
};
};
peer.setSelectionModel(selectionModel);
selectionModel.addSelectionChangeHandler(new Handler() {
@Override
public void onSelectionChange(SelectionChangeEvent event) {
E selectedObject = selectionModel.getSelectedObject();
setValue(selectedObject);
ValueChangeEvent.<E> fire(EnumRadioEditor.this, selectedObject);
}
});
}
@Override
public HandlerRegistration addKeyDownHandler(KeyDownHandler handler) {
return peer.addHandler(handler, KeyDownEvent.getType());
}
@Override
public HandlerRegistration addKeyPressHandler(KeyPressHandler handler) {
return peer.addHandler(handler, KeyPressEvent.getType());
}
@Override
public HandlerRegistration addKeyUpHandler(KeyUpHandler handler) {
return peer.addHandler(handler, KeyUpEvent.getType());
}
@Override
public HandlerRegistration addValueChangeHandler(final ValueChangeHandler<E> handler) {
// don't add to peer, since its changed value is the entire item list
return bus.addHandler(ValueChangeEvent.getType(), handler);
}
@Override
public TakesValueWithChangeHandlersEditor<E> asEditor() {
if (editor == null) {
editor = TakesValueWithChangeHandlersEditor.of(this, this);
}
return editor;
}
@Override
public EnumRadioCellTable<E> asWidget() {
return peer;
}
@Override
public void fireEvent(GwtEvent<?> event) {
bus.fireEvent(event);
}
@Override
public int getTabIndex() {
return peer.getTabIndex();
}
@Override
public E getValue() {
return selectionModel.getSelectedObject();
}
@Override
public boolean isEnabled() {
return peer.isEnabled();
}
@Override
public void setAccessKey(char key) {
peer.setAccessKey(key);
}
@Override
public void setEnabled(boolean enabled) {
peer.setEnabled(enabled);
}
public void setEnabled(E value, boolean enabled) {
peer.setEnabled(value, enabled);
}
@Override
public void setFocus(boolean focused) {
peer.setFocus(focused);
}
@Override
public void setTabIndex(int index) {
peer.setTabIndex(index);
}
@Override
public void setValue(E value) {
selectionModel.setSelected(value, true);
}
}