package org.ovirt.engine.ui.common.widget; import java.util.Collection; import java.util.Collections; import java.util.LinkedList; import java.util.Set; import org.ovirt.engine.ui.common.widget.dialog.ShapedButton; import org.ovirt.engine.ui.common.widget.editor.EntityModelCellTable; import org.ovirt.engine.ui.uicommonweb.UICommand; import org.ovirt.engine.ui.uicommonweb.models.ListModel; import org.ovirt.engine.ui.uicompat.EventArgs; import org.ovirt.engine.ui.uicompat.IEventListener; import com.google.gwt.uibinder.client.UiField; import com.google.gwt.user.client.ui.Composite; import com.google.gwt.user.client.ui.Label; import com.google.gwt.view.client.MultiSelectionModel; public abstract class SplitTable<M extends ListModel<T>, T> extends Composite { private final MultiSelectionModel<T> excludedSelectionModel; private final MultiSelectionModel<T> includedSelectionModel; private final IEventListener<EventArgs> excludedItemsChangedListener; private final IEventListener<EventArgs> includedItemsChangedListener; private UICommand onIncludeButtonPressed; private UICommand onExcludeButtonPressed; @UiField(provided = true) protected ShapedButton includeButton; @UiField(provided = true) protected ShapedButton excludeButton; @UiField(provided = true) protected EntityModelCellTable<M> excludedTable; @UiField(provided = true) protected EntityModelCellTable<M> includedTable; @UiField(provided = true) protected Label excludedTitle; @UiField(provided = true) protected Label includedTitle; @SuppressWarnings("unchecked") public SplitTable(EntityModelCellTable<M> excludedTable, EntityModelCellTable<M> includedTable, String excludedTitle, String includedTitle) { this.excludedTable = excludedTable; this.includedTable = includedTable; this.excludedTitle = new Label(excludedTitle); this.includedTitle = new Label(includedTitle); includeButton = createIncludeButton(); excludeButton = createExcludeButton(); includeButton.setEnabled(false); excludeButton.setEnabled(false); initWidget(); excludedSelectionModel = (MultiSelectionModel) excludedTable.getSelectionModel(); includedSelectionModel = (MultiSelectionModel) includedTable.getSelectionModel(); excludedItemsChangedListener = (ev, sender, args) -> excludedSelectionModel.clear(); includedItemsChangedListener = (ev, sender, args) -> includedSelectionModel.clear(); addSelectionHandler(true); addSelectionHandler(false); addClickHandler(true); addClickHandler(false); } protected abstract ShapedButton createIncludeButton(); protected abstract ShapedButton createExcludeButton(); protected abstract void initWidget(); private void addSelectionHandler(boolean excludedTable) { final MultiSelectionModel<T> selectionModel = getSelectionModel(excludedTable); final ShapedButton button = getButton(excludedTable); selectionModel.addSelectionChangeHandler(event -> button.setEnabled(!selectionModel.getSelectedSet().isEmpty())); } private void addClickHandler(final boolean excludedTableIsSource) { getButton(excludedTableIsSource).addClickHandler(event -> { MultiSelectionModel<T> sourceSelectionModel = getSelectionModel(excludedTableIsSource); UICommand command = excludedTableIsSource ? onIncludeButtonPressed : onExcludeButtonPressed; if (command != null) { command.execute(); } Set<T> selectedItems = sourceSelectionModel.getSelectedSet(); if (excludedTableIsSource) { includeItems(selectedItems, true); } else { excludeItems(selectedItems, true); } }); } /** * Allows items to be moved between {@code excludedTable} and {@code includedTable} * by double-clicking them. Moving items this way retains selection in both tables * (excluding the item that was moved). */ public void enableDoubleClickItemMoving() { excludedTable.addSimulatedDoubleClickHandler(event -> { T clickedItem = (T) event.getDisplay().getVisibleItem(event.getIndex()); if (canMoveItemOnDoubleClick(excludedTable, event.getColumn())) { includeItems(Collections.singletonList(clickedItem), false); } }); includedTable.addSimulatedDoubleClickHandler(event -> { T clickedItem = (T) event.getDisplay().getVisibleItem(event.getIndex()); if (canMoveItemOnDoubleClick(includedTable, event.getColumn())) { excludeItems(Collections.singletonList(clickedItem), false); } }); } private boolean canMoveItemOnDoubleClick(EntityModelCellTable<M> table, int columnIndex) { // Ignore double-click events on selection column return !(table.isSelectionColumnPresent() && columnIndex == EntityModelCellTable.SELECTION_COLUMN_INDEX); } private void includeItems(Collection<T> itemsToInclude, boolean clearSelectionModels) { M excludedTableModel = excludedTable.asEditor().flush(); M includedTableModel = includedTable.asEditor().flush(); if (excludedTableModel.getItems() != null && excludedTableModel.getItems().containsAll(itemsToInclude)) { excludedTableModel.getItems().removeAll(itemsToInclude); Collection<T> items = getListModelItems(includedTableModel); items.addAll(itemsToInclude); includedTableModel.setItems(items); for (T item : itemsToInclude) { excludedSelectionModel.setSelected(item, false); } refresh(clearSelectionModels); } } private void excludeItems(Collection<T> itemsToExclude, boolean clearSelectionModels) { M excludedTableModel = excludedTable.asEditor().flush(); M includedTableModel = includedTable.asEditor().flush(); if (includedTableModel.getItems() != null && includedTableModel.getItems().containsAll(itemsToExclude)) { includedTableModel.getItems().removeAll(itemsToExclude); Collection<T> items = getListModelItems(excludedTableModel); items.addAll(itemsToExclude); excludedTableModel.setItems(items); for (T item : itemsToExclude) { includedSelectionModel.setSelected(item, false); } refresh(clearSelectionModels); } } private Collection<T> getListModelItems(M model) { Collection<T> items = model.getItems(); if (items == null) { items = new LinkedList<>(); model.setItems(items); } return items; } private MultiSelectionModel<T> getSelectionModel(boolean excluded) { return excluded ? excludedSelectionModel : includedSelectionModel; } private ShapedButton getButton(boolean include) { return include ? includeButton : excludeButton; } private EntityModelCellTable<M> getTable(boolean excluded) { return excluded ? excludedTable : includedTable; } private void refresh(boolean clearSelectionModels) { if (clearSelectionModels) { excludedSelectionModel.clear(); includedSelectionModel.clear(); } excludedTable.asEditor().edit(excludedTable.asEditor().flush()); includedTable.asEditor().edit(includedTable.asEditor().flush()); } private void edit(M model, final boolean excludedTableIsEdited) { EntityModelCellTable<M> table = getTable(excludedTableIsEdited); M oldModel = table.asEditor().flush(); IEventListener<EventArgs> listener = excludedTableIsEdited ? excludedItemsChangedListener : includedItemsChangedListener; if (oldModel != null) { oldModel.getItemsChangedEvent().removeListener(listener); } model.getItemsChangedEvent().addListener(listener); table.asEditor().edit(model); } public void edit(M excludedListModel, M includedListModel) { edit(excludedListModel, true); edit(includedListModel, false); } public void edit(M excludedListModel, M includedListModel, UICommand onIncludeButtonPressed, UICommand onExcludeButtonPressed) { edit(excludedListModel, includedListModel); this.onIncludeButtonPressed = onIncludeButtonPressed; this.onExcludeButtonPressed = onExcludeButtonPressed; } }