package org.ovirt.engine.ui.common.widget.editor; import java.util.ArrayList; import java.util.List; import org.ovirt.engine.ui.common.CommonApplicationConstants; import org.ovirt.engine.ui.common.PopupTableResources; import org.ovirt.engine.ui.common.gin.AssetProvider; import org.ovirt.engine.ui.common.utils.ElementTooltipUtils; import org.ovirt.engine.ui.common.widget.HasEditorDriver; import org.ovirt.engine.ui.common.widget.IsEditorDriver; import org.ovirt.engine.ui.common.widget.table.ElementIdCellTable; import org.ovirt.engine.ui.common.widget.table.HasColumns; import org.ovirt.engine.ui.common.widget.table.cell.CheckboxCell; import org.ovirt.engine.ui.common.widget.table.cell.EventHandlingCell; import org.ovirt.engine.ui.common.widget.table.cell.RadioboxCell; import org.ovirt.engine.ui.common.widget.table.header.AbstractSelectAllCheckBoxHeader; import org.ovirt.engine.ui.common.widget.table.header.SafeHtmlHeader; import org.ovirt.engine.ui.uicommonweb.models.EntityModel; import org.ovirt.engine.ui.uicommonweb.models.ListModel; import org.ovirt.engine.ui.uicompat.external.StringUtils; import com.google.gwt.cell.client.Cell; import com.google.gwt.core.client.GWT; import com.google.gwt.dom.client.BrowserEvents; import com.google.gwt.dom.client.Element; import com.google.gwt.dom.client.Style.Unit; import com.google.gwt.resources.client.ClientBundle; import com.google.gwt.resources.client.CssResource; import com.google.gwt.safehtml.shared.SafeHtml; import com.google.gwt.safehtml.shared.SafeHtmlUtils; import com.google.gwt.user.cellview.client.Column; import com.google.gwt.user.cellview.client.Header; import com.google.gwt.user.cellview.client.LoadingStateChangeEvent.LoadingState; import com.google.gwt.view.client.DefaultSelectionEventManager; import com.google.gwt.view.client.MultiSelectionModel; import com.google.gwt.view.client.NoSelectionModel; import com.google.gwt.view.client.SelectionChangeEvent; import com.google.gwt.view.client.SelectionChangeEvent.Handler; import com.google.gwt.view.client.SelectionModel; import com.google.gwt.view.client.SingleSelectionModel; /** * A {@code CellTable} acting as Editor of {@link ListModel} objects containing {@link EntityModel} items. * * @param <M> * List model type. */ public class EntityModelCellTable<M extends ListModel> extends ElementIdCellTable<EntityModel> implements IsEditorDriver<M>, HasColumns { public interface CellTableValidation extends CssResource { String invalidRow(); } public interface CellTableResources extends ClientBundle { @Source("org/ovirt/engine/ui/common/css/CellTableValidation.css") CellTableValidation cellTableValidation(); } public enum SelectionMode { NONE, SINGLE, MULTIPLE } private static final CellTableResources cellTableResources = GWT.create(CellTableResources.class); private static final int DEFAULT_PAGESIZE = 1000; private static final int CHECK_COLUMN_WIDTH = 27; /** * Index of selection (check box) column, if {@linkplain #isSelectionColumnPresent present}. */ public static final int SELECTION_COLUMN_INDEX = 0; private static final CommonApplicationConstants constants = AssetProvider.getConstants(); private final CellTableValidation style; private final HasDataListModelEditorAdapter<M, EntityModel> editorAdapter; private boolean selectionColumnPresent = false; /** * Create a new {@link EntityModelCellTable} with single selection mode. */ public EntityModelCellTable() { this(SelectionMode.SINGLE, (Resources) GWT.create(PopupTableResources.class)); } /** * Create a new {@link EntityModelCellTable} with single selection mode. * * @param resources * Table resources. */ public EntityModelCellTable(Resources resources) { this(SelectionMode.SINGLE, resources); } /** * Create a new {@link EntityModelCellTable}. * * @param selectionMode * Table selection mode. */ public EntityModelCellTable(SelectionMode selectionMode) { this(selectionMode, (Resources) GWT.create(PopupTableResources.class)); } /** * Create a new {@link EntityModelCellTable}. * * @param isMultiple * Whether to allow multiple ({@code true}) or single ({@code false}) selection mode. */ public EntityModelCellTable(boolean isMultiple) { this(isMultiple, (Resources) GWT.create(PopupTableResources.class)); } /** * Create a new {@link EntityModelCellTable}. * * @param selectionMode * Table selection mode. * @param hideCheckbox * Whether to hide selection column or not. */ public EntityModelCellTable(SelectionMode selectionMode, boolean hideCheckbox) { this(selectionMode, (Resources) GWT.create(PopupTableResources.class), hideCheckbox, false); } /** * Create a new {@link EntityModelCellTable}. * * @param isMultiple * Whether to allow multiple ({@code true}) or single ({@code false}) selection mode. * @param hideCheckbox * Whether to hide selection column or not. */ public EntityModelCellTable(boolean isMultiple, boolean hideCheckbox) { this(isMultiple, (Resources) GWT.create(PopupTableResources.class), hideCheckbox); } /** * Create a new {@link EntityModelCellTable}. * * @param isMultiple * Whether to allow multiple ({@code true}) or single ({@code false}) selection mode. * @param hideCheckbox * Whether to hide selection column or not. * @param showSelectAllCheckbox * Whether to show the SelectAll Checkbox in the header or not. */ public EntityModelCellTable(boolean isMultiple, boolean hideCheckbox, boolean showSelectAllCheckbox) { this(isMultiple, (Resources) GWT.create(PopupTableResources.class), hideCheckbox, showSelectAllCheckbox); } /** * Create a new {@link EntityModelCellTable}. * * @param selectionMode * Table selection mode. * @param resources * Table resources. */ public EntityModelCellTable(SelectionMode selectionMode, Resources resources) { this(selectionMode, resources, false, false); } /** * Create a new {@link EntityModelCellTable}. * * @param isMultiple * Whether to allow multiple ({@code true}) or single ({@code false}) selection mode. * @param resources * Table resources. */ public EntityModelCellTable(boolean isMultiple, Resources resources) { this(isMultiple, resources, false, false); } public EntityModelCellTable(boolean isMultiple, Resources resources, boolean hideCheckbox) { this(isMultiple ? SelectionMode.MULTIPLE : SelectionMode.SINGLE, resources, hideCheckbox, false); } public EntityModelCellTable(boolean isMultiple, Resources resources, boolean hideCheckbox, boolean showSelectAllCheckbox) { this(isMultiple ? SelectionMode.MULTIPLE : SelectionMode.SINGLE, resources, hideCheckbox, showSelectAllCheckbox); } @SuppressWarnings("unchecked") public EntityModelCellTable(SelectionMode selectionMode, Resources resources, boolean hideCheckbox, boolean showSelectAllCheckbox) { super(DEFAULT_PAGESIZE, resources); dontApplyResizableHeaderStyle(); style = cellTableResources.cellTableValidation(); style.ensureInjected(); this.editorAdapter = new HasDataListModelEditorAdapter<>(this); // Configure table selection model switch (selectionMode) { case MULTIPLE: setSelectionModel(new MultiSelectionModel<EntityModel>(), DefaultSelectionEventManager.<EntityModel> createCheckboxManager(0)); break; case NONE: setSelectionModel(new NoSelectionModel<EntityModel>()); break; case SINGLE: default: setSelectionModel(new SingleSelectionModel<EntityModel>()); break; } addSelectionChangeHandler(); addCheckBoxColumn(hideCheckbox, showSelectAllCheckbox); } public void addSelectionChangeHandler() { // Handle selection getSelectionModel().addSelectionChangeHandler(new Handler() { @Override public void onSelectionChange(SelectionChangeEvent event) { if (getListModel() == null || getListModel().getItems() == null) { return; } // Clear "IsSelected" for (EntityModel entity : (List<EntityModel>) getListModel().getItems()) { entity.setIsSelected(false); } // Set "IsSelected" SelectionModel<? super EntityModel> selectionModel = EntityModelCellTable.this.getSelectionModel(); if (selectionModel instanceof SingleSelectionModel) { EntityModel selectedObject = ((SingleSelectionModel<EntityModel>) selectionModel).getSelectedObject(); clearCurrentSelectedItems(); if (selectedObject != null) { selectedObject.setIsSelected(true); getListModel().setSelectedItem(selectedObject); } } else if (selectionModel instanceof MultiSelectionModel) { List<EntityModel> selectedItems = new ArrayList<>(); for (EntityModel entity : ((MultiSelectionModel<EntityModel>) selectionModel).getSelectedSet()) { entity.setIsSelected(true); selectedItems.add(entity); } clearCurrentSelectedItems(); getListModel().setSelectedItems(selectedItems); } } private void clearCurrentSelectedItems() { getListModel().setSelectedItems(null); getListModel().setSelectedItem(null); } }); } private void addCheckBoxColumn(boolean hideCheckbox, boolean showSelectAllCheckbox) { if (!hideCheckbox) { // Add selection column Column<EntityModel, Boolean> checkColumn = null; if (getSelectionModel() instanceof SingleSelectionModel) { checkColumn = new Column<EntityModel, Boolean>( new RadioboxCell(true, false)) { @Override public Boolean getValue(EntityModel object) { return getSelectionModel().isSelected(object); } }; addColumn(checkColumn, SafeHtmlUtils.fromSafeConstant(constants.htmlNonBreakingSpace())); } else if (getSelectionModel() instanceof MultiSelectionModel) { checkColumn = new Column<EntityModel, Boolean>( new CheckboxCell(true, false)) { @Override public Boolean getValue(EntityModel object) { return getSelectionModel().isSelected(object); } }; if (showSelectAllCheckbox) { AbstractSelectAllCheckBoxHeader<EntityModel> selectAllHeader = new AbstractSelectAllCheckBoxHeader<EntityModel>() { @Override protected void selectionChanged(Boolean value) { if (getListModel() == null || getListModel().getItems() == null) { return; } handleSelection(value, getListModel(), getSelectionModel()); } @Override public Boolean getValue() { if (getListModel() == null || getListModel().getItems() == null) { return false; } return getCheckValue(getListModel().getItems(), getSelectionModel()); } }; addColumn(checkColumn, selectAllHeader); } else { addColumn(checkColumn, SafeHtmlUtils.fromSafeConstant(constants.htmlNonBreakingSpace())); } } if (checkColumn != null) { setColumnWidth(checkColumn, CHECK_COLUMN_WIDTH, Unit.PX); selectionColumnPresent = true; } addCellPreviewHandler(event -> { int columnIndex = event.getColumn(); Cell<?> cell = getColumn(columnIndex).getCell(); if (cell instanceof EventHandlingCell && ((EventHandlingCell) cell).handlesEvent(event)) { return; } if (BrowserEvents.CLICK.equals(event.getNativeEvent().getType()) && !(getSelectionModel() instanceof NoSelectionModel)) { // Let the selection column deal with this if (event.getColumn() == 0) { return; } getSelectionModel().setSelected(event.getValue(), !getSelectionModel().isSelected(event.getValue())); } }); } } public boolean isSelectionColumnPresent() { return selectionColumnPresent; } M getListModel() { return asEditor().flush(); } public void validate(List<String> errors) { int rowCount = getRowCount(); assert errors != null && errors.size() == rowCount : "errors must be the same size as the contents of the table!"; //$NON-NLS-1$ for (int i = 0; i < rowCount; ++i) { Element element = getRowElement(i); assert element != null : "element shouldn't be null if errors is the same size as the contents of the table!"; //$NON-NLS-1$ String error = errors.get(i); boolean valid = StringUtils.isEmpty(error); if (!valid) { ElementTooltipUtils.setTooltipOnElement(element, SafeHtmlUtils.fromString(error)); element.addClassName(style.invalidRow()); } else { ElementTooltipUtils.destroyTooltip(element); element.removeClassName(style.invalidRow()); } } } @Override public HasEditorDriver<M> asEditor() { return editorAdapter; } public void setCustomSelectionColumn(Column customSelectionColumn, String width) { removeColumn(0); insertColumn(0, customSelectionColumn, SafeHtmlUtils.fromSafeConstant("<br/>")); //$NON-NLS-1$ setColumnWidth(customSelectionColumn, width); } @Override public void addColumn(Column column, String headerString) { super.addColumn(column, headerString); } @Override public void addColumn(Column column, SafeHtml headerHtml) { super.addColumn(column, headerHtml); } /** * {@inheritDoc} * <p> * Note that this method shadows original signature semantics, replacing {@code footerString} with {@code width}. */ @Override public void addColumn(Column column, String headerString, String width) { super.addColumnAndSetWidth(column, headerString, width); } public void addColumn(Column column, SafeHtml headerHtml, String width) { super.addColumnWithHtmlHeader(column, headerHtml, width); } public void addColumn(Column column, Header<?> header, String width) { super.addColumn(column, header); super.setColumnWidth(column, width); } @Override public void addColumnWithHtmlHeader(Column column, SafeHtml header) { super.addColumn(column, header); } @Override public void addColumnWithHtmlHeader(Column column, SafeHtml header, String width) { super.addColumn(column, header); super.setColumnWidth(column, width); } @Override public void addColumn(Column column, SafeHtmlHeader header) { super.addColumn(column, header); } @Override public void addColumn(Column column, SafeHtmlHeader header, String width) { super.addColumn(column, header); super.setColumnWidth(column, width); } /** * {@inheritDoc} * <p> * Note that this method shadows original signature semantics, replacing {@code footerString} with {@code width}. */ @Override public void insertColumn(int beforeIndex, Column column, String headerString, String width) { super.insertColumn(beforeIndex, column, headerString); super.setColumnWidth(column, width); } public void setLoadingState(LoadingState state) { super.onLoadingStateChanged(state); } @Override public void cleanup() { asEditor().cleanup(); super.cleanup(); } }