Package com.sencha.gxt.widget.core.client.grid

Source Code of com.sencha.gxt.widget.core.client.grid.Grid

/**
* Sencha GXT 3.1.0-beta - Sencha for GWT
* Copyright(c) 2007-2014, Sencha, Inc.
* licensing@sencha.com
*
* http://www.sencha.com/products/gxt/license/
*/
package com.sencha.gxt.widget.core.client.grid;

import java.util.HashSet;
import java.util.Set;

import com.google.gwt.cell.client.Cell;
import com.google.gwt.cell.client.Cell.Context;
import com.google.gwt.cell.client.ValueUpdater;
import com.google.gwt.core.client.GWT;
import com.google.gwt.core.client.Scheduler;
import com.google.gwt.core.client.Scheduler.ScheduledCommand;
import com.google.gwt.dom.client.Element;
import com.google.gwt.dom.client.EventTarget;
import com.google.gwt.event.shared.HandlerRegistration;
import com.google.gwt.safehtml.shared.SafeHtmlBuilder;
import com.google.gwt.uibinder.client.UiConstructor;
import com.google.gwt.user.client.Event;
import com.google.gwt.user.client.Timer;
import com.sencha.gxt.core.client.ValueProvider;
import com.sencha.gxt.core.client.dom.XDOM;
import com.sencha.gxt.data.shared.ListStore;
import com.sencha.gxt.data.shared.ModelKeyProvider;
import com.sencha.gxt.data.shared.PropertyAccess;
import com.sencha.gxt.data.shared.loader.BeforeLoadEvent;
import com.sencha.gxt.data.shared.loader.ListLoadConfig;
import com.sencha.gxt.data.shared.loader.ListLoadResult;
import com.sencha.gxt.data.shared.loader.ListLoader;
import com.sencha.gxt.data.shared.loader.LoadEvent;
import com.sencha.gxt.data.shared.loader.LoadExceptionEvent;
import com.sencha.gxt.data.shared.loader.LoaderHandler;
import com.sencha.gxt.dnd.core.client.GridDragSource;
import com.sencha.gxt.dnd.core.client.GridDropTarget;
import com.sencha.gxt.messages.client.DefaultMessages;
import com.sencha.gxt.widget.core.client.Component;
import com.sencha.gxt.widget.core.client.event.BodyScrollEvent;
import com.sencha.gxt.widget.core.client.event.BodyScrollEvent.BodyScrollHandler;
import com.sencha.gxt.widget.core.client.event.BodyScrollEvent.HasBodyScrollHandlers;
import com.sencha.gxt.widget.core.client.event.CellClickEvent;
import com.sencha.gxt.widget.core.client.event.CellClickEvent.CellClickHandler;
import com.sencha.gxt.widget.core.client.event.CellClickEvent.HasCellClickHandlers;
import com.sencha.gxt.widget.core.client.event.CellDoubleClickEvent;
import com.sencha.gxt.widget.core.client.event.CellDoubleClickEvent.CellDoubleClickHandler;
import com.sencha.gxt.widget.core.client.event.CellDoubleClickEvent.HasCellDoubleClickHandlers;
import com.sencha.gxt.widget.core.client.event.CellMouseDownEvent;
import com.sencha.gxt.widget.core.client.event.CellMouseDownEvent.CellMouseDownHandler;
import com.sencha.gxt.widget.core.client.event.CellMouseDownEvent.HasCellMouseDownHandlers;
import com.sencha.gxt.widget.core.client.event.HeaderClickEvent;
import com.sencha.gxt.widget.core.client.event.HeaderClickEvent.HasHeaderClickHandlers;
import com.sencha.gxt.widget.core.client.event.HeaderClickEvent.HeaderClickHandler;
import com.sencha.gxt.widget.core.client.event.HeaderContextMenuEvent;
import com.sencha.gxt.widget.core.client.event.HeaderContextMenuEvent.HasHeaderContextMenuHandlers;
import com.sencha.gxt.widget.core.client.event.HeaderContextMenuEvent.HeaderContextMenuHandler;
import com.sencha.gxt.widget.core.client.event.HeaderDoubleClickEvent;
import com.sencha.gxt.widget.core.client.event.HeaderDoubleClickEvent.HasHeaderDoubleClickHandlers;
import com.sencha.gxt.widget.core.client.event.HeaderDoubleClickEvent.HeaderDoubleClickHandler;
import com.sencha.gxt.widget.core.client.event.HeaderMouseDownEvent;
import com.sencha.gxt.widget.core.client.event.HeaderMouseDownEvent.HasHeaderMouseDownHandlers;
import com.sencha.gxt.widget.core.client.event.HeaderMouseDownEvent.HeaderMouseDownHandler;
import com.sencha.gxt.widget.core.client.event.ReconfigureEvent;
import com.sencha.gxt.widget.core.client.event.ReconfigureEvent.HasReconfigureHandlers;
import com.sencha.gxt.widget.core.client.event.ReconfigureEvent.ReconfigureHandler;
import com.sencha.gxt.widget.core.client.event.RefreshEvent;
import com.sencha.gxt.widget.core.client.event.RefreshEvent.HasRefreshHandlers;
import com.sencha.gxt.widget.core.client.event.RefreshEvent.RefreshHandler;
import com.sencha.gxt.widget.core.client.event.RowClickEvent;
import com.sencha.gxt.widget.core.client.event.RowClickEvent.HasRowClickHandlers;
import com.sencha.gxt.widget.core.client.event.RowClickEvent.RowClickHandler;
import com.sencha.gxt.widget.core.client.event.RowDoubleClickEvent;
import com.sencha.gxt.widget.core.client.event.RowDoubleClickEvent.HasRowDoubleClickHandlers;
import com.sencha.gxt.widget.core.client.event.RowDoubleClickEvent.RowDoubleClickHandler;
import com.sencha.gxt.widget.core.client.event.RowMouseDownEvent;
import com.sencha.gxt.widget.core.client.event.RowMouseDownEvent.HasRowMouseDownHandlers;
import com.sencha.gxt.widget.core.client.event.RowMouseDownEvent.RowMouseDownHandler;
import com.sencha.gxt.widget.core.client.event.SortChangeEvent;
import com.sencha.gxt.widget.core.client.event.SortChangeEvent.HasSortChangeHandlers;
import com.sencha.gxt.widget.core.client.event.SortChangeEvent.SortChangeHandler;
import com.sencha.gxt.widget.core.client.event.ViewReadyEvent;
import com.sencha.gxt.widget.core.client.event.ViewReadyEvent.HasViewReadyHandlers;
import com.sencha.gxt.widget.core.client.event.ViewReadyEvent.ViewReadyHandler;
import com.sencha.gxt.widget.core.client.grid.editing.GridInlineEditing;

/**
* A {@link Grid} provides support for displaying and editing two-dimensional
* tables of cells. The grid gets its data from a {@link ListStore} and its
* column definitions from a {@link ColumnModel}. Each model in the store is
* rendered as a row in the grid. The fields in the model provide the data for
* each column in the row. Any updates to the store are automatically pushed to
* the grid. This includes inserting, removing, sorting and filtering.
* <p/>
* In GXT version 3, {@link ModelKeyProvider}s and {@link ValueProvider}s
* provide the interface between your data model and the list store and
* {@link ColumnConfig} classes. This enables a grid to work with data of any
* object type.
* <p/>
* You can provide your own implementation of these interfaces, or you can use a
* Sencha supplied generator to create them for you automatically. A generator
* runs at compile time to create a Java class that is compiled to JavaScript.
* The Sencha supplied generator can create classes for interfaces that extend
* the {@link PropertyAccess} interface. The generator transparently creates the
* class at compile time and the {@link GWT#create(Class)} method returns an
* instance of that class at run time. The generated class is managed by GWT and
* GXT and you generally do not need to worry about what the class is called,
* where it is located, or other similar details.
* <p/>
* Each grid has a {@link GridView}. The grid view provides many options for
* customizing the grid's appearance (e.g. striping, mouse-over tracking, empty
* text). To set these options, get the current grid view using
* {@link Grid#getView()} and then set the desired option on the grid view.
* <p/>
* To customize the appearance of a column in a grid, provide a cell
* implementation using {@link ColumnConfig#setCell(Cell)}.
* <p />
* Grids support several ways to manage column widths:
* <ol>
* <li>The most basic approach is to simply give pixel widths to each column.
* Columns widths will match the specified values.</li>
* <li>A column can be identified as an auto-expand column. As the width of the
* grid changes, or columns are resized, the specified column's width is
* adjusted so that the column fills the available width with no horizontal
* scrolling. See {@link GridView#setAutoExpandColumn(ColumnConfig)}.</li>
* <li>The grid can resize columns based on relative weights, determined by the
* pixel width assigned to each column. As the width of the grid or columns
* change, the weight is used to allocate the available space. Use
* {@link GridView#setAutoFill(boolean)} or
* {@link GridView#setForceFit(boolean)} to enable this feature:</li>
* <ul>
* <li>With auto fill, the calculations are run when the grid is created (or
* reconfigured). After the grid is rendered, the column widths will not be
* adjusted when the available width changes.</li>
* <li>With force fit the width calculations are run every time there are
* changes to the available width or column sizes.</li>
* </ul>
* <li>To prevent a column from participating in auto fill or force fit, use
* {@link ColumnConfig#setFixed(boolean)}.</li>
* </ol>
* </p>
* The following code snippet illustrates the creation of a simple grid with
* local data for test purposes. For more practical examples that show how to
* load data from remote sources, see the Json Grid, Live Grid, Paging Grid,
* RequestFactory Grid and Xml Grid examples in the online Explorer demo.</p>
*
* <pre>{@code
    // Create an instance of the generated key and value providers for the Data class
    DataProperties dp = GWT.create(DataProperties.class);

    // Create the configurations for each column in the grid
    List<ColumnConfig<Data, ?>> ccs = new LinkedList<ColumnConfig<Data, ?>>();
    ccs.add(new ColumnConfig<Data, String>(dp.name(), 200, "Name"));
    ccs.add(new ColumnConfig<Data, String>(dp.value(), 200, "Value"));
    ColumnModel<Data> cm = new ColumnModel<Test.Data>(ccs);

    // Create the store that the contains the data to display in the grid
    ListStore<Data> s = new ListStore<Test.Data>(dp.key());
    s.add(new Data("name1", "value1"));
    s.add(new Data("name2", "value2"));
    s.add(new Data("name3", "value3"));
    s.add(new Data("name4", "value4"));

    // Create the grid using the store and column configurations
    Grid<Data> g = new Grid<Data>(s, cm);

    // Add the grid to a container
    RootPanel.get().add(g);
* }</pre>
* <p/>
* To use the Sencha supplied generator to create model key providers and value
* providers, extend the <code>PropertyAccess</code> interface, parameterized
* with the type of data you want to access (as shown below) and invoke the
* <code>GWT.create</code> method on its <code>class</code> member (as shown in
* the code snippet above). This creates an instance of the class that can be
* used to initialize the column configuration and list store. In the following
* code snippet we define a new interface called <code>DataProperties</code>
* that extends the <code>PropertyAccess</code> interface and is parameterized
* with <code>Data</code>, a Plain Old Java Object (POJO).
* <p/>
*
* <pre>
  public interface DataProperties extends PropertyAccess<Data> {
    &#64;Path("name")
    ModelKeyProvider<Data> key();
    ValueProvider&lt;Data, String> name();
    ValueProvider&lt;Data, String> value();
  }

  public class Data {
    private String name;
    private String value;

    public Data(String name, String value) {
      super();
      this.name = name;
      this.value = value;
    }
    public String getName() {
      return name;
    }
    public String getValue() {
      return value;
    }
    public void setName(String name) {
      this.name = name;
    }
    public void setValue(String value) {
      this.value = value;
    }
  }
* </pre>
* <p/>
* To enable drag and drop for a grid, add the following:
* <p/>
*
* <pre>
    new GridDragSource<Data>(g);
    GridDropTarget<Data> dt = new GridDropTarget<Data>(g);
    dt.setFeedback(Feedback.BOTH);
* </pre>
* <p/>
* To add reordering support to the drag and drop, include:
*
* <pre>
    dt.setAllowSelfAsSource(true);
* </pre>
*
* @param <M> the model type
* @see ListStore
* @see ColumnModel
* @see ColumnConfig
* @see GridView
* @see GridDragSource
* @see GridDropTarget
* @see GridInlineEditing
* @see CellSelectionModel
*/
public class Grid<M> extends Component implements HasViewReadyHandlers, HasSortChangeHandlers, HasRowClickHandlers,
    HasRowDoubleClickHandlers, HasRowMouseDownHandlers, HasCellClickHandlers, HasCellDoubleClickHandlers,
    HasCellMouseDownHandlers, HasHeaderClickHandlers, HasHeaderDoubleClickHandlers, HasHeaderContextMenuHandlers,
    HasHeaderMouseDownHandlers, HasRefreshHandlers, HasReconfigureHandlers, HasBodyScrollHandlers {

  /**
   * Provides a mechanism by which other components can report whether a cell is
   * selectable. This decouples components that need to know if a cell is
   * selectable from components that know if a cell is selectable.
   */
  public static interface Callback {

    /**
     * Returns true to indicate the given cell is selectable.
     *
     * @param cell the cell to check
     * @return true to indicate the cell is editable
     */
    public boolean isSelectable(GridCell cell);
  }

  /**
   * A reference to a cell in the grid that can be used for a variety of
   * purposes, including, for example, whether it is active or selected.
   */
  public static class GridCell {
    private final int col;
    private final int row;

    /**
     * Creates a reference to a cell in the grid.
     *
     * @param row the row index of the cell
     * @param col the column index of the cell
     */
    public GridCell(int row, int col) {
      this.row = row;
      this.col = col;
    }

    /**
     * Returns the cell column index.
     *
     * @return the cell column index
     */
    public int getCol() {
      return col;
    }

    /**
     * Returns the cell row index.
     *
     * @return the cell row index
     */
    public int getRow() {
      return row;
    }

  }

  /**
   * The current column model for this grid. Should be considered read-only in
   * derived classes unless the class is tightly integrated with the grid's
   * construction and reconfiguration process.
   */
  protected ColumnModel<M> cm;
  /**
   * The current selection model for this grid. Should be considered read-only
   * in derived classes unless the class is tightly integrated with the grid's
   * construction and reconfiguration process.
   */
  protected GridSelectionModel<M> sm;
  /**
   * The current store for this grid. Should be considered read-only in derived
   * classes unless the class is tightly integrated with the grid's construction
   * and reconfiguration process.
   */
  protected ListStore<M> store;
  /**
   * The current view for this grid. Should be considered read-only in derived
   * classes unless the class is tightly integrated with the grid's construction
   * and reconfiguration process.
   */
  protected GridView<M> view;
  /**
   * The current view for this grid. Should be considered read-only in derived
   * classes.
   */
  protected boolean viewReady;

  private ListLoader<?, ?> loader;

  private boolean enableColumnReorder;
  private boolean enableColumnResize = true;
  private boolean hideHeaders;
  private int lazyRowRender = 10;
  private HandlerRegistration loaderRegistration;
  private LoaderHandler<ListLoadConfig, ListLoadResult<?>> loadHandler = new LoaderHandler<ListLoadConfig, ListLoadResult<?>>() {

    @Override
    public void onBeforeLoad(final BeforeLoadEvent<ListLoadConfig> event) {
      Grid.this.onLoaderBeforeLoad();
      Scheduler.get().scheduleFinally(new ScheduledCommand() {
        @Override
        public void execute() {
          if (event.isCancelled()) {
            Grid.this.onLoadLoader();
          }
        }
      });
    }

    @Override
    public void onLoad(LoadEvent<ListLoadConfig, ListLoadResult<?>> event) {
      Grid.this.onLoadLoader();
    }

    @Override
    public void onLoadException(LoadExceptionEvent<ListLoadConfig> event) {
      Grid.this.onLoaderLoadException();
    }
  };

  private boolean loadMask;

  /**
   * Creates a new grid with the given data store and column model.
   *
   * @param store the data store
   * @param cm the column model
   */
  public Grid(ListStore<M> store, ColumnModel<M> cm) {
    this(store, cm, new GridView<M>());
  }

  /**
   * Creates a new grid with the given data store and column model.
   *
   * @param store the data store
   * @param cm the column model
   */
  @UiConstructor
  public Grid(ListStore<M> store, ColumnModel<M> cm, GridView<M> view) {
    this.store = store;
    this.cm = cm;
    this.view = view;

    disabledStyle = null;
    setSelectionModel(new GridSelectionModel<M>());

    setAllowTextSelection(false);

    SafeHtmlBuilder builder = new SafeHtmlBuilder();
    view.getAppearance().render(builder);

    setElement((Element) XDOM.create(builder.toSafeHtml()));
    getElement().makePositionable();

    sinkCellEvents();
  }

  /**
   * Creates a grid to be initialized by derived classes.
   */
  protected Grid() {

  }

  @Override
  public HandlerRegistration addBodyScrollHandler(BodyScrollHandler handler) {
    return addHandler(handler, BodyScrollEvent.getType());
  }

  @Override
  public HandlerRegistration addCellClickHandler(CellClickHandler handler) {
    return addHandler(handler, CellClickEvent.getType());
  }

  @Override
  public HandlerRegistration addCellDoubleClickHandler(CellDoubleClickHandler handler) {
    return addHandler(handler, CellDoubleClickEvent.getType());
  }

  @Override
  public HandlerRegistration addCellMouseDownHandler(CellMouseDownHandler handler) {
    return addHandler(handler, CellMouseDownEvent.getType());
  }

  @Override
  public HandlerRegistration addHeaderClickHandler(HeaderClickHandler handler) {
    return addHandler(handler, HeaderClickEvent.getType());
  }

  @Override
  public HandlerRegistration addHeaderContextMenuHandler(HeaderContextMenuHandler handler) {
    return addHandler(handler, HeaderContextMenuEvent.getType());
  }

  @Override
  public HandlerRegistration addHeaderDoubleClickHandler(HeaderDoubleClickHandler handler) {
    return addHandler(handler, HeaderDoubleClickEvent.getType());
  }

  @Override
  public HandlerRegistration addHeaderMouseDownHandler(HeaderMouseDownHandler handler) {
    return addHandler(handler, HeaderMouseDownEvent.getType());
  }

  @Override
  public HandlerRegistration addReconfigureHandler(ReconfigureHandler handler) {
    return addHandler(handler, ReconfigureEvent.getType());
  }

  @Override
  public HandlerRegistration addRefreshHandler(RefreshHandler handler) {
    return addHandler(handler, RefreshEvent.getType());
  }

  @Override
  public HandlerRegistration addRowClickHandler(RowClickHandler handler) {
    return addHandler(handler, RowClickEvent.getType());
  }

  @Override
  public HandlerRegistration addRowDoubleClickHandler(RowDoubleClickHandler handler) {
    return addHandler(handler, RowDoubleClickEvent.getType());
  }

  @Override
  public HandlerRegistration addRowMouseDownHandler(RowMouseDownHandler handler) {
    return addHandler(handler, RowMouseDownEvent.getType());
  }

  @Override
  public HandlerRegistration addSortChangeHandler(SortChangeHandler handler) {
    return addHandler(handler, SortChangeEvent.getType());
  }

  @Override
  public HandlerRegistration addViewReadyHandler(ViewReadyHandler handler) {
    return addHandler(handler, ViewReadyEvent.getType());
  }

  @Override
  public void focus() {
    view.focus();
  }

  /**
   * Returns the column model.
   *
   * @return the column model
   */
  public ColumnModel<M> getColumnModel() {
    return cm;
  }

  /**
   * Returns the time in ms after the rows get rendered.
   *
   * @return the lazy row rendering time
   */
  public int getLazyRowRender() {
    return lazyRowRender;
  }

  /**
   * Returns the loader.
   *
   * @return the loader
   */
  public ListLoader<?, ?> getLoader() {
    return loader;
  }

  /**
   * Returns the grid's selection model.
   *
   * @return the selection model
   */
  public GridSelectionModel<M> getSelectionModel() {
    return sm;
  }

  /**
   * Returns the grid's store.
   *
   * @return the store
   */
  public ListStore<M> getStore() {
    return store;
  }

  /**
   * Returns the grid's view.
   *
   * @return the grid view
   */
  public GridView<M> getView() {
    return view;
  }

  /**
   * Returns true if column reordering is enabled.
   *
   * @return true if enabled
   */
  public boolean isColumnReordering() {
    return enableColumnReorder;
  }

  /**
   * Returns true if column resizing is enabled.
   *
   * @return true if resizing is enabled
   */
  public boolean isColumnResize() {
    return enableColumnResize;
  }

  /**
   * Returns true if the header is hidden.
   *
   * @return true for hidden
   */
  public boolean isHideHeaders() {
    return hideHeaders;
  }

  /**
   * Returns true if the load mask in enabled.
   *
   * @return the load mask state
   */
  public boolean isLoadMask() {
    return loadMask;
  }

  /**
   * Returns true if the view is ready.
   *
   * @return the view ready state
   */
  public boolean isViewReady() {
    return viewReady;
  }

  @Override
  public void onBrowserEvent(Event ce) {
    int type = ce.getTypeInt();

    CellWidgetImplHelper.onBrowserEvent(this, ce);

    Cell<?> cell = handleEventForCell(ce);

    // we dont want selection model to get click event initiated from cells that
    // handle selection
    // for example, a combo box we dont want row selected when trigger is
    // clicked
    if (type == Event.ONCLICK && cell != null && cell.handlesSelection()) {
      return;
    }

    super.onBrowserEvent(ce);
    switch (type) {
      case Event.ONCLICK:
        onClick(ce);
        break;
      case Event.ONDBLCLICK:
        onDoubleClick(ce);
        break;
      case Event.ONMOUSEDOWN:
        onMouseDown(ce);
        break;
      case Event.ONMOUSEUP:
        onMouseUp(ce);
        break;
      case Event.ONFOCUS:
        onFocus(ce);
        break;
      case Event.ONBLUR:
        onBlur(ce);
        break;
    }
    view.handleComponentEvent(ce);

  }

  /**
   * Reconfigures the grid to use a different Store and Column Model. The View
   * will be bound to the new objects and refreshed.
   *
   * @param store the new store
   * @param cm the new column model
   */
  public void reconfigure(ListStore<M> store, ColumnModel<M> cm) {
    if (!viewReady) {
      this.store = store;
      this.cm = cm;
      setSelectionModel(sm);
      return;
    }
    if (loadMask) {
      mask(DefaultMessages.getMessages().loadMask_msg());
    }
    view.initData(store, cm);

    this.store = store;
    this.cm = cm;
    sinkCellEvents();
    // rebind the sm
    setSelectionModel(sm);
    if (isViewReady()) {
      view.refresh(true);
    }
    if (loadMask) {
      unmask();
    }
    fireEvent(new ReconfigureEvent());
  }

  @Override
  public void setAllowTextSelection(boolean enable) {
    allowTextSelection = enable;
  }

  /**
   * True to enable column reordering via drag and drop (defaults to false).
   *
   * @param enableColumnReorder true to enable
   */
  public void setColumnReordering(boolean enableColumnReorder) {
    this.enableColumnReorder = enableColumnReorder;
  }

  /**
   * Sets whether columns may be resized (defaults to true).
   *
   * @param enableColumnResize true to allow column resizing
   */
  public void setColumnResize(boolean enableColumnResize) {
    this.enableColumnResize = enableColumnResize;
  }

  /**
   * Sets whether the header should be hidden (defaults to false).
   *
   * @param hideHeaders true to hide the header
   */
  public void setHideHeaders(boolean hideHeaders) {
    this.hideHeaders = hideHeaders;
  }

  /**
   * Sets the time in ms after the row gets rendered (defaults to 10). 0 means
   * that the rows get rendered as soon as the grid gets rendered.
   *
   * @param lazyRowRender the time in ms after the rows get rendered.
   */
  public void setLazyRowRender(int lazyRowRender) {
    this.lazyRowRender = lazyRowRender;
  }

  /**
   * Sets the loader.
   *
   * @param loader the loader
   */
  @SuppressWarnings({"rawtypes", "unchecked"})
  public void setLoader(ListLoader<?, ?> loader) {
    if (this.loaderRegistration != null) {
      loaderRegistration.removeHandler();
      loaderRegistration = null;
    }
    this.loader = loader;
    if (loader != null) {
      loaderRegistration = loader.addLoaderHandler((LoaderHandler) loadHandler);
    }
  }

  /**
   * Sets whether a load mask should be displayed during load operations
   * (defaults to false).
   *
   * @param loadMask true to show a mask
   */
  public void setLoadMask(boolean loadMask) {
    this.loadMask = loadMask;
  }

  /**
   * Sets the grid selection model.
   *
   * @param sm the selection model
   */
  public void setSelectionModel(GridSelectionModel<M> sm) {
    if (this.sm != null) {
      this.sm.bindGrid(null);
    }
    this.sm = sm;
    if (sm != null) {
      sm.bindGrid(this);
    }
  }

  /**
   * Sets the grid's view. May only be called before the Grid is first attached.
   *
   * @param view the view to use for this grid
   */
  public void setView(GridView<M> view) {
    assertPreRender();
    this.view = view;

    // rebind the sm
    if (getSelectionModel() != sm) {
      setSelectionModel(sm);
    }
  }

  /**
   * Navigate in the requested direction to the next selectable cell, given the
   * row, column and step.
   *
   * @param row the starting row index
   * @param col the starting column index
   * @param step the step size and direction
   * @param callback a callback that determines whether the given cell is
   *          selectable
   * @return the next cell or <code>null</code> if no cell matches the criteria
   */
  public GridCell walkCells(int row, int col, int step, Callback callback) {
    boolean first = true;
    int clen = cm.getColumnCount();
    int rlen = store.size();
    if (step < 0) {
      if (col < 0) {
        row--;
        first = false;
      }
      while (row >= 0) {
        if (!first) {
          col = clen - 1;
        }
        first = false;
        while (col >= 0) {
          GridCell cell = new GridCell(row, col);
          if (callback.isSelectable(cell)) {
            return cell;
          }
          col--;
        }
        row--;
      }
    } else {
      if (col >= clen) {
        row++;
        first = false;
      }
      while (row < rlen) {
        if (!first) {
          col = 0;
        }
        first = false;
        while (col < clen) {
          GridCell cell = new GridCell(row, col);
          if (callback.isSelectable(cell)) {
            return cell;
          }
          col++;
        }
        row++;
      }
    }
    return null;
  }

  /**
   * Invoked after the view element is first attached, performs the final steps
   * before the grid is completely initialized.
   */
  protected void afterRenderView() {
    view.afterRender();
    viewReady = true;
    onAfterRenderView();

    fireEvent(new ViewReadyEvent());
  }

  /**
   * Returns true if the given cell consumes the given event.
   *
   * @param cell the cell to inspect
   * @param eventType the event
   * @return true if the cell consumes the given event
   */
  protected boolean cellConsumesEventType(Cell<?> cell, String eventType) {
    Set<String> consumedEvents = cell.getConsumedEvents();
    return consumedEvents != null && consumedEvents.contains(eventType);
  }

  @Override
  protected void doAttachChildren() {
    super.doAttachChildren();
    view.doAttach();
  }

  @Override
  protected void doDetachChildren() {
    super.doDetachChildren();
    view.doDetach();
  }

  /**
   * Fires an event to the cell specified by the given record data model and
   * column.
   *
   * @param event the event to fire
   * @param eventType the type of event
   * @param cellParent the containing parent element
   * @param m the record data model containing the cell
   * @param context the context of the cell (row, column and key)
   * @param column the column containing the cell
   */
  protected <N> Cell<?> fireEventToCell(Event event, String eventType, Element cellParent, final M m, Context context,
      final ColumnConfig<M, N> column) {
    Cell<N> cell = column.getCell();
    if (cell != null && cellConsumesEventType(cell, eventType)) {
      N cellValue = null;
      if (store.hasRecord(m)) {
        cellValue = store.getRecord(m).getValue(column.getValueProvider());
      } else {
        cellValue = column.getValueProvider().getValue(m);
      }
      cell.onBrowserEvent(context, cellParent, cellValue, event, new ValueUpdater<N>() {
        @Override
        public void update(N value) {
          Grid.this.getStore().getRecord(m).addChange(column.getValueProvider(), value);
        }
      });
      return cell;
    }
    return null;
  }

  /**
   * Inspects the given event and fires it to a cell if possible.
   *
   * @param event the event to handle
   */
  protected Cell<?> handleEventForCell(Event event) {
    // Get the event target.
    EventTarget eventTarget = event.getEventTarget();
    if (!Element.is(eventTarget)) {
      return null;
    }
    final Element target = event.getEventTarget().cast();

    int rowIndex = getView().findRowIndex(target);
    int colIndex = getView().findCellIndex(target, null);

    M value = getStore().get(rowIndex);
    ColumnConfig<M, ?> config = cm.getColumn(colIndex);
    Element cellParent = getView().getCell(rowIndex, colIndex);
    if (value != null && config != null && cellParent != null) {
      Context context = new Context(rowIndex, colIndex, getStore().getKeyProvider().getKey(value));
      return fireEventToCell(event, event.getType(), cellParent.getFirstChildElement(), value, context, config);
    }
    return null;
  }

  @Override
  protected void notifyHide() {
    super.notifyHide();
    view.notifyHide();
  }

  @Override
  protected void notifyShow() {
    super.notifyShow();
    view.notifyShow();
  }

  @Override
  protected void onAfterFirstAttach() {
    super.onAfterFirstAttach();
    if (lazyRowRender > 0) {
      Timer t = new Timer() {
        @Override
        public void run() {
          afterRenderView();
        }
      };
      t.schedule(lazyRowRender);
    } else {
      afterRenderView();
    }
  }

  /**
   * Invoked after the view has been rendered, may be overridden to perform any
   * activities that require a rendered view.
   */
  protected void onAfterRenderView() {
  }

  @Override
  protected void onAttach() {
    if (!isOrWasAttached()) {
      view.init(this);
    }
    super.onAttach();
  }



  /**
   * Handles the browser click event. Propagates the event to the cell and row
   * if possible.
   *
   * @param e the click event
   */
  protected void onClick(Event e) {
    Element target = Element.as(e.getEventTarget());
    int rowIndex = view.findRowIndex(target);
    if (rowIndex != -1) {
      int colIndex = view.findCellIndex(target, null);
      if (colIndex != -1) {
        fireEvent(new CellClickEvent(rowIndex, colIndex, e));
      }
      fireEvent(new RowClickEvent(rowIndex, colIndex, e));
    }
  }

  @Override
  protected void onDisable() {
    super.onDisable();
    mask();
  }

  /**
   * Handles the browser double click event. Propagates the event to the cell
   * and row if possible.
   *
   * @param e the double click event
   */
  protected void onDoubleClick(Event e) {
    Element target = Element.as(e.getEventTarget());
    int rowIndex = view.findRowIndex(target);
    if (rowIndex != -1) {
      int colIndex = view.findCellIndex(target, null);
      if (colIndex != -1) {
        fireEvent(new CellDoubleClickEvent(rowIndex, colIndex, e));
      }
      fireEvent(new RowDoubleClickEvent(rowIndex, colIndex, e));
    }
  }

  @Override
  protected void onEnable() {
    super.onEnable();
    unmask();
  }

  /**
   * Invoked before the loader loads new data, displays the Loading... mask if
   * it is enabled.
   */
  protected void onLoaderBeforeLoad() {
    if (isLoadMask()) {
      mask(DefaultMessages.getMessages().loadMask_msg());
    }
  }

  /**
   * Invoked if the loader encounters an exception, cancels the Loading... mask
   * if it is enabled.
   */
  protected void onLoaderLoadException() {
    if (isLoadMask()) {
      unmask();
    }
  }

  /**
   * Invoked after the loader loads new data, cancels the Loading... mask if it
   * is enabled.
   */
  protected void onLoadLoader() {
    if (isLoadMask()) {
      unmask();
    }
  }

  /**
   * Handles the browser mouse down event. Propagates the event to the cell and
   * row if possible.
   *
   * @param e the mouse down event
   */
  protected void onMouseDown(Event e) {
    Element target = Element.as(e.getEventTarget());
    int rowIndex = view.findRowIndex(target);
    if (rowIndex != -1) {
      int colIndex = view.findCellIndex(target, null);
      if (colIndex != -1) {
        fireEvent(new CellMouseDownEvent(rowIndex, colIndex, e));
      }
      fireEvent(new RowMouseDownEvent(rowIndex, colIndex, e));
    }
  }

  /**
   * Handles the browser mouse up event.
   *
   * @param e the mouse up event
   */
  protected void onMouseUp(Event e) {

  }

  @Override
  protected void onResize(int width, int height) {
    super.onResize(width, height);
    if (viewReady) {
      view.calculateVBar(true);
    } else {
      view.layout();
    }
  }

  /**
   * Sinks all the events consumed by the cells in the column configs.
   */
  protected void sinkCellEvents() {
    Set<String> consumedEvents = new HashSet<String>();
    for (int i = 0, len = cm.getColumnCount(); i < len; i++) {
      ColumnConfig<M, ?> c = cm.getColumn(i);
      Cell<?> cell = c.getCell();
      if (cell != null) {
        Set<String> cellEvents = cell.getConsumedEvents();
        if (cellEvents != null) {
          consumedEvents.addAll(cellEvents);
        }
      }
    }

    CellWidgetImplHelper.sinkEvents(this, consumedEvents);
  }

}
TOP

Related Classes of com.sencha.gxt.widget.core.client.grid.Grid

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.