diff --git a/org.adempiere.ui.zk/META-INF/MANIFEST.MF b/org.adempiere.ui.zk/META-INF/MANIFEST.MF
index 10460b97de..cfab22d183 100644
--- a/org.adempiere.ui.zk/META-INF/MANIFEST.MF
+++ b/org.adempiere.ui.zk/META-INF/MANIFEST.MF
@@ -102,6 +102,7 @@ Export-Package: fi.jawsy.jawwa.zk.atmosphere,
org.adempiere.webui.factory,
org.adempiere.webui.grid,
org.adempiere.webui.info,
+ org.adempiere.webui.listbox.renderer,
org.adempiere.webui.panel,
org.adempiere.webui.panel.action,
org.adempiere.webui.part,
@@ -185,9 +186,9 @@ Export-Package: fi.jawsy.jawwa.zk.atmosphere,
web.js.ckez.ext.CKeditor.plugins.widget.images,
web.js.ckez.ext.CKeditor.skins,
web.js.ckez.ext.CKeditor.vendor,
+ web.js.dragdrop.attachment,
web.js.html2canvas,
web.js.jawwa.atmosphere,
- web.js.dragdrop.attachment,
web.js.jquery.maskedinput,
web.js.org.idempiere.commons,
web.js.org.idempiere.websocket,
diff --git a/org.adempiere.ui.zk/WEB-INF/src/metainfo/zk/lang-addon.xml b/org.adempiere.ui.zk/WEB-INF/src/metainfo/zk/lang-addon.xml
index ff4a87316b..5141210598 100644
--- a/org.adempiere.ui.zk/WEB-INF/src/metainfo/zk/lang-addon.xml
+++ b/org.adempiere.ui.zk/WEB-INF/src/metainfo/zk/lang-addon.xml
@@ -57,6 +57,6 @@ Copyright (C) 2007 Ashley G Ramdass (ADempiere WebUI).
-
+
diff --git a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/component/ListitemGroup.java b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/component/ListitemGroup.java
new file mode 100644
index 0000000000..90021965d2
--- /dev/null
+++ b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/component/ListitemGroup.java
@@ -0,0 +1,231 @@
+/***********************************************************************
+ * This file is part of iDempiere ERP Open Source *
+ * http://www.idempiere.org *
+ * *
+ * Copyright (C) Contributors *
+ * *
+ * This program is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU General Public License *
+ * as published by the Free Software Foundation; either version 2 *
+ * of the License, or (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the Free Software *
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, *
+ * MA 02110-1301, USA. *
+ * *
+ * Contributors: *
+ * - hengsin *
+ **********************************************************************/
+package org.adempiere.webui.component;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.adempiere.webui.util.ZKUpdateUtil;
+import org.zkoss.zk.ui.Component;
+import org.zkoss.zk.ui.UiException;
+import org.zkoss.zk.ui.event.Event;
+import org.zkoss.zk.ui.event.EventListener;
+import org.zkoss.zk.ui.event.Events;
+import org.zkoss.zul.Label;
+import org.zkoss.zul.Listbox;
+import org.zkoss.zul.Listcell;
+import org.zkoss.zul.Listgroup;
+import org.zkoss.zul.Listitem;
+import org.zkoss.zul.Span;
+
+/**
+ * Single level grouping for {@link Listbox}.
+ * Note that due to a class hierarchy issue, this wouldn't works with {@link org.adempiere.webui.component.Listbox}.
+ */
+public class ListitemGroup extends Listgroup {
+ /**
+ * generated serial id
+ */
+ private static final long serialVersionUID = -1790997329703776505L;
+
+ /** Listitem attribute to store visibility of a group */
+ public static final String GROUP_LISTITEM_VISIBLE_KEY = "groupListitemVisible";
+
+ /** The list of Listitem of this group */
+ private List m_items = new ArrayList();
+
+ /**
+ * Default constructor
+ */
+ public ListitemGroup() {
+ super();
+ }
+
+ /**
+ * @param label group label
+ */
+ public ListitemGroup(String label) {
+ super(label);
+ }
+
+ /**
+ * @param
+ * @param label
+ * @param value
+ */
+ public ListitemGroup(String label, T value) {
+ super(label, value);
+ }
+
+ /**
+ * @return group label/header
+ */
+ public String getLabel() {
+ final Component cell = getFirstChild();
+ return cell != null && cell instanceof ListitemGroupHeader ? ((ListitemGroupHeader)cell).getTitle() : null;
+ }
+
+ @Override
+ public void setLabel(String label) {
+ getOrCreateGroupHeader().setTitle(label);
+ }
+
+ /**
+ * Set column span of group header (first cell)
+ * @param colspan
+ */
+ public void setColspan(int colspan) {
+ getOrCreateGroupHeader().setSpan(colspan);
+ }
+
+ /**
+ * Get/create group header cell
+ * @return {@link ListitemGroupHeader}
+ */
+ private ListitemGroupHeader getOrCreateGroupHeader() {
+ Component cell = getChildren().size() == 0 || getChildren().size() == 1 ? getFirstChild() : getFirstChild().getNextSibling();
+ if (cell == null || cell instanceof ListitemGroupHeader) {
+ if (cell == null) cell = new ListitemGroupHeader();
+ cell.applyProperties();
+ cell.setParent(this);
+ return (ListitemGroupHeader)cell;
+ }
+ throw new UiException("Unsupported child for setLabel: "+cell);
+ }
+
+ @Override
+ public void setOpen(boolean open) {
+ super.setOpen(open);
+ getOrCreateGroupHeader().setOpen(isOpen());
+
+ if (getParent() != null)
+ {
+ for (Listitem item : m_items)
+ {
+ boolean visible = true;
+ String value = (String) item.getAttribute(GROUP_LISTITEM_VISIBLE_KEY);
+ if (value != null)
+ visible = value.equals("true");
+ item.setVisible(isOpen() && visible); // hide the row of the children when group is not open
+ }
+ }
+ }
+
+ /**
+ * Add Listitem to group
+ * @param item
+ */
+ public void add(Listitem item) {
+ m_items.add(item);
+ }
+
+ /**
+ * Custom Listcell class for list group header
+ */
+ public static class ListitemGroupHeader extends Listcell implements EventListener
+ {
+ /**
+ * generated serial id
+ */
+ private static final long serialVersionUID = -4070011056533999557L;
+ private Span span;
+ private Label lbl;
+
+ private String title;
+ private boolean open;
+
+ /**
+ * Default constructor
+ */
+ public ListitemGroupHeader()
+ {
+ super();
+ init();
+ setTitle(null);
+ setOpen(true);
+ ZKUpdateUtil.setVflex(this, "1");
+ }
+
+ /**
+ * Layout header cell
+ */
+ private void init()
+ {
+ setZclass("z-listgroup-header");
+
+ span = new Span();
+ span.setZclass("z-listgroup-icon");
+ appendChild(span);
+ span.addEventListener(Events.ON_CLICK, this);
+
+ lbl = new Label();
+ lbl.setStyle("cursor: pointer");
+ appendChild(lbl);
+ lbl.addEventListener(Events.ON_CLICK, this);
+ }
+
+ /**
+ * @return group header text
+ */
+ public String getTitle() {
+ return title;
+ }
+
+ /**
+ * Set group header text
+ * @param title
+ */
+ public void setTitle(String title) {
+ this.title = title;
+ lbl.setValue(this.title);
+ }
+
+ /**
+ * @return true if group is open, false otherwise
+ */
+ public boolean isOpen() {
+ return open;
+ }
+
+ /**
+ * Set group state to open or close
+ * @param open true for open state, false for close state
+ */
+ public void setOpen(boolean open) {
+ this.open = open;
+ span.setSclass(this.open ? "z-icon-listgroup-open" : "z-icon-listgroup-close");
+ }
+
+ @Override
+ public void onEvent(Event e) throws Exception
+ {
+ if (e.getName().equals(Events.ON_CLICK))
+ {
+ ((ListitemGroup) getParent()).setOpen(!isOpen());
+ }
+ }
+ }
+
+}
diff --git a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/listbox/renderer/AbstractGroupListitemRenderer.java b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/listbox/renderer/AbstractGroupListitemRenderer.java
new file mode 100644
index 0000000000..454c7fd947
--- /dev/null
+++ b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/listbox/renderer/AbstractGroupListitemRenderer.java
@@ -0,0 +1,154 @@
+/***********************************************************************
+ * This file is part of iDempiere ERP Open Source *
+ * http://www.idempiere.org *
+ * *
+ * Copyright (C) Contributors *
+ * *
+ * This program is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU General Public License *
+ * as published by the Free Software Foundation; either version 2 *
+ * of the License, or (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the Free Software *
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, *
+ * MA 02110-1301, USA. *
+ * *
+ * Contributors: *
+ * - hengsin *
+ **********************************************************************/
+package org.adempiere.webui.listbox.renderer;
+
+import org.adempiere.webui.component.ListitemGroup;
+import org.adempiere.webui.component.ListitemGroup.ListitemGroupHeader;
+import org.zkoss.zul.Listbox;
+import org.zkoss.zul.Listcell;
+import org.zkoss.zul.Listgroup;
+import org.zkoss.zul.ListgroupRendererExt;
+import org.zkoss.zul.Listgroupfoot;
+import org.zkoss.zul.Listitem;
+import org.zkoss.zul.ListitemRenderer;
+import org.zkoss.zul.ListitemRendererExt;
+
+/**
+ * Renderer for {@link Listbox} with single level grouping.
+ * Note that due to a class hierarchy issue, this wouldn't works with {@link org.adempiere.webui.component.Listbox}.
+ * Note2: not working very well with {@link Listbox#setCheckmark(true)}. Recommended to roll your own checkbox cell if you need multiple selection.
+ * @author hengsin
+ * @param Common ancestor type for Group and Item class.
+ */
+public abstract class AbstractGroupListitemRenderer implements ListitemRenderer, ListitemRendererExt, ListgroupRendererExt {
+
+ public AbstractGroupListitemRenderer() {
+ }
+
+ @Override
+ public Listgroup newListgroup(Listbox listbox) {
+ Listgroup group = new ListitemGroup();
+ group.applyProperties();
+ return group;
+ }
+
+ @Override
+ public Listgroupfoot newListgroupfoot(Listbox listbox) {
+ Listgroupfoot groupfoot = new Listgroupfoot();
+ groupfoot.applyProperties();
+ return groupfoot;
+ }
+
+ @Override
+ public Listitem newListitem(Listbox listbox) {
+ Listitem listitem = new Listitem();
+ listitem.applyProperties();
+ return listitem;
+ }
+
+ @Override
+ public Listcell newListcell(Listitem item) {
+ return null;// Default Cell
+ }
+
+ @Override
+ public int getControls() {
+ return ListitemRendererExt.DETACH_ON_RENDER; // Default Value
+ }
+
+ /**
+ * @return number of columns for listbox
+ */
+ public abstract int getColumnCount();
+
+ /**
+ * @param data group
+ * @return group header title
+ */
+ public abstract String getGroupHeaderTitle(T data);
+
+ /**
+ * Renders the data to the specified list item.
+ * @param item the listitem to render the result.
+ * @param data item within a group
+ * @param index the row/list index of the data that is currently being rendered
+ */
+ public abstract void renderListitem(Listitem item, T data, int index);
+
+ /**
+ * Render group
+ * @param item
+ * @param data group
+ * @param index
+ */
+ public void renderGroup(Listitem item, T data, int index)
+ {
+ ListitemGroupHeader cell = new ListitemGroupHeader();
+ cell.applyProperties();
+ cell.setSpan(getColumnCount());
+ cell.setTitle(getGroupHeaderTitle(data));
+ item.appendChild(cell);
+ }
+
+ /**
+ * Render group footer
+ * @param item
+ * @param data footer
+ * @param index
+ */
+ public void renderGroupfoot(Listitem item, T data, int index)
+ {
+
+ }
+
+ @Override
+ public void render(Listitem item, T data, int index) throws Exception {
+ if(item instanceof ListitemGroup)
+ {
+ item.removeChild(item.getFirstChild());
+ renderGroup(item, data, index);
+ }
+ else
+ {
+ if (item.getListgroup() instanceof ListitemGroup)
+ {
+ ListitemGroup group = (ListitemGroup) item.getListgroup();
+ group.add(item);
+ }
+
+ if (item instanceof Listgroupfoot)
+ {
+ item.setZclass("z-groupfoot");
+ renderGroupfoot(item, data, index);
+ }
+ else
+ {
+ item.setStyle("vertical-align: top;");
+ item.setValue(data);
+ renderListitem(item, data, index);
+ }
+ }
+ }
+}
diff --git a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/listbox/renderer/AbstractListitemRenderer.java b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/listbox/renderer/AbstractListitemRenderer.java
new file mode 100644
index 0000000000..0fbbd822ca
--- /dev/null
+++ b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/listbox/renderer/AbstractListitemRenderer.java
@@ -0,0 +1,70 @@
+/***********************************************************************
+ * This file is part of iDempiere ERP Open Source *
+ * http://www.idempiere.org *
+ * *
+ * Copyright (C) Contributors *
+ * *
+ * This program is free software; you can redistribute it and/or *
+ * modify it under the terms of the GNU General Public License *
+ * as published by the Free Software Foundation; either version 2 *
+ * of the License, or (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the Free Software *
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, *
+ * MA 02110-1301, USA. *
+ * *
+ * Contributors: *
+ * - hengsin *
+ **********************************************************************/
+package org.adempiere.webui.listbox.renderer;
+
+import org.zkoss.zul.ListModel;
+import org.zkoss.zul.Listbox;
+import org.zkoss.zul.Listcell;
+import org.zkoss.zul.Listitem;
+import org.zkoss.zul.ListitemRenderer;
+import org.zkoss.zul.ListitemRendererExt;
+
+public abstract class AbstractListitemRenderer implements ListitemRenderer, ListitemRendererExt {
+
+ public AbstractListitemRenderer() {
+ }
+
+ @Override
+ public Listitem newListitem(Listbox listbox) {
+ Listitem listitem = new Listitem();
+ listitem.applyProperties();
+ return listitem;
+ }
+
+ @Override
+ public Listcell newListcell(Listitem item) {
+ return null;// Default Cell
+ }
+
+ @Override
+ public int getControls() {
+ return ListitemRendererExt.DETACH_ON_RENDER; // Default Value
+ }
+
+ /**
+ * Renders the data to the specified list item.
+ * @param item the listitem to render the result.
+ * @param data data that is returned from {@link ListModel#getElementAt}
+ * @param index the row/list index of the data that is currently being rendered
+ */
+ public abstract void renderListitem(Listitem item, T data, int index);
+
+ @Override
+ public void render(Listitem item, T data, int index) throws Exception {
+ item.setStyle("vertical-align: top;");
+ item.setValue(data);
+ renderListitem(item, data, index);
+ }
+}
diff --git a/org.adempiere.ui.zk/WEB-INF/src/web/theme/default/css/fragment/group.css.dsp b/org.adempiere.ui.zk/WEB-INF/src/web/theme/default/css/fragment/group.css.dsp
index d3bdb87a03..b51c0c09ad 100644
--- a/org.adempiere.ui.zk/WEB-INF/src/web/theme/default/css/fragment/group.css.dsp
+++ b/org.adempiere.ui.zk/WEB-INF/src/web/theme/default/css/fragment/group.css.dsp
@@ -31,3 +31,27 @@ tr.z-group {
.z-group-icon-open:before {
content: "\f0d7";
}
+
+div.z-listbox-body .z-listgroup-header {
+ padding-top: 4px;
+ padding-bottom: 4px;
+ border-bottom: 1px solid rgb(207, 207, 207);
+}
+.z-listgroup-header-content > .z-listitem-checkable.z-listitem-checkbox {
+ display: none;
+}
+.z-listgroup-icon {
+ display: inline-block;
+ font-weight: bold;
+ font-style: normal;
+ height: 22px;
+ width: 22px;
+ font-size: 18px;
+ padding-top: 2px;
+}
+.z-icon-listgroup-close::before {
+ content: "\f105";
+}
+.z-icon-listgroup-open::before {
+ content: "\f107";
+}