Java tutorial
/** * Sencha GXT 4.0.0 - Sencha for GWT * Copyright (c) 2006-2015, Sencha Inc. * * licensing@sencha.com * http://www.sencha.com/products/gxt/license/ * * ================================================================================ * Open Source License * ================================================================================ * This version of Sencha GXT is licensed under the terms of the Open Source GPL v3 * license. You may use this license only if you are prepared to distribute and * share the source code of your application under the GPL v3 license: * http://www.gnu.org/licenses/gpl.html * * If you are NOT prepared to distribute and share the source code of your * application under the GPL v3 license, other commercial and oem licenses * are available for an alternate download of Sencha GXT. * * Please see the Sencha GXT Licensing page at: * http://www.sencha.com/products/gxt/license/ * * For clarification or additional options, please contact: * licensing@sencha.com * ================================================================================ * * * ================================================================================ * Disclaimer * ================================================================================ * THIS SOFTWARE IS DISTRIBUTED "AS-IS" WITHOUT ANY WARRANTIES, CONDITIONS AND * REPRESENTATIONS WHETHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE * IMPLIED WARRANTIES AND CONDITIONS OF MERCHANTABILITY, MERCHANTABLE QUALITY, * FITNESS FOR A PARTICULAR PURPOSE, DURABILITY, NON-INFRINGEMENT, PERFORMANCE AND * THOSE ARISING BY STATUTE OR FROM CUSTOM OR USAGE OF TRADE OR COURSE OF DEALING. * ================================================================================ */ package com.sencha.gxt.widget.core.client.grid; import java.util.HashSet; import java.util.List; 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.dom.client.NativeEvent; 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.core.client.gestures.LongPressOrTapGestureRecognizer; import com.sencha.gxt.core.client.gestures.PointerEventsSupport; import com.sencha.gxt.core.client.gestures.TouchData; 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> { @Path("name") ModelKeyProvider<Data> key(); ValueProvider<Data, String> name(); ValueProvider<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; this.view.grid = this; this.view.ds = store; disabledStyle = null; setSelectionModel(new GridSelectionModel<M>()); setAllowTextSelection(false); SafeHtmlBuilder builder = new SafeHtmlBuilder(); view.getAppearance().render(builder); setElement((Element) XDOM.create(builder.toSafeHtml())); getElement().makePositionable(); getElement().setTabIndex(0); sinkCellEvents(); // use either long press or tap to select addGestureRecognizer(new LongPressOrTapGestureRecognizer() { @Override protected void onLongPress(TouchData touchData) { Event event = touchData.getLastNativeEvent().cast(); onMouseDown(event); super.onLongPress(touchData); } @Override protected void onTap(TouchData touchData) { Event event = touchData.getLastNativeEvent().cast(); onMouseDown(event); onClick(event); // need to make sure to call view's mouse methods as well // view can be set post-construction - make sure to use Grid's member variable rather than the constructor param GridView<M> view = Grid.this.view; if (view != null) { // since we're not preventingDefault, no reason to call view.onMouseDown view.onClick(event); } super.onTap(touchData); } @Override protected void handlePreventDefault(NativeEvent event) { // don't call preventDefault here } }); } /** * 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: // onTap is being called in super.onBrowserEvent, we don't want to call click twice on MSEdge // also - various grids rely on the other mouse events, only want to prevent the onClick if (!PointerEventsSupport.impl.isSupported()) { onClick(ce); } break; case Event.ONDBLCLICK: onDoubleClick(ce); break; case Event.ONMOUSEDOWN: onMouseDown(ce); break; case Event.ONMOUSEUP: onMouseUp(ce); break; } view.handleComponentEvent(ce); } @Override protected void onFocus(Event event) { super.onFocus(event); view.onFocus(event); } /** * 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; if (isAttached()) { getElement().disableTextSelectionSingle(!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; this.view.grid = this; this.view.ds = store; // 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() { view.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); } }