IDEMPIERE-1635 Upgrade to zk7. Fixed issue with href=min for grid. Sync paging size with GridDataLoader limit. Fixed css for tree. Make grid more compact. Fixed issue with customize grid.

This commit is contained in:
Heng Sin Low 2014-03-22 09:39:39 +08:00
parent ad9534725f
commit 981969a147
12 changed files with 724 additions and 60 deletions

View File

@ -58,6 +58,7 @@ import org.compiere.model.DataStatusEvent;
import org.compiere.model.DataStatusListener;
import org.compiere.model.GridField;
import org.compiere.model.GridTab;
import org.compiere.model.GridTable;
import org.compiere.model.GridWindow;
import org.compiere.model.I_AD_Preference;
import org.compiere.model.MLookup;
@ -1277,6 +1278,10 @@ DataStatusListener, IADTabpanel, IdSpace, IFieldEditorContainer
if (listPanel.isVisible()) {
listPanel.updateListIndex();
listPanel.dynamicDisplay(col);
if (GridTable.DATA_REFRESH_MESSAGE.equals(e.getAD_Message()) ||
"Sorted".equals(e.getAD_Message())) {
Clients.resize(listPanel.getListbox());
}
}
}
@ -1396,6 +1401,7 @@ DataStatusListener, IADTabpanel, IdSpace, IFieldEditorContainer
if (listPanel.isVisible()) {
listPanel.refresh(gridTab);
listPanel.scrollToCurrentRow();
Clients.resize(listPanel.getListbox());
} else {
listPanel.deactivate();
}

View File

@ -71,7 +71,7 @@ import org.zkoss.zul.impl.XulElement;
public class GridTabRowRenderer implements RowRenderer<Object[]>, RowRendererExt, RendererCtrl, EventListener<Event> {
public static final String GRID_ROW_INDEX_ATTR = "grid.row.index";
private static final String CELL_DIV_STYLE = "border: none; height: 100%; cursor: pointer; ";
private static final String CELL_DIV_STYLE = "height: 100%; cursor: pointer; ";
private static final String CELL_DIV_STYLE_ALIGN_CENTER = CELL_DIV_STYLE + "text-align:center; ";
private static final String CELL_DIV_STYLE_ALIGN_RIGHT = CELL_DIV_STYLE + "text-align:right; ";
@ -367,12 +367,11 @@ public class GridTabRowRenderer implements RowRenderer<Object[]>, RowRendererExt
}
Cell cell = new Cell();
cell.setWidth("28px");
cell.setTooltiptext(Msg.getMsg(Env.getCtx(), "Select"));
Checkbox selection = new Checkbox();
selection.setAttribute(GRID_ROW_INDEX_ATTR, rowIndex);
selection.setChecked(gridTab.isSelected(rowIndex));
cell.setStyle("background-color: transparent !important;");
cell.setStyle("border: none;");
selection.addEventListener(Events.ON_CHECK, this);
if (!selection.isChecked()) {
@ -385,8 +384,8 @@ public class GridTabRowRenderer implements RowRenderer<Object[]>, RowRendererExt
row.appendChild(cell);
cell = new Cell();
cell.setWidth("18px");
cell.addEventListener(Events.ON_CLICK, this);
cell.setStyle("border: none;");
cell.setTooltiptext(Util.cleanAmp(Msg.getMsg(Env.getCtx(), "EditRecord")));
row.appendChild(cell);
@ -453,6 +452,7 @@ public class GridTabRowRenderer implements RowRenderer<Object[]>, RowRendererExt
row.setStyle("cursor:pointer");
row.addEventListener(Events.ON_CLICK, rowListener);
row.addEventListener(Events.ON_OK, rowListener);
row.setTooltiptext("Row " + (rowIndex+1));
}
/**
@ -462,14 +462,13 @@ public class GridTabRowRenderer implements RowRenderer<Object[]>, RowRendererExt
if (currentRow != null && currentRow.getParent() != null && currentRow != row) {
Cell cell = (Cell) currentRow.getChildren().get(1);
if (cell != null) {
cell.setStyle("background-color: transparent");
cell.setSclass("row-indicator");
}
}
currentRow = row;
Cell cell = (Cell) currentRow.getChildren().get(1);
if (cell != null) {
cell.setSclass("row-indicator-seld");
cell.setSclass("row-indicator-selected");
}
currentRowIndex = gridTab.getCurrentRow();
@ -748,7 +747,7 @@ public class GridTabRowRenderer implements RowRenderer<Object[]>, RowRendererExt
public void onEvent(Event event) throws Exception {
if (event.getTarget() instanceof Cell) {
Cell cell = (Cell) event.getTarget();
if (cell.getSclass() != null && cell.getSclass().indexOf("row-indicator-seld") >= 0)
if (cell.getSclass() != null && cell.getSclass().indexOf("row-indicator-selected") >= 0)
Events.sendEvent(gridPanel, new Event(DetailPane.ON_EDIT_EVENT, gridPanel));
else
Events.sendEvent(event.getTarget().getParent(), event);

View File

@ -43,6 +43,7 @@ import org.compiere.util.DisplayType;
import org.compiere.util.Env;
import org.compiere.util.Msg;
import org.compiere.util.Util;
import org.zkoss.lang.Library;
import org.zkoss.zk.au.out.AuFocus;
import org.zkoss.zk.au.out.AuScript;
import org.zkoss.zk.ui.AbstractComponent;
@ -155,7 +156,10 @@ public class GridView extends Vbox implements EventListener<Event>, IdSpace, IFi
else
{
pageSize = MSysConfig.getIntValue(MSysConfig.ZK_PAGING_SIZE, DEFAULT_PAGE_SIZE);
}
if (Library.getProperty("org.zkoss.zul.grid.DataLoader.limit") == null) {
Library.setProperty("org.zkoss.zul.grid.DataLoader.limit", Integer.toString(pageSize));
}
}
//default true for better UI experience
modeless = MSysConfig.getBooleanValue(MSysConfig.ZK_GRID_EDIT_MODELESS, true);
@ -168,15 +172,16 @@ public class GridView extends Vbox implements EventListener<Event>, IdSpace, IFi
gridFooter.setStyle(HEADER_GRID_STYLE);
addEventListener("onSelectRow", this);
addEventListener("onCustomizeGrid", this);
}
protected void createListbox() {
listbox = new Grid();
listbox.setEmptyMessage(Util.cleanAmp(Msg.getMsg(Env.getCtx(), "FindZeroRecords")));
listbox = new Grid();
listbox.setSizedByContent(false);
listbox.setVflex("1");
listbox.setHflex("1");
listbox.setSclass("adtab-grid");
listbox.setEmptyMessage(Util.cleanAmp(Msg.getMsg(Env.getCtx(), "Processing")));
}
public void setDetailPaneMode(boolean detailPaneMode) {
@ -291,6 +296,8 @@ public class GridView extends Vbox implements EventListener<Event>, IdSpace, IFi
if (!isInit()) {
init(gridTab);
}
if (this.isVisible())
Clients.resize(listbox);
}
/**
@ -324,6 +331,8 @@ public class GridView extends Vbox implements EventListener<Event>, IdSpace, IFi
public void updateListIndex() {
if (gridTab == null || !gridTab.isOpen()) return;
updateEmptyMessage();
int rowIndex = gridTab.getCurrentRow();
if (pageSize > 0) {
if (paging.getTotalSize() != gridTab.getRowCount())
@ -413,7 +422,7 @@ public class GridView extends Vbox implements EventListener<Event>, IdSpace, IFi
}
org.zkoss.zul.Column selection = new Column();
selection.setWidth("28px");
selection.setWidth("22px");
try{
selection.setSort("none");
} catch (Exception e) {}
@ -425,7 +434,7 @@ public class GridView extends Vbox implements EventListener<Event>, IdSpace, IFi
columns.appendChild(selection);
org.zkoss.zul.Column indicator = new Column();
indicator.setWidth("18px");
indicator.setWidth("22px");
try {
indicator.setSort("none");
} catch (Exception e) {}
@ -483,7 +492,6 @@ public class GridView extends Vbox implements EventListener<Event>, IdSpace, IFi
if (headerWidth > estimatedWidth)
estimatedWidth = headerWidth;
//TODO: test whether still needed for zk7
//hflex=min for first column not working well
if (i > 0)
{
@ -521,6 +529,8 @@ public class GridView extends Vbox implements EventListener<Event>, IdSpace, IFi
private void render()
{
updateEmptyMessage();
listbox.addEventListener(Events.ON_CLICK, this);
updateModel();
@ -549,6 +559,17 @@ public class GridView extends Vbox implements EventListener<Event>, IdSpace, IFi
}
private void updateEmptyMessage() {
if (gridTab.getRowCount() == 0)
{
listbox.setEmptyMessage(Util.cleanAmp(Msg.getMsg(Env.getCtx(), "FindZeroRecords")));
}
else
{
listbox.setEmptyMessage(Util.cleanAmp(Msg.getMsg(Env.getCtx(), "Processing")));
}
}
private void updateModel() {
listModel = new GridTableListModel((GridTable)tableModel, windowNo);
listModel.setPageSize(pageSize);
@ -628,6 +649,7 @@ public class GridView extends Vbox implements EventListener<Event>, IdSpace, IFi
listModel.setPage(pgNo);
onSelectedRowChange(0);
gridTab.clearSelection();
Clients.resize(listbox);
}
}
else if (event.getTarget() == selectAll)
@ -653,6 +675,10 @@ public class GridView extends Vbox implements EventListener<Event>, IdSpace, IFi
selectAll.setChecked(false);
}
}
else if (event.getName().equals("onCustomizeGrid"))
{
reInit();
}
}
private boolean isAllSelected() {
@ -990,18 +1016,25 @@ public class GridView extends Vbox implements EventListener<Event>, IdSpace, IFi
}
public void reInit() {
this.setupFields(gridTab);
if(listbox.getFrozen()!=null)
{
listbox.removeChild(listbox.getFrozen());
}
if (listbox.getColumns() != null) {
listbox.removeChild(listbox.getColumns());
listbox.getChildren().clear();
listbox.detach();
if (paging != null) {
paging.detach();
paging = null;
}
renderer = null;
init = false;
setupColumns();
init = true;
updateModel();
Grid tmp = listbox;
createListbox();
tmp.copyEventListeners(listbox);
insertBefore(listbox, gridFooter);
refresh(gridTab);
scrollToCurrentRow();
Clients.resize(listbox);
}
public GridField[] getFields() {

View File

@ -161,6 +161,9 @@ public class WPerformanceIndicator extends Panel implements EventListener<Event>
private void onAfterSize(AfterSizeEvent event) {
int width = event.getWidth();
int height = event.getHeight();
if (width == 0 && height == 0)
return;
//set normal height
if (height == 0) {
height = width > 300 ? width * 40 / 100 : width * 85 / 100;

View File

@ -17,7 +17,15 @@
package org.adempiere.webui.component;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import org.zkoss.zk.ui.IdSpace;
import org.zkoss.zk.ui.event.Event;
import org.zkoss.zk.ui.event.EventListener;
/**
*
@ -28,9 +36,11 @@ import org.zkoss.zk.ui.IdSpace;
public class Grid extends org.zkoss.zul.Grid implements IdSpace
{
private static final long serialVersionUID = -4483759833677794926L;
public Grid() {
private transient Map<String, List<EventListenerInfo>> listeners;
public Grid() {
super();
listeners = new HashMap<String, List<EventListenerInfo>>();
}
public void makeNoStrip() {
@ -42,5 +52,73 @@ public class Grid extends org.zkoss.zul.Grid implements IdSpace
appendChild(rows);
return rows;
}
}
@Override
public boolean addEventListener(int priority, String evtnm,
EventListener<? extends Event> listener) {
boolean b = super.addEventListener(priority, evtnm, listener);
if (b) {
final EventListenerInfo listenerInfo = new EventListenerInfo(priority, listener);
List<EventListenerInfo> list = listeners.get(evtnm);
if (list != null) {
for (Iterator<EventListenerInfo> it = list.iterator(); it.hasNext();) {
final EventListenerInfo li = it.next();
if (li.listener.equals(listener)) {
if (li.priority == priority)
return false; //nothing to do
it.remove(); //re-added later
break;
}
}
list.add(listenerInfo);
} else {
listeners.put(evtnm, list = new LinkedList<EventListenerInfo>());
list.add(listenerInfo);
}
}
return b;
}
@Override
public boolean removeEventListener(String evtnm,
EventListener<? extends Event> listener) {
boolean b = super.removeEventListener(evtnm, listener);
if (b) {
List<EventListenerInfo> list = listeners.get(evtnm);
if (list != null) {
for (Iterator<EventListenerInfo> it = list.iterator(); it.hasNext();) {
final EventListenerInfo li = it.next();
if (li.listener.equals(listener)) {
it.remove();
break;
}
}
}
}
return b;
}
public void copyEventListeners(Grid grid) {
for(String evtnm : listeners.keySet()) {
if (evtnm.equals("onInitModel"))
continue;
List<EventListenerInfo> list = listeners.get(evtnm);
for(EventListenerInfo info : list) {
grid.addEventListener(info.priority, evtnm, info.listener);
}
}
}
private static class EventListenerInfo {
private final int priority;
private final EventListener<? extends Event> listener;
private EventListenerInfo(int priority, EventListener<? extends Event> listener) {
this.priority = priority;
this.listener = listener;
}
}
}

View File

@ -26,6 +26,7 @@ import org.adempiere.webui.part.AbstractUIPart;
import org.compiere.model.MMenu;
import org.compiere.util.CLogger;
import org.compiere.util.Env;
import org.zkoss.zk.ui.Desktop;
import org.zkoss.zk.ui.Executions;
import org.zkoss.zk.ui.Session;
import org.zkoss.zk.ui.event.Event;
@ -122,7 +123,7 @@ public abstract class AbstractDesktop extends AbstractUIPart implements IDesktop
*/
public void unregisterWindow(int WindowNo) {
List<Object> windows = getWindows();
if (WindowNo < windows.size())
if (windows != null && WindowNo < windows.size())
windows.set(WindowNo, null);
Env.clearWinContext(WindowNo);
}
@ -134,7 +135,7 @@ public abstract class AbstractDesktop extends AbstractUIPart implements IDesktop
*/
public Object findWindow(int WindowNo) {
List<Object> windows = getWindows();
if (WindowNo < windows.size())
if (windows != null && WindowNo < windows.size())
return windows.get(WindowNo);
else
return null;
@ -299,14 +300,19 @@ public abstract class AbstractDesktop extends AbstractUIPart implements IDesktop
}
protected List<Object> getWindows(){
Session session = getComponent().getDesktop().getSession();
@SuppressWarnings("unchecked")
List<Object> list = (List<Object>) session.getAttribute("windows.list");
if (list == null) {
list = new ArrayList<Object>();
session.setAttribute("windows.list", list);
Desktop desktop = getComponent().getDesktop();
if (desktop != null) {
Session session = desktop.getSession();
@SuppressWarnings("unchecked")
List<Object> list = (List<Object>) session.getAttribute("windows.list");
if (list == null) {
list = new ArrayList<Object>();
session.setAttribute("windows.list", list);
}
return Collections.synchronizedList(list);
} else {
return null;
}
return Collections.synchronizedList(list);
}
}

View File

@ -212,7 +212,6 @@ public abstract class AbstractMenuPanel extends Panel implements EventListener<E
{
Toolbarbutton newBtn = new Toolbarbutton(null, ThemeManager.getThemeResource("images/New10.png"));
newBtn.setSclass("menu-href-newbtn");
newBtn.setStyle("padding-left:3px; margin-bottom:5px");
return newBtn;
}

View File

@ -571,7 +571,7 @@ public class CustomizeGridViewPanel extends Panel
// FDialog.info(m_WindowNo, null, "Saved");
getParent().detach();
if(gridPanel!=null){
gridPanel.reInit();
Events.postEvent("onCustomizeGrid", gridPanel, null);
}
} else {
FDialog.error(m_WindowNo, null, "SaveError", custom.toString());

View File

@ -77,8 +77,6 @@ public class WindowContainer extends AbstractUIPart
});
Tabpanels tabpanels = new Tabpanels();
tabpanels.setHeight("100%");
tabpanels.setWidth("100%");
Tabs tabs = new Tabs();
tabbox.appendChild(tabs);

View File

@ -154,11 +154,27 @@ html,body {
height: 16px;
}
.menu-href-newbtn {
height: 17px;
line-height: 10px;
padding: 1px 1px;
padding-bottom: 2px;
padding-left: 2px;
margin-bottom: 2px;
}
.menu-href-newbtn img {
width: 10px;
height: 10px;
}
.menu-href-newbtn .z-toolbarbutton-content {
height: 10px;
width: 10px;
display: inline-block;
line-height: 10px;
}
.z-toolbar.z-toolbar-tabs {
padding-top: 0px;
}
@ -181,6 +197,8 @@ html,body {
.fav-new-btn {
margin-left: 4px;
margin-bottom: 3px;
padding-left: 1px;
}
.fav-new-btn img {
@ -405,7 +423,7 @@ div.wc-modal, div.wc-modal-none, div.wc-highlighted, div.wc-highlighted-none {
margin: 0;
padding: 0;
border: 0;
position: absolute !important;
position: relative !important;
background-color: #FFFFFF
}
@ -742,8 +760,8 @@ div.wc-modal, div.wc-modal-none, div.wc-highlighted, div.wc-highlighted-none {
.adwindow-status-docinfo {
display: inline-block;
float: right;
padding-right: 4px;
position: absolute;
right: 4px;
}
.docstatus-normal .z-label {
@ -805,6 +823,9 @@ div.wc-modal, div.wc-modal-none, div.wc-highlighted, div.wc-highlighted-none {
.adwindow-detailpane-toolbar .z-toolbarbutton {
float: left;
display: inline-block;
padding: 0px;
padding-left: 1px;
width: 20px;
}
.adwindow-detailpane-toolbar .z-toolbarbutton img {
@ -920,7 +941,7 @@ div.wc-modal, div.wc-modal-none, div.wc-highlighted, div.wc-highlighted-none {
}
.z-grid tbody tr.highlight td.row-indicator-selected {
background-color: transparent !important;
background-color: #FFFFCC !important;
background-image: url(${c:encodeURL('/theme/default/images/EditRecord16.png')}) !important;
background-position: center;
background-repeat: no-repeat;
@ -1088,7 +1109,8 @@ input:focus, textarea:focus, .z-combobox-input:focus, z-datebox-input:focus {
background-color: transparent;
background-image: none;
width: 20px;
height: 18px;
height: 22px;
min-height: 22px;
border: none;
position: absolute;
right: 1px;
@ -1096,7 +1118,8 @@ input:focus, textarea:focus, .z-combobox-input:focus, z-datebox-input:focus {
}
.editor-button :hover {
background-color: #ddd;
-webkit-filter: contrast(1.5);
filter: contrast(150%);
}
.editor-button img {
@ -1789,6 +1812,7 @@ table.z-vbox > tbody > tr > td > table {
<%-- help window --%>
.help-window {
position: relative;
}
.help-window-header {
padding: 10px 0 10px 20px;
@ -1894,12 +1918,13 @@ table.z-vbox > tbody > tr > td > table {
width: 100%;
}
.payment-rule-editor .z-combobox-input {
box-sizing: border-box;
-moz-box-sizing: border-box; /* Firefox */
display: inline-block;
padding-right: 44px;
width: 100%;
height: 21px;
height: 24px;
border-bottom-right-radius: 6px;
border-top-right-radius: 6px;
border-right: 0px;
}
.payment-rule-editor .z-combobox-input:focus {
border: 1px solid #0000ff;
@ -1908,27 +1933,17 @@ table.z-vbox > tbody > tr > td > table {
padding-right: 22px !important;
}
.payment-rule-editor .z-combobox-button {
padding: 0px;
margin: 0px;
display: inline-block;
border: none;
position: absolute;
right: 22px;
right: 0px;
top: 1px;
}
.payment-rule-editor .z-combobox.no-button .z-combobox-button {
right: 1px;
}
.payment-rule-editor .z-combobox .z-combobox-button-hover {
background-color: #ddd;
background-position: 0px 0px;
}
.payment-rule-editor .editor-button {
border-radius: 0px;
}
.payment-rule-editor .editor-button :hover {
border-radius: 0px;
background-color: #ddd;
right: 24px;
}
<%-- chart --%>
@ -1947,3 +1962,31 @@ table.z-vbox > tbody > tr > td > table {
display: inline-block;
width: 100%;
}
.z-column-content, .z-listheader-content, .z-listcell-content {
padding: 2px 3px 1px;
}
.z-grid-body .z-cell {
padding: 2px 3px;
}
.z-treecell-content {
padding: 2px 1px;
}
.z-tab-button :hover {
color: blue;
}
.z-row .z-cell, .z-listitem .z-listcell, .z-listitem.z-listitem-selected>.z-listcell {
border-left: 1px solid #cfcfcf;
}
.z-grid-emptybody td {
text-align: left;
}
.z-grid-body {
background-color: #FFF;
}

View File

@ -244,8 +244,10 @@ Export-Package: Lib,
org.apache.commons.fileupload.servlet;version="1.2.2",
org.apache.commons.fileupload.util;version="1.2.2",
org.apache.commons.io;version="2.1.0",
org.apache.commons.io.comparator,
org.apache.commons.io.filefilter;version="2.1.0",
org.apache.commons.io.input;version="2.1.0",
org.apache.commons.io.monitor,
org.apache.commons.io.output;version="2.1.0",
org.apache.html.dom,
org.apache.wml,
@ -539,6 +541,9 @@ Export-Package: Lib,
org.zkoss.bind.converter.sys,
org.zkoss.bind.impl,
org.zkoss.bind.sys,
org.zkoss.bind.sys.debugger,
org.zkoss.bind.sys.debugger.impl,
org.zkoss.bind.sys.debugger.impl.info,
org.zkoss.bind.sys.tracker,
org.zkoss.bind.tracker.impl,
org.zkoss.bind.validator,
@ -585,6 +590,7 @@ Export-Package: Lib,
org.zkoss.web.servlet.dsp.impl,
org.zkoss.web.servlet.http,
org.zkoss.web.servlet.xel,
org.zkoss.web.theme,
org.zkoss.web.util.resource,
org.zkoss.xel,
org.zkoss.xel.el,
@ -651,6 +657,7 @@ Export-Package: Lib,
org.zkoss.zul.ext,
org.zkoss.zul.impl,
org.zkoss.zul.mesg,
org.zkoss.zul.theme,
racc,
rdoc,
rdoc.dot,
@ -751,6 +758,7 @@ Export-Package: Lib,
web.js.ckez.ext.CKeditor.skins.moono,
web.js.ckez.ext.CKeditor.skins.moono.images,
web.js.gmaps,
web.js.gmaps.css,
web.js.gmaps.ext,
web.js.timelinez,
web.js.timelinez.css,
@ -795,42 +803,53 @@ Export-Package: Lib,
web.js.zul,
web.js.zul.box,
web.js.zul.box.css,
web.js.zul.box.less,
web.js.zul.box.mold,
web.js.zul.db,
web.js.zul.db.css,
web.js.zul.db.less,
web.js.zul.db.mold,
web.js.zul.fud,
web.js.zul.grid,
web.js.zul.grid.css,
web.js.zul.grid.less,
web.js.zul.grid.mold,
web.js.zul.inp,
web.js.zul.inp.css,
web.js.zul.inp.less,
web.js.zul.inp.mold,
web.js.zul.lang,
web.js.zul.layout,
web.js.zul.layout.css,
web.js.zul.layout.less,
web.js.zul.layout.mold,
web.js.zul.med,
web.js.zul.med.mold,
web.js.zul.menu,
web.js.zul.menu.css,
web.js.zul.menu.less,
web.js.zul.menu.mold,
web.js.zul.mesh,
web.js.zul.mesh.css,
web.js.zul.mesh.less,
web.js.zul.mesh.mold,
web.js.zul.sel,
web.js.zul.sel.css,
web.js.zul.sel.less,
web.js.zul.sel.mold,
web.js.zul.tab,
web.js.zul.tab.css,
web.js.zul.tab.less,
web.js.zul.tab.mold,
web.js.zul.utl,
web.js.zul.utl.mold,
web.js.zul.wgt,
web.js.zul.wgt.css,
web.js.zul.wgt.less,
web.js.zul.wgt.mold,
web.js.zul.wnd,
web.js.zul.wnd.css,
web.js.zul.wnd.less,
web.js.zul.wnd.mold,
web.zk,
web.zk.img,
@ -854,6 +873,8 @@ Export-Package: Lib,
web.zul.img.tab,
web.zul.img.tree,
web.zul.img.wnd,
web.zul.less,
web.zul.less.font,
webrick,
webrick.httpauth,
webrick.httpservlet,

View File

@ -0,0 +1,478 @@
/* GridDataLoader.java
{{IS_NOTE
Purpose:
Description:
History:
Oct 29, 2009 10:44:57 AM, Created by henrichen
}}IS_NOTE
Copyright (C) 2009 Potix Corporation. All Rights Reserved.
{{IS_RIGHT
This program is distributed under GPL Version 3.0 in the hope that
it will be useful, but WITHOUT ANY WARRANTY.
}}IS_RIGHT
*/
package org.zkoss.zul.impl;
import java.util.LinkedHashSet;
import java.util.Set;
import org.zkoss.lang.Library;
import org.zkoss.lang.Objects;
import org.zkoss.xel.VariableResolver;
import org.zkoss.zk.ui.Component;
import org.zkoss.zk.ui.UiException;
import org.zkoss.zk.ui.util.ForEachStatus;
import org.zkoss.zk.ui.util.Template;
import org.zkoss.zk.ui.ext.render.Cropper;
import org.zkoss.zul.Grid;
import org.zkoss.zul.Group;
import org.zkoss.zul.GroupRendererExt;
import org.zkoss.zul.Groupfoot;
import org.zkoss.zul.Label;
import org.zkoss.zul.ListModel;
import org.zkoss.zul.Row;
import org.zkoss.zul.RowRenderer;
import org.zkoss.zul.RowRendererExt;
import org.zkoss.zul.Rows;
import org.zkoss.zul.event.ListDataEvent;
import org.zkoss.zul.ext.GroupingInfo;
import org.zkoss.zul.ext.Paginal;
import org.zkoss.zul.impl.GroupsListModel.GroupDataInfo;
/**
* Generic {@link Grid} data loader.
* @author henrichen
* @since 5.0.0
*/
public class GridDataLoader implements DataLoader, Cropper {
private Grid _grid;
//--DataLoader--//
public void init(Component owner, int offset, int limit) {
_grid = (Grid) owner;
}
public void reset() {
//do nothing
}
final public Component getOwner() {
return _grid;
}
public int getOffset() {
return 0;
}
public int getLimit() {
String limit = Library.getProperty("org.zkoss.zul.grid.DataLoader.limit");
if (limit != null) {
return Integer.parseInt(limit);
}
return 50;
}
public int getTotalSize() {
final Rows rows = _grid.getRows();
final ListModel model = _grid.getModel();
return model != null ? model.getSize() : rows != null ? rows.getVisibleItemCount() : 0;
}
private static int INVALIDATE_THRESHOLD = 10;
public void doListDataChange(ListDataEvent event) {
//when this is called _model is never null
final Rows rows = _grid.getRows();
final int newsz = event.getModel().getSize(), oldsz = rows.getChildren().size();
int min = event.getIndex0(), max = event.getIndex1(), cnt;
switch (event.getType()) {
case ListDataEvent.INTERVAL_ADDED:
cnt = newsz - oldsz;
if (cnt <= 0) {
syncModel(-1, -1); //out of sync, force sync
return;
//throw new UiException("Adding causes a smaller list?");
}
if ((oldsz <= 0 || cnt > INVALIDATE_THRESHOLD)
&& !inPagingMold())
rows.invalidate();
//Invalidate rows to improve the performance since it is faster
//to remove a lot of individual rows. It is safer than invalidating
//the whole grid since header might have an input affecting the model
//(e.g., ZK-985: demo's data filter)
//The memory leak of IE is better with outer (Bug 3147518),
//and even better with _grid.invalidate() but better to solve ZK-985
if (min < 0)
if (max < 0) min = 0;
else min = max - cnt + 1;
if (min > oldsz) min = oldsz;
RowRenderer renderer = null;
final Component next =
min < oldsz ? rows.getChildren().get(min): null;
while (--cnt >= 0) {
if (renderer == null)
renderer = (RowRenderer) getRealRenderer();
rows.insertBefore(newUnloadedItem(renderer, min++), next);
}
break;
case ListDataEvent.INTERVAL_REMOVED:
cnt = oldsz - newsz;
if (cnt <= 0) {
syncModel(-1, -1); //out of sync, force sync
return;
//throw new UiException("Removal causes a larger list?");
}
if ((newsz <= 0 || cnt > INVALIDATE_THRESHOLD)
&& !inPagingMold())
rows.invalidate();
//Invalidate rows to improve the performance see above
if (min >= 0) max = min + cnt - 1;
else if (max < 0) max = cnt - 1; //0 ~ cnt - 1
if (max > oldsz - 1) max = oldsz - 1;
//detach from end (due to groupfoot issue)
Component comp = rows.getChildren().get(max);
while (--cnt >= 0) {
Component p = comp.getPreviousSibling();
comp.detach();
comp = p;
}
break;
default: //CONTENTS_CHANGED
syncModel(min, max < 0 ? -1 : (max - min + 1));
}
}
/** Creates a new and unloaded row. */
protected Component newUnloadedItem(Object renderer, int index) {
final RowRenderer renderer0 = (RowRenderer) renderer;
final ListModel model = ((Grid)getOwner()).getModel();
Row row = null;
if (model instanceof GroupsListModel) {
final GroupsListModel gmodel = (GroupsListModel) model;
final GroupingInfo info = gmodel.getDataInfo(index);
switch(info.getType()){
case GroupDataInfo.GROUP:
row = newGroup(renderer0);
((Group)row).setOpen(info.isOpen());
break;
case GroupDataInfo.GROUPFOOT:
row = newGroupfoot(renderer0);
break;
default:
row = newRow(renderer0);
}
}else{
row = newRow(renderer0);
}
((LoadStatus)row.getExtraCtrl()).setLoaded(false);
((LoadStatus)row.getExtraCtrl()).setIndex(index);
newUnloadedCell(renderer0, row);
return row;
}
private Row newRow(RowRenderer renderer) {
Row row = null;
if (renderer instanceof RowRendererExt)
row = ((RowRendererExt)renderer).newRow((Grid)getOwner());
if (row == null) {
row = new Row();
row.applyProperties();
}
return row;
}
private Group newGroup(RowRenderer renderer) {
Group group = null;
if (renderer instanceof GroupRendererExt)
group = ((GroupRendererExt)renderer).newGroup((Grid)getOwner());
if (group == null) {
group = new Group();
group.applyProperties();
}
return group;
}
private Groupfoot newGroupfoot(RowRenderer renderer) {
Groupfoot groupfoot = null;
if (renderer instanceof GroupRendererExt)
groupfoot = ((GroupRendererExt)renderer).newGroupfoot((Grid)getOwner());
if (groupfoot == null) {
groupfoot = new Groupfoot();
groupfoot.applyProperties();
}
return groupfoot;
}
private Component newUnloadedCell(RowRenderer renderer, Row row) {
Component cell = null;
if (renderer instanceof RowRendererExt)
cell = ((RowRendererExt)renderer).newCell(row);
if (cell == null) {
cell = newRenderLabel(null);
cell.applyProperties();
}
cell.setParent(row);
return cell;
}
/** Returns the label for the cell generated by the default renderer.
*/
private static Label newRenderLabel(String value) {
final Label label =
new Label(value != null && value.length() > 0 ? value: " ");
label.setPre(true); //to make sure &nbsp; is generated, and then occupies some space
return label;
}
public Object getRealRenderer() {
final RowRenderer renderer = _grid.getRowRenderer();
return renderer != null ? renderer : _defRend;
}
private static final RowRenderer _defRend = new RowRenderer() {
public void render(final Row row, final Object data, final int index) {
final Rows rows = (Rows)row.getParent();
final Grid grid = (Grid)rows.getParent();
Template tm = getTemplate(grid, rows, "model");
GroupingInfo info = null;
if (row instanceof Group) {
final Template tm2 = getTemplate(grid, rows, "model:group");
if (tm2 != null)
tm = tm2;
if (grid.getModel() instanceof GroupsListModel) {
final GroupsListModel gmodel = (GroupsListModel) grid.getModel();
info = gmodel.getDataInfo(index);
}
} else if (row instanceof Groupfoot) {
final Template tm2 = getTemplate(grid, rows, "model:groupfoot");
if (tm2 != null)
tm = tm2;
}
if (tm == null) {
final Label label = newRenderLabel(Objects.toString(data));
label.applyProperties();
label.setParent(row);
row.setValue(data);
} else {
final GroupingInfo groupingInfo = info;
final Component[] items = tm.create(rows, row,
new VariableResolver() {
public Object resolveVariable(String name) {
if ("each".equals(name)) {
return data;
} else if ("forEachStatus".equals(name)) {
return new ForEachStatus() {
public ForEachStatus getPrevious() {
return null;
}
public Object getEach() {
return data;
}
public int getIndex() {
return index;
}
public Integer getBegin() {
return 0;
}
public Integer getEnd() {
return grid.getModel().getSize();
}
};
} else if ("groupingInfo".equals(name)) {
return groupingInfo;
} else {
return null;
}
}
}, null);
if (items.length != 1)
throw new UiException("The model template must have exactly one row, not "+items.length);
final Row nr = (Row)items[0];
//sync open state
if (nr instanceof Group && row instanceof Group) {
((Group)nr).setOpen(((Group)row).isOpen());
}
if (nr.getValue() == null) //template might set it
nr.setValue(data);
row.setAttribute("org.zkoss.zul.model.renderAs", nr);
//indicate a new row is created to replace the existent one
row.detach();
}
}
};
private static Template getTemplate(Grid grid, Rows rows, String name) {
final Template tm = grid.getTemplate(name);
return tm != null ? tm: rows != null ? rows.getTemplate(name): null;
// Also allow model's template to be declared in Rows
}
public void syncModel(int offset, int limit) {
int min = offset;
int max = offset + limit - 1;
final ListModel model = _grid.getModel();
Rows rows = _grid.getRows();
final int newsz = model.getSize();
final int oldsz = rows != null ? rows.getChildren().size(): 0;
final Paginal pgi = _grid.getPaginal();
final boolean inPaging = inPagingMold();
final boolean shallInvalidated = //Bug 3147518: avoid memory leak
(min < 0 || min == 0) && (max < 0 || max >= newsz || max >= oldsz);
int newcnt = newsz - oldsz;
int atg = pgi != null ? _grid.getActivePage(): 0;
RowRenderer renderer = null;
Component next = null;
if (oldsz > 0) {
if (min < 0) min = 0;
else if (min > oldsz - 1) min = oldsz - 1;
if (max < 0) max = oldsz - 1;
else if (max > oldsz - 1) max = oldsz - 1;
if (min > max) {
int t = min; min = max; max = t;
}
int cnt = max - min + 1; //# of affected
if (rows != null) {
if (model instanceof GroupsListModel) {
//detach all from end to front since groupfoot
//must be detached before group
newcnt += cnt; //add affected later
if ((shallInvalidated || newcnt > INVALIDATE_THRESHOLD)
&& !inPaging)
rows.invalidate();
//Invalidate rows to improve the performance see above
Component comp = rows.getChildren().get(max);
next = comp.getNextSibling();
while (--cnt >= 0) {
Component p = comp.getPreviousSibling();
comp.detach();
comp = p;
}
} else { //ListModel
int addcnt = 0;
Component row = rows.getChildren().get(min);
while (--cnt >= 0) {
next = row.getNextSibling();
if (cnt < -newcnt) { //if shrink, -newcnt > 0
row.detach(); //remove extra
} else if (((LoadStatus)((Row)row).getExtraCtrl()).isLoaded()) {
if (renderer == null)
renderer = (RowRenderer)getRealRenderer();
row.detach(); //always detach
rows.insertBefore(newUnloadedItem(renderer, min), next);
++addcnt;
}
++min;
row = next;
}
if ((shallInvalidated || addcnt > INVALIDATE_THRESHOLD || addcnt + newcnt > INVALIDATE_THRESHOLD)
&& !inPaging)
rows.invalidate();
//Invalidate rows to improve the performance see above
}
}
} else {
min = 0;
//auto create but it means <grid model="xx"><rows/>... will fail
if (rows == null) {
rows = new Rows();
rows.setParent(_grid);
}
}
for (; --newcnt >= 0; ++min) {
if (renderer == null)
renderer = (RowRenderer) getRealRenderer();
rows.insertBefore(newUnloadedItem(renderer, min), next);
}
if (pgi != null) {
if (atg >= pgi.getPageCount())
atg = pgi.getPageCount() - 1;
pgi.setActivePage(atg);
if (pgi.getTotalSize() != newsz)
pgi.setTotalSize(newsz); //Bug ZK-1888 - Grid in paging mold doesn't change pages count
}
}
protected boolean inPagingMold() {
return "paging".equals(_grid.getMold());
}
public void updateModelInfo() {
// do nothing
}
public void setLoadAll(boolean b) {
//do nothing
}
//--Cropper--//
public boolean isCropper() {
return _grid != null &&
inPagingMold()
&& _grid.getPageSize() <= getTotalSize();
//Single page is considered as not a cropper.
//isCropper is called after a component is removed, so
//we have to test >= rather than >
}
public Set<? extends Component> getAvailableAtClient() {
if (!isCropper())
return null;
final Paginal pgi = _grid.getPaginal();
int pgsz = pgi.getPageSize();
int ofs = pgi.getActivePage() * pgsz;
return getAvailableAtClient(ofs, pgsz);
}
protected Set<? extends Component> getAvailableAtClient(int offset, int limit) {
final Set<Component> avail = new LinkedHashSet<Component>(32);
final Rows rows = _grid.getRows();
Row row = (Row) rows.getFirstChild();
while(row != null) {
if (limit == 0) break;
if (row.isVisible()) {
if (--offset < 0) {
--limit;
avail.add(row);
}
}
if (row instanceof Group) {
final Group g = (Group) row;
if (!g.isOpen()) {
for (int j = 0, len = g.getItemCount(); j < len && row != null; j++)
row = (Row) row.getNextSibling();
}
}
if (row != null)
row = (Row) row.getNextSibling();
}
return avail;
}
public Component getCropOwner() {
return _grid;
}
}