/* * To change this license header, choose License Headers in Project Properties. * To change this template file, choose Tools | Templates * and open the template in the editor. */ package com.eas.grid; import java.util.HashMap; import java.util.List; import java.util.Map; import com.google.gwt.cell.client.Cell; import com.google.gwt.dom.client.BrowserEvents; import com.google.gwt.dom.client.Element; import com.google.gwt.dom.client.NativeEvent; import com.google.gwt.dom.client.NodeList; import com.google.gwt.dom.client.TableCellElement; import com.google.gwt.dom.client.TableColElement; import com.google.gwt.dom.client.TableRowElement; import com.google.gwt.dom.client.TableSectionElement; import com.google.gwt.event.dom.client.KeyCodes; import com.google.gwt.i18n.client.LocaleInfo; import com.google.gwt.safehtml.shared.SafeHtmlBuilder; import com.google.gwt.user.cellview.client.AbstractCellTable; import com.google.gwt.user.cellview.client.CellTable; import com.google.gwt.user.cellview.client.Column; import com.google.gwt.user.cellview.client.HasKeyboardPagingPolicy; import com.google.gwt.user.cellview.client.Header; import com.google.gwt.user.client.Event; import com.google.gwt.view.client.CellPreviewEvent; import com.google.gwt.view.client.ListDataProvider; import com.google.gwt.view.client.ProvidesKey; /** * * @author mg * @param <T> */ public class GridSection<T> extends CellTable<T> { protected static final String HIDDEN_COLUMN_WIDTH = "0.00000000000000000001px"; protected AbstractCellTable<T>[] columnsPartners; protected WidthCallback widthPropagator; protected ColumnsRemover columnsRemover; protected AbstractCellTable<T> headerSource; protected AbstractCellTable<T> footerSource; protected boolean draggableRows; public GridSection(ProvidesKey<T> keyProvider) { super(15, ThemedGridResources.instance, keyProvider, null, true, false); setKeyboardPagingPolicy(HasKeyboardPagingPolicy.KeyboardPagingPolicy.CURRENT_PAGE); setLoadingIndicator(null); setEmptyTableWidget(null); getElement().getStyle().setProperty("borderCollapse", "collapse"); setKeyboardSelectionHandler(new CellTableKeyboardSelectionHandler<T>(this) { @Override public void onCellPreview(CellPreviewEvent<T> event) { NativeEvent nativeEvent = event.getNativeEvent(); String eventType = event.getNativeEvent().getType(); if (BrowserEvents.KEYDOWN.equals(eventType) && !event.isCellEditing()) { /* * Handle keyboard navigation, unless the cell is being * edited. If the cell is being edited, we do not want to * change rows. * * Prevent default on navigation events to prevent default * scrollbar behavior. */ int oldRow = GridSection.this.getKeyboardSelectedRow(); int oldColumn = GridSection.this.getKeyboardSelectedColumn(); boolean isRtl = LocaleInfo.getCurrentLocale().isRTL(); int keyCodeLineEnd = isRtl ? KeyCodes.KEY_LEFT : KeyCodes.KEY_RIGHT; int keyCodeLineStart = isRtl ? KeyCodes.KEY_RIGHT : KeyCodes.KEY_LEFT; int keyCode = nativeEvent.getKeyCode(); super.onCellPreview(event); if (keyCode == keyCodeLineEnd) { GridSection.this.setKeyboardSelectedRow(oldRow); if (GridSection.this.getKeyboardSelectedColumn() < oldColumn) GridSection.this.setKeyboardSelectedColumn(oldColumn); } else if (keyCode == keyCodeLineStart) { GridSection.this.setKeyboardSelectedRow(oldRow); if (GridSection.this.getKeyboardSelectedColumn() > oldColumn) GridSection.this.setKeyboardSelectedColumn(oldColumn); } } else super.onCellPreview(event); } }); } public boolean isDraggableRows() { return draggableRows; } public void setDraggableRows(boolean aValue) { if (draggableRows != aValue) { draggableRows = aValue; if (isAttached()) redraw(); } } protected boolean ctrlKey; @Override protected void onBrowserEvent2(Event event) { ctrlKey = event.getCtrlKey(); super.onBrowserEvent2(event); } public boolean isCtrlKey() { return ctrlKey; } public AbstractCellTable<T>[] getColumnsPartners() { return columnsPartners; } @Override public void setKeyboardSelectedRow(int row, int subrow, boolean stealFocus) { onBlur();// When selecting cells with shift key, focused cell is not // cleared without such hack. super.setKeyboardSelectedRow(row, subrow, stealFocus); } @Override public void setKeyboardSelectedColumn(int column, boolean stealFocus) { onBlur();// When selecting cells with shift key, focused cell is not // cleared without such hack. super.setKeyboardSelectedColumn(column, stealFocus); } public void setColumnsPartners(AbstractCellTable<T>[] aPartners) { columnsPartners = aPartners; } public WidthCallback getWidthPropagator() { return widthPropagator; } public void setWidthPropagator(WidthCallback aValue) { widthPropagator = aValue; } @Override public Header<?> getHeader(int index) { return headerSource != null ? headerSource.getHeader(index) : super.getHeader(index); } @Override public Header<?> getFooter(int index) { return footerSource != null ? footerSource.getFooter(index) : super.getFooter(index); } public ColumnsRemover getColumnsRemover() { return columnsRemover; } public void setColumnsRemover(ColumnsRemover aValue) { columnsRemover = aValue; } @Override public void removeColumn(int index) { if (columnsRemover != null) { columnsRemover.removeColumn(index); } else { Column<T, ?> col = getColumn(index); hiddenColumns.remove(col); super.removeColumn(index); NodeList<Element> colGroups = getElement().getElementsByTagName("colgroup"); if (colGroups != null && colGroups.getLength() == 1) { TableColElement colGroup = colGroups.getItem(0).cast(); if (getColumnCount() < colGroup.getChildCount()) { // It seems, that GWT's bug is still here. if (index >= 0 && index < colGroup.getChildCount()) { colGroup.removeChild(colGroup.getChild(index)); } } } } } public AbstractCellTable<T> getHeaderSource() { return headerSource; } public void setHeaderSource(AbstractCellTable<T> aValue) { headerSource = aValue; } public AbstractCellTable<T> getFooterSource() { return footerSource; } public void setFooterSource(AbstractCellTable<T> aValue) { footerSource = aValue; } protected Map<Column<T, ?>, String> hiddenColumns = new HashMap<>(); public boolean isColumnHidden(Column<T, ?> aColumn) { return hiddenColumns.containsKey(aColumn); } public boolean hideColumn(Column<T, ?> aColumn) { if (!isColumnHidden(aColumn)) { hiddenColumns.put(aColumn, getColumnWidth(aColumn)); super.setColumnWidth(aColumn, HIDDEN_COLUMN_WIDTH); return true; } else { return false; } } @Override public void setColumnWidth(Column<T, ?> aColumn, String width) { if (isColumnHidden(aColumn)) { super.setColumnWidth(aColumn, HIDDEN_COLUMN_WIDTH); } else { super.setColumnWidth(aColumn, width); } } public boolean showColumn(Column<T, ?> aColumn) { if (isColumnHidden(aColumn)) { String wasWidth = hiddenColumns.remove(aColumn); super.setColumnWidth(aColumn, wasWidth); return true; } else { return false; } } @Override protected void doSetColumnWidth(int index, String aValue) { if (index >= 0 && index < getColumnCount()) { super.doSetColumnWidth(index, aValue); if (columnsPartners != null) { for (AbstractCellTable<T> partner : columnsPartners) { if (index >= 0 && index < partner.getColumnCount()) { Column<T, ?> col = partner.getColumn(index); if (aValue == null) { partner.clearColumnWidth(col); } else { partner.setColumnWidth(col, aValue); } } } } widthChanged(); } } protected void widthChanged() { if (widthPropagator != null) { widthPropagator.changed(); } } @Override public TableSectionElement getTableHeadElement() { return super.getTableHeadElement(); } public TableCellElement getCell(int aRow, int aCol) { NodeList<TableRowElement> rows = getTableBodyElement().getRows(); if (aRow >= 0 && aRow < rows.getLength()) { TableRowElement row = rows.getItem(aRow); NodeList<TableCellElement> cells = row.getCells(); if (aCol >= 0 && aCol < cells.getLength()) { return cells.getItem(aCol); } } return null; } @Override public Element getKeyboardSelectedElement() { return super.getKeyboardSelectedElement(); } public void focusCell(int aRow, int aCol) { setKeyboardSelectedColumn(aCol); setKeyboardSelectedRow(aRow); setFocus(true); } public <C> void redrawAllRowsInColumn(int aIndex, ListDataProvider<T> aDataProvider) { if (aIndex >= 0 && aIndex < getColumnCount()) { int start = getVisibleRange().getStart(); Column<T, C> column = (Column<T, C>) getColumn(aIndex); Cell<C> cell = column.getCell(); List<T> data = aDataProvider.getList(); ProvidesKey<T> keys = getKeyProvider(); NodeList<TableRowElement> rows = getTableBodyElement().getRows(); for (int i = 0; i < rows.getLength(); i++) { TableRowElement row = rows.getItem(i); NodeList<TableCellElement> cells = row.getCells(); if (aIndex >= 0 && aIndex < cells.getLength()) { TableCellElement toRerender = cells.getItem(aIndex); if (toRerender != null) { SafeHtmlBuilder sb = new SafeHtmlBuilder(); int dataIdx = start + i; if (dataIdx >= 0 && dataIdx < data.size()) { T object = data.get(dataIdx); Cell.Context cx = new Cell.Context(start + i, aIndex, keys.getKey(object)); cell.render(cx, column.getValue(object), sb); // Take into account, that cell builder supports // some // maps // to cells' divs // and generates them. So we have to work with first // <div> // in <td>. toRerender.getFirstChildElement().setInnerSafeHtml(sb.toSafeHtml()); } } } } } } @Override public TableSectionElement getTableFootElement() { return super.getTableFootElement(); } public String getColumnWidth(Column<T, ?> col, boolean withHidden) { if (col != null) { if (withHidden) { if (hiddenColumns.containsKey(col)) { return hiddenColumns.get(col); } else { return getColumnWidth(col); } } else { return getColumnWidth(col); } } else { return null; } } protected static Map<Element, GridSection<?>> sections = new HashMap<>(); public static GridSection<?> getInstance(Element aTableElement) { return sections.get(aTableElement); } @Override protected void onAttach() { super.onAttach(); sections.put(getElement(), this); } @Override protected void onDetach() { sections.remove(getElement()); super.onDetach(); } @Override public void redraw() { super.redraw(); } @Override public void redrawFooters() { super.redrawFooters(); } @Override public void redrawHeaders() { super.redrawHeaders(); } @Override public void redrawRow(int absRowIndex) { super.redrawRow(absRowIndex); } }