/*
* Copyright (c) 2008-2009 GWT Mosaic Georgios J. Georgopoulos.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package org.gwt.mosaic.ui.client;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Set;
import org.gwt.mosaic.core.client.DOM;
import org.gwt.mosaic.override.client.HTMLTable.CellFormatter;
import org.gwt.mosaic.ui.client.event.RowHighlightHandler;
import org.gwt.mosaic.ui.client.event.RowSelectionHandler;
import org.gwt.mosaic.ui.client.list.DefaultListModel;
import org.gwt.mosaic.ui.client.list.ListDataEvent;
import org.gwt.mosaic.ui.client.list.ListDataListener;
import org.gwt.mosaic.ui.client.list.ListHeader;
import org.gwt.mosaic.ui.client.list.ListModel;
import org.gwt.mosaic.ui.client.table.DataTable;
import org.gwt.mosaic.ui.client.table.FixedWidthFlexTable;
import org.gwt.mosaic.ui.client.table.ScrollTable2;
import org.gwt.mosaic.ui.client.table.SortableGrid;
import org.gwt.mosaic.ui.client.table.AbstractScrollTable.ColumnResizePolicy;
import org.gwt.mosaic.ui.client.table.AbstractScrollTable.ResizePolicy;
import org.gwt.mosaic.ui.client.table.AbstractScrollTable.SortPolicy;
import org.gwt.mosaic.ui.client.table.SelectionGrid.SelectionPolicy;
import org.gwt.mosaic.ui.client.table.SortableGrid.ColumnSorter;
import org.gwt.mosaic.ui.client.table.SortableGrid.ColumnSorterCallback;
import org.gwt.mosaic.ui.client.table.TableModelHelper.ColumnSortList;
import com.google.gwt.dom.client.TableRowElement;
import com.google.gwt.event.dom.client.ChangeEvent;
import com.google.gwt.event.dom.client.ChangeHandler;
import com.google.gwt.event.dom.client.DoubleClickHandler;
import com.google.gwt.event.dom.client.KeyCodes;
import com.google.gwt.event.shared.HandlerRegistration;
import com.google.gwt.user.client.Element;
import com.google.gwt.user.client.Event;
import com.google.gwt.user.client.ui.Focusable;
import com.google.gwt.user.client.ui.Widget;
import com.google.gwt.user.client.ui.impl.FocusImpl;
/**
* This widget is used to create a list of items where one or more of the items
* may be selected. A {@code ListBox} may contain multiple columns. A separate
* model, {@link ListModel}, maintains the contents of the list.
*
* @author georgopoulos.georgios(at)gmail.com
*
* @parem <T>
*/
public class ListBox<T> extends LayoutComposite implements Focusable,
ListDataListener {
/**
* The render used to set cell contents.
*
* @param <T>
*/
public interface CellRenderer<T> {
/**
* Render the contents of a cell.
*
* @param listBox the {@code ListBox} that is asking the renderer to draw
* @param row the row index
* @param column the column index
* @param item the item to render
*/
void renderCell(ListBox<T> listBox, int row, int column, T item);
}
/**
* @param <T>
*/
public interface ColumnComparator<T> {
int compare(T t1, T t2, int column);
}
private static final FocusImpl impl = FocusImpl.getFocusImplForPanel();
private static final int INSERT_AT_END = -1;
private final ScrollTable2 scrollTable;
private final DataTable dataTable = new DataTable();
private final FixedWidthFlexTable headerTable = new FixedWidthFlexTable();
/**
* The cell renderer used on the data table.
*/
private CellRenderer<T> cellRenderer = new CellRenderer<T>() {
public void renderCell(ListBox<T> listBox, int row, int column, T item) {
if (item instanceof Widget) {
listBox.setWidget(row, column, (Widget) item);
} else {
listBox.setText(row, column, item.toString());
}
}
};
/**
* The column comparator used by the column sorter.
*/
private ColumnComparator<T> columnComparator;
/**
* The table rows in model order.
*/
private List<Element> rowsInModelOrder = new ArrayList<Element>();
private ListModel<T> dataModel;
private ListHeader listHeader;
/**
* Creates an empty list box in single selection mode.
*/
public ListBox() {
this(null);
}
public ListBox(String[] columns) {
super(impl.createFocusable());
setHeader(new ListHeader(columns));
scrollTable = new ScrollTable2(dataTable, headerTable);
scrollTable.setResizePolicy(ResizePolicy.FILL_WIDTH);
scrollTable.setCellPadding(3);
scrollTable.setCellSpacing(0);
setMultipleSelect(false);
dataTable.resize(0, getColumnCount());
getLayoutPanel().add(scrollTable);
// sinkEvents(Event.FOCUSEVENTS | Event.KEYEVENTS | Event.ONCLICK
// | Event.MOUSEEVENTS | Event.ONMOUSEWHEEL);
sinkEvents(Event.ONCLICK | Event.ONMOUSEOVER | Event.ONMOUSEOUT
| Event.ONFOCUS | Event.ONKEYDOWN);
// Hide focus outline in Mozilla/Webkit/Opera
DOM.setStyleAttribute(getElement(), "outline", "0px");
// Hide focus outline in IE 6/7
DOM.setElementAttribute(getElement(), "hideFocus", "true");
}
public ColumnComparator<T> getColumnComparator() {
return columnComparator;
}
public void setColumnComparator(ColumnComparator<T> columnComparator) {
this.columnComparator = columnComparator;
if (columnComparator == null) {
dataTable.setColumnSorter(null);
} else {
dataTable.setColumnSorter(new ColumnSorter() {
@Override
public void onSortColumn(SortableGrid grid, ColumnSortList sortList,
ColumnSorterCallback callback) {
// Get the primary column and sort order
final int column = sortList.getPrimaryColumn();
final boolean ascending = sortList.isPrimaryAscending();
// Get all of the cell elements
int rowCount = grid.getRowCount();
List<Element> trList = new ArrayList<Element>(rowCount);
for (int i = 0; i < rowCount; i++) {
trList.add(grid.getRowFormatter().getElement(i));
}
// Sort the row elements
if (ascending) {
Collections.sort(trList, new Comparator<Element>() {
@SuppressWarnings("unchecked")
public int compare(Element o1, Element o2) {
T t1 = (T) o1.getPropertyObject("data");
T t2 = (T) o2.getPropertyObject("data");
return ListBox.this.columnComparator.compare(t1, t2, column);
}
});
} else {
Collections.sort(trList, new Comparator<Element>() {
@SuppressWarnings("unchecked")
public int compare(Element o1, Element o2) {
T t1 = (T) o1.getPropertyObject("data");
T t2 = (T) o2.getPropertyObject("data");
return ListBox.this.columnComparator.compare(t2, t1, column);
}
});
}
// Convert tdElems to trElems, reversing if needed
Element[] trElems = new Element[rowCount];
trList.toArray(trElems);
// Use the callback to complete the sorting
callback.onSortingComplete(trElems);
}
});
}
}
public void setHeader(ListHeader listHeader) {
this.listHeader = listHeader;
while (headerTable.getRowCount() > 0) {
headerTable.removeRow(headerTable.getRowCount() - 1);
}
if (listHeader.size() > 0) {
for (int column = 0, size = listHeader.size(); column < size; ++column) {
headerTable.setHTML(0, column, listHeader.get(column).getName());
}
headerTable.setVisible(true);
dataTable.resizeColumns(listHeader.size());
} else {
headerTable.setText(0, 0, null);
headerTable.setVisible(false);
dataTable.resizeColumns(1);
}
ListDataEvent event = new ListDataEvent(getModel(),
ListDataEvent.Type.CONTENTS_CHANGED, 0, getModel().getSize());
contentsChanged(event);
}
public ListHeader getHeader() {
return listHeader;
}
public HandlerRegistration addDoubleClickHandler(DoubleClickHandler handler) {
return ((DataTable) scrollTable.getDataTable()).addDoubleClickHandler(handler);
}
public HandlerRegistration addRowSelectionHandler(RowSelectionHandler handler) {
return scrollTable.getDataTable().addRowSelectionHandler(handler);
}
public HandlerRegistration addRowHighlightHandler(RowHighlightHandler handler) {
return scrollTable.getDataTable().addRowHighlightHandler(handler);
}
private void checkIndex(int index) {
if (index < 0 || index >= getItemCount()) {
throw new IndexOutOfBoundsException();
}
}
/**
* One or more items have been added to the list. The {@code event} argument
* can supply the indices for the range of items added.
*
* @param event the event
* @see org.gwt.mosaic.ui.client.list.ListDataListener#intervalAdded(org.gwt.mosaic.ui.client.list.ListDataEvent)
*/
public void intervalAdded(ListDataEvent event) {
if (dataModel == event.getSource()) {
for (int i = event.getIndex0(), n = event.getIndex1(); i <= n && i >= 0; ++i) {
if (i < getItemCount()) {
renderItemOnInsert(dataModel.getElementAt(i), i);
} else {
renderItemOnInsert(dataModel.getElementAt(i), INSERT_AT_END);
}
}
}
}
/**
* One or more items have been removed from the list. The {@code event}
* argument can supply the indicies for range of items removed.
*
* @param event the event
* @see org.gwt.mosaic.ui.client.list.ListDataListener#intervalRemoved(org.gwt.mosaic.ui.client.list.ListDataEvent)
*/
public void intervalRemoved(ListDataEvent event) {
if (dataModel == event.getSource()) {
for (int i = event.getIndex1(), n = event.getIndex0(); i >= n && i >= 0; --i) {
renderOnRemove(i);
}
}
}
/**
* The contents of the list have changed in some way. This method will be
* called if the change cannot be notified via the
* {@link #intervalAdded(ListDataEvent)} or the
* {@link #intervalRemoved(ListDataEvent)} methods.
*
* @param event the event
* @see org.gwt.mosaic.ui.client.list.ListDataListener#contentsChanged(org.gwt.mosaic.ui.client.list.ListDataEvent)
*/
public void contentsChanged(ListDataEvent event) {
if (dataModel == event.getSource()) {
int index0 = event.getIndex0(), index1 = event.getIndex1();
if (index1 < getItemCount() && index0 == index1) {
++index1;
}
for (; index0 < index1; ++index0) {
if (index0 < getItemCount()) {
renderItemOnUpdate(index0, dataModel.getElementAt(index0));
} else {
renderItemOnInsert(dataModel.getElementAt(index0), INSERT_AT_END);
}
}
}
}
private void eatEvent(Event event) {
DOM.eventCancelBubble(event, true);
DOM.eventPreventDefault(event);
}
/**
* Gets the {@link CellFormatter} associated with this table.
*
* @return this table's cell formatter
*/
public CellFormatter getCellFormatter() {
return scrollTable.getDataTable().getCellFormatter();
}
/**
* Get the {@link CellRenderer} used to render cells.
*
* @return the current renderer
*/
public CellRenderer<T> getCellRenderer() {
return cellRenderer;
}
/**
* Gets the number of columns in this grid.
*
* @return the number of columns
*/
public int getColumnCount() {
return dataTable.getColumnCount();
}
/**
* @return the column resize policy
*/
public ColumnResizePolicy getColumnResizePolicy() {
return scrollTable.getColumnResizePolicy();
}
public int getColumnWidth(int column) {
return scrollTable.getColumnWidth(column);
}
public PopupMenu getContextMenu() {
return dataTable.getContextMenu();
}
/**
* Gets the item at the specified index.
*
* @param index the index of the item to be retrieved
* @return the item
* @throws IndexOutOfBoundsException if the index is out of range
*/
@SuppressWarnings("unchecked")
public T getItem(int index) {
checkIndex(index);
return (T) dataTable.getRowFormatter().getElement(index).getPropertyObject(
"data");
}
/**
* Gets the number of items present in the list box.
*
* @return the number of items
*/
public int getItemCount() {
return dataTable.getRowCount();
}
/**
* Get the absolute maximum width of a column.
*
* @param column the column index
* @return the maximum allowable width of the column
*/
public int getMaximumColumnWidth(int column) {
return scrollTable.getMaximumColumnWidth(column);
}
/**
* Get the absolute minimum width of a column.
*
* @param column the column index
* @return the minimum allowable width of the column
*/
public int getMinimumColumnWidth(int column) {
return scrollTable.getMinimumColumnWidth(column);
}
/**
* Returns the data model.
*
* @return the {@code ListModel} that provides the displayed list of items
*/
public ListModel<T> getModel() {
if (dataModel == null) {
setModel(new DefaultListModel<T>());
}
return dataModel;
}
/**
* Get the preferred width of a column.
*
* @param column the column index
* @return the preferred width of the column
*/
public int getPreferredColumnWidth(int column) {
return scrollTable.getPreferredColumnWidth(column);
}
/**
* Gets the currently selected item. If multiple items are selected, this
* method will return the first selected item ({@link #isItemSelected(int)}
* can be used to query individual items).
*
* @return the selected index, or {@code -1} if none is selected
* @see #isItemSelected(int)
*/
public int getSelectedIndex() {
if (dataTable.isSelectionEnabled()) {
Set<Integer> selection = dataTable.getSelectedRows();
if (selection != null && selection.size() > 0) {
return selection.iterator().next();
}
}
return -1;
}
/**
* Returns a {@code Set} of all the selected indices.
*
* @return all of the selected indices in a {@code Set}
*/
public Set<Integer> getSelectedIndices() {
return dataTable.getSelectedRows();
}
/**
* @return the current sort policy
*/
public SortPolicy getSortPolicy() {
return scrollTable.getSortPolicy();
}
public int getTabIndex() {
return impl.getTabIndex(getElement());
}
/**
* Returns true if the specified column is sortable.
*
* @param column the column index
* @return true if the column is sortable, false if it is not sortable
*/
public boolean isColumnSortable(int column) {
return scrollTable.isColumnSortable(column);
}
/**
* Returns true if the specified column can be truncated. If it cannot be
* truncated, its minimum width will be adjusted to ensure the cell content is
* visible.
*
* @param column the column index
* @return true if the column is truncatable, false if it is not
*/
public boolean isColumnTruncatable(int column) {
return scrollTable.isColumnTruncatable(column);
}
/**
* Determines whether an individual list item is selected.
*
* @param index the index of the item to be tested
* @return {@code true} if the item is selected
* @throws IndexOutOfBoundsException if the index is out of range
* @see #getSelectedIndices()
*/
public boolean isItemSelected(int index) {
checkIndex(index);
return dataTable.isRowSelected(index);
}
/**
* Gets whether this list allows multiple selection.
*
* @return {@code true} if multiple selection is allowed
* @see #setMultipleSelect(boolean)
*/
public boolean isMultipleSelect() {
return dataTable.isSelectionEnabled()
&& (dataTable.getSelectionPolicy() == SelectionPolicy.MULTI_ROW || dataTable.getSelectionPolicy() == SelectionPolicy.CHECKBOX);
}
/**
* @return {@code true} if selection is enabled, {@code false} otherwise
*/
public boolean isSelectionEnabled() {
return dataTable.isSelectionEnabled();
}
private void moveDown() {
if (selectFirstItemIfNodeSelected()) {
return;
}
selectNextItem();
}
private void moveUp() {
if (selectFirstItemIfNodeSelected()) {
return;
}
selectPrevItem();
}
/**
* {@inheritDoc}
*
* @see com.google.gwt.widgetideas.table.client.overrides.HTMLTable
*/
@Override
public void onBrowserEvent(Event event) {
switch (DOM.eventGetType(event)) {
case Event.ONKEYDOWN:
int keyCode = DOM.eventGetKeyCode(event);
switch (keyCode) {
case KeyCodes.KEY_UP:
moveUp();
eatEvent(event);
break;
case KeyCodes.KEY_DOWN:
moveDown();
eatEvent(event);
break;
case KeyCodes.KEY_LEFT:
DOM.scrollIntoView((Element) dataTable.getRowFormatter().getElement(
getSelectedIndex()).getFirstChild());
break;
case KeyCodes.KEY_RIGHT:
DOM.scrollIntoView((Element) dataTable.getRowFormatter().getElement(
getSelectedIndex()).getLastChild());
break;
default:
super.onBrowserEvent(event);
break;
}
break;
case Event.ONCLICK:
setFocus(true);
default:
super.onBrowserEvent(event);
}
}
/**
* Renders an inserted item.
*
* @param item the item to be inserted
* @param index the index at which to insert it
*/
protected void renderItemOnInsert(T item, int index) {
if ((index == INSERT_AT_END) || (index == dataTable.getRowCount())) {
index = dataTable.getRowCount();
}
dataTable.insertRow(index);
// Map item with <tr>
Element trElem = dataTable.getRowFormatter().getElement(index);
trElem.setPropertyObject("data", item);
rowsInModelOrder.add(index, trElem);
// Set the data in the new row
for (int cellIndex = 0, n = dataTable.getColumnCount(); cellIndex < n; ++cellIndex) {
cellRenderer.renderCell(this, index, cellIndex, item);
}
}
/**
* Renders an updated item.
*
* @param index the index of the item to be set
* @param item the item's new value
* @throws IndexOutOfBoundsException if the index is out of range
*/
protected void renderItemOnUpdate(int index, T item) {
checkIndex(index);
if (item == null) {
throw new NullPointerException("Cannot set an item to null");
}
// Map item with <tr>
Element trElem = dataTable.getRowFormatter().getElement(index);
trElem.setPropertyObject("data", item);
// Set the data in the row
for (int cellIndex = 0, n = dataTable.getColumnCount(); cellIndex < n; ++cellIndex) {
cellRenderer.renderCell(this, index, cellIndex, item);
}
}
/**
* Removes all items from the list box.
*/
protected void renderOnClear() {
for (Element elem : rowsInModelOrder) {
elem.setPropertyObject("data", null);
}
dataTable.resizeRows(0);
rowsInModelOrder.clear();
}
/**
* Removes the item at the specified index.
*
* @param index the index of the item to be removed
* @throws IndexOutOfBoundsException if the index is out of range
*/
protected void renderOnRemove(int index) {
checkIndex(index);
final Element tr = dataTable.getRowFormatter().getElement(index);
tr.setPropertyObject("data", null);
dataTable.removeRow(index);
rowsInModelOrder.remove(index);
}
/**
* Selects the first item in the list if no items are currently selected. This
* method assumes that the list has at least 1 item.
*
* @return {@code true} if no item was previosly selected and the first item
* in the list was selected, {@code false} otherwise
*/
private boolean selectFirstItemIfNodeSelected() {
if (getSelectedIndex() == -1) {
setSelectedIndex(0);
return true;
}
return false;
}
private void selectNextItem() {
int index = getSelectedIndex();
if (index == -1) {
return;
}
if (index < getItemCount() - 1) {
setSelectedIndex(++index);
} else {
// we're at the end, loop around to the start
setSelectedIndex(0);
}
DOM.scrollIntoView((Element) dataTable.getRowFormatter().getElement(
getSelectedIndex()).getFirstChild());
}
private void selectPrevItem() {
int index = getSelectedIndex();
if (index == -1) {
return;
}
if (index > 0) {
setSelectedIndex(--index);
} else {
// we're at the start, loop around to the end
setSelectedIndex(getItemCount() - 1);
}
DOM.scrollIntoView((Element) dataTable.getRowFormatter().getElement(
getSelectedIndex()).getFirstChild());
}
public void setAccessKey(char key) {
impl.setAccessKey(getElement(), key);
}
/**
* Set the {@link CellRenderer} used to render cell contents.
*
* @param cellRenderer the new renderer
*/
public void setCellRenderer(CellRenderer<T> cellRenderer) {
this.cellRenderer = cellRenderer;
}
/**
* Set the resize policy applied to user actions that resize columns.
*
* @param columnResizePolicy the resize policy
*/
public void setColumnResizePolicy(ColumnResizePolicy columnResizePolicy) {
scrollTable.setColumnResizePolicy(columnResizePolicy);
}
/**
* Enable or disable sorting on a specific column. All columns are sortable by
* default.
*
* @param column the index of the column
* @param sortable {@code true} to enable sorting for this column, {@code
* false} to disable
*/
public void setColumnSortable(int column, boolean sortable) {
scrollTable.setColumnSortable(column, sortable);
}
/**
* Enable or disable truncation on a specific column. When enabled, the column
* width will be adjusted to fit the content. All columns are truncatable by
* default.
*
* @param column the index of the column
* @param truncatable true to enable truncation, false to disable
*/
public void setColumnTruncatable(int column, boolean truncatable) {
scrollTable.setColumnTruncatable(column, truncatable);
}
/**
* Set the width of a column.
*
* @param column the index of the column
* @param width the width in pixels
* @return the new column width
*/
public int setColumnWidth(int column, int width) {
return scrollTable.setColumnWidth(column, width);
}
public void setContextMenu(PopupMenu contextMenu) {
dataTable.setContextMenu(contextMenu);
}
public void setFocus(boolean focused) {
if (focused) {
impl.focus(getElement());
} else {
impl.blur(getElement());
}
}
/**
* Sets whether an individual list item is selected.
* <p>
* Note that setting the selected index programmatically does <em>not</em>
* cause the {@link ChangeHandler#onChange(ChangeEvent)} event to be fired.
*
* @param index the index of the item to be selected or unselected
* @param selected {@code true} to select the item
* @throws IndexOutOfBoundsException if the index is out of range
* @see #setSelectedIndex(int)
*/
public void setItemSelected(int index, boolean selected) {
checkIndex(index);
if (selected) {
dataTable.selectRow(index, false);
} else {
dataTable.deselectRow(index);
}
}
/**
* Set the maximum width of the column.
*
* @param column the column index
* @param maxWidth the maximum width
*/
public void setMaximumColumnWidth(int column, int maxWidth) {
scrollTable.setMaximumColumnWidth(column, maxWidth);
}
/**
* Set the minimum width of the column.
*
* @param column the column index
* @param minWidth the minimum width
*/
public void setMinimumColumnWidth(int column, int minWidth) {
scrollTable.setMinimumColumnWidth(column, minWidth);
}
/**
* Sets the model that represents the contents of the {@code ListBox}, and
* then clears the list's selection.
*
* @param dataModel the {@link ListModel} that provides the list of items for
* display
* @see #getModel
*/
public void setModel(ListModel<T> dataModel) {
if (dataModel == null) {
throw new IllegalArgumentException("model must be non null");
}
if (this.dataModel == dataModel) {
return;
}
if (this.dataModel != null) {
this.dataModel.removeListDataListener(this);
dataTable.deselectAllRows();
renderOnClear();
}
this.dataModel = dataModel;
// TODO bulk update
for (int i = 0, n = dataModel.getSize(); i < n; ++i) {
renderItemOnInsert(dataModel.getElementAt(i), INSERT_AT_END);
}
this.dataModel.addListDataListener(this);
layout();
}
/**
* Sets whether this list allows multiple selections.
*
* @param multiple {@code true} to allow multiple selections
*/
public void setMultipleSelect(boolean multiple) {
dataTable.setSelectionPolicy(multiple ? SelectionPolicy.MULTI_ROW
: SelectionPolicy.ONE_ROW);
}
/**
* Set the preferred width of a column. The table will attempt maintain the
* preferred width of the column. If it cannot, the preferred widths will
* serve as relative weights when distributing available width.
*
* @param column the column index
* @param preferredWidth the preferred width
*/
public void setPreferredColumnWidth(int column, int preferredWidth) {
scrollTable.setPreferredColumnWidth(column, preferredWidth);
}
/**
* Sets the currently selected index.
* <p>
* After calling this method, only the specified item in the list will remain
* selected. For a {@code ListBox} with multiple selection enabled, see
* {@link #setItemSelected(int, boolean)} to select multiple items at a time.
* <p>
* Note that setting the selected index programmatically does <em>not</em>
* cause the {@link ChangeHandler#onChange(ChangeEvent)} event to be fired.
* fired.
*
* @param index the index of the item to be selected
* @see #setItemSelected(int, boolean)
* @see #getSelectedIndex()
*/
public void setSelectedIndex(int index) {
checkIndex(index);
dataTable.selectRow(index, true);
}
/**
* Enable or disable row selection.
*
* @param enabled {@code true} to enable, {@code false} to disable
*/
public void setSelectionEnabled(boolean enabled) {
dataTable.setSelectionEnabled(enabled);
}
/**
* Set the {@link SortPolicy} that defines what columns users can sort.
*
* @param sortPolicy the {@link SortPolicy}
*/
public void setSortPolicy(SortPolicy sortPolicy) {
scrollTable.setSortPolicy(sortPolicy);
}
public void setTabIndex(int index) {
impl.setTabIndex(getElement(), index);
}
public void setText(int row, int column, String text) {
dataTable.setText(modelToView(row), column, text);
}
public void setHTML(int row, int column, String html) {
dataTable.setHTML(modelToView(row), column, html);
}
public void setWidget(int row, int column, Widget widget) {
dataTable.setWidget(modelToView(row), column, widget);
}
protected int modelToView(int index) {
TableRowElement trElem = rowsInModelOrder.get(index).cast();
return trElem.getSectionRowIndex() - 1;
}
}