Package com.google.gwt.gen2.table.client

Source Code of com.google.gwt.gen2.table.client.SelectionGrid$SelectionGridCellFormatter

/*
* Copyright 2008 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.gen2.table.client;

import com.google.gwt.dom.client.InputElement;
import com.google.gwt.gen2.event.shared.HandlerRegistration;
import com.google.gwt.gen2.table.event.client.CellHighlightEvent;
import com.google.gwt.gen2.table.event.client.CellHighlightHandler;
import com.google.gwt.gen2.table.event.client.CellUnhighlightEvent;
import com.google.gwt.gen2.table.event.client.CellUnhighlightHandler;
import com.google.gwt.gen2.table.event.client.HasCellHighlightHandlers;
import com.google.gwt.gen2.table.event.client.HasCellUnhighlightHandlers;
import com.google.gwt.gen2.table.event.client.HasRowHighlightHandlers;
import com.google.gwt.gen2.table.event.client.HasRowSelectionHandlers;
import com.google.gwt.gen2.table.event.client.HasRowUnhighlightHandlers;
import com.google.gwt.gen2.table.event.client.RowHighlightEvent;
import com.google.gwt.gen2.table.event.client.RowHighlightHandler;
import com.google.gwt.gen2.table.event.client.RowSelectionEvent;
import com.google.gwt.gen2.table.event.client.RowSelectionHandler;
import com.google.gwt.gen2.table.event.client.RowUnhighlightEvent;
import com.google.gwt.gen2.table.event.client.RowUnhighlightHandler;
import com.google.gwt.gen2.table.event.client.TableEvent.Row;
import com.google.gwt.gen2.table.override.client.Grid;
import com.google.gwt.gen2.table.override.client.OverrideDOM;
import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.Element;
import com.google.gwt.user.client.Event;
import com.google.gwt.user.client.Window;

import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;

/**
* A variation of the {@link Grid} that supports row or cell highlight and row
* selection.
*
* <h3>CSS Style Rules</h3>
*
* <ul class="css">
*
* <li>tr.selected { applied to selected rows }</li>
*
* <li>tr.highlighted { applied to row currently being highlighted }</li>
*
* <li>td.highlighted { applied to cell currently being highlighted }</li>
*
* </ul>
*/
public class SelectionGrid extends Grid implements HasRowHighlightHandlers,
    HasRowUnhighlightHandlers, HasCellHighlightHandlers,
    HasCellUnhighlightHandlers, HasRowSelectionHandlers {
  /**
   * This class contains methods used to format a table's cells.
   */
  public class SelectionGridCellFormatter extends CellFormatter {
    @Override
    protected Element getRawElement(int row, int column) {
      if (selectionPolicy.hasInputColumn()) {
        column += 1;
      }
      return super.getRawElement(row, column);
    }
  }

  /**
   * This class contains methods used to format a table's rows.
   */
  public class SelectionGridRowFormatter extends RowFormatter {
    @Override
    protected Element getRawElement(int row) {
      return super.getRawElement(row);
    }
  }

  /**
   * Selection policies.
   *
   * <ul>
   * <li>ONE_ROW - one row can be selected at a time</li>
   * <li>MULTI_ROW - multiple rows can be selected at a time</li>
   * <li>CHECKBOX - multiple rows can be selected using checkboxes</li>
   * <li>RADIO - one row can be selected using radio buttons</li>
   * </ul>
   */
  public static enum SelectionPolicy {
    ONE_ROW(null), MULTI_ROW(null), CHECKBOX("<input type='checkbox'/>"), RADIO(
        "<input name='%NAME%' type='radio'/>");

    private String inputHtml;

    private SelectionPolicy(String inputHtml) {
      this.inputHtml = inputHtml;
    }

    /**
     * @return true if the policy requires a selection column
     */
    public boolean hasInputColumn() {
      return inputHtml != null;
    }

    /**
     * @return the HTML string used for this policy
     */
    private String getInputHtml() {
      return inputHtml;
    }
  }

  /**
   * Unique IDs assigned to the grids.
   */
  private static int uniqueID = 0;

  /**
   * The cell element currently being highlight.
   */
  private Element highlightedCellElem = null;

  /**
   * The index of the cell currently being highlight.
   */
  private int highlightedCellIndex = -1;

  /**
   * The row element currently being highlight.
   */
  private Element highlightedRowElem = null;

  /**
   * The index of the row currently being highlight.
   */
  private int highlightedRowIndex = -1;

  /**
   * Unique ID of this grid.
   */
  private int id;

  /**
   * The index of the row that the user selected last.
   */
  private int lastSelectedRowIndex = -1;

  /**
   * The rows that are currently selected.
   */
  private Map<Integer, Element> selectedRows = new HashMap<Integer, Element>();

  /**
   * A boolean indicating if selection is enabled disabled.
   */
  private boolean selectionEnabled = true;

  /**
   * The selection policy determines if the user can select zero, one, or many
   * rows.
   */
  private SelectionPolicy selectionPolicy = SelectionPolicy.MULTI_ROW;

  /**
   * Construct a new {@link SelectionGrid}.
   */
  public SelectionGrid() {
    super();
    id = uniqueID++;
    setCellFormatter(new SelectionGridCellFormatter());
    setRowFormatter(new SelectionGridRowFormatter());

    // Sink highlight and selection events
    sinkEvents(Event.ONMOUSEOVER | Event.ONMOUSEOUT | Event.ONMOUSEDOWN
        | Event.ONCLICK);
  }

  /**
   * Constructs a {@link SelectionGrid} with the requested size.
   *
   * @param rows the number of rows
   * @param columns the number of columns
   * @throws IndexOutOfBoundsException
   */
  public SelectionGrid(int rows, int columns) {
    this();
    resize(rows, columns);
  }

  public HandlerRegistration addCellHighlightHandler(
      CellHighlightHandler handler) {
    return addHandler(CellHighlightEvent.TYPE, handler);
  }

  public HandlerRegistration addCellUnhighlightHandler(
      CellUnhighlightHandler handler) {
    return addHandler(CellUnhighlightEvent.TYPE, handler);
  }

  public HandlerRegistration addRowHighlightHandler(RowHighlightHandler handler) {
    return addHandler(RowHighlightEvent.TYPE, handler);
  }

  public HandlerRegistration addRowSelectionHandler(RowSelectionHandler handler) {
    return addHandler(RowSelectionEvent.TYPE, handler);
  }

  public HandlerRegistration addRowUnhighlightHandler(
      RowUnhighlightHandler handler) {
    return addHandler(RowUnhighlightEvent.TYPE, handler);
  }

  /**
   * Deselect all selected rows in the data table.
   */
  public void deselectAllRows() {
    deselectAllRows(true);
  }

  /**
   * Deselect a row in the grid. This method is safe to call even if the row is
   * not selected, or doesn't exist (out of bounds).
   *
   * @param row the row index
   */
  public void deselectRow(int row) {
    deselectRow(row, true);
  }

  /**
   * @return the set of selected row indexes
   */
  public Set<Integer> getSelectedRows() {
    return selectedRows.keySet();
  }

  /**
   * Explicitly gets the {@link SelectionGridCellFormatter}. The results of
   * {@link com.google.gwt.gen2.table.override.client.HTMLTable#getCellFormatter()}
   * may also be downcast to a {@link SelectionGridCellFormatter}.
   *
   * @return the FlexTable's cell formatter
   */
  public SelectionGridCellFormatter getSelectionGridCellFormatter() {
    return (SelectionGridCellFormatter) getCellFormatter();
  }

  /**
   * Explicitly gets the {@link SelectionGridRowFormatter}. The results of
   * {@link com.google.gwt.gen2.table.override.client.HTMLTable#getRowFormatter()}
   * may also be downcast to a {@link SelectionGridRowFormatter}.
   *
   * @return the FlexTable's cell formatter
   */
  public SelectionGridRowFormatter getSelectionGridRowFormatter() {
    return (SelectionGridRowFormatter) getRowFormatter();
  }

  /**
   * @return the selection policy
   */
  public SelectionPolicy getSelectionPolicy() {
    return selectionPolicy;
  }

  @Override
  public int insertRow(int beforeRow) {
    deselectAllRows();
    return super.insertRow(beforeRow);
  }

  /**
   * @param row the row index
   * @return true if the row is selected, false if not
   */
  public boolean isRowSelected(int row) {
    return selectedRows.containsKey(new Integer(row));
  }

  /**
   * @return true if selection is enabled
   */
  public boolean isSelectionEnabled() {
    return selectionEnabled;
  }

  @Override
  public void onBrowserEvent(Event event) {
    super.onBrowserEvent(event);
    switch (DOM.eventGetType(event)) {
      // Highlight the cell on mouse over
      case Event.ONMOUSEOVER:
        Element cellElem = getEventTargetCell(event);
        if (cellElem != null) {
          highlightCell(cellElem);
        }
        break;

      // Unhighlight on mouse out
      case Event.ONMOUSEOUT:
        Element toElem = DOM.eventGetToElement(event);
        if (highlightedRowElem != null
            && (toElem == null || !highlightedRowElem.isOrHasChild(toElem))) {
          // Check that the coordinates are not directly over the cell
          int clientX = event.getClientX() + Window.getScrollLeft();
          int clientY = event.getClientY() + Window.getScrollTop();
          int rowLeft = highlightedRowElem.getAbsoluteLeft();
          int rowTop = highlightedRowElem.getAbsoluteTop();
          int rowWidth = highlightedRowElem.getOffsetWidth();
          int rowHeight = highlightedRowElem.getOffsetHeight();
          int rowBottom = rowTop + rowHeight;
          int rowRight = rowLeft + rowWidth;
          if (clientX > rowLeft && clientX < rowRight && clientY > rowTop
              && clientY < rowBottom) {
            return;
          }

          // Unhighlight the current cell
          highlightCell(null);
        }
        break;

      // Prevent native inputs from being checked
      case Event.ONCLICK:
        onMouseClick(event);
        break;
    }
  }

  protected void onMouseClick(Event event) {
    Element targetRow = null;
    Element targetCell = null;

    // Ignore if selection is disabled
    if (!selectionEnabled) {
      return;
    }

    // Get the target row
    targetCell = getEventTargetCell(event);
    if (targetCell == null) {
      return;
    }
    targetRow = DOM.getParent(targetCell);
    int targetRowIndex = getRowIndex(targetRow);

    // Select the row
    if (selectionPolicy == SelectionPolicy.MULTI_ROW) {
      boolean shiftKey = DOM.eventGetShiftKey(event);
      boolean ctrlKey = DOM.eventGetCtrlKey(event)
          || DOM.eventGetMetaKey(event);

      // Prevent default text selection
      if (ctrlKey || shiftKey) {
        event.preventDefault();
      }

      // Select the rows
      selectRow(targetRowIndex, ctrlKey, shiftKey);
    } else if (selectionPolicy == SelectionPolicy.ONE_ROW
        || (selectionPolicy == SelectionPolicy.RADIO && targetCell == targetRow.getFirstChild())) {
      selectRow(-1, targetRow, true, true);
      lastSelectedRowIndex = targetRowIndex;
    } else if (selectionPolicy == SelectionPolicy.CHECKBOX
        && targetCell == targetRow.getFirstChild()) {
      selectRow(targetRowIndex, true, DOM.eventGetShiftKey(event));
    }
  }

  @Override
  public void removeRow(int row) {
    deselectAllRows();
    super.removeRow(row);
  }

  /**
   * Select all rows in the table.
   */
  public void selectAllRows() {
    // Get the currently selected rows
    Set<Row> oldRowSet = getSelectedRowsSet();

    // Select all rows
    RowFormatter rowFormatter = getRowFormatter();
    int rowCount = getRowCount();
    for (int i = 0; i < rowCount; i++) {
      if (!selectedRows.containsKey(i)) {
        selectRow(i, rowFormatter.getElement(i), false, false);
      }
    }

    // Trigger the event
    fireRowSelectionEvent(oldRowSet);
  }

  /**
   * Select a row in the data table.
   *
   * @param row the row index
   * @param unselectAll unselect all other rows first
   * @throws IndexOutOfBoundsException
   */
  public void selectRow(int row, boolean unselectAll) {
    selectRow(row, getRowFormatter().getElement(row), unselectAll, true);
  }

  /**
   * Select a row in the data table. Simulate the effect of a shift click and/or
   * control click. This method ignores the selection policy, which only applies
   * to user selection via mouse events.
   *
   * @param row the row index
   * @param ctrlKey true to simulate a control click
   * @param shiftKey true to simulate a shift selection
   * @throws IndexOutOfBoundsException
   */
  public void selectRow(int row, boolean ctrlKey, boolean shiftKey) {
    // Check the row bounds
    checkRowBounds(row);

    // Get the old list of selected rows
    Set<Row> oldRowList = getSelectedRowsSet();

    // Deselect all rows
    if (!ctrlKey) {
      deselectAllRows(false);
    }

    boolean isSelected = selectedRows.containsKey(new Integer(row));
    if (shiftKey && (lastSelectedRowIndex > -1)) {
      // Shift+select rows
      SelectionGridRowFormatter formatter = getSelectionGridRowFormatter();
      int firstRow = Math.min(row, lastSelectedRowIndex);
      int lastRow = Math.max(row, lastSelectedRowIndex);
      lastRow = Math.min(lastRow, getRowCount() - 1);
      for (int curRow = firstRow; curRow <= lastRow; curRow++) {
        if (isSelected) {
          deselectRow(curRow, false);
        } else {
          selectRow(curRow, formatter.getRawElement(curRow), false, false);
        }
      }

      // Fire Event
      lastSelectedRowIndex = row;
      fireRowSelectionEvent(oldRowList);
    } else if (isSelected) {
      // Ctrl+unselect a selected row
      deselectRow(row, false);
      lastSelectedRowIndex = row;
      fireRowSelectionEvent(oldRowList);
    } else {
      // Select the row
      SelectionGridRowFormatter formatter = getSelectionGridRowFormatter();
      selectRow(row, formatter.getRawElement(row), false, false);
      lastSelectedRowIndex = row;
      fireRowSelectionEvent(oldRowList);
    }
  }

  /**
   * Enable or disable row selection.
   *
   * @param enabled true to enable, false to disable
   */
  public void setSelectionEnabled(boolean enabled) {
    selectionEnabled = enabled;

    // Update the input elements
    if (selectionPolicy.hasInputColumn()) {
      SelectionGridCellFormatter formatter = getSelectionGridCellFormatter();
      int rowCount = getRowCount();
      for (int i = 0; i < rowCount; i++) {
        Element td = formatter.getRawElement(i, -1);
        setInputEnabled(selectionPolicy, td, enabled);
      }
    }
  }

  /**
   * Set the selection policy, which determines if the user can select zero,
   * one, or multiple rows.
   *
   * @param selectionPolicy the selection policy
   */
  public void setSelectionPolicy(SelectionPolicy selectionPolicy) {
    if (this.selectionPolicy == selectionPolicy) {
      return;
    }
    deselectAllRows();

    // Update the input column
    if (selectionPolicy.hasInputColumn()) {
      if (this.selectionPolicy.hasInputColumn()) {
        // Update the existing input column
        String inputHtml = getInputHtml(selectionPolicy);
        for (int i = 0; i < numRows; i++) {
          Element tr = getRowFormatter().getElement(i);
          tr.getFirstChildElement().setInnerHTML(inputHtml);
        }
      } else {
        // Add an input column to every row
        String inputHtml = getInputHtml(selectionPolicy);
        Element td = createCell();
        td.setInnerHTML(inputHtml);
        for (int i = 0; i < numRows; i++) {
          Element tr = getRowFormatter().getElement(i);
          tr.insertBefore(td.cloneNode(true), tr.getFirstChildElement());
        }
      }
    } else if (this.selectionPolicy.hasInputColumn()) {
      // Remove the input column from every row
      for (int i = 0; i < numRows; i++) {
        Element tr = getRowFormatter().getElement(i);
        tr.removeChild(tr.getFirstChildElement());
      }
    }
    this.selectionPolicy = selectionPolicy;

    // Update the enabled state
    setSelectionEnabled(selectionEnabled);
  }

  @Override
  protected Element createRow() {
    Element tr = super.createRow();
    if (selectionPolicy.hasInputColumn()) {
      Element td = createCell();
      td.setPropertyString("align", "center");
      td.setInnerHTML(getInputHtml(selectionPolicy));
      DOM.insertChild(tr, td, 0);
      if (!selectionEnabled) {
        setInputEnabled(selectionPolicy, td, false);
      }
    }
    return tr;
  }

  /**
   * Deselect all selected rows in the data table.
   *
   * @param fireEvent true to fire events
   */
  protected void deselectAllRows(boolean fireEvent) {
    // Get the old list of selected rows
    Set<Row> oldRows = null;
    if (fireEvent) {
      oldRows = getSelectedRowsSet();
    }

    // Deselect all rows
    boolean hasInputColumn = selectionPolicy.hasInputColumn();
    for (Element rowElem : selectedRows.values()) {
      setStyleName(rowElem, "selected", false);
      if (hasInputColumn) {
        setInputSelected(getSelectionPolicy(),
            (Element) rowElem.getFirstChildElement(), false);
      }
    }

    // Clear out the rows
    selectedRows.clear();

    // Fire event
    if (fireEvent) {
      fireRowSelectionEvent(oldRows);
    }
  }

  /**
   * Deselect a row in the grid. This method is safe to call even if the row is
   * not selected, or doesn't exist (out of bounds).
   *
   * @param row the row index
   * @param fireEvent true to fire events
   */
  protected void deselectRow(int row, boolean fireEvent) {
    Element rowElem = selectedRows.remove(new Integer(row));
    if (rowElem != null) {
      // Get the old list of selected rows
      Set<Row> oldRows = null;
      if (fireEvent) {
        oldRows = getSelectedRowsSet();
      }

      // Deselect the row
      setStyleName(rowElem, "selected", false);
      if (selectionPolicy.hasInputColumn()) {
        setInputSelected(getSelectionPolicy(),
            (Element) rowElem.getFirstChildElement(), false);
      }

      // Fire Event
      if (fireEvent) {
        fireRowSelectionEvent(oldRows);
      }
    }
  }

  /**
   * Fire a {@link RowSelectionEvent}. This method will automatically add the
   * currently selected rows.
   *
   * @param oldRowSet the set of previously selected rows
   */
  protected void fireRowSelectionEvent(Set<Row> oldRowSet) {
    Set<Row> newRowList = getSelectedRowsSet();
    if (newRowList.equals(oldRowSet)) {
      return;
    }
    fireEvent(new RowSelectionEvent(oldRowSet, newRowList));
  }

  @Override
  protected int getCellIndex(Element rowElem, Element cellElem) {
    int index = super.getCellIndex(rowElem, cellElem);
    if (selectionPolicy.hasInputColumn()) {
      index--;
    }
    return index;
  }

  @Override
  protected int getDOMCellCount(int row) {
    int count = super.getDOMCellCount(row);
    if (getSelectionPolicy().hasInputColumn()) {
      count--;
    }
    return count;
  }

  /**
   * Get the html used to create the native input selection element.
   *
   * @param selectionPolicy the associated {@link SelectionPolicy}
   * @return the html representation of the input element
   */
  protected String getInputHtml(SelectionPolicy selectionPolicy) {
    String inputHtml = selectionPolicy.getInputHtml();
    if (inputHtml != null) {
      inputHtml = inputHtml.replace("%NAME%", "__gwtSelectionGrid" + id);
    }
    return inputHtml;
  }

  /**
   * @return a map or selected row indexes to their elements
   */
  protected Map<Integer, Element> getSelectedRowsMap() {
    return selectedRows;
  }

  /**
   * @return a list of selected rows to pass into a {@link RowSelectionEvent}
   */
  protected Set<Row> getSelectedRowsSet() {
    Set<Row> rowSet = new TreeSet<Row>();
    for (Integer rowIndex : selectedRows.keySet()) {
      rowSet.add(new Row(rowIndex.intValue()));
    }
    return rowSet;
  }

  /**
   * Set the current highlighted cell.
   *
   * @param cellElem the cell element
   */
  protected void highlightCell(Element cellElem) {
    // Ignore if the cell is already being highlighted
    if (cellElem == highlightedCellElem) {
      return;
    }

    // Get the row element
    Element rowElem = null;
    if (cellElem != null) {
      rowElem = DOM.getParent(cellElem);
    }

    // Unhighlight the current cell
    if (highlightedCellElem != null) {
      setStyleName(highlightedCellElem, "highlighted", false);
      fireEvent(new CellUnhighlightEvent(highlightedRowIndex,
          highlightedCellIndex));
      highlightedCellElem = null;
      highlightedCellIndex = -1;

      // Unhighlight the current row if it changed
      if (rowElem != highlightedRowElem) {
        setStyleName(highlightedRowElem, "highlighted", false);
        fireEvent(new RowUnhighlightEvent(highlightedRowIndex));
        highlightedRowElem = null;
        highlightedRowIndex = -1;
      }
    }

    // Highlight the cell
    if (cellElem != null) {
      setStyleName(cellElem, "highlighted", true);
      highlightedCellElem = cellElem;
      highlightedCellIndex = OverrideDOM.getCellIndex(cellElem);

      // Highlight the row if it changed
      if (highlightedRowElem == null) {
        setStyleName(rowElem, "highlighted", true);
        highlightedRowElem = rowElem;
        highlightedRowIndex = getRowIndex(highlightedRowElem);
        fireEvent(new RowHighlightEvent(highlightedRowIndex));
      }

      // Fire listeners
      fireEvent(new CellHighlightEvent(highlightedRowIndex,
          highlightedCellIndex));
    }
  }

  /**
   * Select a row in the data table.
   *
   * @param row the row index, or -1 if unknown
   * @param rowElem the row element
   * @param unselectAll true to unselect all currently selected rows
   * @param fireEvent true to fire the select event to listeners
   */
  protected void selectRow(int row, Element rowElem, boolean unselectAll,
      boolean fireEvent) {
    // Get the row index if needed
    if (row < 0) {
      row = getRowIndex(rowElem);
    }

    // Ignore request if row already selected
    Integer rowI = new Integer(row);
    if (selectedRows.containsKey(rowI)) {
      return;
    }

    // Get the old list of selected rows
    Set<Row> oldRowSet = null;
    if (fireEvent) {
      oldRowSet = getSelectedRowsSet();
    }

    // Deselect current rows
    if (unselectAll) {
      deselectAllRows(false);
    }

    // Select the new row
    selectedRows.put(rowI, rowElem);
    setStyleName(rowElem, "selected", true);
    if (selectionPolicy.hasInputColumn()) {
      setInputSelected(getSelectionPolicy(),
          (Element) rowElem.getFirstChildElement(), true);
    }

    // Fire grid listeners
    if (fireEvent) {
      fireRowSelectionEvent(oldRowSet);
    }
  }

  @Override
  protected void setBodyElement(Element element) {
    super.setBodyElement(element);
    if (!selectionEnabled) {
      setSelectionEnabled(selectionEnabled);
    }
  }

  /**
   * Enabled or disabled the native input element in the given cell. This method
   * should correspond with the HTML returned from {@link #getInputHtml}.
   *
   * @param selectionPolicy the associated {@link SelectionPolicy}
   * @param td the cell containing the element
   * @param enabled true to enable, false to disable
   */
  protected void setInputEnabled(SelectionPolicy selectionPolicy, Element td,
      boolean enabled) {
    ((InputElement) td.getFirstChild()).setDisabled(!enabled);
  }

  /**
   * Select the native input element in the given cell. This method should
   * correspond with the HTML returned from {@link #getInputHtml}.
   *
   * @param selectionPolicy the associated {@link SelectionPolicy}
   * @param td the cell containing the element
   * @param selected true to select, false to deselect
   */
  protected void setInputSelected(SelectionPolicy selectionPolicy, Element td,
      boolean selected) {
    ((InputElement) td.getFirstChild()).setChecked(selected);
  }
}
TOP

Related Classes of com.google.gwt.gen2.table.client.SelectionGrid$SelectionGridCellFormatter

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.