package org.ovirt.engine.ui.webadmin.widget.table; import java.util.ArrayList; import java.util.List; import org.ovirt.engine.ui.webadmin.idhandler.WithElementId; import org.ovirt.engine.ui.webadmin.uicommon.model.SearchableTableModelProvider; import org.ovirt.engine.ui.webadmin.widget.action.AbstractActionPanel; import org.ovirt.engine.ui.webadmin.widget.table.column.ColumnWithElementId; import org.ovirt.engine.ui.webadmin.widget.table.refresh.RefreshManager; import org.ovirt.engine.ui.webadmin.widget.table.refresh.RefreshPanel; import com.google.gwt.core.client.Scheduler; import com.google.gwt.core.client.Scheduler.ScheduledCommand; import com.google.gwt.dom.client.NativeEvent; import com.google.gwt.event.dom.client.ClickEvent; import com.google.gwt.event.dom.client.KeyCodes; import com.google.gwt.event.dom.client.KeyDownEvent; import com.google.gwt.event.dom.client.KeyDownHandler; import com.google.gwt.safehtml.shared.SafeHtml; import com.google.gwt.safehtml.shared.SafeHtmlUtils; import com.google.gwt.uibinder.client.UiField; import com.google.gwt.uibinder.client.UiHandler; import com.google.gwt.user.cellview.client.CellTable.Resources; import com.google.gwt.user.cellview.client.Column; import com.google.gwt.user.cellview.client.ColumnSortEvent.AsyncHandler; import com.google.gwt.user.cellview.client.HasKeyboardSelectionPolicy.KeyboardSelectionPolicy; import com.google.gwt.user.cellview.client.Header; import com.google.gwt.user.cellview.client.LoadingStateChangeEvent.LoadingState; import com.google.gwt.user.cellview.client.SafeHtmlHeader; import com.google.gwt.user.cellview.client.TextHeader; import com.google.gwt.user.client.Event; import com.google.gwt.user.client.ui.ButtonBase; import com.google.gwt.user.client.ui.SimplePanel; import com.google.gwt.user.client.ui.Widget; import com.google.gwt.view.client.CellPreviewEvent; import com.google.gwt.view.client.SelectionModel; /** * Base class used to implement action table widgets. * <p> * Subclasses are free to style the UI, given that they declare: * <ul> * <li>{@link #actionPanel} widget into which action button widgets will be rendered * <li>{@link #prevPageButton} widget representing the "previous page" button * <li>{@link #nextPageButton} widget representing the "next page" button * <li>{@link #refreshPageButton} widget representing the "refresh current page" button * <li>{@link #tableContainer} widget for displaying the actual table * </ul> * * @param <T> * Table row data type. */ public abstract class AbstractActionTable<T> extends AbstractActionPanel<T> { @UiField @WithElementId public ButtonBase prevPageButton; @UiField @WithElementId public ButtonBase nextPageButton; @UiField(provided = true) RefreshPanel refreshPanel; @UiField SimplePanel tableContainer; @UiField SimplePanel tableHeaderContainer; private final OrderedMultiSelectionModel<T> selectionModel; private final ActionCellTable<T> table; private final ActionCellTable<T> tableHeader; private boolean multiSelectionDisabled; protected boolean showDefaultHeader; private final RefreshManager refreshManager; public AbstractActionTable(SearchableTableModelProvider<T, ?> dataProvider) { this(dataProvider, null); } public AbstractActionTable(SearchableTableModelProvider<T, ?> dataProvider, Resources resources) { this(dataProvider, resources, null); } public AbstractActionTable(SearchableTableModelProvider<T, ?> dataProvider, Resources resources, Resources headerRresources) { super(dataProvider); this.selectionModel = new OrderedMultiSelectionModel<T>(dataProvider); refreshManager = new RefreshManager(dataProvider.getModel()); refreshPanel = refreshManager.getRefreshPanel(); this.table = new ActionCellTable<T>(dataProvider, resources) { @Override protected void onBrowserEvent2(Event event) { // Enable multiple selection only when Control/Shift key is pressed if ("click".equals(event.getType()) && !multiSelectionDisabled) { selectionModel.setMultiSelectEnabled(event.getCtrlKey()); selectionModel.setMultiRangeSelectEnabled(event.getShiftKey()); } super.onBrowserEvent2(event); } @Override protected int getKeyboardSelectedRow() { if (selectionModel.getLastSelectedRow() == -1) { return super.getKeyboardSelectedRow(); } return selectionModel.getLastSelectedRow(); } @Override protected void onLoad() { super.onLoad(); if (selectionModel.getLastSelectedRow() == -1) { return; } Scheduler.get().scheduleDeferred(new ScheduledCommand() { @Override public void execute() { setFocus(true); } }); } @Override public void setRowData(int start, List<? extends T> values) { super.setRowData(start, values); selectionModel.resolveChanges(); // Ensure that paging buttons are updated whenever new data is set prevPageButton.setEnabled(getDataProvider().canGoBack()); nextPageButton.setEnabled(getDataProvider().canGoForward()); }; }; // Create table header row this.tableHeader = new ActionCellTable<T>(dataProvider, headerRresources); this.tableHeader.setRowData(new ArrayList<T>()); showDefaultHeader = headerRresources == null; this.selectionModel.setDataDisplay(table); } @Override protected SearchableTableModelProvider<T, ?> getDataProvider() { return (SearchableTableModelProvider<T, ?>) super.getDataProvider(); } @Override protected void initWidget(Widget widget) { super.initWidget(widget); initTable(); } /** * Initialize the table widget and attach it to the corresponding panel. */ void initTable() { // Set up table data provider getDataProvider().addDataDisplay(table); // Add default sort handler that delegates to the data provider AsyncHandler columnSortHandler = new AsyncHandler(table); table.addColumnSortHandler(columnSortHandler); // Set up table selection model table.setSelectionModel(selectionModel); // Enable keyboard selection table.setKeyboardSelectionPolicy(KeyboardSelectionPolicy.ENABLED); // Add context menu handler for table widget addContextMenuHandler(table); // Add arrow key handler table.addDomHandler(new KeyDownHandler() { @Override public void onKeyDown(KeyDownEvent event) { boolean shiftPageDown = event.isShiftKeyDown() && KeyCodes.KEY_PAGEDOWN == event.getNativeKeyCode(); boolean shiftPageUp = event.isShiftKeyDown() && KeyCodes.KEY_PAGEUP == event.getNativeKeyCode(); boolean ctrlA = event.isControlKeyDown() && ('a' == event.getNativeKeyCode() || 'A' == event.getNativeKeyCode()); boolean arrow = KeyDownEvent.isArrow(event.getNativeKeyCode()); if (shiftPageUp || shiftPageDown || ctrlA || arrow) { event.preventDefault(); event.stopPropagation(); } else { return; } if (shiftPageDown) { selectionModel.selectAllNext(); } else if (shiftPageUp) { selectionModel.selectAllPrev(); } else if (ctrlA) { selectionModel.selectAll(); } else if (arrow) { selectionModel.setMultiSelectEnabled(event.isControlKeyDown() && !multiSelectionDisabled); selectionModel.setMultiRangeSelectEnabled(event.isShiftKeyDown() && !multiSelectionDisabled); if (event.isDownArrow()) { selectionModel.selectNext(); } else if (event.isUpArrow()) { selectionModel.selectPrev(); } } } }, KeyDownEvent.getType()); // Add right click handler table.addCellPreviewHandler(new CellPreviewEvent.Handler<T>() { @Override public void onCellPreview(CellPreviewEvent<T> event) { if (event.getNativeEvent().getButton() != NativeEvent.BUTTON_RIGHT || !"mousedown".equals(event.getNativeEvent().getType())) { return; } final T value = event.getValue(); if (!selectionModel.isSelected(value)) { Scheduler.get().scheduleDeferred(new ScheduledCommand() { @Override public void execute() { selectionModel.setMultiSelectEnabled(false); selectionModel.setMultiRangeSelectEnabled(false); selectionModel.setSelected(value, true); } }); } } }); // Use fixed table layout table.setWidth("100%", true); tableHeader.setWidth("100%", true); // Attach table widget to the corresponding panel tableContainer.setWidget(table); tableHeaderContainer.setWidget(tableHeader); tableHeaderContainer.setVisible(!showDefaultHeader); } @UiHandler("prevPageButton") void handlePrevPageButtonClick(ClickEvent event) { getDataProvider().goBack(); } @UiHandler("nextPageButton") void handleNextPageButtonClick(ClickEvent event) { getDataProvider().goForward(); } @UiHandler("refreshPanel") void handleRefreshPanelClick(ClickEvent event) { getDataProvider().refresh(); } void configureColumnElementId(ColumnWithElementId column) { column.configureElementId(getElementId(), null); } void addColumn(Column<T, ?> column, Header<?> header) { table.addColumn(column, header); tableHeader.addColumn(column, header); // Configure column content element ID options if necessary if (column instanceof ColumnWithElementId) { configureColumnElementId((ColumnWithElementId) column); } } void setColumnWidth(Column<T, ?> column, String width) { table.setColumnWidth(column, width); tableHeader.setColumnWidth(column, width); } /** * Adds a new table column, without specifying the column width. */ public void addColumn(Column<T, ?> column, String headerText) { addColumn(column, new TextHeader(headerText)); } /** * Adds a new table column, using the given column width. */ public void addColumn(Column<T, ?> column, String headerText, String width) { addColumn(column, headerText); setColumnWidth(column, width); } /** * Adds a new table column with HTML in the header text, using the given column width. * <p> * {@code headerHtml} must honor the {@link SafeHtml} contract as specified in * {@link SafeHtmlUtils#fromSafeConstant(String) fromSafeConstant}. * * @see SafeHtmlUtils#fromSafeConstant(String) */ public void addColumnWithHtmlHeader(Column<T, ?> column, String headerHtml, String width) { SafeHtml headerValue = SafeHtmlUtils.fromSafeConstant(headerHtml); SafeHtmlHeader header = new SafeHtmlHeader(headerValue); addColumn(column, header); setColumnWidth(column, width); } /** * Removes the given column. */ public void removeColumn(Column<T, ?> column) { table.removeColumn(column); tableHeader.removeColumn(column); } /** * Ensures that the given column is added (or removed), unless it's already present (or absent). */ public void ensureColumnPresent(Column<T, ?> column, String headerText, boolean present) { if (present && table.getColumnIndex(column) == -1) { addColumn(column, headerText); } else if (!present && table.getColumnIndex(column) != -1) { removeColumn(column); } } public OrderedMultiSelectionModel<T> getSelectionModel() { return selectionModel; } public void setTableSelectionModel(SelectionModel<T> selectionModel, CellPreviewEvent.Handler<T> selectionEventManager) { table.setSelectionModel(selectionModel, selectionEventManager); } public boolean isMultiSelectionDisabled() { return multiSelectionDisabled; } public void setMultiSelectionDisabled(boolean multiSelectionDisabled) { this.multiSelectionDisabled = multiSelectionDisabled; } public void onFocus() { refreshManager.onFocus(); } public void onBlur() { refreshManager.onBlur(); } @Override protected List<T> getSelectedItems() { return new ArrayList<T>(selectionModel.getSelectedList()); } public void setLoadingState(LoadingState state) { table.setLoadingState(state); } }