Package com.google.gwt.user.cellview.client

Source Code of com.google.gwt.user.cellview.client.CellTable$ImplTrident

/*
* Copyright 2010 Google Inc.
*
* 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 com.google.gwt.user.cellview.client;

import com.google.gwt.cell.client.Cell;
import com.google.gwt.core.client.GWT;
import com.google.gwt.core.client.Scheduler;
import com.google.gwt.dom.client.Document;
import com.google.gwt.dom.client.Element;
import com.google.gwt.dom.client.EventTarget;
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.TableElement;
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.resources.client.ClientBundle;
import com.google.gwt.resources.client.CssResource;
import com.google.gwt.resources.client.ImageResource;
import com.google.gwt.resources.client.ImageResource.ImageOptions;
import com.google.gwt.resources.client.ImageResource.RepeatStyle;
import com.google.gwt.user.cellview.client.HasDataPresenter.LoadingState;
import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.Event;
import com.google.gwt.view.client.ProvidesKey;
import com.google.gwt.view.client.SelectionModel;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

/**
* A list view that supports paging and columns.
*
* @param <T> the data type of each row
*/
public class CellTable<T> extends AbstractHasData<T> {

  /**
   * A cleaner version of the table that uses less graphics.
   */
  public static interface CleanResources extends Resources {

    @Source("CellTableClean.css")
    CleanStyle cellTableStyle();
  }

  /**
   * A cleaner version of the table that uses less graphics.
   */
  public static interface CleanStyle extends Style {
    String footer();

    String header();
  }

  /**
   * A ClientBundle that provides images for this widget.
   */
  public static interface Resources extends ClientBundle {

    /**
     * The background used for footer cells.
     */
    @Source("cellTableHeaderBackground.png")
    @ImageOptions(repeatStyle = RepeatStyle.Horizontal)
    ImageResource cellTableFooterBackground();

    /**
     * The background used for header cells.
     */
    @ImageOptions(repeatStyle = RepeatStyle.Horizontal)
    ImageResource cellTableHeaderBackground();

    /**
     * The loading indicator used while the table is waiting for data.
     */
    ImageResource cellTableLoading();

    /**
     * The background used for selected cells.
     */
    @Source("cellListSelectedBackground.png")
    @ImageOptions(repeatStyle = RepeatStyle.Horizontal)
    ImageResource cellTableSelectedBackground();

    /**
     * The styles used in this widget.
     */
    @Source("CellTable.css")
    Style cellTableStyle();
  }

  /**
   * Styles used by this widget.
   */
  public static interface Style extends CssResource {

    /**
     * Applied to every cell.
     */
    String cell();

    /**
     * Applied to the table.
     */
    String cellTable();

    /**
     * Applied to even rows.
     */
    String evenRow();

    /**
     * Applied to the first column.
     */
    String firstColumn();

    /**
     * Applied to the first column footers.
     */
    String firstColumnFooter();

    /**
     * Applied to the first column headers.
     */
    String firstColumnHeader();

    /**
     * Applied to footers cells.
     */
    String footer();

    /**
     * Applied to headers cells.
     */
    String header();

    /**
     * Applied to the hovered row.
     */
    String hoveredRow();

    /**
     * Applied to the keyboard selected cell.
     */
    String keyboardSelectedCell();

    /**
     * Applied to the keyboard selected row.
     */
    String keyboardSelectedRow();

    /**
     * Applied to the last column.
     */
    String lastColumn();

    /**
     * Applied to the last column footers.
     */
    String lastColumnFooter();

    /**
     * Applied to the last column headers.
     */
    String lastColumnHeader();

    /**
     * Applied to the loading indicator.
     */
    String loading();

    /**
     * Applied to odd rows.
     */
    String oddRow();

    /**
     * Applied to selected rows.
     */
    String selectedRow();
  }

  /**
   * Implementation of {@link CellTable}.
   */
  private static class Impl {

    private final com.google.gwt.user.client.Element tmpElem =
        Document.get().createDivElement().cast();

    /**
     * Convert the rowHtml into Elements wrapped by the specified table section.
     *
     * @param table the {@link CellTable}
     * @param sectionTag the table section tag
     * @param rowHtml the Html for the rows
     * @return the section element
     */
    protected TableSectionElement convertToSectionElement(
        CellTable<?> table, String sectionTag, String rowHtml) {
      // Attach an event listener so we can catch synchronous load events from
      // cached images.
      DOM.setEventListener(tmpElem, table);

      // Render the rows into a table.
      // IE doesn't support innerHtml on a TableSection or Table element, so we
      // generate the entire table.
      sectionTag = sectionTag.toLowerCase();
      String innerHtml = "<table><" + sectionTag + ">" + rowHtml + "</"
          + sectionTag + "></table>";
      tmpElem.setInnerHTML(innerHtml);
      TableElement tableElem = tmpElem.getFirstChildElement().cast();

      // Detach the event listener.
      DOM.setEventListener(tmpElem, null);

      // Get the section out of the table.
      if ("tbody".equals(sectionTag)) {
        return tableElem.getTBodies().getItem(0);
      } else if ("thead".equals(sectionTag)) {
        return tableElem.getTHead();
      } else if ("tfoot".equals(sectionTag)) {
        return tableElem.getTFoot();
      }
      throw new IllegalArgumentException(
          "Invalid table section tag: " + sectionTag);
    }

    /**
     * Render a table section in the table.
     *
     * @param table the {@link CellTable}
     * @param section the {@link TableSectionElement} to replace
     * @param html the html to render
     */
    protected void replaceAllRows(
        CellTable<?> table, TableSectionElement section, String html) {
      // If the widget is not attached, attach an event listener so we can catch
      // synchronous load events from cached images.
      if (!table.isAttached()) {
        DOM.setEventListener(table.getElement(), table);
      }

      // Render the html.
      section.setInnerHTML(html);

      // Detach the event listener.
      if (!table.isAttached()) {
        DOM.setEventListener(table.getElement(), null);
      }
    }
  }

  /**
   * Implementation of {@link CellTable} used by IE.
   */
  @SuppressWarnings("unused")
  private static class ImplTrident extends Impl {

    /**
     * IE doesn't support innerHTML on tbody, nor does it support removing or
     * replacing a tbody. The only solution is to remove and replace the rows
     * themselves.
     */
    @Override
    protected void replaceAllRows(
        CellTable<?> table, TableSectionElement section, String html) {
      // Remove all children.
      Element child = section.getFirstChildElement();
      while (child != null) {
        Element next = child.getNextSiblingElement();
        section.removeChild(child);
        child = next;
      }

      // Add new child elements.
      TableSectionElement newSection = convertToSectionElement(
          table, section.getTagName(), html);
      child = newSection.getFirstChildElement();
      while (child != null) {
        Element next = child.getNextSiblingElement();
        section.appendChild(child);
        child = next;
      }
    }
  }

  /**
   * The default page size.
   */
  private static final int DEFAULT_PAGESIZE = 15;

  private static Resources DEFAULT_RESOURCES;

  /**
   * The table specific {@link Impl}.
   */
  private static Impl TABLE_IMPL;

  private static Resources getDefaultResources() {
    if (DEFAULT_RESOURCES == null) {
      DEFAULT_RESOURCES = GWT.create(CleanResources.class);
    }
    return DEFAULT_RESOURCES;
  }

  private boolean cellIsEditing;
  private final TableColElement colgroup;

  private final List<Column<T, ?>> columns = new ArrayList<Column<T, ?>>();

  private boolean dependsOnSelection;

  private final List<Header<?>> footers = new ArrayList<Header<?>>();

  private boolean handlesSelection;

  private final List<Header<?>> headers = new ArrayList<Header<?>>();

  /**
   * Set to true when the footer is stale.
   */
  private boolean headersStale;

  private TableRowElement hoveringRow;

  private int keyboardSelectedColumn = 0;

  private int keyboardSelectedRow = 0;

  /**
   * Indicates whether or not the scheduled redraw has been cancelled.
   */
  private boolean redrawCancelled;

  /**
   * The command used to redraw the table after adding columns.
   */
  private final Scheduler.ScheduledCommand redrawCommand =
      new Scheduler.ScheduledCommand() {
        public void execute() {
          redrawScheduled = false;
          if (redrawCancelled) {
            redrawCancelled = false;
            return;
          }
          redraw();
        }
      };

  /**
   * Indicates whether or not a redraw is scheduled.
   */
  private boolean redrawScheduled;

  private final Style style;
  private final TableElement table;
  private final TableSectionElement tbody;
  private final TableSectionElement tbodyLoading;
  private final TableSectionElement tfoot;
  private final TableSectionElement thead;

  /**
   * Constructs a table with a default page size of 15.
   */
  public CellTable() {
    this(DEFAULT_PAGESIZE);
  }

  /**
   * Constructs a table with the given page size.
   *
   * @param pageSize the page size
   */
  public CellTable(final int pageSize) {
    this(pageSize, getDefaultResources());
  }

  /**
   * Constructs a table with the given page size with the specified
   * {@link Resources}.
   *
   * @param pageSize the page size
   * @param resources the resources to use for this widget
   */
  public CellTable(final int pageSize, Resources resources) {
    super(Document.get().createTableElement(), pageSize);
    if (TABLE_IMPL == null) {
      TABLE_IMPL = GWT.create(Impl.class);
    }
    this.style = resources.cellTableStyle();
    this.style.ensureInjected();

    table = getElement().cast();
    table.setCellSpacing(0);
    colgroup = Document.get().createColGroupElement();
    table.appendChild(colgroup);
    thead = table.createTHead();
    table.appendChild(tbody = Document.get().createTBodyElement());
    table.appendChild(tbodyLoading = Document.get().createTBodyElement());
    tfoot = table.createTFoot();
    setStyleName(this.style.cellTable());

    // Create the loading indicator.
    {
      TableCellElement td = Document.get().createTDElement();
      TableRowElement tr = Document.get().createTRElement();
      tbodyLoading.appendChild(tr);
      tr.appendChild(td);
      td.setAlign("center");
      td.setInnerHTML("<div class='" + style.loading() + "'></div>");
      setLoadingIconVisible(false);
    }

    // Sink events.
    sinkEvents(
        Event.ONCLICK | Event.ONMOUSEOVER | Event.ONMOUSEOUT | Event.ONKEYUP
            | Event.ONKEYDOWN);
  }

  /**
   * Adds a column to the table.
   */
  public void addColumn(Column<T, ?> col) {
    addColumn(col, (Header<?>) null, (Header<?>) null);
  }

  /**
   * Adds a column to the table with an associated header.
   */
  public void addColumn(Column<T, ?> col, Header<?> header) {
    addColumn(col, header, null);
  }

  /**
   * Adds a column to the table with an associated header and footer.
   */
  public void addColumn(Column<T, ?> col, Header<?> header, Header<?> footer) {
    headers.add(header);
    footers.add(footer);
    columns.add(col);
    updateDependsOnSelection();

    // Sink events used by the new column.
    Set<String> consumedEvents = new HashSet<String>();
    {
      Set<String> cellEvents = col.getCell().getConsumedEvents();
      if (cellEvents != null) {
        consumedEvents.addAll(cellEvents);
      }
    }
    if (header != null) {
      Set<String> headerEvents = header.getCell().getConsumedEvents();
      if (headerEvents != null) {
        consumedEvents.addAll(headerEvents);
      }
    }
    if (footer != null) {
      Set<String> footerEvents = footer.getCell().getConsumedEvents();
      if (footerEvents != null) {
        consumedEvents.addAll(footerEvents);
      }
    }
    CellBasedWidgetImpl.get().sinkEvents(this, consumedEvents);

    headersStale = true;
    scheduleRedraw();
  }

  /**
   * Adds a column to the table with an associated String header.
   */
  public void addColumn(Column<T, ?> col, String headerString) {
    addColumn(col, new TextHeader(headerString), null);
  }

  /**
   * Adds a column to the table with an associated String header and footer.
   */
  public void addColumn(
      Column<T, ?> col, String headerString, String footerString) {
    addColumn(col, new TextHeader(headerString), new TextHeader(footerString));
  }

  /**
   * Add a style name to the {@link TableColElement} at the specified index,
   * creating it if necessary.
   *
   * @param index the column index
   * @param styleName the style name to add
   */
  public void addColumnStyleName(int index, String styleName) {
    ensureTableColElement(index).addClassName(styleName);
  }

  public int getBodyHeight() {
    int height = getClientHeight(tbody);
    return height;
  }

  public int getHeaderHeight() {
    int height = getClientHeight(thead);
    return height;
  }

  /**
   * Get the {@link TableRowElement} for the specified row. If the row element
   * has not been created, null is returned.
   *
   * @param row the row index
   * @return the row element, or null if it doesn't exists
   * @throws IndexOutOfBoundsException if the row index is outside of the
   *           current page
   */
  public TableRowElement getRowElement(int row) {
    checkRowBounds(row);
    NodeList<TableRowElement> rows = tbody.getRows();
    return rows.getLength() > row ? rows.getItem(row) : null;
  }

  @Override
  public void onBrowserEvent(Event event) {
    CellBasedWidgetImpl.get().onBrowserEvent(this, event);
    super.onBrowserEvent(event);

    String eventType = event.getType();
    boolean keyUp = "keyup".equals(eventType);
    boolean keyDown = "keydown".equals(eventType);

    // Ignore keydown events unless the cell is in edit mode
    if (keyDown && !cellIsEditing) {
      return;
    }
    if (keyUp && !cellIsEditing) {
      if (handleKey(event)) {
        return;
      }
    }

    // Find the cell where the event occurred.
    EventTarget eventTarget = event.getEventTarget();
    TableCellElement tableCell = null;
    if (eventTarget != null && Element.is(eventTarget)) {
      tableCell = findNearestParentCell(Element.as(eventTarget));
    }
    if (tableCell == null) {
      return;
    }

    // Determine if we are in the header, footer, or body. Its possible that
    // the table has been refreshed before the current event fired (ex. change
    // event refreshes before mouseup fires), so we need to check each parent
    // element.
    Element trElem = tableCell.getParentElement();
    if (trElem == null) {
      return;
    }
    TableRowElement tr = TableRowElement.as(trElem);
    Element sectionElem = tr.getParentElement();
    if (sectionElem == null) {
      return;
    }
    TableSectionElement section = TableSectionElement.as(sectionElem);

    // Forward the event to the associated header, footer, or column.
    int col = tableCell.getCellIndex();
    if (section == thead) {
      Header<?> header = headers.get(col);
      if (header != null
          && cellConsumesEventType(header.getCell(), eventType)) {
        header.onBrowserEvent(tableCell, event);
      }
    } else if (section == tfoot) {
      Header<?> footer = footers.get(col);
      if (footer != null
          && cellConsumesEventType(footer.getCell(), eventType)) {
        footer.onBrowserEvent(tableCell, event);
      }
    } else if (section == tbody) {
      // Update the hover state.
      int row = tr.getSectionRowIndex();
      if ("mouseover".equals(eventType)) {
        if (hoveringRow != null) {
          hoveringRow.removeClassName(style.hoveredRow());
        }
        hoveringRow = tr;
        tr.addClassName(style.hoveredRow());
      } else if ("mouseout".equals(eventType)) {
        hoveringRow = null;
        tr.removeClassName(style.hoveredRow());
      }

      // Update selection. Selection occurs before firing the event to the cell
      // in case the cell operates on the currently selected item.
      T value = getDisplayedItem(row);
      SelectionModel<? super T> selectionModel = getSelectionModel();
      if (selectionModel != null && "click".equals(eventType)
          && !handlesSelection) {
        selectionModel.setSelected(value, true);
      }

      fireEventToCell(event, eventType, tableCell, value, col, row);
    }
  }

  public void redrawFooters() {
    createHeaders(true);
  }

  public void redrawHeaders() {
    createHeaders(false);
  }

  /**
   * Remove a column.
   *
   * @param col the column to remove
   */
  public void removeColumn(Column<T, ?> col) {
    int index = columns.indexOf(col);
    if (index < 0) {
      throw new IllegalArgumentException(
          "The specified column is not part of this table.");
    }
    removeColumn(index);
  }

  /**
   * Remove a column.
   *
   * @param index the column index
   */
  public void removeColumn(int index) {
    if (index < 0 || index >= columns.size()) {
      throw new IndexOutOfBoundsException(
          "The specified column index is out of bounds.");
    }
    columns.remove(index);
    headers.remove(index);
    footers.remove(index);
    updateDependsOnSelection();
    headersStale = true;
    scheduleRedraw();

    // We don't unsink events because other handlers or user code may have sunk
    // them intentionally.
  }

  /**
   * Remove a style from the {@link TableColElement} at the specified index.
   *
   * @param index the column index
   * @param styleName the style name to remove
   */
  public void removeColumnStyleName(int index, String styleName) {
    if (index >= colgroup.getChildCount()) {
      return;
    }
    ensureTableColElement(index).removeClassName(styleName);
  }

  @Override
  protected void renderRowValues(StringBuilder sb, List<T> values, int start,
      SelectionModel<? super T> selectionModel) {
    createHeadersAndFooters();

    ProvidesKey<T> keyProvider = getKeyProvider();
    String evenRowStyle = style.evenRow();
    String oddRowStyle = style.oddRow();
    String cellStyle = style.cell();
    String firstColumnStyle = " " + style.firstColumn();
    String lastColumnStyle = " " + style.lastColumn();
    String selectedRowStyle = " " + style.selectedRow();
    int columnCount = columns.size();
    int length = values.size();
    int end = start + length;
    for (int i = start; i < end; i++) {
      T value = values.get(i - start);
      boolean isSelected = (selectionModel == null || value == null)
          ? false : selectionModel.isSelected(value);
      sb.append("<tr onclick='' class='");
      sb.append(i % 2 == 0 ? evenRowStyle : oddRowStyle);
      if (isSelected) {
        sb.append(selectedRowStyle);
      }
      sb.append("'>");
      int curColumn = 0;
      for (Column<T, ?> column : columns) {
        sb.append("<td class='").append(cellStyle);
        if (curColumn == 0) {
          sb.append(firstColumnStyle);
        }
        // The first and last column could be the same column.
        if (curColumn == columnCount - 1) {
          sb.append(lastColumnStyle);
        }
        sb.append("'><div>");
        if (value != null) {
          column.render(value, keyProvider, sb);
        }
        sb.append("</div></td>");
        curColumn++;
      }
      sb.append("</tr>");
    }
  }

  @Override
  Element convertToElements(String html) {
    return TABLE_IMPL.convertToSectionElement(CellTable.this, "tbody", html);
  }

  @Override
  boolean dependsOnSelection() {
    return dependsOnSelection;
  }

  @Override
  Element getChildContainer() {
    return tbody;
  }

  @Override
  void onUpdateSelection() {
    // Refresh headers.
    for (Header<?> header : headers) {
      if (header != null && header.getCell().dependsOnSelection()) {
        createHeaders(false);
        break;
      }
    }

    // Refresh footers.
    for (Header<?> footer : footers) {
      if (footer != null && footer.getCell().dependsOnSelection()) {
        createHeaders(true);
        break;
      }
    }
  }

  @Override
  void replaceAllChildren(List<T> values, String html) {
    // Cancel any pending redraw.
    if (redrawScheduled) {
      redrawCancelled = true;
    }
    TABLE_IMPL.replaceAllRows(
        CellTable.this, tbody, CellBasedWidgetImpl.get().processHtml(html));
  }

  @Override
  void resetFocus() {
    int pageStart = getPageStart();
    int offset = keyboardSelectedRow - pageStart;
    // Check that both the row and column still exist. The column may not exist
    // if a column is removed.
    if (offset >= 0 && offset < getChildCount()
        && keyboardSelectedColumn < columns.size()) {
      TableRowElement tr = getRowElement(offset);
      TableCellElement td = tr.getCells().getItem(keyboardSelectedColumn);
      tr.addClassName(style.keyboardSelectedRow());
      td.addClassName(style.keyboardSelectedCell());
      td.setTabIndex(0);
      td.focus(); // TODO (rice) only focus if we were focused previously
    }
  }

  @Override
  void setLoadingState(LoadingState state) {
    setLoadingIconVisible(state == LoadingState.LOADING);
  }

  @Override
  void setSelected(Element elem, boolean selected) {
    setStyleName(elem, style.selectedRow(), selected);
  }

  /**
   * Check if a cell consumes the specified event type.
   *
   * @param cell the cell
   * @param eventType the event type to check
   * @return true if consumed, false if not
   */
  private boolean cellConsumesEventType(Cell<?> cell, String eventType) {
    Set<String> consumedEvents = cell.getConsumedEvents();
    return consumedEvents != null && consumedEvents.contains(eventType);
  }

  /**
   * Render the header or footer.
   *
   * @param isFooter true if this is the footer table, false if the header table
   */
  private void createHeaders(boolean isFooter) {
    List<Header<?>> theHeaders = isFooter ? footers : headers;
    TableSectionElement section = isFooter ? tfoot : thead;
    String className = isFooter ? style.footer() : style.header();

    boolean hasHeader = false;
    StringBuilder sb = new StringBuilder();
    sb.append("<tr>");
    int columnCount = columns.size();
    int curColumn = 0;
    for (Header<?> header : theHeaders) {
      sb.append("<th class='").append(className);
      if (curColumn == 0) {
        sb.append(" ");
        sb.append(
            isFooter ? style.firstColumnFooter() : style.firstColumnHeader());
      }
      // The first and last columns could be the same column.
      if (curColumn == columnCount - 1) {
        sb.append(" ");
        sb.append(
            isFooter ? style.lastColumnFooter() : style.lastColumnHeader());
      }
      sb.append("'>");
      if (header != null) {
        hasHeader = true;
        header.render(sb);
      }
      sb.append("</th>");
      curColumn++;
    }
    sb.append("</tr>");

    // Render the section contents.
    TABLE_IMPL.replaceAllRows(this, section, sb.toString());

    // If the section isn't used, hide it.
    setVisible(section, hasHeader);
  }

  private void createHeadersAndFooters() {
    if (headersStale) {
      headersStale = false;
      createHeaders(false);
      createHeaders(true);
    }
  }

  /**
   * Get the {@link TableColElement} at the specified index, creating it if
   * necessary.
   *
   * @param index the column index
   * @return the {@link TableColElement}
   */
  private TableColElement ensureTableColElement(int index) {
    // Ensure that we have enough columns.
    for (int i = colgroup.getChildCount(); i <= index; i++) {
      colgroup.appendChild(Document.get().createColElement());
    }
    return colgroup.getChild(index).cast();
  }

  /**
   * Find the cell that contains the element. Note that the TD element is not
   * the parent. The parent is the div inside the TD cell.
   *
   * @param elem the element
   * @return the parent cell
   */
  private TableCellElement findNearestParentCell(Element elem) {
    while ((elem != null) && (elem != table)) {
      // TODO: We need is() implementations in all Element subclasses.
      // This would allow us to use TableCellElement.is() -- much cleaner.
      String tagName = elem.getTagName();
      if ("td".equalsIgnoreCase(tagName) || "th".equalsIgnoreCase(tagName)) {
        return elem.cast();
      }
      elem = elem.getParentElement();
    }
    return null;
  }

  /**
   * Fire an event to the Cell within the specified {@link TableCellElement}.
   */
  @SuppressWarnings("unchecked")
  private <C> void fireEventToCell(Event event, String eventType,
      TableCellElement tableCell, T value, int col, int row) {
    Column<T, C> column = (Column<T, C>) columns.get(col);
    Cell<C> cell = column.getCell();
    if (cellConsumesEventType(cell, eventType)) {
      C cellValue = column.getValue(value);
      ProvidesKey<T> providesKey = getKeyProvider();
      Object key = providesKey == null ? value : providesKey.getKey(value);
      Element parentElem = tableCell.getFirstChildElement();
      boolean cellWasEditing = cell.isEditing(parentElem, cellValue, key);
      column.onBrowserEvent(
          parentElem, getPageStart() + row, value, event, providesKey);
      cellIsEditing = cell.isEditing(parentElem, cellValue, key);
      if (cellWasEditing && !cellIsEditing) {
        resetFocus();
      }
    }
  }

  private native int getClientHeight(Element element) /*-{
    return element.clientHeight;
  }-*/;

  private boolean handleKey(Event event) {
    int keyCode = event.getKeyCode();
    int oldColumn = keyboardSelectedColumn;
    int oldRow = keyboardSelectedRow;
    int numColumns = columns.size();
    int numRows = getRowCount();
    switch (keyCode) {
      case KeyCodes.KEY_UP:
        if (keyboardSelectedRow > 0) {
          --keyboardSelectedRow;
        }
        break;
      case KeyCodes.KEY_DOWN:
        if (keyboardSelectedRow < numRows - 1) {
          ++keyboardSelectedRow;
        }
        break;
      case KeyCodes.KEY_LEFT:
        if (keyboardSelectedColumn == 0 && keyboardSelectedRow > 0) {
          keyboardSelectedColumn = numColumns - 1;
          --keyboardSelectedRow;
        } else if (keyboardSelectedColumn > 0) {
          --keyboardSelectedColumn;
        }
        break;
      case KeyCodes.KEY_RIGHT:
        if (keyboardSelectedColumn == numColumns - 1
            && keyboardSelectedRow < numRows - 1) {
          keyboardSelectedColumn = 0;
          ++keyboardSelectedRow;
        } else if (keyboardSelectedColumn < numColumns - 1) {
          ++keyboardSelectedColumn;
        }
        break;
    }

    if (keyboardSelectedColumn != oldColumn || keyboardSelectedRow != oldRow) {
      int pageStart = getPageStart();
      int pageSize = getPageSize();

      // Remove old selection markers
      TableRowElement row = getRowElement(oldRow - pageStart);
      row.removeClassName(style.keyboardSelectedRow());
      TableCellElement td = row.getCells().getItem(oldColumn);
      td.removeClassName(style.keyboardSelectedCell());
      td.removeAttribute("tabIndex");

      // Move page start if needed
      if (keyboardSelectedRow >= pageStart + pageSize) {
        setPageStart(keyboardSelectedRow - pageSize + 1);
      } else if (keyboardSelectedRow < pageStart) {
        setPageStart(keyboardSelectedRow);
      }

      // Add new selection markers and re-establish focus
      resetFocus();
      return true;
    }

    return false;
  }

  /**
   * Schedule a redraw for the end of the event loop.
   */
  private void scheduleRedraw() {
    redrawCancelled = false;
    if (!redrawScheduled) {
      redrawScheduled = true;
      Scheduler.get().scheduleFinally(redrawCommand);
    }
  }

  /**
   * Show or hide the loading icon.
   *
   * @param visible true to show, false to hide.
   */
  private void setLoadingIconVisible(boolean visible) {
    // Clear the current data.
    if (visible) {
      tbody.setInnerText("");
    }

    // Update the colspan.
    TableCellElement td = tbodyLoading.getRows().getItem(0).getCells().getItem(
        0);
    td.setColSpan(Math.max(1, columns.size()));
    setVisible(tbodyLoading, visible);
  }

  /**
   * Update the dependsOnSelection and handlesSelection booleans.
   */
  private void updateDependsOnSelection() {
    dependsOnSelection = false;
    handlesSelection = false;
    for (Column<T, ?> column : columns) {
      Cell<?> cell = column.getCell();
      if (cell.dependsOnSelection()) {
        dependsOnSelection = true;
      }
      if (cell.handlesSelection()) {
        handlesSelection = true;
      }
    }
  }
}
TOP

Related Classes of com.google.gwt.user.cellview.client.CellTable$ImplTrident

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.