package org.ovirt.engine.ui.common.widget.editor; import java.util.Arrays; import java.util.HashSet; import java.util.List; import java.util.Set; import org.ovirt.engine.ui.common.css.OvirtCss; import org.ovirt.engine.ui.common.editor.UiCommonEditor; import org.ovirt.engine.ui.common.widget.table.cell.RadioboxCell; import org.ovirt.engine.ui.common.widget.table.column.AbstractEnumColumn; 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.GwtEvent; import com.google.gwt.event.shared.HandlerManager; 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.ui.HasEnabled; import com.google.gwt.view.client.ListDataProvider; 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>>, UiCommonEditor<E>, HasValueChangeHandlers<E>, LeafValueEditor<E> { public interface EnumRadioCellTableResources extends CellTable.Resources { interface TableStyle extends CellTable.Style { /** * Applied to enabled rows */ String cellTableEnabledRow(); /** * Applied to disabled rows */ String cellTableDisabledRow(); } @Override @Source({ CellTable.Style.DEFAULT_CSS, "org/ovirt/engine/ui/common/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/>"); //$NON-NLS-1$ /** * 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/>"); //$NON-NLS-1$ private final Set<E> disabledSet; public ExRadioboxCell(boolean dependsOnSelection, boolean handlesSelection, Set<E> disabledSet) { super(dependsOnSelection, handlesSelection); this.disabledSet = disabledSet; } @SuppressWarnings("unchecked") @Override public void render(Context context, Boolean value, SafeHtmlBuilder sb, String id) { // 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, id); } } } /** * holds the enum values that are disabled */ private final Set<E> disabledSet; private final EnumRadioCellTableResources resources; private final AbstractEnumColumn<E, E> nameColumn; public EnumRadioCellTable(Class<E> enumClass, final EnumRadioCellTableResources resources) { super(15, resources, new ListDataProvider<>(Arrays.asList(enumClass.getEnumConstants()))); this.resources = resources; disabledSet = new HashSet<>(); RowStyles<E> rowStyles = (row, rowIndex) -> disabledSet.contains(row) ? resources.cellTableStyle().cellTableDisabledRow() : resources.cellTableStyle().cellTableEnabledRow(); setRowStyles(rowStyles); // Radio Column Column<E, Boolean> radioColumn = new Column<E, Boolean>( new ExRadioboxCell<>(true, false, disabledSet)) { @Override public Boolean getValue(E object) { return getSelectionModel().isSelected(object); } }; // Text Column nameColumn = new AbstractEnumColumn<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); } if (enabled) { nameColumn.getCell().setStyleClass(OvirtCss.LABEL_ENABLED); } else { nameColumn.getCell().setStyleClass(OvirtCss.LABEL_DISABLED); } } /** * Enables a specific enum value */ public void setEnabled(E value, boolean enabled) { if (enabled) { disabledSet.remove(value); } else { disabledSet.add(value); } redraw(); } protected Set<E> getDisabledSet() { return disabledSet; } } private final HandlerManager handlerManager; 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) { this.handlerManager = new HandlerManager(this); peer = new EnumRadioCellTable<>(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(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 handlerManager.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) { handlerManager.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); } @Override public void setAccessible(boolean accessible) { peer.setVisible(accessible); } @Override public boolean isAccessible() { return peer.isVisible(); } @Override public LeafValueEditor<E> getActualEditor() { return this; } @Override public void disable(String disabilityHint) { // not implemented } @Override public void markAsValid() { // not implemented } @Override public void markAsInvalid(List<String> validationHints) { // not implemented } @Override public boolean isValid() { return true; } }