
com.extjs.gxt.ui.client.widget.grid.GridView Maven / Gradle / Ivy
/*
* Ext GWT - Ext for GWT
* Copyright(c) 2007, 2008, Ext JS, LLC.
* [email protected]
*
* http://extjs.com/license
*/
package com.extjs.gxt.ui.client.widget.grid;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Stack;
import com.extjs.gxt.ui.client.Events;
import com.extjs.gxt.ui.client.GXT;
import com.extjs.gxt.ui.client.XDOM;
import com.extjs.gxt.ui.client.Style.HorizontalAlignment;
import com.extjs.gxt.ui.client.Style.SortDir;
import com.extjs.gxt.ui.client.core.DomHelper;
import com.extjs.gxt.ui.client.core.El;
import com.extjs.gxt.ui.client.data.ModelData;
import com.extjs.gxt.ui.client.data.SortInfo;
import com.extjs.gxt.ui.client.event.BaseEvent;
import com.extjs.gxt.ui.client.event.BaseObservable;
import com.extjs.gxt.ui.client.event.ColumnModelEvent;
import com.extjs.gxt.ui.client.event.ComponentEvent;
import com.extjs.gxt.ui.client.event.DragEvent;
import com.extjs.gxt.ui.client.event.DragListener;
import com.extjs.gxt.ui.client.event.GridEvent;
import com.extjs.gxt.ui.client.event.Listener;
import com.extjs.gxt.ui.client.event.SelectionListener;
import com.extjs.gxt.ui.client.fx.Draggable;
import com.extjs.gxt.ui.client.js.JsArray;
import com.extjs.gxt.ui.client.store.ListStore;
import com.extjs.gxt.ui.client.store.Record;
import com.extjs.gxt.ui.client.store.StoreEvent;
import com.extjs.gxt.ui.client.store.StoreListener;
import com.extjs.gxt.ui.client.util.Point;
import com.extjs.gxt.ui.client.util.Region;
import com.extjs.gxt.ui.client.util.Size;
import com.extjs.gxt.ui.client.widget.BoxComponent;
import com.extjs.gxt.ui.client.widget.menu.CheckMenuItem;
import com.extjs.gxt.ui.client.widget.menu.Item;
import com.extjs.gxt.ui.client.widget.menu.Menu;
import com.extjs.gxt.ui.client.widget.menu.MenuItem;
import com.google.gwt.core.client.GWT;
import com.google.gwt.core.client.JavaScriptObject;
import com.google.gwt.dom.client.Element;
import com.google.gwt.dom.client.NodeList;
import com.google.gwt.dom.client.Style;
import com.google.gwt.dom.client.TableSectionElement;
import com.google.gwt.i18n.client.DateTimeFormat;
import com.google.gwt.i18n.client.NumberFormat;
import com.google.gwt.user.client.Command;
import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.DeferredCommand;
import com.google.gwt.user.client.Event;
import com.google.gwt.user.client.EventListener;
/**
*
* This class encapsulates the user interface of an {@link Grid}. Methods of
* this class may be used to access user interface elements to enable special
* display effects. Do not change the DOM structure of the user interface.
*
*
* This class does not provide ways to manipulate the underlying data. The data
* model of a Grid is held in an {@link ListStore}.
*
*
*/
public class GridView extends BaseObservable {
class GridSplitBar extends BoxComponent {
private int startX;
private int colIndex;
private boolean dragging;
private Draggable d;
private DragListener listener = new DragListener() {
@Override
public void dragEnd(DragEvent de) {
onDragEnd(de);
}
@Override
public void dragStart(DragEvent de) {
onDragStart(de);
}
};
protected void onDragEnd(DragEvent e) {
dragging = false;
headerDisabled = false;
setStyleAttribute("borderLeft", "none");
el().setStyleAttribute("opacity", "0");
el().setWidth(splitterWidth);
bar.el().setVisibility(false);
int endX = e.event.getClientX();
int diff = endX - startX;
onColumnSplitterMoved(colIndex, cm.getColumnWidth(colIndex) + diff);
}
protected void onDragStart(DragEvent e) {
headerDisabled = true;
dragging = true;
setStyleAttribute("borderLeft", "1px solid black");
setStyleAttribute("cursor", "default");
el().setStyleAttribute("opacity", "1");
el().setWidth(1);
startX = e.event.getClientX();
int cols = cm.getColumnCount();
for (int i = 0, len = cols; i < len; i++) {
Element hd = getHeaderCell(i);
if (hd != null) {
Region rr = El.fly(hd).getRegion();
if (startX > rr.right - 5 && startX < rr.right + 5) {
colIndex = findHeaderIndex(hd);
break;
}
}
}
if (colIndex > -1) {
Element c = getHeaderCell(colIndex);
int x = startX;
int minx = x - fly((com.google.gwt.user.client.Element) c).getX()
- grid.getMinColumnWidth();
int maxx = (grid.el().getX() + grid.el().getWidth()) - e.event.getClientX();
d.setXConstraint(minx, maxx);
}
}
protected void onMouseMove(Event event) {
if (dragging) {
return;
}
int x = event.getClientX();
Region r = activeHdRegion;
int hw = splitterWidth;
el().setY(grid.el().getY());
el().setHeight(grid.getHeight());
Style ss = getElement().getStyle();
if (x - r.left <= hw && cm.isResizable(activeHdIndex - 1)) {
bar.el().setVisibility(true);
el().setX(r.left);
ss.setProperty("cursor", GXT.isSafari ? "e-resize" : "col-resize");
} else if (r.right - x <= hw && cm.isResizable(activeHdIndex)) {
el().setX(r.right - (hw / 2));
bar.el().setVisibility(true);
ss.setProperty("cursor", GXT.isSafari ? "w-resize" : "col-resize");
} else {
bar.el().setVisibility(false);
ss.setProperty("cursor", "");
}
}
@Override
protected void onRender(com.google.gwt.user.client.Element target, int index) {
super.onRender(target, index);
setElement(DOM.createDiv(), target, index);
el().setStyleAttribute("cursor", "col-resize");
setStyleAttribute("position", "absolute");
setWidth(5);
el().setVisibility(false);
el().setStyleAttribute("backgroundColor", "white");
el().setStyleAttribute("opacity", "0");
d = new Draggable(this);
d.setUseProxy(false);
d.setConstrainVertical(true);
d.addDragListener(listener);
}
}
// config
protected boolean enableRowBody;
protected GridViewConfig viewConfig;
protected Grid grid;
protected ColumnModel cm;
protected ListStore ds;
protected String emptyText = " ";
protected boolean deferEmptyText;
protected boolean enableHdMenu = true;
protected boolean autoFill;
protected boolean forceFit;
protected int scrollOffset = 19;
protected boolean userResized;
protected int borderWidth = 2;
protected int splitterWidth = 5;
protected GridTemplates templates;
// elements
protected El el, mainWrap, mainHd, innerHd, scroller, mainBody, focusEl;
protected int activeHdIndex;
private boolean showDirtyCells = true;
private String cellSelector = "td.x-grid3-cell";
private String hdStyle = "x-grid3-hd";
private String rowSelector = "div.x-grid3-row";
private GridSplitBar bar;
private Menu menu;
private SortInfo sortState;
private int lastViewWidth;
private boolean headerDisabled;
private Element activeHd;
private Region activeHdRegion;
private Element activeHdBtn;
private StoreListener listener;
private EventListener headerListener;
private EventListener scrollListener;
private Listener columnListener;
private int cellSelectorDepth = 4;
private int rowSelectorDepth = 10;
private Element overRow;
/**
* Returns the cell.
*
* @param elem the cell element or a child element
* @return the cell element
*/
public Element findCell(Element elem) {
if (elem == null) {
return null;
}
return fly(elem).findParentElement(cellSelector, cellSelectorDepth);
}
/**
* Returns the cell index.
*
* @param elem the cell or child element
* @param requiredStyle an optional required style name
* @return the cell index or -1 if not found
*/
public int findCellIndex(Element elem, String requiredStyle) {
Element cell = findCell(elem);
if (cell != null && (requiredStyle == null || fly(cell).hasStyleName(requiredStyle))) {
return getCellIndex(cell);
}
return -1;
}
/**
* Returns the row element.
*
* @param el the row element or any child element
* @return the matching row element
*/
public Element findRow(Element el) {
if (el == null) {
return null;
}
return fly(el).findParentElement(rowSelector, rowSelectorDepth);
}
/**
* Returns the row index.
*
* @param elem the row or child of the row element
* @return the index
*/
public int findRowIndex(Element elem) {
Element r = findRow(elem);
return r != null ? r.getPropertyInt("rowIndex") : -1;
}
/**
* Focus the cell and scrolls into view.
*
* @param rowIndex the row index
* @param colIndex the column index
* @param hscroll true to scroll horizontally
*/
public void focusCell(int rowIndex, int colIndex, boolean hscroll) {
Point xy = ensureVisible(rowIndex, colIndex, hscroll);
if (xy != null) {
focusEl.setXY(xy);
if (GXT.isGecko) {
focusEl.setFocus(true);
} else {
DeferredCommand.addCommand(new Command() {
public void execute() {
focusEl.setFocus(true);
}
});
}
}
}
/**
* Focus the row and scrolls into view.
*
* @param rowIndex the row index
*/
public void focusRow(int rowIndex) {
focusCell(rowIndex, 0, true);
}
/**
* Returns the grid's body element.
*
* @return the body element
*/
public El getBody() {
return scroller;
}
/**
* Returns the grid's <TD> HtmlElement at the specified coordinates.
*
* @param row the row index in which to find the cell
* @param col the column index of the cell
* @return the <TD> at the specified coordinates
*/
public Element getCell(int row, int col) {
return getRow(row).getElementsByTagName("td").getItem(col);
}
/**
* Returns the cell selector depth.
*
* @return the cell selector depth
*/
public int getCellSelectorDepth() {
return cellSelectorDepth;
}
/**
* Returns the empty text.
*
* @return the empty text
*/
public String getEmptyText() {
return emptyText;
}
/**
* Returns the <TD> HtmlElement which represents the Grid's header cell for
* the specified column index.
*
* @param index the column index
* @return the <TD> element.
*/
public Element getHeaderCell(int index) {
return mainHd.dom.getElementsByTagName("td").getItem(index);
}
/**
* Return the <TR> HtmlElement which represents a Grid row for the
* specified index.
*
* @param row the row index
* @return the <TR> element
*/
public Element getRow(int row) {
Element[] rows = getRows();
if (row < rows.length) {
return rows[row];
}
return null;
}
/**
* Returns the row selector depth.
*
* @return the row selector depth
*/
public int getRowSelectorDepth() {
return rowSelectorDepth;
}
/**
* Returns the view config.
*
* @return the view config
*/
public GridViewConfig getViewConfig() {
return viewConfig;
}
/**
* Returns true if auto fill is enabled.
*
* @return true for auto fill
*/
public boolean isAutoFill() {
return autoFill;
}
/**
* Returns true if force fit is enabled.
*
* @return true for force fit
*/
public boolean isForceFit() {
return forceFit;
}
/**
* Returns true if dirty cell markers are enabled.
*
* @return true of dirty cell markers
*/
public boolean isShowDirtyCells() {
return showDirtyCells;
}
public void layout() {
if (mainBody == null) {
return;
}
El c = grid.el();
Size csize = c.getStyleSize();
int vw = csize.width;
int vh = 0;
if (vw < 10 || csize.height < 20) {
return;
}
resize();
if (forceFit) {
if (lastViewWidth != vw) {
fitColumns(false, false, -1);
lastViewWidth = vw;
}
} else {
autoExpand(false);
syncHeaderScroll();
}
templateOnLayout(vw, vh);
}
/**
* Rebuilds the grid using its current configuration and data.
*
* @param headerToo true to refresh the header
*/
public void refresh(boolean headerToo) {
if (grid instanceof EditorGrid) {
((EditorGrid) grid).stopEditing(true);
}
String result = renderBody();
mainBody.setInnerHtml(result);
if (headerToo) {
updateHeaders();
}
processRows(0, true);
layout();
applyEmptyText();
fireEvent(Events.Refresh);
}
/**
* Scrolls the grid to the top.
*/
public void scrollToTop() {
scroller.setScrollTop(0);
scroller.setScrollLeft(0);
}
/**
* True to auto expand the columns to fit the grid when the grid is
* created.
*
* @param autoFill true to expand
*/
public void setAutoFill(boolean autoFill) {
this.autoFill = autoFill;
}
/**
* The number of levels to search for cells in event delegation (defaults to
* 4).
*
* @param cellSelectorDepth the cell selector depth
*/
public void setCellSelectorDepth(int cellSelectorDepth) {
this.cellSelectorDepth = cellSelectorDepth;
}
/**
* Default text to display in the grid body when no rows are available
* (defaults to '').
*
* @param emptyText the empty text
*/
public void setEmptyText(String emptyText) {
this.emptyText = emptyText;
}
/**
* True to auto expand/contract the size of the columns to fit the grid width
* and prevent horizontal scrolling.
*
* @param forceFit true to force fit
*/
public void setForceFit(boolean forceFit) {
this.forceFit = forceFit;
}
/**
* The number of levels to search for rows in event delegation (defaults to
* 10).
*
* @param rowSelectorDepth the row selector depth
*/
public void setRowSelectorDepth(int rowSelectorDepth) {
this.rowSelectorDepth = rowSelectorDepth;
}
/**
* True to display a red triangle in the upper left corner of any cells which
* are "dirty" as defined by any existing records in the data store (defaults
* to true).
*
* @param showDirtyCells true to display the dirty flag
*/
public void setShowDirtyCells(boolean showDirtyCells) {
this.showDirtyCells = showDirtyCells;
}
/**
* Sets the view config.
*
* @param viewConfig the view config
*/
public void setViewConfig(GridViewConfig viewConfig) {
this.viewConfig = viewConfig;
}
protected void addRowStyle(Element elem, String style) {
if (elem != null) {
fly(elem).addStyleName(style);
}
}
protected void afterRender() {
applyEmptyText();
}
protected void autoExpand(boolean preventUpdate) {
if (!userResized && grid.getAutoExpandColumn() != null) {
int tw = cm.getTotalWidth(false);
int aw = grid.getWidth(true) - scrollOffset;
if (tw != aw) {
int ci = cm.getIndexById(grid.getAutoExpandColumn());
int currentWidth = cm.getColumnWidth(ci);
int cw = Math.min(Math.max(((aw - tw) + currentWidth), grid.getAutoExpandMin()),
grid.getAutoExpandMax());
if (cw != currentWidth) {
cm.setColumnWidth(ci, cw, true);
if (!preventUpdate) {
updateColumnWidth(ci, cw);
}
}
}
}
}
protected Menu createContextMenu(final int colIndex) {
final Menu menu = new Menu();
if (cm.isSortable(colIndex)) {
MenuItem item = new MenuItem();
item.setText(GXT.MESSAGES.gridView_sortAscText());
item.setIconStyle("my-icon-asc");
item.addSelectionListener(new SelectionListener() {
public void componentSelected(ComponentEvent ce) {
ds.sort(cm.getDataIndex(colIndex), SortDir.ASC);
}
});
menu.add(item);
item = new MenuItem();
item.setText(GXT.MESSAGES.gridView_sortDescText());
item.setIconStyle("my-icon-desc");
item.addSelectionListener(new SelectionListener() {
public void componentSelected(ComponentEvent ce) {
ds.sort(cm.getDataIndex(colIndex), SortDir.DESC);
}
});
menu.add(item);
}
MenuItem columns = new MenuItem();
columns.setText(GXT.MESSAGES.gridView_columnsText());
columns.setIconStyle("x-cols-icon");
final Menu columnMenu = new Menu();
int cols = cm.getColumnCount();
for (int i = 0; i < cols; i++) {
if (cm.getColumnHeader(i) == null || cm.getColumnHeader(i).equals("") || cm.isFixed(i)) {
continue;
}
final int fcol = i;
final CheckMenuItem check = new CheckMenuItem();
check.setHideOnClick(false);
check.setText(cm.getColumnHeader(i));
check.setChecked(!cm.isHidden(i));
check.addSelectionListener(new SelectionListener() {
public void componentSelected(ComponentEvent ce) {
cm.setHidden(fcol, !cm.isHidden(fcol));
restrictMenu(columnMenu);
}
});
columnMenu.add(check);
}
restrictMenu(columnMenu);
columns.setSubMenu(columnMenu);
menu.add(columns);
return menu;
}
protected void deleteRows(ListStore store, int firstRow, int lastRow) {
if (store.getCount() < 1) {
refresh(false);
return;
}
removeRows(firstRow, lastRow);
}
protected void doAttach() {
DOM.setEventListener(mainHd.dom, headerListener);
if (bar != null) {
DOM.setEventListener(bar.getElement(), bar);
}
if (scroller != null) {
DOM.setEventListener(scroller.dom, scrollListener);
}
}
protected void doDetach() {
DOM.setEventListener(mainHd.dom, null);
if (bar != null) {
DOM.setEventListener(bar.getElement(), null);
}
if (scroller != null) {
DOM.setEventListener(scroller.dom, null);
}
}
protected String doRender(List cs, List rows, int startRow, int colCount,
boolean stripe) {
int last = colCount - 1;
String tstyle = "width:" + getTotalWidth() + ";";
StringBuilder buf = new StringBuilder();
StringBuilder cb = new StringBuilder();
for (int j = 0; j < rows.size(); j++) {
ModelData model = (ModelData) rows.get(j);
Record r = ds.hasRecord(model) ? ds.getRecord(model) : null;
int rowIndex = (j + startRow);
for (int i = 0; i < colCount; i++) {
ColumnData c = cs.get(i);
c.css = c.css == null ? "" : c.css;
String rv = getRenderedValue(c, rowIndex, i, model, c.id);
String css = (i == 0 ? "x-grid-cell-first " : (i == last ? "x-grid3-cell-last " : " "))
+ " " + (c.css == null ? "" : c.css);
String attr = c.cellAttr != null ? c.cellAttr : "";
String cellAttr = c.cellAttr != null ? c.cellAttr : "";
if (showDirtyCells && r != null && r.getChanges().containsKey(c.id)) {
css += " x-grid3-dirty-cell";
}
cb.append("" + rv
+ " ");
}
String alt = "";
if (stripe && ((rowIndex + 1) % 2 == 0)) {
alt += " x-grid3-row-alt";
}
if (showDirtyCells && r != null && r.isDirty()) {
alt += " x-grid3-dirty-row";
}
if (viewConfig != null) {
alt += " " + viewConfig.getRowStyle(model, rowIndex, ds);
}
buf.append(""
+ cb.toString()
+ " "
+ (enableRowBody
? ("${body} ")
: "") + "
");
cb = new StringBuilder();
}
return buf.toString();
}
protected Point ensureVisible(int row, int col, boolean hscroll) {
if (row < 0 || row > ds.getCount()) {
return null;
}
if (col == -1) {
col = 0;
}
Element rowEl = getRow(row);
Element cellEl = null;
if (!(!hscroll && col == 0)) {
while (cm.isHidden(col)) {
col++;
}
cellEl = getCell(row, col);
}
if (rowEl == null) {
return null;
}
Element c = scroller.dom;
int ctop = 0;
Element p = rowEl, stope = el.dom;
while (p != null && p != stope) {
ctop += p.getOffsetTop();
p = p.getOffsetParent().cast();
}
ctop -= mainHd.dom.getOffsetHeight();
int cbot = ctop + rowEl.getOffsetHeight();
int ch = c.getOffsetHeight();
int stop = c.getScrollTop();
int sbot = stop + ch;
if (ctop < stop) {
c.setScrollTop(ctop);
} else if (cbot > sbot) {
if (hscroll && (cm.getTotalWidth() > scroller.getWidth() - scrollOffset)) {
cbot += scrollOffset;
}
c.setScrollTop(cbot -= ch);
}
if (hscroll) {
int cleft = cellEl.getOffsetLeft();
int cright = cleft + cellEl.getOffsetWidth();
int sleft = c.getScrollLeft();
int sright = sleft + c.getOffsetWidth();
if (cleft < sleft) {
c.setScrollLeft(cleft);
} else if (cright > sright) {
c.setScrollLeft(cright - scroller.getStyleWidth());
}
}
return cellEl != null ? fly(cellEl).getXY() : new Point(c.getScrollLeft(), fly(rowEl).getY());
}
protected Element findHeaderCell(Element elem) {
Element cell = findCell(elem);
return cell != null && fly(cell).hasStyleName(hdStyle) ? cell : null;
}
protected int findHeaderIndex(Element elem) {
return findCellIndex(elem, hdStyle);
}
protected void fitColumns(boolean preventRefresh, boolean onlyExpand, int omitColumn) {
int tw = cm.getTotalWidth(false);
double aw = grid.el().getWidth(true) - scrollOffset;
if (aw < 0) {
aw = grid.el().getStyleWidth();
}
if (aw < 20 || aw > 2000) { // not initialized, so don't screw up the
// default widths
return;
}
int extra = (int) aw - tw;
if (extra == 0) {
return;
}
int vc = cm.getColumnCount(true);
int ac = vc - (omitColumn != -1 ? 1 : 0);
if (ac == 0) {
ac = 1;
omitColumn = -1;
}
int colCount = cm.getColumnCount();
Stack cols = new Stack();
int extraCol = 0;
int width = 0;
int w;
for (int i = 0; i < colCount; i++) {
if (!cm.isHidden(i) && !cm.isFixed(i) && i != omitColumn) {
w = cm.getColumnWidth(i);
cols.push(i);
extraCol = i;
cols.push(w);
width += w;
}
}
double frac = (aw - cm.getTotalWidth()) / width;
while (cols.size() > 0) {
w = cols.pop();
int i = cols.pop();
int ww = Math.max(grid.getMinColumnWidth(), (int) Math.floor(w + w * frac));
cm.setColumnWidth(i, ww, true);
}
if ((tw = cm.getTotalWidth(false)) > aw) {
int adjustCol = ac != vc ? omitColumn : extraCol;
cm.setColumnWidth(adjustCol, (int) Math.max(1, cm.getColumnWidth(adjustCol) - (tw - aw)),
true);
}
if (!preventRefresh) {
updateAllColumnWidths();
}
}
protected El fly(Element elem) {
return El.fly(elem);
}
protected int getCellIndex(Element elem) {
if (elem != null) {
String id = getCellIndexId(elem);
if (id != null) {
return cm.getIndexById(id);
}
}
return -1;
}
protected List getColumnData() {
int colCount = cm.getColumnCount();
List cs = new ArrayList();
for (int i = 0; i < colCount; i++) {
String name = cm.getDataIndex(i);
ColumnData data = new ColumnData();
data.name = name == null ? cm.getColumnId(i) : name;
data.renderer = cm.getRenderer(i);
data.id = cm.getColumnId(i);
data.style = getColumnStyle(i, false);
cs.add(data);
}
return cs;
}
protected Element getEditorParent() {
return scroller.dom;
}
protected String getRenderedValue(ColumnData data, int rowIndex, int colIndex, ModelData m,
String property) {
GridCellRenderer r = cm.getRenderer(colIndex);
if (r != null) {
return r.render(ds.getAt(rowIndex), property, data, rowIndex, colIndex, ds);
}
Object val = m.get(property);
ColumnConfig c = cm.getColumn(colIndex);
if (val != null && c.getNumberFormat() != null) {
Number n = (Number) val;
NumberFormat nf = cm.getColumn(colIndex).getNumberFormat();
val = nf.format(n.doubleValue());
} else if (val != null && c.getDateTimeFormat() != null) {
DateTimeFormat dtf = c.getDateTimeFormat();
val = dtf.format((Date) val);
}
if (val != null) {
return val.toString();
}
return "";
}
protected Element[] getRows() {
if (!hasRows()) {
return new Element[0];
}
NodeList ns = mainBody.dom.getChildNodes();
Element[] rows = new Element[ns.getLength()];
for (int i = 0, len = ns.getLength(); i < len; i++) {
rows[i] = ns.getItem(i).cast();
}
return rows;
}
protected native JavaScriptObject getRowsNative(Element body) /*-{
var fc = body.firstChild;
if (fc && fc.className == 'x-grid-empty') {
return [];
}
return body.childNodes;
}-*/;
protected Point getScrollState() {
return new Point(scroller.getScrollLeft(), scroller.getScrollTop());
}
protected String getTotalWidth() {
return cm.getTotalWidth() + "px";
}
protected void handleComponentEvent(ComponentEvent ce) {
Element row = findRow(ce.getTarget());
switch (ce.type) {
case Event.ONMOUSEOVER:
if (row != null) onRowOver(row);
break;
case Event.ONMOUSEOUT:
if (overRow != null) onRowOut(overRow);
break;
case Event.ONMOUSEDOWN:
onMouseDown((GridEvent) ce);
break;
}
}
/**
* Initializes the view.
*
* @param grid the grid
*/
protected void init(Grid grid) {
this.grid = grid;
this.cm = grid.getColumnModel();
initListeners();
initTemplates();
initData(grid.getStore(), cm);
initUI(grid);
}
/**
* Initializes the data.
*
* @param ds the data store
* @param cm the column model
*/
protected void initData(ListStore ds, ColumnModel cm) {
if (this.ds != null) {
this.ds.removeStoreListener(listener);
}
if (ds != null) {
ds.addStoreListener(listener);
}
this.ds = ds;
if (this.cm != null) {
this.cm.removeListener(Events.HiddenChange, columnListener);
this.cm.removeListener(Events.HeaderChange, columnListener);
this.cm.removeListener(Events.WidthChange, columnListener);
}
if (cm != null) {
cm.addListener(Events.HiddenChange, columnListener);
cm.addListener(Events.HeaderChange, columnListener);
cm.addListener(Events.WidthChange, columnListener);
}
this.cm = cm;
}
protected void initElements() {
NodeList cs = grid.getElement().getFirstChild().getChildNodes();
el = grid.el().firstChild();
mainWrap = new El((com.google.gwt.user.client.Element) cs.getItem(0));
mainHd = mainWrap.firstChild();
if (grid.isHideHeaders()) {
mainHd.setVisible(false);
}
innerHd = mainHd.firstChild();
scroller = mainWrap.getChild(1);
scroller.addEventsSunk(Event.ONSCROLL);
if (forceFit) {
scroller.setStyleAttribute("overflowX", "hidden");
}
mainBody = scroller.firstChild();
focusEl = scroller.getChild(1);
}
protected void initListeners() {
listener = new StoreListener() {
@Override
public void storeAdd(StoreEvent se) {
onAdd(ds, se.models, se.index);
}
@Override
public void storeBeforeDataChanged(StoreEvent se) {
onBeforeDataChanged(se);
}
@Override
public void storeClear(StoreEvent se) {
onClear(se);
}
@Override
public void storeDataChanged(StoreEvent se) {
onDataChanged(se);
}
@Override
public void storeFilter(StoreEvent se) {
onDataChanged(se);
}
@Override
public void storeRemove(StoreEvent se) {
onRemove(ds, se.model, se.index, false);
}
@Override
public void storeUpdate(StoreEvent se) {
onUpdate(ds, se.model);
}
};
headerListener = new EventListener() {
public void onBrowserEvent(Event event) {
switch (DOM.eventGetType(event)) {
case Event.ONMOUSEOVER:
handleHdOver(event);
break;
case Event.ONMOUSEOUT:
handleHdOut(event);
break;
case Event.ONMOUSEMOVE:
handleHdMove(event);
break;
case Event.ONCLICK:
handleHdDown(event);
break;
}
}
};
scrollListener = new EventListener() {
public void onBrowserEvent(Event event) {
if (event.getTypeInt() == Event.ONSCROLL) {
syncScroll();
}
}
};
columnListener = new Listener() {
public void handleEvent(ColumnModelEvent be) {
switch (be.type) {
case Events.HiddenChange:
onHiddenChange(cm, be.colIndex, be.hidden);
break;
case Events.HeaderChange:
onHeaderChange();
break;
case Events.WidthChange:
onColumnWidthChange(be.colIndex, be.width);
break;
}
}
};
}
protected void initTemplates() {
templates = GWT.create(GridTemplates.class);
}
protected void initUI(final Grid grid) {
grid.addListener(Events.HeaderClick, new Listener() {
public void handleEvent(GridEvent e) {
if (!e.getTargetEl().is(".x-grid3-hd-btn")) {
onHeaderClick(grid, e.colIndex);
}
}
});
}
protected void insertRows(ListStore store, int firstRow, int lastRow, boolean isUpdate) {
if (isUpdate && firstRow == 0 && lastRow == store.getCount() - 1) {
refresh(false);
return;
}
Element e = mainBody.dom.getFirstChildElement();
if (e != null && !hasRows()) {
mainBody.dom.setInnerHTML("");
}
String html = renderRows(firstRow, lastRow);
Element before = getRow(firstRow);
if (before != null) {
DomHelper.insertBefore((com.google.gwt.user.client.Element) before, html);
} else {
DomHelper.insertHtml("beforeEnd", mainBody.dom, html);
}
if (!isUpdate) {
processRows(firstRow, false);
}
focusRow(firstRow);
}
protected void onAdd(ListStore store, List models, int index) {
insertRows(store, index, index + (models.size() - 1), false);
}
protected void onBeforeDataChanged(StoreEvent se) {
if (grid.isLoadMask()) {
grid.el().mask(GXT.MESSAGES.loadMask_msg());
}
}
protected void onCellDeselect(int row, int col) {
Element cell = getCell(row, col);
if (cell != null) {
fly(cell).removeStyleName("x-grid3-cell-selected");
}
}
protected void onCellSelect(int row, int col) {
Element cell = getCell(row, col);
if (cell != null) {
fly(cell).addStyleName("x-grid3-cell-selected");
}
}
protected void onClear(StoreEvent se) {
refresh(false);
}
protected void onClick(ComponentEvent ce) {
Element row = findRow(ce.getTarget());
if (row != null) {
GridEvent e = (GridEvent) ce;
e.rowIndex = findRowIndex(row);
grid.fireEvent(Events.RowClick, e);
}
}
protected void onColumnMove(ColumnModel cm, int oldIndex, int newIndex) {
// indexMap = null;
Point s = getScrollState();
refresh(true);
restoreScroll(s);
templateAfterMove(newIndex);
}
protected void onColumnSplitterMoved(int colIndex, int width) {
userResized = true;
width = Math.max(grid.getMinColumnWidth(), width);
cm.setColumnWidth(colIndex, width);
if (forceFit) {
fitColumns(true, false, colIndex);
updateAllColumnWidths();
} else {
updateColumnWidth(colIndex, width);
if (GXT.isIE) {
syncHeaderScroll();
}
}
GridEvent e = new GridEvent(grid);
e.colIndex = colIndex;
e.width = width;
grid.fireEvent(Events.ColumnResize, e);
}
protected void onColumnWidthChange(int column, int width) {
updateColumnWidth(column, width);
}
protected void onDataChanged(StoreEvent se) {
refresh(false);
updateHeaderSortState();
if (grid.isLoadMask()) {
grid.el().unmask();
}
}
protected void onHeaderChange() {
updateHeaders();
}
protected void onHeaderClick(Grid grid, int column) {
if (headerDisabled || !cm.isSortable(column)) {
return;
}
grid.store.sort(cm.getDataIndex(column), null);
}
protected void onHiddenChange(ColumnModel cm, int col, boolean hidden) {
updateColumnHidden(col, hidden);
}
protected void onMouseDown(GridEvent ge) {
}
protected void onRemove(ListStore ds, ModelData m, int index, boolean isUpdate) {
removeRow(index);
processRows(0, false);
applyEmptyText();
}
protected void onRowDeselect(int rowIndex) {
Element row = getRow(rowIndex);
if (row != null) {
removeRowStyle(row, "x-grid3-row-selected");
}
}
protected void onRowOut(Element row) {
if (grid.isTrackMouseOver() && overRow != null) {
removeRowStyle(overRow, "x-grid3-row-over");
overRow = null;
}
}
protected void onRowOver(Element row) {
if (grid.isTrackMouseOver()) {
if (overRow != row) {
addRowStyle(row, "x-grid3-row-over");
overRow = row;
}
}
}
protected void onRowSelect(int rowIndex) {
Element row = getRow(rowIndex);
if (row != null) {
onRowOut(row);
addRowStyle(row, "x-grid3-row-selected");
}
}
protected void onUpdate(ListStore store, ModelData model) {
refreshRow(store.indexOf(model));
}
protected void processRows(int startRow, boolean skipStripe) {
if (ds.getCount() < 1) {
return;
}
skipStripe = !skipStripe || !grid.isStripeRows();
Element[] rows = getRows();
String cls = " x-grid3-row-alt ";
for (int i = 0, len = rows.length; i < len; i++) {
Element row = rows[i];
row.setPropertyInt("rowIndex", i);
if (!skipStripe) {
boolean isAlt = ((i + 1) % 2 == 0);
boolean hasAlt = (" " + row.getClassName() + " ").indexOf(cls) != -1;
if (isAlt == hasAlt) {
continue;
}
if (isAlt) {
row.setClassName(row.getClassName() + " " + cls);
} else {
row.setClassName(row.getClassName().replaceFirst(cls, ""));
}
}
}
}
protected void refreshRow(int row) {
ModelData m = ds.getAt(row);
if (m != null) {
insertRows(ds, row, row, true);
getRow(row).setPropertyInt("rowIndex", row);
onRemove(ds, m, row + 1, true);
GridEvent e = new GridEvent(grid);
e.rowIndex = row;
e.model = ds.getAt(row);
fireEvent(Events.RowUpdated, e);
}
}
protected void removeRow(int row) {
Element r = getRow(row);
if (r != null) {
fly(r).removeFromParent();
}
}
protected void removeRows(int firstRow, int lastRow) {
for (int rowIndex = firstRow; rowIndex <= lastRow; rowIndex++) {
mainBody.getChild(firstRow).removeFromParent();
}
}
protected void removeRowStyle(Element row, String style) {
fly(row).removeStyleName(style);
}
protected void render() {
this.cm = grid.getColumnModel();
if (this.autoFill) {
fitColumns(true, true, -1);
} else if (this.forceFit) {
fitColumns(true, false, -1);
} else if (this.grid.getAutoExpandColumn() != null) {
this.autoExpand(true);
}
renderUI();
grid.sinkEvents(Event.ONCLICK | Event.ONDBLCLICK | Event.MOUSEEVENTS);
}
protected String renderBody() {
String markup = renderRows(0, -1);
return templates.body(markup);
}
protected String renderHeaders() {
int count = cm.getColumnCount();
StringBuffer sb = new StringBuffer();
for (int i = 0; i < count; i++) {
String istyle = cm.getColumnAlignment(i) == HorizontalAlignment.RIGHT ? "padding-right:16px"
: null;
sb.append(templates.headerCell(cm.getColumnId(i), cm.getColumnHeader(i), getColumnStyle(i,
true), cm.getColumnToolTip(i), istyle, GXT.BLANK_IMAGE_URL, enableHdMenu));
}
return templates.header(sb.toString(), "width: " + getTotalWidth() + ";");
}
protected String renderRows(int startRow, int endRow) {
int colCount = cm.getColumnCount();
if (ds.getCount() < 1) {
return "";
}
List cs = getColumnData();
if (endRow == -1) {
endRow = ds.getCount() - 1;
}
List rs = ds.getRange(startRow, endRow);
return doRender(cs, rs, startRow, colCount, grid.isStripeRows());
}
protected void renderUI() {
String header = renderHeaders();
String body = templates.body("");
String html = templates.master(body, header);
grid.getElement().setInnerHTML(html);
initElements();
mainBody.setInnerHtml(renderRows(0, -1));
processRows(0, true);
if (grid.isEnableColumnResize()) {
bar = new GridSplitBar();
bar.render(grid.getElement());
}
mainHd.addEventsSunk(Event.MOUSEEVENTS | Event.ONCLICK);
updateHeaderSortState();
if (!GXT.isGecko) {
grid.disableTextSelection(true);
}
updateInnerHeaders();
}
protected void resize() {
if (mainBody == null) {
return;
}
El c = grid.el();
Size csize = c.getStyleSize();
int vw = csize.width;
int vh = 0;
if (vw < 10 || csize.height < 20) {
return;
}
if (grid.isAutoHeight()) {
el.setWidth(csize.width);
scroller.dom.getStyle().setProperty("overflow", "visible");
} else {
el.setSize(csize.width, csize.height);
int hdHeight = mainHd.getHeight();
vh = csize.height - hdHeight;
scroller.setSize(vw + 1, vh);
if (innerHd != null) {
innerHd.dom.getStyle().setProperty("width", vw + "px");
}
}
}
protected void restoreScroll(Point state) {
scroller.setScrollLeft(state.x);
scroller.setScrollTop(state.y);
}
protected void templateAfterMove(int index) {
// template method
}
protected void templateOnAllColumnWidthsUpdated(List ws, String tw) {
// template method
}
protected void templateOnColumnHiddenUpdated(int col, boolean hidden, String tw) {
// template method
}
protected void templateOnColumnWidthUpdated(int col, String w, String tw) {
// template method
}
protected void templateOnLayout(int vw, int vh) {
// template method
}
protected void templateUpdateColumnText(int col, String text) {
// template method
}
protected void updateAllColumnWidths() {
String tw = getTotalWidth();
int clen = cm.getColumnCount();
Stack ws = new Stack();
JsArray jsws = new JsArray();
for (int i = 0; i < clen; i++) {
ws.push(getColumnWidth(i));
jsws.add(getColumnWidth(i));
}
innerHd.firstChild().firstChild().setWidth(tw);
for (int i = 0; i < clen; i++) {
Element hd = getHeaderCell(i);
hd.getStyle().setProperty("width", ws.get(i));
}
JavaScriptObject rows = getRowsNative(mainBody.dom);
updateAllColumnWidthsNative(rows, clen, tw, jsws.getJsObject());
updateInnerHeaders();
templateOnAllColumnWidthsUpdated(ws, tw);
}
protected void updateColumnHidden(int index, boolean hidden) {
String tw = getTotalWidth();
String display = hidden ? "none" : "";
innerHd.dom.getFirstChildElement().getFirstChildElement().getStyle().setProperty("width", tw);
Element hd = getHeaderCell(index);
hd.getStyle().setProperty("display", display);
Element[] ns = getRows();
for (int i = 0, len = ns.length; i < len; i++) {
Element elem = ns[i];
elem.getStyle().setProperty("width", tw);
elem.getFirstChildElement().getStyle().setProperty("width", tw);
TableSectionElement e = elem.getFirstChild().cast();
Element cell = e.getRows().getItem(0).getChildNodes().getItem(index).cast();
cell.getStyle().setProperty("display", display);
}
templateOnColumnHiddenUpdated(index, hidden, tw);
lastViewWidth = -1;
layout();
}
protected void updateColumnWidth(int col, int width) {
String tw = getTotalWidth();
String w = getColumnWidth(col);
innerHd.firstChild().firstChild().setWidth(tw);
Element hd = getHeaderCell(col);
hd.getStyle().setProperty("width", w);
updateColumnWidthNative(getRowsNative(mainBody.dom), col, w, tw);
updateInnerHeaders();
templateOnColumnWidthUpdated(col, w, tw);
}
protected void updateHeaders() {
innerHd.firstChild().setInnerHtml(renderHeaders());
}
protected void updateHeaderSortState() {
SortInfo state = ds.getSortState();
if (state == null || state.getSortField() == null) {
return;
}
if (sortState == null || (!sortState.getSortField().equals(state.getSortField()))
|| sortState.getSortDir() != state.getSortDir()) {
GridEvent e = new GridEvent(grid);
e.sortInfo = state;
grid.fireEvent(Events.SortChange, e);
}
sortState = new SortInfo(state.getSortField(), state.getSortDir());
int sortColumn = cm.findColumnIndex(state.getSortField());
if (sortColumn != -1) {
updateSortIcon(sortColumn, state.getSortDir());
}
}
protected void updateSortIcon(int colIndex, SortDir dir) {
Element[] hds = mainHd.select("td");
for (int i = 0; i < hds.length; i++) {
fly(hds[i]).removeStyleName("sort-asc");
fly(hds[i]).removeStyleName("sort-desc");
}
String s = dir == SortDir.DESC ? "sort-desc" : "sort-asc";
fly(hds[colIndex]).addStyleName(s);
}
private void applyEmptyText() {
if (emptyText != null && !hasRows()) {
String w = getTotalWidth();
mainBody.setInnerHtml("" + emptyText
+ "");
}
}
private native String getCellIndexId(Element elem) /*-{
if (!$wnd.GXT.___colRe) {
$wnd.GXT.___colRe = new RegExp("x-grid3-td-([^\\s]+)");
}
if (elem) {
var m = elem.className.match($wnd.GXT.___colRe);
if(m && m[1]){
return m[1];
}
}
return null;
}-*/;
private String getColumnStyle(int colIndex, boolean isHeader) {
String style = !isHeader ? cm.getColumnStyle(colIndex) : "";
if (style == null) style = "";
style += "width:" + getColumnWidth(colIndex) + ";";
if (cm.isHidden(colIndex)) {
style += "display:none;";
}
HorizontalAlignment align = cm.getColumnAlignment(colIndex);
if (align != null) {
style += "text-align:" + align.name() + ";";
}
return style;
}
private String getColumnWidth(int col) {
int w = cm.getColumnWidth(col);
return (XDOM.isVisibleBox ? w : (w - borderWidth > 0 ? w - borderWidth : 0)) + "px";
}
private void handleHdDown(Event e) {
if (activeHdBtn == null) {
return;
}
if (DOM.isOrHasChild((com.google.gwt.user.client.Element) activeHdBtn,
(com.google.gwt.user.client.Element) e.getTarget())) {
e.cancelBubble(true);
e.preventDefault();
final Element hd = findHeaderCell(e.getTarget());
fly(hd).addStyleName("x-grid3-hd-menu-open");
int colIndex = findHeaderIndex(e.getTarget());
menu = createContextMenu(colIndex);
GridEvent ge = new GridEvent(grid);
ge.colIndex = colIndex;
ge.menu = menu;
if (!grid.fireEvent(Events.HeaderContextMenu, ge)) {
return;
}
menu.addListener(Events.Hide, new Listener() {
public void handleEvent(BaseEvent be) {
fly(hd).removeStyleName("x-grid3-hd-menu-open");
}
});
menu.show((com.google.gwt.user.client.Element) activeHdBtn, "tl-bl?");
}
}
private void handleHdMove(Event e) {
if (activeHd != null && !headerDisabled && bar != null) {
bar.onMouseMove(e);
}
}
private void handleHdOut(Event e) {
Element hd = findHeaderCell(e.getTarget());
if (hd != null) {
activeHd = null;
fly(hd).removeStyleName("x-grid3-hd-over");
hd.getStyle().setProperty("cursor", "");
}
}
private void handleHdOver(Event e) {
Element hd = findHeaderCell(e.getTarget());
if (hd != null && !headerDisabled) {
activeHd = hd;
activeHdIndex = getCellIndex(hd);
activeHdRegion = fly(hd).getRegion();
if (!cm.isMenuDisabled(activeHdIndex)) {
fly(hd).addStyleName("x-grid3-hd-over");
activeHdBtn = fly(hd).childElement(".x-grid3-hd-btn");
if (activeHdBtn != null) {
activeHdBtn.getStyle().setProperty("height", fly(hd).firstChild().getHeight() + "px");
}
}
}
}
private boolean hasRows() {
Element e = mainBody.dom.getFirstChildElement();
return e != null && !e.getClassName().equals("x-grid-empty");
}
private void restrictMenu(Menu columns) {
int count = 0;
for (int i = 0, len = cm.getColumnCount(); i < len; i++) {
if (!cm.isHidden(i) && !cm.isFixed(i)) {
count++;
}
}
if (count == 1) {
for (Item item : columns.getItems()) {
CheckMenuItem ci = (CheckMenuItem) item;
if (ci.isChecked()) {
ci.disable();
}
}
} else {
for (Item item : columns.getItems()) {
item.enable();
}
}
}
private void syncHeaderScroll() {
innerHd.setScrollLeft(scroller.getScrollLeft());
// second time for IE (1/2 time first fails, other browsers ignore)
innerHd.setScrollLeft(scroller.getScrollLeft());
}
private void syncScroll() {
syncHeaderScroll();
GridEvent ge = new GridEvent(grid);
ge.scrollLeft = scroller.getScrollLeft();
ge.scrollTop = scroller.getScrollTop();
grid.fireEvent(Events.BodyScroll, ge);
}
private native void updateAllColumnWidthsNative(JavaScriptObject rows, int clen, String tw,
JavaScriptObject ws) /*-{
var ns = rows;
for(var i = 0, len = ns.length; i < len; i++){
ns[i].style.width = tw;
ns[i].firstChild.style.width = tw;
var row = ns[i].firstChild.rows[0];
for(var j = 0; j < clen; j++){
row.childNodes[j].style.width = ws[j];
}
}
}-*/;
private native void updateColumnWidthNative(JavaScriptObject rows, int col, String w, String tw) /*-{
var ns = rows;
for(var i = 0, len = ns.length; i < len; i++){
ns[i].style.width = tw;
ns[i].firstChild.style.width = tw;
ns[i].firstChild.rows[0].childNodes[col].style.width = w;
}
}-*/;
private void updateInnerHeaders() {
int clen = cm.getColumnCount();
for (int i = 0; i < clen; i++) {
Element hd = getHeaderCell(i);
El.fly(hd.getFirstChildElement()).setWidth(cm.getColumnWidth(i) - 2, true);
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy