![JAR search and dependency download from the Maven repository](/logo.png)
com.sksamuel.jqm4gwt.table.JQMTableGrid Maven / Gradle / Ivy
package com.sksamuel.jqm4gwt.table;
import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import com.google.gwt.dom.client.Document;
import com.google.gwt.dom.client.Element;
import com.google.gwt.dom.client.ImageElement;
import com.google.gwt.dom.client.TableCellElement;
import com.google.gwt.dom.client.TableRowElement;
import com.google.gwt.event.logical.shared.ValueChangeEvent;
import com.google.gwt.event.logical.shared.ValueChangeHandler;
import com.google.gwt.event.shared.HandlerRegistration;
import com.google.gwt.uibinder.client.UiChild;
import com.google.gwt.user.client.ui.ComplexPanel;
import com.google.gwt.user.client.ui.HasValue;
import com.google.gwt.user.client.ui.Widget;
import com.sksamuel.jqm4gwt.Empty;
import com.sksamuel.jqm4gwt.JQMCommon;
import com.sksamuel.jqm4gwt.JQMContext;
import com.sksamuel.jqm4gwt.JQMContext.WidgetDefaults;
import com.sksamuel.jqm4gwt.StrUtils;
import com.sksamuel.jqm4gwt.html.CustomFlowPanel;
/**
* Advanced wrapper for Html Table element. Not supposed to be used "per se", see descendants.
*
* @author slavap
*/
public class JQMTableGrid extends CustomFlowPanel implements HasValue> {
// both are used in jqm4gwt.css
public static final String THEAD_GROUPS = "jqm4gwt-thead-groups";
public static final String IMG_ONLY = "img-only";
protected boolean loaded;
protected final ComplexPanel tHead;
protected final ComplexPanel tBody;
protected ComplexPanel tFoot;
/** populated based on colNames parsing */
protected final List columns = new ArrayList();
/** populated directly by addColTitleWidget(), probably from UiBinder template */
protected final Map colTitleWidgets = new LinkedHashMap();
/** populated based on colGroups parsing */
protected final List headGroups = new ArrayList();
/** populated directly by addColGroupWidget(), probably from UiBinder template */
protected final Map colGroupWidgets = new LinkedHashMap();
protected String colNames;
protected String colGroups;
protected String footColTitles;
private String cells;
private Collection dataStr;
/** Boolean - true means add as <th> */
private Map dataObj;
protected JQMTableGrid() {
super(Document.get().createTableElement());
Element table = getElement();
table.setId(Document.get().createUniqueId());
tHead = new CustomFlowPanel(Document.get().createTHeadElement());
tBody = new CustomFlowPanel(Document.get().createTBodyElement());
add(tHead);
add(tBody);
}
@Override
protected void onLoad() {
super.onLoad();
loaded = true;
WidgetDefaults dflt = JQMContext.getWidgetDefaults();
if (dflt != null) dflt.loaded(this);
if (tBody.getWidgetCount() == 0 && !colTitleWidgets.isEmpty()) populateBody();
}
@Override
protected void onUnload() {
loaded = false;
super.onUnload();
}
protected void checkFooterCreated() {
if (tFoot != null) return;
tFoot = new CustomFlowPanel(Document.get().createTFootElement());
int i = getWidgetIndex(tBody);
if (i >= 0) insert(tFoot, i);
else add(tFoot);
}
public String getColNames() {
return colNames;
}
/**
* @param colNames - comma separated column titles with optional priority (1 = highest, 6 = lowest),
* formally: title~name=priority
*
If you need comma in title use \, to preserve it.
*
Column title can be valid HTML, i.e. <abbr title="Rotten Tomato Rating">Rating</abbr>=1
*
Example: Rank,Movie Title,Year=3,Reviews=5
*
To make a column persistent so it's not available for hiding, just omit priority.
* This will make the column visible at all widths and won't be available in the column chooser menu.
*/
public void setColNames(String colNames) {
if (this.colNames == colNames || this.colNames != null && this.colNames.equals(colNames)) return;
this.colNames = colNames;
if (Empty.is(this.colNames)) {
populateColumns(null);
return;
}
List lst = StrUtils.commaSplit(this.colNames);
List cols = new ArrayList();
for (String i : lst) {
String s = StrUtils.replaceAllBackslashCommas(i.trim());
cols.add(ColumnDef.create(s, false/*headGroup*/));
}
populateColumns(cols);
}
public String getFootColTitles() {
return footColTitles;
}
/**
* @param footColTitles - comma separated footer column titles.
*
If you need comma in title use \, to preserve it.
*
Column title can be valid HTML, i.e. <abbr title="Rotten Tomato Rating">Rating</abbr>
*
Example: Rank,Movie Title,Year,Reviews
*/
public void setFootColTitles(String footColTitles) {
if (this.footColTitles == footColTitles
|| this.footColTitles != null && this.footColTitles.equals(footColTitles)) return;
this.footColTitles = footColTitles;
if (Empty.is(this.footColTitles)) {
if (tFoot != null) {
tFoot.clear();
tFoot.getElement().setInnerText(null);
}
return;
}
if (tFoot != null) {
tFoot.clear();
tFoot.getElement().setInnerText(null);
} else {
checkFooterCreated();
}
TableRowElement tr = Document.get().createTRElement();
List lst = StrUtils.commaSplit(this.footColTitles);
for (String i : lst) {
String s = StrUtils.replaceAllBackslashCommas(i.trim());
TableCellElement th = Document.get().createTHElement();
th.setInnerHTML(s);
tr.appendChild(th);
}
tFoot.getElement().insertFirst(tr);
}
public String getColGroups() {
return colGroups;
}
/**
* @param colGroups - comma separated grouped column headers(titles) with colspan, rowspan,
* and priority (1 = highest, 6 = lowest),
* formally: colspan;rowspan=GroupTitle~ColumnName=priority
*
If you need comma in title use \, to preserve it.
*
Group title can be valid HTML, i.e. 4=<abbr title="Previous Year Results">2012</abbr>=1
*
Example: 3=Q1 2012=5, 3=Q2 2012=4, 3=Q3 2012=3, 3=Q4 2012=2, 3=2012 Totals=1
*/
public void setColGroups(String colGroups) {
if (this.colGroups == colGroups || this.colGroups != null && this.colGroups.equals(colGroups)) return;
this.colGroups = colGroups;
colGroupWidgets.clear();
if (this.colGroups == null || this.colGroups.isEmpty()) {
setHeadGroups(null);
return;
}
List lst = StrUtils.commaSplit(this.colGroups);
Set groups = new LinkedHashSet();
for (String i : lst) {
String s = StrUtils.replaceAllBackslashCommas(i.trim());
groups.add(ColumnDef.create(s, true/*headGroup*/));
}
setHeadGroups(groups);
}
private void setHeadGroups(Set groups) {
headGroups.clear();
if (groups != null) headGroups.addAll(groups);
populateHeadGroups();
}
public String getCells() {
return cells;
}
/**
* @param cells - comma separated table cells, each string/cell can be valid HTML.
* If you need comma in cell value use \, to preserve it.
*
Example: <th>1</th>, The Matrix, 1999, 8.7, <th>2</th>, Falling Down, 1993, 7.5
*/
public void setCells(String cells) {
if (this.cells == cells || this.cells != null && this.cells.equals(cells)) return;
this.cells = cells;
if (this.cells == null || this.cells.isEmpty()) {
setDataStr(null);
return;
}
List lst = StrUtils.commaSplit(this.cells);
ListIterator iter = lst.listIterator();
while (iter.hasNext()) {
String s = iter.next();
s = StrUtils.replaceAllBackslashCommas(s.trim());
iter.set(s);
}
setDataStr(lst);
}
public Collection getBodyData() {
return dataStr;
}
/**
* Set and refresh table cells/body. Each string/cell in collection can be valid HTML.
*/
public void setBodyData(Collection data) {
this.cells = null;
setDataStr(data);
}
private void setDataStr(Collection lst) {
dataObj = null;
dataStr = lst;
refreshBody();
}
@SuppressWarnings("unused")
private void setDataObj(Map lst) {
dataObj = lst;
dataStr = null;
refreshBody();
}
/** Adjusts body to match current columns */
public void refreshBody() {
tBody.clear();
populateBody();
}
private void populateColumns(List cols) {
int cnt = columns.size();
int newCnt = cols != null ? cols.size() : 0;
columns.clear();
if (cols != null) columns.addAll(cols);
colTitleWidgets.clear();
removeHeadRow();
populateHead();
if (cnt == newCnt) {
if (tBody.getWidgetCount() == 0) populateBody();
return;
}
refreshBody();
}
private static class HeadGroupsPanel extends CustomFlowPanel {
public HeadGroupsPanel(Element e, String addnlClasses) {
super(e);
if (addnlClasses != null && !addnlClasses.isEmpty()) {
JQMCommon.addStyleNames(this, addnlClasses);
}
}
}
/** Space separated classes for adding to head groups panel. */
protected String getHeadGroupsClasses() {
return THEAD_GROUPS;
}
protected ComplexPanel findHeadRow() {
for (int i = 0; i < tHead.getWidgetCount(); i++) {
Widget child = tHead.getWidget(i);
if (child instanceof HeadGroupsPanel) continue;
if (child instanceof ComplexPanel && isTag(TableRowElement.TAG, child.getElement())) {
return (ComplexPanel) child;
}
}
return null;
}
/**
* @param rows - collection of rows which are just added to head.
*/
protected void addedToHead(ComplexPanel... rows) {
}
private ComplexPanel getHeadRow() {
ComplexPanel r = findHeadRow();
if (r != null) return r;
r = new CustomFlowPanel(Document.get().createTRElement());
tHead.add(r);
addedToHead(r);
return r;
}
private void removeHeadRow() {
ComplexPanel r = findHeadRow();
if (r != null) tHead.remove(r);
}
protected ComplexPanel findHeadGroupsRow() {
for (int i = 0; i < tHead.getWidgetCount(); i++) {
Widget child = tHead.getWidget(i);
if (child instanceof HeadGroupsPanel) return (ComplexPanel) child;
}
return null;
}
private ComplexPanel getHeadGroupsRow() {
ComplexPanel r = findHeadGroupsRow();
if (r != null) return r;
ComplexPanel headRow = getHeadRow();
r = new HeadGroupsPanel(Document.get().createTRElement(), getHeadGroupsClasses());
if (headRow == null) {
tHead.add(r);
addedToHead(r);
} else {
tHead.remove(headRow);
tHead.add(r);
tHead.add(headRow);
addedToHead(r, headRow);
}
return r;
}
private void removeHeadGroupsRow() {
ComplexPanel r = findHeadGroupsRow();
if (r != null) tHead.remove(r);
}
protected void populateHead() {
if (!columns.isEmpty()) {
int i = 0;
for (ColumnDef col : columns) {
addToHead(col.getTitle(), col.getPriority(), i++);
}
return;
}
if (!colTitleWidgets.isEmpty()) {
int i = 0;
for (Entry j : colTitleWidgets.entrySet()) {
addToHead(j.getKey(), j.getValue().getTitle(), j.getValue().getPriority(), i++);
}
return;
}
}
/**
* @param col
* @param priority
*/
protected void setColPriority(ComplexPanel col, String priority) {
}
protected ComplexPanel addToHead(String title, String priority, int index) {
if (index < 0) return null;
ComplexPanel col = getCol(getHeadRow(), index, true/*addTh*/);
if (col == null) return null;
setColPriority(col, priority);
col.getElement().setInnerHTML(title);
applyImgOnly(col);
return col;
}
protected ComplexPanel addToHead(Widget w, String title, String priority, int index) {
ComplexPanel col = addToHead(title, priority, index);
if (col == null) return null;
col.clear();
if (w != null) col.add(w);
return col;
}
protected void populateBody() {
if (dataStr != null) {
int i = 0;
for (String s : dataStr) {
addToBody(s, i++);
}
return;
}
if (dataObj != null) {
int i = 0;
for (Entry j : dataObj.entrySet()) {
addToBody(j.getKey(), i++, j.getValue() != null ? j.getValue() : false);
}
return;
}
}
/** important for body, because exact number of columns should be known when populating */
protected int getNumOfCols() {
if (!columns.isEmpty()) return getHeadRegularColCnt() + columns.size();
if (loaded && !colTitleWidgets.isEmpty()) return getHeadRegularColCnt() + colTitleWidgets.size();
return getHeadRegularColCnt();
}
private int getHeadRegularColCnt() {
int headColumnCnt = 0;
if (!headGroups.isEmpty()) {
for (ColumnDef i : headGroups) {
if (!i.isGroup()) headColumnCnt++;
}
}
return headColumnCnt;
}
protected void populateHeadGroups() {
if (!headGroups.isEmpty()) {
int i = 0;
for (ColumnDef grp : headGroups) {
addToHeadGroups(grp, i++);
}
return;
}
if (!colGroupWidgets.isEmpty()) {
int i = 0;
for (Entry j : colGroupWidgets.entrySet()) {
addToHeadGroups(j.getKey(), j.getValue(), i++);
}
return;
}
removeHeadGroupsRow();
}
protected ComplexPanel addToHeadGroups(ColumnDef grp, int index) {
if (grp == null || index < 0) return null;
final int colspan = grp.getColspan();
final int rowspan = grp.getRowspan();
final boolean isTitleTh = isTh(grp.getTitle());
boolean addTh = colspan > 1 || rowspan > 1 || isTitleTh;
ComplexPanel col = getCol(getHeadGroupsRow(), index, addTh);
if (col == null) return null;
setColPriority(col, grp.getPriority());
col.getElement().setInnerHTML(isTitleTh ? removeTh(grp.getTitle()) : grp.getTitle());
if (colspan > 1) JQMCommon.setAttribute(col, "colspan", String.valueOf(colspan));
if (rowspan > 1) JQMCommon.setAttribute(col, "rowspan", String.valueOf(rowspan));
applyImgOnly(col);
return col;
}
protected ComplexPanel addToHeadGroups(Widget w, ColumnDef grp, int index) {
ComplexPanel col = addToHeadGroups(grp, index);
if (col == null) return null;
col.clear();
if (w != null) col.add(w);
return col;
}
private static boolean isTh(String s) {
return s != null && !s.isEmpty() && (s.startsWith("") || s.startsWith(" "));
}
private static String removeTh(String s) {
if (s == null || s.isEmpty() || !isTh(s)) return s;
return s.substring(" ".length(), s.length() - " ".length()).trim();
}
private static boolean isImgOnly(Element elt) {
if (elt == null) return false;
String s = elt.getInnerHTML();
if (s == null || s.isEmpty()) return false;
int p = s.indexOf('<');
if (p == -1) return false;
int endP = p + 1 + ImageElement.TAG.length();
String t = s.substring(p + 1, endP);
if (!ImageElement.TAG.equalsIgnoreCase(t)) return false;
p = s.indexOf('>', endP);
if (p == -1) return false;
p = s.indexOf('<', p + 1);
return p == -1;
}
private static void applyImgOnly(Widget w) {
if (w == null) return;
Element elt = w.getElement();
if (isImgOnly(elt)) elt.addClassName(IMG_ONLY);
else elt.removeClassName(IMG_ONLY);
}
private ComplexPanel getRowByAbsIdx(int index) {
if (index < 0 || getNumOfCols() <= 0) return null;
int row = index / getNumOfCols();
ComplexPanel r = getRow(row);
return r;
}
/**
* @param colIdx - in range of 0..getNumOfCols-1
*/
protected boolean isColCellTypeTh(int colIdx) {
return false;
}
private void addBodyCell(ComplexPanel row, int colIdx, String cell, boolean addTh, Widget... widgets) {
if (!addTh) addTh = isColCellTypeTh(colIdx);
ComplexPanel c = getCol(row, colIdx, addTh);
if (c == null) return;
c.clear();
if (widgets != null && widgets.length > 0) {
for (Widget w : widgets) c.add(w);
} else {
c.getElement().setInnerHTML(cell);
}
applyImgOnly(c);
}
protected void addToBody(String cell, int index) {
if (cell == null) return;
ComplexPanel r = getRowByAbsIdx(index);
if (r == null) return;
int col = index % getNumOfCols();
boolean addTh = isTh(cell);
if (addTh) cell = removeTh(cell);
addBodyCell(r, col, cell, addTh);
}
protected void addToBody(Widget w, int index, boolean addTh) {
ComplexPanel r = getRowByAbsIdx(index);
if (r == null) return;
int col = index % getNumOfCols();
addBodyCell(r, col, null/*cell*/, addTh, w);
}
private static boolean isTag(String tag, Element elt) {
if (tag == null || elt == null) return false;
return tag.equalsIgnoreCase(elt.getTagName());
}
private ComplexPanel getRow(int row) {
int cnt = -1;
for (int i = 0; i < tBody.getWidgetCount(); i++) {
Widget child = tBody.getWidget(i);
if (child instanceof ComplexPanel && isTag(TableRowElement.TAG, child.getElement())) {
cnt++;
if (cnt == row) return (ComplexPanel) child;
}
}
ComplexPanel r = null;
for (int i = cnt; i < row; i++) {
r = new CustomFlowPanel(Document.get().createTRElement());
tBody.add(r);
}
return r;
}
private static ComplexPanel getCol(ComplexPanel r, int col, boolean addTh) {
if (r == null || col < 0) return null;
int cnt = -1;
for (int i = 0; i < r.getWidgetCount(); i++) {
Widget child = r.getWidget(i);
if (child instanceof ComplexPanel
&& (isTag(TableCellElement.TAG_TH, child.getElement())
|| isTag(TableCellElement.TAG_TD, child.getElement()))) {
cnt++;
if (cnt == col) return (ComplexPanel) child;
}
}
ComplexPanel c = null;
for (int i = cnt; i < col; i++) {
c = addTh ? new CustomFlowPanel(Document.get().createTHElement())
: new CustomFlowPanel(Document.get().createTDElement());
r.add(c);
}
return c;
}
/**
* @param asTh - <th> will be used for creating cell instead of <td>,
* so such cell will be styled differently, like columnNames/header cells.
*/
@UiChild(tagname = "cell")
public void addCellWidget(Widget w, Boolean asTh) {
if (dataStr != null) {
tBody.clear();
dataStr = null;
}
if (dataObj == null) dataObj = new LinkedHashMap();
dataObj.put(w, asTh);
addToBody(w, dataObj.size() - 1, asTh != null ? asTh : false);
}
protected void clearColNames() {
if (colNames != null) {
removeHeadRow();
columns.clear();
colNames = null;
}
}
protected void clearHead() {
clearColNames();
colTitleWidgets.clear();
clearColGroups();
colGroupWidgets.clear();
}
@UiChild(tagname = "colTitle")
public void addColTitleWidget(Widget w, String priority, String text, String colName) {
clearColNames();
ColumnDef colDef = new ColumnDef(text, priority);
if (!Empty.is(colName)) colDef.setName(colName);
colTitleWidgets.put(w, colDef);
addToHead(w, colDef.getTitle(), colDef.getPriority(), colTitleWidgets.size() - 1);
}
protected void clearColGroups() {
if (colGroups != null) {
removeHeadGroupsRow();
headGroups.clear();
colGroups = null;
}
}
@UiChild(tagname = "colGroup")
public void addColGroupWidget(Widget w, String priority, String text, String colName,
Integer colspan, Integer rowspan) {
clearColGroups();
ColumnDef colDef = new ColumnDef(text, priority);
if (!Empty.is(colName)) colDef.setName(colName);
if (colspan != null && colspan > 0) colDef.setColspan(colspan);
if (rowspan != null && rowspan > 0) colDef.setRowspan(rowspan);
colGroupWidgets.put(w, colDef);
addToHeadGroups(w, colDef, colGroupWidgets.size() - 1);
}
@Override
public HandlerRegistration addValueChangeHandler(ValueChangeHandler> handler) {
return addHandler(handler, ValueChangeEvent.getType());
}
@Override
public Collection getValue() {
return getBodyData();
}
@Override
public void setValue(Collection value) {
setValue(value, false/*fireEvents*/);
}
@Override
public void setValue(Collection value, boolean fireEvents) {
Collection oldValue = fireEvents ? getValue() : null;
setBodyData(value);
if (fireEvents) {
Collection newValue = getValue();
ValueChangeEvent.fireIfNotEqual(this, oldValue, newValue);
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy