From 981969a147cb83ff84a4e2c1e9a811009bf0981f Mon Sep 17 00:00:00 2001 From: Heng Sin Low Date: Sat, 22 Mar 2014 09:39:39 +0800 Subject: [PATCH] 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. --- .../adempiere/webui/adwindow/ADTabpanel.java | 6 + .../webui/adwindow/GridTabRowRenderer.java | 13 +- .../adempiere/webui/adwindow/GridView.java | 65 ++- .../apps/graph/WPerformanceIndicator.java | 3 + .../org/adempiere/webui/component/Grid.java | 84 ++- .../webui/desktop/AbstractDesktop.java | 24 +- .../webui/panel/AbstractMenuPanel.java | 1 - .../webui/panel/CustomizeGridViewPanel.java | 2 +- .../adempiere/webui/part/WindowContainer.java | 2 - .../theme/default/css/theme.css.dsp | 85 +++- org.zkoss.zk.library/META-INF/MANIFEST.MF | 21 + .../org/zkoss/zul/impl/GridDataLoader.java | 478 ++++++++++++++++++ 12 files changed, 724 insertions(+), 60 deletions(-) create mode 100644 org.zkoss.zk.library/src/org/zkoss/zul/impl/GridDataLoader.java diff --git a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/adwindow/ADTabpanel.java b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/adwindow/ADTabpanel.java index c6e87db789..f10687be14 100644 --- a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/adwindow/ADTabpanel.java +++ b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/adwindow/ADTabpanel.java @@ -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(); } diff --git a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/adwindow/GridTabRowRenderer.java b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/adwindow/GridTabRowRenderer.java index ba260bff98..93a69bb07d 100644 --- a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/adwindow/GridTabRowRenderer.java +++ b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/adwindow/GridTabRowRenderer.java @@ -71,7 +71,7 @@ import org.zkoss.zul.impl.XulElement; public class GridTabRowRenderer implements RowRenderer, RowRendererExt, RendererCtrl, EventListener { 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, 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, 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, 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, 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, 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); diff --git a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/adwindow/GridView.java b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/adwindow/GridView.java index 6e0f941b50..bba3ddefa9 100644 --- a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/adwindow/GridView.java +++ b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/adwindow/GridView.java @@ -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, 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, 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, IdSpace, IFi if (!isInit()) { init(gridTab); } + if (this.isVisible()) + Clients.resize(listbox); } /** @@ -324,6 +331,8 @@ public class GridView extends Vbox implements EventListener, 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, 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, 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, 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, IdSpace, IFi private void render() { + updateEmptyMessage(); + listbox.addEventListener(Events.ON_CLICK, this); updateModel(); @@ -549,6 +559,17 @@ public class GridView extends Vbox implements EventListener, 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, 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, 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, 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() { diff --git a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/apps/graph/WPerformanceIndicator.java b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/apps/graph/WPerformanceIndicator.java index db620f176b..fadb4e98e5 100644 --- a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/apps/graph/WPerformanceIndicator.java +++ b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/apps/graph/WPerformanceIndicator.java @@ -161,6 +161,9 @@ public class WPerformanceIndicator extends Panel implements EventListener 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; diff --git a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/component/Grid.java b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/component/Grid.java index 7de51abd9e..3ecf136b1d 100644 --- a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/component/Grid.java +++ b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/component/Grid.java @@ -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> listeners; + + public Grid() { super(); + listeners = new HashMap>(); } 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 listener) { + boolean b = super.addEventListener(priority, evtnm, listener); + if (b) { + final EventListenerInfo listenerInfo = new EventListenerInfo(priority, listener); + List list = listeners.get(evtnm); + if (list != null) { + for (Iterator 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()); + list.add(listenerInfo); + } + } + return b; + } + + @Override + public boolean removeEventListener(String evtnm, + EventListener listener) { + boolean b = super.removeEventListener(evtnm, listener); + if (b) { + List list = listeners.get(evtnm); + if (list != null) { + for (Iterator 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 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 listener; + + private EventListenerInfo(int priority, EventListener listener) { + this.priority = priority; + this.listener = listener; + } + } } diff --git a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/desktop/AbstractDesktop.java b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/desktop/AbstractDesktop.java index 38ed19fc41..9964c940f7 100644 --- a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/desktop/AbstractDesktop.java +++ b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/desktop/AbstractDesktop.java @@ -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 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 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 getWindows(){ - Session session = getComponent().getDesktop().getSession(); - @SuppressWarnings("unchecked") - List list = (List) session.getAttribute("windows.list"); - if (list == null) { - list = new ArrayList(); - session.setAttribute("windows.list", list); + Desktop desktop = getComponent().getDesktop(); + if (desktop != null) { + Session session = desktop.getSession(); + @SuppressWarnings("unchecked") + List list = (List) session.getAttribute("windows.list"); + if (list == null) { + list = new ArrayList(); + session.setAttribute("windows.list", list); + } + return Collections.synchronizedList(list); + } else { + return null; } - return Collections.synchronizedList(list); } } diff --git a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/panel/AbstractMenuPanel.java b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/panel/AbstractMenuPanel.java index 31df8170f7..2f39e8543a 100644 --- a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/panel/AbstractMenuPanel.java +++ b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/panel/AbstractMenuPanel.java @@ -212,7 +212,6 @@ public abstract class AbstractMenuPanel extends Panel implements EventListener 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; +} \ No newline at end of file diff --git a/org.zkoss.zk.library/META-INF/MANIFEST.MF b/org.zkoss.zk.library/META-INF/MANIFEST.MF index 20bda67a13..a9bb0436b8 100644 --- a/org.zkoss.zk.library/META-INF/MANIFEST.MF +++ b/org.zkoss.zk.library/META-INF/MANIFEST.MF @@ -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, diff --git a/org.zkoss.zk.library/src/org/zkoss/zul/impl/GridDataLoader.java b/org.zkoss.zk.library/src/org/zkoss/zul/impl/GridDataLoader.java new file mode 100644 index 0000000000..dc246a5ab0 --- /dev/null +++ b/org.zkoss.zk.library/src/org/zkoss/zul/impl/GridDataLoader.java @@ -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   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 ... 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 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 getAvailableAtClient(int offset, int limit) { + final Set avail = new LinkedHashSet(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; + } +}