Java tutorial
/* * Tatami: * Copyright (C) 2007 Objet Direct * Copyright (C) 2007 France Telecom * Contact: tatami@googlegroups.com * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * USA * * Authors: Ronan Dunklau * Initial developer(s): Ronan Dunklau * Contributor(s): */ package com.objetdirect.tatami.client.grid; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import com.google.gwt.core.client.JavaScriptObject; import com.google.gwt.user.client.DOM; import com.google.gwt.user.client.Event; import com.google.gwt.user.client.ui.Widget; import com.objetdirect.tatami.client.AbstractDojo; import com.objetdirect.tatami.client.DojoController; import com.objetdirect.tatami.client.HasAdaptiveSize; import com.objetdirect.tatami.client.JSHelper; import com.objetdirect.tatami.client.data.AbstractDataStore; import com.objetdirect.tatami.client.data.DataStorePaginator; import com.objetdirect.tatami.client.data.DatumChangeListener; import com.objetdirect.tatami.client.data.FetchEventSource; import com.objetdirect.tatami.client.data.FetchListener; import com.objetdirect.tatami.client.data.Item; import com.objetdirect.tatami.client.data.Request; import com.objetdirect.tatami.client.grid.editor.GridEditor; import com.objetdirect.tatami.client.grid.formatters.Formatter; /** * This class is a simple wrapper around Dojo Grid. * It relies on an underlying GridDataStore, and uses a GridLayout to define its * appearance * * @author rdunklau * */ public class Grid extends AbstractDojo implements FetchListener, DatumChangeListener, HasAdaptiveSize { /** * The underlying dataStore */ protected AbstractDataStore store; protected String rowSelector; public String getRowSelector() { return rowSelector; } public void setRowSelector(String rowSelector) { this.rowSelector = rowSelector; } /** * The layout , used to define how the grid should be presented */ protected GridLayout layout; /** * List containing the grid listeners * @see : {@link GridListener} */ protected List<GridListener> gridListeners = new ArrayList<GridListener>(); /** * List containing currently selected indexes */ protected List<Integer> selectedIndexes = new ArrayList<Integer>(); /** * Number of displayed rows */ private int count; /** * Should the grid autoAdapt it's height ? */ protected boolean autoHeight = false; /** * Should the grid autoAdapt it's width ? */ protected boolean autoWidth = false; /** * Indicates wether a user click on a cell header sorts the grid * according to this cell field. * */ protected boolean userSortable = true; /** * Indicates which view should be elastic * -1 indicates no one. */ private int elasticView = -1; /** * Indicates wether the sort is ascending or descending */ private boolean isSortAsc = true; /** * Indicates which column serves to sort the grid */ private int sortIndex = -1; /** * Indicates the maximum number of item that is return from a fetch operation * (Used by onScroll paging) */ private int maximumFetchCountAtAtime = 25; private DataStorePaginator paginator; private boolean renderGridOnLoad = true; private RowStyler styler; /** * Default constructor * It creates a new empty grid */ public Grid() { this(new GridDataStore(), new GridView()); this.setWidth("100%"); //this.setHeight("200px"); } /** * Constructs a grid with the specified store and * a layout containing only one view * * @param store : store used to create the grid * @param view : the only view in the layout */ public Grid(AbstractDataStore store, GridView view) { this(store, new GridLayout()); layout.addView(view); } /** * Constructs a grid with the specified store and * layout * * @param store * @param layout */ public Grid(AbstractDataStore store, GridLayout layout) { super(); this.styler = new RowStyler() { public String getRowCSSStyles(int rowIndex, boolean selected, boolean mouseover, boolean odd) { return null; } public String getRowCSSClasses(int rowIndex, boolean selected, boolean mouseover, boolean odd) { return null; } }; this.paginator = new DataStorePaginator(store); setStore(store); this.layout = layout; DOM.sinkEvents(getElement(), Event.MOUSEEVENTS); DOM.sinkEvents(getElement(), Event.FOCUSEVENTS); } public void onAttach() { super.onAttach(); setAutoWidth(this.autoWidth); setAutoHeight(this.autoHeight); this.setUserSortable(userSortable); } /** * Removes all items from the grid and from the underlying data store */ public void clearGrid() { store.clearDataStore(); if (dojoWidget != null) { dojoClearGrid(dojoWidget); updateGrid(); } } private native void dojoClearGrid(JavaScriptObject dojoWidget)/*-{ dojoWidget._clearData(); }-*/; /** * Defines wether the grid should display a handle on the left * to select rows without selecting any cell in particular * * @param shouldItExist */ @Deprecated public void setRowBar(boolean shouldItExist) { layout.setRowBar(shouldItExist); } /** * @return true if the grid displays a rowbar, false otherwise */ @Deprecated public boolean hasRowBar() { return layout.hasRowBar(); } /** * Adds a column with the specified name * @param name : name of the column */ public Cell addColumn(String name) { return addColumn(name, String.valueOf(layout.getNbCells()), null, null, null); } /** * @param name : name of the column * @param fieldName : the fieldname used to get data to fill the grid */ public Cell addColumn(String name, String fieldName) { return addColumn(name, fieldName, null, null, null); } /** * @param name : name of the column * @param fieldName : the fieldname used to get data to fill the grid * @param widthInPx : the width of the column , in pixels. */ public Cell addColumn(String name, String fieldName, int widthInPx) { return addColumn(name, fieldName, widthInPx + "px", null, null); } /** * @param name : name of the column * @param fieldName : the fieldname used to get data to fill the grid * @param editor :: the cell editor */ public Cell addColumn(String name, String fieldName, GridEditor editor) { return addColumn(name, fieldName, null, editor, null); } /** * @param name : name of the column * @param fieldName : the fieldname used to get data to fill the grid * @param cellFormatter : the cell formatter */ public Cell addColumn(String name, String fieldName, Formatter cellFormatter) { return addColumn(name, fieldName, null, null, cellFormatter); } /** * @param name : name of the column * @param fieldName : the fieldname used to get data to fill the grid * @param editor :: the cell editor * @param cellFormatter : the cell formatter */ public Cell addColumn(String name, String fieldName, GridEditor editor, Formatter cellFormatter) { return addColumn(name, fieldName, null, editor, cellFormatter); } /** * @param name : name of the column * @param editor : the cell editor * @param cellFormatter : the cell formatter * @param width : column's width in standard css unit */ public Cell addColumn(String name, GridEditor editor, Formatter cellFormatter, String width) { return addColumn(name, null, width, editor, cellFormatter); } /** * @param name : name of the column * @param fieldName : the fieldname used to get data to fill the grid * @param width : column's width in standard css unit * @param editor : the cell editor * @param cellFormatter : the cell formatter * */ public Cell addColumn(String name, String fieldName, String width, GridEditor editor, Formatter cellFormatter) { Cell cellToAdd = new Cell(fieldName, name); if (width != null) { cellToAdd.setWidth(width); } if (cellFormatter != null) { cellToAdd.setFormatter(cellFormatter); } if (editor != null) { cellToAdd.setEditor(editor); } addCell(cellToAdd); return cellToAdd; } /** * @param cell : the cell to be added to the underlying layout */ public void addCell(Cell cell) { layout.addCellToLastRowFromLastView(cell); } /** * @param name : name of the column * @param index : index where it should be inserted */ public Cell insertColumn(String name, int index) { return insertColumn(name, String.valueOf(index), null, null, null, index); } /** * @param name : name of the column * @param fieldName : the fieldname used to get data to fill the grid * @param index : index where it should be inserted */ public Cell insertColumn(String name, String fieldName, int index) { return insertColumn(name, fieldName, null, null, null, index); } /** * @param name : name of the column * @param fieldName : the fieldname used to get data to fill the grid * @param editor :: it's editor * @param index : index where it should be inserted */ public Cell insertColumn(String name, String fieldName, GridEditor editor, int index) { return insertColumn(name, fieldName, null, editor, null, index); } /** * @param name : name of the column * @param fieldName : the fieldname used to get data to fill the grid * @param cellFormatter : the cell formatter * @param index : index where it should be inserted */ public Cell insertColumn(String name, String fieldName, Formatter cellFormatter, int index) { return insertColumn(name, fieldName, null, null, cellFormatter, index); } /** * @param name : name of the column * @param fieldName : the fieldname used to get data to fill the grid * @param editor :: it's editor * @param cellFormatter : the cell formatter * @param index : index where it should be inserted */ public Cell insertColumn(String name, String fieldName, GridEditor editor, Formatter cellFormatter, int index) { return insertColumn(name, fieldName, null, editor, cellFormatter, index); } /** * @param name : name of the column * @param fieldName : the fieldname used to get data to fill the grid * @param width : column's width , in standard css unit * @param editor :: it's editor * @param cellFormatter : the cell formatter * @param index : index where it should be inserted */ public Cell insertColumn(String name, String fieldName, String width, GridEditor editor, Formatter cellFormatter, int index) { Cell cellToAdd = new Cell(fieldName, name); if (width != null) { cellToAdd.setWidth(width); } if (cellFormatter != null) { cellToAdd.setFormatter(cellFormatter); } if (editor != null) { cellToAdd.setEditor(editor); } insertCell(cellToAdd, index); return cellToAdd; } /** * @param cell : the cell to insert * @param index : the index where it should be inserted */ public void insertCell(Cell cell, int index) { layout.addCellToLastRowFromLastView(cell, index); } /** * @param row : an array of Object containing the various columns content (in order) * A mapping is automatically performed between the Object array and the fieldnames used by the colums * An id is generated if the id is not one of the shown columns. * Then , it inserts the constructed item in the underlying data store * * @param index : the index where the row should be inserted * */ public void addRow(Object[] row, int index) { Item item = new Item(); for (int i = 0; i < row.length; i++) { String field = layout.getCellAmongAllViews(i).getField(); item.setValue(field, row[i]); } if (store.getIdentity(item) == null) { item.setId(new Integer(index) + row[0].toString()); } addRow(item, index); } /** * @param row : an array of Object containing the various columns content (in order) * A mapping is automatically performed between the Object array and the fieldnames used by the colums * An id is generated if the id is not one of the shown columns. * Then , it inserts the constructed item in the underlying data store */ public void addRow(Object[] row) { addRow(row, this.count); } /** * @param item : the item to insert in the grid * @param index : the row index where it should be inserted * @return TODO */ public Item addRow(Item item, int index) { //If the dojo grid widget is built, it is responsible for creating a row, adding the item //to the datastore. //Else, the item is directly added to the datastore, and dojo's grid will //ask it for items when it loads. return store.add(item, makeParentInfo(index)); } private native JavaScriptObject makeParentInfo(int index)/*-{ var toReturn = {index: index}; if(index < 0){ toReturn = null; } return toReturn; }-*/; /** * @param item : the item to insert in the underlying datastore. * It creates a new row after the existing ones. */ public Item addRow(Item item) { return addRow(item, this.count); } /** * Removes the row at given index * @param index */ public void removeRow(int index) { Item toRemove = getItemFromRow(index); if (toRemove != null) { removeRow(toRemove); } } /** * Removes the column at given index * @param index */ public void removeColumn(int index) { removeColumn(layout.getCellAmongAllViews(index)); } /** * Removes the given Cell * @param cell */ public void removeColumn(Cell cell) { layout.removeCellAmongAllViews(cell); } /** * Removes the item from the datastore, removing the grid row * by the same time * @param item */ public void removeRow(Item item) { store.remove(item); } /** * Internal method used to force dojo's grid widget * to add the given row at the given index * * @param item * @param index */ private native void dojoAddRow(JavaScriptObject dojoWidget, Item item, int index)/*-{ dojoWidget.addRow(item , index); }-*/; /** * Returns the item corresponding to the given row index * * @param rowNumber * @return */ public Item getItemFromRow(int rowNumber) { Item result = null; if (dojoWidget != null) { result = dojoGetItemFromRow(getDojoWidget(), rowNumber); } return result; } /** * Internal method used to retrieve the item from it's rowindex * @param rowNumber * @return */ private native Item dojoGetItemFromRow(JavaScriptObject dojoWidget, int rowNumber) /*-{ return dojoWidget.getItem(rowNumber); }-*/; /** * @param item : item from which we want to know the corresponding row * @return item's row index */ public int getRowFromItem(Item item) { int result = -1; if (dojoWidget != null) { result = dojoGetRowFromItem(getDojoWidget(), item); } return result; } /** * Internal method used to retrieve an item's row index * * @param item * @return */ private native int dojoGetRowFromItem(JavaScriptObject dojoWidget, Item item)/*-{ return dojoWidget.getItemIndex(item); }-*/; /* (non-Javadoc) * @see com.objetdirect.tatami.client.HasDojo#createDojoWidget() */ public void createDojoWidget() throws Exception { store.doAfterCreation(); JavaScriptObject jsstore = store.getDojoWidget(); JavaScriptObject jslayout = this.layout.toJSObject(); if (this.rowSelector == null && layout.hasRowBar()) { this.rowSelector = "20px"; } this.dojoWidget = createDojoGrid(jsstore, jslayout, autoHeight, autoWidth, userSortable, elasticView, sortIndex, isSortAsc, maximumFetchCountAtAtime, renderGridOnLoad, rowSelector, JSHelper.convertObjectToJSObject(store.getLastRequest().getQuery())); } /* (non-Javadoc) * @see com.objetdirect.tatami.client.HasDojo#getDojoName() */ public String getDojoName() { return "dojox.grid.DataGrid"; } /** * @param jsstore : the dojo store used to construct the grid * @param layout : the jsobject representing the layout * @param autoHeight : true if the grid should autoAdapt its height, false otherwise * @param autoWidth : true if the grid should autoAdapt its width, false otherwise * @param canSort : wether the user can sort the grid by clicking the cell header * @param elasticView : "elastic view"'s indexes * @param sortCol : if != -1 , sorts the grid according to the column whose index is given * @param ascending : if sortCol != -1 , ascending indicates wether the sort should be in ascending or * descending order * @param maximumFetchCountAtAtime : maximum number of items fetched at a time. Used by dojo * when it does his "scroll paging" * @return a javascript object representing the dojo grid object. */ private native JavaScriptObject createDojoGrid(JavaScriptObject jsstore, JavaScriptObject layout, boolean autoHeight, boolean autoWidth, boolean canSort, int elasticView, int sortCol, boolean ascending, int maximumFetchCountAtAtime, boolean autoRender, String rowSelector, JavaScriptObject query)/*-{ //FIXME: When the sort : {sortCol : index, isAsc : boolean} constructor //parameter comes back in DataGrid, remove this silly sortinfo calculation. var si = sortCol +1; si *= (ascending == true ? 1 : -1); var gridOptions = { store : jsstore , sortInfo: si , structure : layout , width: "100%" , height: "100%" , gwtWidget : this , query : query, autoRender: autoRender }; if(rowSelector){ gridOptions.rowSelector = rowSelector; } var grid = new $wnd.dojox.grid.TatamiGrid(gridOptions); return grid; }-*/; /* (non-Javadoc) * @see com.objetdirect.tatami.client.AbstractDojo#onDojoLoad() */ @Override public void onDojoLoad() { defineTatamiGrid(); } /* (non-Javadoc) * @see com.google.gwt.user.client.ui.UIObject#setHeight(java.lang.String) */ @Override public void setHeight(String height) { super.setHeight(height); if (dojoWidget != null) { resizeGrid(dojoWidget); } } /* (non-Javadoc) * @see com.google.gwt.user.client.ui.UIObject#setSize(java.lang.String, java.lang.String) */ @Override public void setSize(String width, String height) { super.setSize(width, height); if (dojoWidget != null) { resizeGrid(dojoWidget); } } /* (non-Javadoc) * @see com.google.gwt.user.client.ui.UIObject#setWidth(java.lang.String) */ @Override public void setWidth(String width) { super.setWidth(width); if (dojoWidget != null) { resizeGrid(dojoWidget); } } /** * Calls the dojo "resize" method on the grid. It is needed to apply * selected size changes. * @param widget : the dojo widget */ private native void resizeGrid(JavaScriptObject widget)/*-{ widget.resize(); }-*/; /** * Declare the TatamiGrid Class in Dojo, * overriding the default onSelectionChanged and onCellClick functions */ private native void defineTatamiGrid()/*-{ $wnd.dojo.require("dojo.html"); $wnd.dojo.declare("dojox.grid.TatamiGrid" , $wnd.dojox.grid.DataGrid , { onSelectionChanged : function(){ this.gwtWidget.@com.objetdirect.tatami.client.grid.Grid::onSelectionChange(Lcom/google/gwt/core/client/JavaScriptObject;)(this.selection.selected); }, onCellClick : function(e){ this._click[0] = this._click[1]; this._click[1] = e; if(!this.edit.isEditCell(e.rowIndex, e.cellIndex)){ this.focus.setFocusCell(e.cell, e.rowIndex); } this.onRowClick(e); var cell = e.cell; this.gwtWidget.@com.objetdirect.tatami.client.grid.Grid::onCellClick(ILjava/lang/String;I)(e.rowIndex, (e.cell.field == undefined ? null : e.cell.field) , e.cellIndex); }, onCellDblClick: function(e){ if($wnd.dojo.isIE){ this.edit.setEditCell(this._click[1].cell, this._click[1].rowIndex); }else if(this._click[0].rowIndex != this._click[1].rowIndex){ this.edit.setEditCell(this._click[0].cell, this._click[0].rowIndex); }else{ this.edit.setEditCell(e.cell, e.rowIndex); } this.gwtWidget.@com.objetdirect.tatami.client.grid.Grid::onCellDblClick(ILjava/lang/String;I)(e.rowIndex, (e.cell.field == undefined ? null : e.cell.field) , e.cellIndex); }, onStyleRow: function(inRow){ var i = inRow; i.selected = i.selected ? i.selected : false; i.customClasses += (i.odd?" dojoxGridRowOdd":"") + (i.selected?" dojoxGridRowSelected":"") + (i.over?" dojoxGridRowOver":""); i.customClasses += " " +this.gwtWidget.@com.objetdirect.tatami.client.grid.Grid::getRowCSSClasses(IZZZ)(i.index , i.selected , i.over, i.odd); i.customStyles += " "+ this.gwtWidget.@com.objetdirect.tatami.client.grid.Grid::getRowCSSStyles(IZZZ)(i.index , i.selected , i.over, i.odd); this.focus.styleRow(inRow); this.edit.styleRow(inRow); }, _onNew: function(item, parentInfo){ var indexToPut = this.rowCount; if(parentInfo != undefined && parentInfo.index!= undefined){ indexToPut = parentInfo.index; } this.updateRowCount(this.rowCount+1); this._addItem(item,indexToPut); this.updateRows(indexToPut +1, this.rowCount - indexToPut); this.showMessage(); } }); }-*/; /* (non-Javadoc) * @see com.objetdirect.tatami.client.AbstractDojo#doBeforeDestruction() */ @Override public void doBeforeDestruction() { this.store.doBeforeDestruction(); } private static boolean isWidget(Object o) { return o instanceof Widget; } /* (non-Javadoc) * @see com.objetdirect.tatami.client.AbstractDojo#doAfterCreation() */ @Override public void doAfterCreation() { DojoController.startup(this); } /** * Internal method called whenever a cell click is performed * * @param rowIndex * @param colField * @param colIndex */ private void onCellClick(int rowIndex, String colField, int colIndex) { notifyGridListenersOnCellClick(rowIndex, colField, colIndex); } /** * Internal method called whenever a cell dblclick is performed * * @param rowIndex * @param colField * @param colIndex */ private void onCellDblClick(int rowIndex, String colField, int colIndex) { notifyGridListenersOnCellDblClick(rowIndex, colField, colIndex); } /* (non-Javadoc) * @see com.objetdirect.tatami.client.data.FetchListener#onBegin(int) * * Called by the store when the item fetch begin. */ public void onBegin(FetchEventSource source, int size, Request request) { delegateBeginning(size, request.toJSObject()); } /* (non-Javadoc) * @see com.objetdirect.tatami.client.data.FetchListener#onComplete(com.objetdirect.tatami.client.data.Item[]) * * Called by the store when the item fetch ends */ public void onComplete(FetchEventSource source, List<?> items, Request request) { JavaScriptObject itemArray = JavaScriptObject.createArray(); for (Iterator<?> iterator = items.iterator(); iterator.hasNext();) { Item item = (Item) iterator.next(); itemArray = addItemToJSArray(itemArray, item); } delegateProcessRows(itemArray, request.toJSObject()); } /** * Internal helper method which adds an item to a javascript array * * @param array * @param item * @return */ private native JavaScriptObject addItemToJSArray(JavaScriptObject array, Item item)/*-{ array.push(item); return array; }-*/; /* (non-Javadoc) * @see com.objetdirect.tatami.client.data.LoadItemListener#onItem(com.objetdirect.tatami.client.data.Item) * * Called by the store each time a new item has been fetched */ public void onItem(FetchEventSource source, Item item) { } /* (non-Javadoc) * @see com.objetdirect.tatami.client.data.LoadItemListener#onError() * * Called by the store if an error occured during the fetch. */ public void onError(FetchEventSource source) { } /** * This method is used to callback the dojo "beginReturn" method. * @param size : the expected number of items which are fetched * @param grid : the dojo grid object */ private native void delegateBeginning(int size, JavaScriptObject request)/*-{ request.onBegin(size,request); }-*/; /** * This method is used to callback the dojo "processRows" * @param items : a javascript object representing an array of items fetched * @param request : the dojo request object */ private native void delegateProcessRows(JavaScriptObject items, JavaScriptObject request)/*-{ request.onComplete(items,request); }-*/; /** * Asks the grid to refresh its data from the store. */ public void updateGrid() { if (dojoWidget != null) { dojoUpdateGrid(getDojoWidget()); } else { renderGridOnLoad = true; } } /** * Internal method used to update the grid */ private native void dojoUpdateGrid(JavaScriptObject dojoWidget)/*-{ dojoWidget.render(); }-*/; /** * Asks the grid to rebuild itself from its layout */ public void updateView() { if (dojoWidget != null) { dojoUpdateView(getDojoWidget(), JSHelper.convertObjectToJSObject(layout)); } } /** * Internal method used to update grid layout * @param layout */ private native void dojoUpdateView(JavaScriptObject dojoWidget, JavaScriptObject layout)/*-{ dojoWidget.setStructure(layout); }-*/; /** * @return wether the grid should auto adapt its height */ public boolean isAutoHeight() { return autoHeight; } /** * @param autoHeight : true indicates that the grid should auto adapt its height, * false that its height is fixes */ public void setAutoHeight(boolean autoHeight) { this.autoHeight = autoHeight; if (dojoWidget != null) { dojoSetAutoHeight(getDojoWidget(), autoHeight); } } /** * Internal method used to set autoheight in dojo * @param autoHeight */ private native void dojoSetAutoHeight(JavaScriptObject dojoWidget, boolean autoHeight)/*-{ dojoWidget.autoHeight = autoHeight; }-*/; /** * @return wether the grid should auto adapt its width */ public boolean isAutoWidth() { return autoWidth; } /** * @param autoWidth : true if the grid should auto adapt its width, false otherwise */ public void setAutoWidth(boolean autoWidth) { this.autoWidth = autoWidth; if (dojoWidget != null) { dojoSetAutoWidth(getDojoWidget(), autoWidth); } } /** * Internal method used to set autoWidth in dojo * @param autoWidth */ private native void dojoSetAutoWidth(JavaScriptObject dojoWidget, boolean autoWidth)/*-{ dojoWidget.autoWidth = autoWidth; }-*/; /* (non-Javadoc) * @see com.objetdirect.tatami.client.data.DatumChangeListener#onDataChange(com.objetdirect.tatami.client.data.Item, java.lang.String, java.lang.Object, java.lang.Object) */ public void onDataChange(Item item, String attributeName, Object oldValue, Object newValue) { notifyGridListenersOnDataChange(item, attributeName, oldValue, newValue); } /* (non-Javadoc) * @see com.objetdirect.tatami.client.data.DatumChangeListener#onDelete(com.objetdirect.tatami.client.data.Item) */ public void onDelete(Item item) { count--; } /* (non-Javadoc) * @see com.objetdirect.tatami.client.data.DatumChangeListener#onNew(com.objetdirect.tatami.client.data.Item) */ public void onNew(Item item) { count++; } /** * * @return Current column count */ public int getColumnCount() { return layout.getNbCells(); } /** * * @return Current row count */ public int getRowCount() { return count; } /** * @return wether the user can sort the grid by clicking on the cell headers */ public boolean isUserSortable() { return userSortable; } /** * Sets wether the user can sort the grid by clicking on * the column header. * * Default : true. * * @param userSortable */ public void setUserSortable(boolean userSortable) { this.userSortable = userSortable; } /** * Sets the grid sorting. * * @param sortIndex : the column index by which the grid * should be sorted * @param ascending : indicates wether the sort should be * performed in ascending or descending order */ public void setSortIndex(int sortIndex, boolean ascending) { this.sortIndex = sortIndex; this.isSortAsc = ascending; if (dojoWidget != null) { dojoSetSortIndex(getDojoWidget(), sortIndex, ascending); } } /** * Internal method to set the sort index in dojo grid * * @param sortIndex : the column index which should be used to sort * @param ascending : wether the sort should be performed in ascending or descending order */ private native void dojoSetSortIndex(JavaScriptObject dojoWidget, int sortIndex, boolean ascending)/*-{ dojoWidget.setSortIndex(sortIndex , ascending); }-*/; /** * Adds a listener for grid events * * @param listener : a grid Listener * @see {@link GridListener} */ public void addGridListener(GridListener listener) { gridListeners.add(listener); } /** * Removes a listener for grid events * * @param listener : a grid Listener * @see {@link GridListener} */ public void removeGridListener(GridListener listener) { gridListeners.remove(listener); } /** * Internal method used to transform dojo's onSelectionChange event into * something more usable in GWT * * @param selection */ private native void onSelectionChange(JavaScriptObject selection)/*-{ var javaSelectionList = this.@com.objetdirect.tatami.client.grid.Grid::selectedIndexes; javaSelectionList.@java.util.List::clear()(); if(selection != undefined){ for(var i = 0 ; i < selection.length ; i++){ if(selection[i]){ this.@com.objetdirect.tatami.client.grid.Grid::addSelectedIndex(I)(i); } } } this.@com.objetdirect.tatami.client.grid.Grid::notifyGridListenersOnSelectionChange()(); }-*/; /** * Internal method used to add a selected index to the selectedIndexes list * * @param i */ private void addSelectedIndex(int i) { selectedIndexes.add(new Integer(i)); } /** * Notifies grid listeners that a cell click occured * * @param rowIndex : the row index where the click happened * @param colField : the attribute which is used by the clicked cell to get it's data * (null if the column is a constant one) * @param colIndex : the column index where the click happened */ private void notifyGridListenersOnCellClick(int rowIndex, String colField, int colIndex) { for (Iterator<GridListener> iterator = gridListeners.iterator(); iterator.hasNext();) { GridListener gridListener = iterator.next(); gridListener.onCellClick(this, rowIndex, colIndex, colField); } } /** * Notifies grid listeners that a cell DblClick occured * * @param rowIndex : the row index where the click happened * @param colField : the attribute which is used by the clicked cell to get it's data * (null if the column is a constant one) * @param colIndex : the column index where the click happened */ private void notifyGridListenersOnCellDblClick(int rowIndex, String colField, int colIndex) { for (Iterator<GridListener> iterator = gridListeners.iterator(); iterator.hasNext();) { GridListener gridListener = iterator.next(); gridListener.onCellDblClick(this, rowIndex, colIndex, colField); } } /** * Notifies grid listeners that a data change occured * * @param item : the item which has changed * @param attributeName : the attribute which value has changed * @param oldValue : value before it changed * @param newValue : new value */ private void notifyGridListenersOnDataChange(Item item, String attributeName, Object oldValue, Object newValue) { for (Iterator<GridListener> iterator = gridListeners.iterator(); iterator.hasNext();) { GridListener gridListener = iterator.next(); gridListener.onDataChange(this, item, attributeName, oldValue, newValue); } } /** * Notifies grid listeners that the row selection has changed * */ private void notifyGridListenersOnSelectionChange() { for (Iterator<GridListener> iterator = gridListeners.iterator(); iterator.hasNext();) { GridListener gridListener = iterator.next(); gridListener.onSelectionChanged(this); } } /** * @return the underlying data store */ public AbstractDataStore getStore() { return store; } /** * Sets the store that the grid should use * * @param store : the store which should be use to aquire data */ public void setStore(AbstractDataStore store) { if (this.store != null) { this.store.removeDatumChangeListener(this); this.paginator.removeFetchListener(this); } this.store = store; store.addDatumChangeListener(this); paginator.setStore(store); paginator.addFetchListener(this); store.doAfterCreation(); if (dojoWidget != null) { dojoSetStore(getDojoWidget(), store.getDojoWidget()); } } /** * Internal method used to propagate the store change to dojo * * @param store */ private native void dojoSetStore(JavaScriptObject dojoWidget, JavaScriptObject store)/*-{ dojoWidget.setStore(store); }-*/; /** * Get the row indexes which are selected * * @return : an array of int containing the selected indexes */ public int[] getSelectedIndexes() { int[] select = new int[selectedIndexes.size()]; for (int i = 0; i < selectedIndexes.size(); i++) { select[i] = ((Number) selectedIndexes.get(i)).intValue(); } return select; } /** * Get the items corresponding to the selected rows * * @return : an array of selected Items */ public Item[] getSelectedItems() { Item[] selectedItems = new Item[selectedIndexes.size()]; for (int i = 0; i < selectedIndexes.size(); i++) { Number selectedIndex = selectedIndexes.get(i); selectedItems[i] = getItemFromRow(selectedIndex.intValue()); } return selectedItems; } /** * @return the number of rows per page in Tatami's pagination system */ public int getRowsPerPage() { return paginator.getRowsPerPage(); } /** * Sets the number of rows per page. * * @param rowsPerPage : the number of rows per page. * A value of -1 indicates that all rows should fit on a single page. * That does not mean they are all loaded at one time, since only the displayed rows are loaded, * and updated when the user scrolls. * * Default : -1 */ public void setRowsPerPage(int rowsPerPage) { paginator.setRowsPerPage(rowsPerPage); } /** * @return the view index corresponding to the "elastic" view. * The elastic view is the one which width is adapted to fit the remaining space */ public int whichElasticView() { return elasticView; } /** * @param elasticViewIndex : the index of the view that should be elastic * The elastic view is the one which width is adapted to fit the remaining space */ public void setElasticView(int elasticViewIndex) { this.elasticView = elasticViewIndex; if (dojoWidget != null) { dojoSetElasticView(elasticView, dojoWidget); } } private native void dojoSetElasticView(int index, JavaScriptObject widget)/*-{ widget.elasticView = index; }-*/; /** * Goes to the nextPage */ public void nextPage() { paginator.nextPage(); } /** * Goes to the previous page */ public void previousPage() { paginator.previousPage(); } /** * @param index : the page index where to go */ public void goToPage(int index) { paginator.fetchPage(index); } private int getIndexToUpdate() { int rowsPerPage = paginator.getRowsPerPage(); int index = rowsPerPage < 0 ? 0 : rowsPerPage * paginator.getCurrentPage(); return index; } /** * @return : the maximum number of item that should be fetched at a time. * This is used by dojo during its "sroll paging" */ public int getMaximumFetchCountAtAtime() { return maximumFetchCountAtAtime; } /** * Sets the maximum number that are returned at a time. * It indicates how many items should be loaded. When the user scrolls, * items to be displayed are fetched by groups of maximumFetchCountAtAtime * items. * * A typical setting for this value is 1.5 the number of rows to be displayed at a time. * (That is , the grid won't have to load items until the user scrolls) * * @param maximumFetchCountAtAtime */ public void setMaximumFetchCountAtAtime(int maximumFetchCountAtAtime) { this.maximumFetchCountAtAtime = maximumFetchCountAtAtime; if (dojoWidget != null) { setDojoMaximumFetchCountAtATime(getDojoWidget(), maximumFetchCountAtAtime); } } /** * Internal method used to change maximumFetchCountAtAtime in dojo's widget * * @param maximumFetchCountAtAtime */ private native void setDojoMaximumFetchCountAtATime(JavaScriptObject dojoWidget, int maximumFetchCountAtAtime)/*-{ dojoWidget.rowsPerPage = maximumFetchCountAtAtime; }-*/; /** * Remove currently selected rows */ public void removeSelectedRows() { if (getDojoWidget() != null) { dojoRemoveSelectedRows(getDojoWidget()); } } /** * Internal method which removes selected rows in dojo. */ private native void dojoRemoveSelectedRows(JavaScriptObject dojoWidget)/*-{ dojoWidget.removeSelectedRows(); }-*/; /** * @return the current GridLayout */ public GridLayout getLayout() { return layout; } /** * Sets the layout used to display the grid * * @param layout : a grid layout */ public void setLayout(GridLayout layout) { this.layout = layout; } /** * Adds a filter to the next fetch. * * @param fieldName : the field name on which we want to filter * @param criterium : what must this filter be equal to */ public void addFilter(String fieldName, Object criterium) { Request request = store.getLastRequest(); request.getQuery().put(fieldName, criterium); if (dojoWidget != null) { dojoSetQuery(request.toJSObject()); } } /** * Removes a filter for the next fetch * * @param fieldName : the field on which we don't want to filter anymore */ public void removeFilter(String fieldName) { Request request = store.getLastRequest(); request.getQuery().remove(fieldName); if (dojoWidget != null) { dojoSetQuery(request.toJSObject()); } } /** * Internal method used to set query in dojo's widget * @param query */ private native void dojoSetQuery(JavaScriptObject query)/*-{ this.@com.objetdirect.tatami.client.AbstractDojo::dojoWidget.setQuery(query.query); }-*/; /** * Convenience method to get the content from a specific cell * * @param rowIndex * @param colIndex * @return the content from the cell at row "rowindex" and at column "colindex" * if this cell is filled from the datastore. Returns null otherwise */ public Object getDataAt(int rowIndex, int colIndex) { return getItemFromRow(rowIndex).getValue(layout.getCellAmongAllViews(colIndex).getField(), null); } /** * Forces the grid to reload data from the store for the given row * * @param rowIndex */ public void updateRow(int rowIndex) { if (dojoWidget != null) { dojoUpdateRow(rowIndex); } } /** * Internal method used to perform actual row update * * @param rowIndex */ private native void dojoUpdateRow(int rowIndex)/*-{ var grid = this.@com.objetdirect.tatami.client.AbstractDojo::dojoWidget; grid.updateRow(rowIndex); }-*/; /** * @return true if the grid should be rendered as soon as it is attached, false otherwise */ public boolean isRenderGridOnLoad() { return renderGridOnLoad; } /** * @param renderGridOnLoad : wether the grid should be rendered as soon as it is attached */ public void setRenderGridOnLoad(boolean renderGridOnLoad) { this.renderGridOnLoad = renderGridOnLoad; } /** * Used to determine which css classes should be applied to the * given row in the given state * * @param rowIndex : index of the row which we want to know th css classes * @param selected : wheter this row is currently selected or not * @param mouseover : wether the mouse is over the row * @param odd : wether the row index is odd * @return */ private String getRowCSSClasses(int rowIndex, boolean selected, boolean mouseover, boolean odd) { return styler.getRowCSSClasses(rowIndex, selected, mouseover, odd); } /** * Used to determine which css styles should be applied to the * given row in the given state * * @param rowIndex : index of the row which we want to know th css classes * @param selected : wheter this row is currently selected or not * @param mouseover : wether the mouse is over the row * @param odd : wether the row index is odd * @return */ private String getRowCSSStyles(int rowIndex, boolean selected, boolean mouseover, boolean odd) { return styler.getRowCSSStyles(rowIndex, selected, mouseover, odd); } /** * @return the current row styler */ public RowStyler getStyler() { return styler; } /** * @param styler sets the current row styler */ public void setStyler(RowStyler styler) { this.styler = styler; } public void adaptSize() { if (dojoWidget != null) { resizeGrid(dojoWidget); } } }