From 9e7ea1ba9674426dd7bfd953fadf19891311ce7e Mon Sep 17 00:00:00 2001 From: Heng Sin Low Date: Tue, 29 Jan 2013 00:19:49 +0800 Subject: [PATCH] IDEMPIERE-369 Master Detail layout improvements. 1) Make the opening of window and switching between header and detail tab faster. 2) Implemented lazy loading for collapsed detail tabs. 3) Remember the collapsed/open state of detail tabs. 4) Improvement to the breadcrumbe popup menu. --- .../src/org/adempiere/webui/LayoutUtils.java | 25 ++ .../org/adempiere/webui/ValuePreference.java | 13 +- .../adempiere/webui/adwindow/ADSortTab.java | 11 +- .../adempiere/webui/adwindow/ADTabpanel.java | 205 ++++++++++--- .../adempiere/webui/adwindow/ADWindow.java | 41 +++ .../webui/adwindow/ADWindowContent.java | 1 + .../adwindow/AbstractADWindowContent.java | 24 +- .../adempiere/webui/adwindow/BreadCrumb.java | 148 +++++++++- .../webui/adwindow/CompositeADTabbox.java | 279 ++++++++++-------- .../adempiere/webui/adwindow/DetailPane.java | 138 ++++++++- .../adempiere/webui/adwindow/IADTabbox.java | 49 ++- .../adempiere/webui/adwindow/IADTabpanel.java | 49 ++- .../theme/default/css/theme.css.dsp | 15 +- 13 files changed, 798 insertions(+), 200 deletions(-) diff --git a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/LayoutUtils.java b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/LayoutUtils.java index 9758fc0b04..f2ea109029 100644 --- a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/LayoutUtils.java +++ b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/LayoutUtils.java @@ -74,6 +74,11 @@ public final class LayoutUtils { || ((" " + sclass + " ").indexOf(" " + cls + " ") > -1); } + /** + * + * @param label + * @return wrapped label + */ public static Component makeRightAlign(Label label) { Div div = new Div(); div.setStyle("text-align: right"); @@ -154,6 +159,10 @@ public final class LayoutUtils { Clients.response("_openPopupWindow_", new AuScript(window, script.toString())); } + /** + * + * @param component + */ public static void redraw(AbstractComponent component) { StringWriter writer = new StringWriter(1024); try { @@ -163,4 +172,20 @@ public final class LayoutUtils { e.printStackTrace(); } } + + /** + * @param component + * @return true if the component and all its parent are visible + */ + public static boolean isReallyVisible(Component component) { + if (!component.isVisible()) return false; + Component parent = component.getParent(); + while (parent != null) { + if (!parent.isVisible()) + return false; + + parent = parent.getParent(); + } + return true; + } } diff --git a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/ValuePreference.java b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/ValuePreference.java index 02986f5ae4..7c2e238abf 100644 --- a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/ValuePreference.java +++ b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/ValuePreference.java @@ -229,16 +229,9 @@ public class ValuePreference extends Window implements EventListener } } // ValuePreference - private AbstractADWindowContent findADWindowContent(Component ref) { - Component parent = ref.getParent(); - while(parent != null) { - if (parent.getAttribute(ADWindow.AD_WINDOW_ATTRIBUTE_KEY) != null) { - ADWindow adwindow = (ADWindow) parent.getAttribute(ADWindow.AD_WINDOW_ATTRIBUTE_KEY); - return adwindow.getADWindowContent(); - } - parent = parent.getParent(); - } - return null; + private AbstractADWindowContent findADWindowContent(Component comp) { + ADWindow adwindow = ADWindow.findADWindow(comp); + return adwindow != null ? adwindow.getADWindowContent() : null; } private Properties m_ctx; diff --git a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/adwindow/ADSortTab.java b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/adwindow/ADSortTab.java index 6100d35095..8bd7778d2b 100644 --- a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/adwindow/ADSortTab.java +++ b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/adwindow/ADSortTab.java @@ -936,7 +936,7 @@ public class ADSortTab extends Panel implements IADTabpanel } @Override - public boolean isActive() { + public boolean isActivated() { return active; } @@ -979,5 +979,14 @@ public class ADSortTab extends Panel implements IADTabpanel public int getTabNo() { return tabNo; } + + @Override + public void setDetailPane(DetailPane detailPane) { + } + + @Override + public DetailPane getDetailPane() { + return null; + } } //ADSortTab 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 5eaaca78cf..6917c7ec07 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 @@ -55,11 +55,15 @@ import org.compiere.model.DataStatusListener; import org.compiere.model.GridField; import org.compiere.model.GridTab; import org.compiere.model.GridWindow; +import org.compiere.model.I_AD_Preference; import org.compiere.model.MLookup; +import org.compiere.model.MPreference; +import org.compiere.model.MTable; import org.compiere.model.MToolBarButton; import org.compiere.model.MToolBarButtonRestrict; import org.compiere.model.MTree; import org.compiere.model.MTreeNode; +import org.compiere.model.Query; import org.compiere.model.X_AD_FieldGroup; import org.compiere.model.X_AD_ToolBarButton; import org.compiere.util.CLogger; @@ -70,13 +74,13 @@ import org.compiere.util.Msg; import org.compiere.util.Util; import org.zkoss.zk.au.out.AuFocus; import org.zkoss.zk.ui.Component; -import org.zkoss.zk.ui.Desktop; import org.zkoss.zk.ui.Executions; import org.zkoss.zk.ui.HtmlBasedComponent; import org.zkoss.zk.ui.IdSpace; import org.zkoss.zk.ui.event.Event; import org.zkoss.zk.ui.event.EventListener; import org.zkoss.zk.ui.event.Events; +import org.zkoss.zk.ui.event.OpenEvent; import org.zkoss.zk.ui.util.Clients; import org.zkoss.zul.Button; import org.zkoss.zul.Cell; @@ -84,6 +88,7 @@ import org.zkoss.zul.Center; import org.zkoss.zul.DefaultTreeNode; import org.zkoss.zul.Div; import org.zkoss.zul.Separator; +import org.zkoss.zul.South; import org.zkoss.zul.Space; import org.zkoss.zul.Style; import org.zkoss.zul.TreeModel; @@ -108,6 +113,8 @@ import org.zkoss.zul.impl.XulElement; public class ADTabpanel extends Div implements Evaluatee, EventListener, DataStatusListener, IADTabpanel, IdSpace { + private static final String ON_SAVE_OPEN_PREFERENCE_EVENT = "onSaveOpenPreference"; + public static final String ON_POST_INIT_EVENT = "onPostInit"; public static final String ON_SWITCH_VIEW_EVENT = "onSwitchView"; @@ -159,17 +166,17 @@ DataStatusListener, IADTabpanel, IdSpace List allCollapsibleGroups = new ArrayList(); - private Component formContainer = null; + private Borderlayout formContainer = null; private ADTreePanel treePanel = null; private GridTabDataBinder dataBinder; - private boolean active = false; + private boolean activated = false; private Group currentGroup; - private Component detailPane; + private DetailPane detailPane; private boolean detailPaneMode; @@ -195,6 +202,7 @@ DataStatusListener, IADTabpanel, IdSpace } }); addEventListener(ON_POST_INIT_EVENT, this); + addEventListener(ON_SAVE_OPEN_PREFERENCE_EVENT, this); } private void initComponents() @@ -214,19 +222,32 @@ DataStatusListener, IADTabpanel, IdSpace listPanel.getListbox().addEventListener(Events.ON_DOUBLE_CLICK, this); } - public void addDetails(Component component) { + public void setDetailPane(DetailPane component) { detailPane = component; - if (formContainer instanceof Borderlayout) { - Borderlayout borderLayout = (Borderlayout) formContainer; - borderLayout.appendSouth(detailPane); - - borderLayout.getSouth().setCollapsible(true); - borderLayout.getSouth().setSplittable(true); - borderLayout.getSouth().setOpen(true); - borderLayout.getSouth().setSclass("adwindow-gridview-detail"); - } else { - formContainer.appendChild(component); - } + + Borderlayout borderLayout = (Borderlayout) formContainer; + South south = borderLayout.getSouth(); + if (south == null) { + south = new South(); + borderLayout.appendChild(south); + south.setWidgetOverride("doClick_", "function (evt){this.$doClick_(evt);" + + "var target = evt.domTarget;if (!target.id) target = target.parentNode;" + + "if(this.$n('colled') == target) {" + + "var se = new zk.Event(this, 'onSlide', null, {toServer: true}); zAu.send(se); } }"); + south.addEventListener(Events.ON_OPEN, this); + south.addEventListener("onSlide", this); + } + south.appendChild(component); + + south.setVisible(true); + south.setCollapsible(true); + south.setSplittable(true); + south.setOpen(isOpenDetailPane()); + south.setSclass("adwindow-gridview-detail"); + } + + public DetailPane getDetailPane() { + return detailPane; } /** @@ -291,6 +312,7 @@ DataStatusListener, IADTabpanel, IdSpace div.setHflex("1"); div.setSclass("adtab-form"); div.setStyle("overflow-y: visible;"); + div.setSpacing("0px"); layout.appendChild(center); formContainer = layout; @@ -305,6 +327,7 @@ DataStatusListener, IADTabpanel, IdSpace div.setStyle("overflow-y: visible;"); div.setVflex("1"); div.setWidth("100%"); + div.setSpacing("0px"); StringBuilder cssContent = new StringBuilder(); cssContent.append(".adtab-form-borderlayout .z-south-colpsd:before { "); @@ -867,7 +890,7 @@ DataStatusListener, IADTabpanel, IdSpace setAttribute(ATTR_ON_ACTIVATE_POSTED, Boolean.TRUE); } - active = activate; + activated = activate; if (listPanel.isVisible()) { if (activate) listPanel.activate(gridTab); @@ -940,18 +963,72 @@ DataStatusListener, IADTabpanel, IdSpace else if (WPaymentEditor.ON_SAVE_PAYMENT.equals(event.getName())) { windowPanel.onSavePayment(); } - else if (ON_POST_INIT_EVENT.equals(event.getName())) { + else if (ON_POST_INIT_EVENT.equals(event.getName())) { + if (isDetailVisible() && detailPane.getSelectedADTabpanel() != null) { + detailPane.getSelectedADTabpanel().activate(true); + } + } + else if (event.getTarget() instanceof South) { if (detailPane != null) { - Desktop desktop = Executions.getCurrent().getDesktop(); - //for unknown reason, this is needed once per desktop to fixed the layout of the detailpane. - if (desktop.getAttribute("adtabpanel.detailpane.postinit.redraw") == null) { - desktop.setAttribute("adtabpanel.detailpane.postinit.redraw", Boolean.TRUE); - Events.postEvent(new Event(LayoutUtils.ON_REDRAW_EVENT, detailPane)); + boolean openEvent = event instanceof OpenEvent; + if (openEvent) { + Events.echoEvent(ON_SAVE_OPEN_PREFERENCE_EVENT, this, ((OpenEvent)event).isOpen()); + if (!((OpenEvent)event).isOpen()) { + return; + } } + if (detailPane.getParent() == null) { + formContainer.appendSouth(detailPane); + } + IADTabpanel tabPanel = detailPane.getSelectedADTabpanel(); + if (tabPanel != null) { + if (!tabPanel.isActivated()) { + tabPanel.activate(true); + } + if (!tabPanel.isGridView()) { + tabPanel.switchRowPresentation(); + } + } + } + } + else if (event.getName().equals(ON_SAVE_OPEN_PREFERENCE_EVENT)) { + Boolean value = (Boolean) event.getData(); + int windowId = getGridTab().getAD_Window_ID(); + int adTabId = getGridTab().getAD_Tab_ID(); + if (windowId > 0 && adTabId > 0) { + Query query = new Query(Env.getCtx(), MTable.get(Env.getCtx(), I_AD_Preference.Table_ID), "AD_Window_ID=? AND Attribute=?", null); + MPreference preference = query.setOnlyActiveRecords(true) + .setApplyAccessFilter(true) + .setParameters(windowId, adTabId+"|DetailPane.IsOpen") + .first(); + if (preference != null && preference.getAD_Preference_ID() > 0) { + preference.setValue(value ? "Y" : "N"); + } else { + preference = new MPreference(Env.getCtx(), 0, null); + preference.setAD_Window_ID(windowId); + preference.setAttribute(adTabId+"|DetailPane.IsOpen"); + preference.setValue(value ? "Y" : "N"); + } + preference.save(); + //update current context + Env.getCtx().setProperty("P"+windowId+"|"+adTabId+"|DetailPane.IsOpen", value ? "Y" : "N"); } } } + private boolean isOpenDetailPane() { + boolean open = true; + int windowId = getGridTab().getAD_Window_ID(); + int adTabId = getGridTab().getAD_Tab_ID(); + if (windowId > 0 && adTabId > 0) { + String preference = Env.getPreference(Env.getCtx(), windowId, adTabId+"|DetailPane.IsOpen", false); + if (preference != null && preference.trim().length() > 0) { + open = "Y".equals(preference); + } + } + return open; + } + private void navigateTo(DefaultTreeNode value) { MTreeNode treeNode = value.getData(); // We Have a TreeNode @@ -1177,6 +1254,10 @@ DataStatusListener, IADTabpanel, IdSpace listPanel.focus(); } + /** + * + * @param columnName + */ public void setFocusToField(String columnName) { if (formContainer.isVisible()) { boolean found = false; @@ -1220,25 +1301,40 @@ DataStatusListener, IADTabpanel, IdSpace return listPanel; } - public boolean isActive() { - return active; + @Override + public boolean isActivated() { + return activated; } - + @Override public void setDetailPaneMode(boolean detailPaneMode) { if (this.detailPaneMode != detailPaneMode) { this.detailPaneMode = detailPaneMode; if (detailPaneMode) { - detailPane = null; - if (formContainer instanceof Borderlayout) { - Borderlayout borderLayout = (Borderlayout) formContainer; - if (borderLayout.getSouth() != null) { - borderLayout.getSouth().detach(); - } - } - } + hideDetail(); + } else { + showDetail(); + } this.setVflex("true"); listPanel.setDetailPaneMode(detailPaneMode); + } + } + + private void showDetail() { + if (formContainer.getSouth() != null) { + formContainer.getSouth().setVisible(true); + if (formContainer.getSouth().isOpen() && detailPane != null && detailPane.getParent() == null) { + formContainer.appendSouth(detailPane); + } + } + } + + private void hideDetail() { + if (formContainer.getSouth() != null) { + formContainer.getSouth().setVisible(false); + if (detailPane != null && detailPane.getParent() != null) { + detailPane.detach(); + } } } @@ -1287,5 +1383,46 @@ DataStatusListener, IADTabpanel, IdSpace public int getTabNo() { return tabNo; } + + /** + * activate current selected detail tab if it is visible + */ + public void activateDetailIfVisible() { + if (isDetailVisible()) { + IADTabpanel tabPanel = detailPane.getSelectedADTabpanel(); + if (tabPanel != null && !tabPanel.isActivated()) { + tabPanel.activate(true); + if (!tabPanel.isGridView()) { + tabPanel.switchRowPresentation(); + } + } + } + + } + + /** + * + * @return true if the detailpane is visible + */ + public boolean isDetailVisible() { + if (formContainer.getSouth() == null || !formContainer.getSouth().isVisible() + || !formContainer.getSouth().isOpen()) { + return false; + } + + return detailPane != null; + } + + /** + * + * @return true if have one or more detail tabs + */ + public boolean hasDetailTabs() { + if (formContainer.getSouth() == null || !formContainer.getSouth().isVisible()) { + return false; + } + + return detailPane != null && detailPane.getTabcount() > 0; + } } diff --git a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/adwindow/ADWindow.java b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/adwindow/ADWindow.java index c6303ceca6..725cc3f135 100644 --- a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/adwindow/ADWindow.java +++ b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/adwindow/ADWindow.java @@ -51,11 +51,22 @@ public class ADWindow extends AbstractUIPart private static final CCache imageCache = new CCache(null, "WindowImageCache", 5, false); + /** + * + * @param ctx + * @param adWindowId + */ public ADWindow(Properties ctx, int adWindowId) { this(ctx, adWindowId, null); } + /** + * + * @param ctx + * @param adWindowId + * @param query + */ public ADWindow(Properties ctx, int adWindowId, MQuery query) { if(adWindowId <= 0) @@ -75,11 +86,19 @@ public class ADWindow extends AbstractUIPart image = windowContent.getImage(); } + /** + * + * @return title of window + */ public String getTitle() { return _title; } + /** + * + * @return image for the country + */ public MImage getMImage() { return image; @@ -118,6 +137,7 @@ public class ADWindow extends AbstractUIPart } } + @Override public Component getComponent() { return windowPanelComponent; } @@ -129,7 +149,28 @@ public class ADWindow extends AbstractUIPart return windowContent; } + /** + * + * @param windowNo + * @return adwindow instance for windowNo ( if any ) + */ public static ADWindow get(int windowNo) { return (ADWindow) SessionManager.getAppDesktop().findWindow(windowNo); } + + /** + * @param comp + * @return adwindow instance if found, null otherwise + */ + public static ADWindow findADWindow(Component comp) { + Component parent = comp.getParent(); + while(parent != null) { + if (parent.getAttribute(AD_WINDOW_ATTRIBUTE_KEY) != null) { + ADWindow adwindow = (ADWindow) parent.getAttribute(AD_WINDOW_ATTRIBUTE_KEY); + return adwindow; + } + parent = parent.getParent(); + } + return null; + } } diff --git a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/adwindow/ADWindowContent.java b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/adwindow/ADWindowContent.java index 0d80540643..054fb12a94 100644 --- a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/adwindow/ADWindowContent.java +++ b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/adwindow/ADWindowContent.java @@ -75,6 +75,7 @@ public class ADWindowContent extends AbstractADWindowContent } else { layout.setPage(page); } + layout.setSpacing("0px"); //toolbar Div north = new Div(); diff --git a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/adwindow/AbstractADWindowContent.java b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/adwindow/AbstractADWindowContent.java index d431865ae6..a1a5071957 100644 --- a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/adwindow/AbstractADWindowContent.java +++ b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/adwindow/AbstractADWindowContent.java @@ -140,7 +140,9 @@ import org.zkoss.zul.Window.Mode; public abstract class AbstractADWindowContent extends AbstractUIPart implements ToolbarListener, EventListener, DataStatusListener, ActionListener { - private static final CLogger logger; + private static final String ON_DEFER_SET_DETAILPANE_SELECTION_EVENT = "onDeferSetDetailpaneSelection"; + + private static final CLogger logger; static { @@ -213,6 +215,7 @@ public abstract class AbstractADWindowContent extends AbstractUIPart implements Component comp = super.createPart(parent); comp.addEventListener(LayoutUtils.ON_REDRAW_EVENT, this); + comp.addEventListener(ON_DEFER_SET_DETAILPANE_SELECTION_EVENT, this); return comp; } @@ -976,7 +979,7 @@ public abstract class AbstractADWindowContent extends AbstractUIPart implements if (eventData != null && eventData instanceof Object[] && ((Object[])eventData).length == 2) { Object[] indexes = (Object[]) eventData; - int newTabIndex = (Integer)indexes[1]; + final int newTabIndex = (Integer)indexes[1]; final int originalTabIndex = adTabbox.getSelectedIndex(); final int originalTabRow = adTabbox.getSelectedGridTab().getCurrentRow(); @@ -986,7 +989,14 @@ public abstract class AbstractADWindowContent extends AbstractUIPart implements public void onCallback(Boolean result) { if (result) { - adTabbox.setDetailpaneSelection(originalTabIndex, originalTabRow); + if (newTabIndex < originalTabIndex) + { + if (adTabbox.isDetailPaneLoaded()) + adTabbox.setDetailPaneSelectedTab(originalTabIndex, originalTabRow); + else { + Events.echoEvent(new Event(ON_DEFER_SET_DETAILPANE_SELECTION_EVENT, getComponent(), new Integer[]{originalTabIndex, originalTabRow})); + } + } } else { @@ -1027,6 +1037,10 @@ public abstract class AbstractADWindowContent extends AbstractUIPart implements } LayoutUtils.redraw((AbstractComponent) getComponent()); } + else if (event.getName().equals(ON_DEFER_SET_DETAILPANE_SELECTION_EVENT)) { + Integer[] data = (Integer[]) event.getData(); + adTabbox.setDetailPaneSelectedTab(data[0], data[1]); + } } private void setActiveTab(final int newTabIndex, final Callback callback) { @@ -1099,10 +1113,10 @@ public abstract class AbstractADWindowContent extends AbstractUIPart implements IADTabpanel newTabpanel = adTabbox.getSelectedTabpanel(); - boolean activated = newTabpanel.isActive(); + boolean activated = newTabpanel.isActivated(); if (oldTabpanel != null) oldTabpanel.activate(false); - if (activated) + if (!activated) newTabpanel.activate(true); back = (newTabIndex < oldTabIndex); diff --git a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/adwindow/BreadCrumb.java b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/adwindow/BreadCrumb.java index 8e1fbd3487..8faebf134a 100644 --- a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/adwindow/BreadCrumb.java +++ b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/adwindow/BreadCrumb.java @@ -28,6 +28,7 @@ import org.adempiere.webui.component.Tabpanel; import org.adempiere.webui.component.ToolBar; import org.adempiere.webui.component.ToolBarButton; import org.adempiere.webui.component.Window; +import org.adempiere.webui.component.ZkCssHelper; import org.adempiere.webui.event.ToolbarListener; import org.adempiere.webui.window.WRecordInfo; import org.compiere.model.DataStatusEvent; @@ -37,6 +38,7 @@ import org.compiere.util.Env; import org.compiere.util.Msg; import org.compiere.util.Util; import org.zkoss.zhtml.Text; +import org.zkoss.zk.au.out.AuScript; import org.zkoss.zk.ui.Component; import org.zkoss.zk.ui.Execution; import org.zkoss.zk.ui.Executions; @@ -62,6 +64,10 @@ import org.zkoss.zul.Space; */ public class BreadCrumb extends Div implements EventListener { + private static final String ON_MOUSE_OVER_ECHO_EVENT = "onMouseOverEcho"; + + private static final String ON_MOUSE_OUT_ECHO_EVENT = "onMouseOutEcho"; + private static final String INFO_INDICATOR_IMAGE = "/images/InfoIndicator16.png"; private static final String ERROR_INDICATOR_IMAGE = "/images/ErrorIndicator16.png"; @@ -160,12 +166,24 @@ public class BreadCrumb extends Div implements EventListener { toolbar.setStyle("background-image: none; background-color: transparent; border: none;"); setWidgetAttribute(AdempiereWebUI.WIDGET_INSTANCE_NAME, "breadcrumb"); + + this.addEventListener(ON_MOUSE_OUT_ECHO_EVENT, this); } + /** + * + * @param listener + */ public void setToolbarListener(ToolbarListener listener) { this.toolbarListener = listener; } + /** + * + * @param label + * @param id + * @param clickable + */ public void addPath(String label, String id, boolean clickable) { if (clickable) { BreadCrumbLink a = new BreadCrumbLink(); @@ -193,6 +211,10 @@ public class BreadCrumb extends Div implements EventListener { } } + /** + * + * @return list of parent links + */ public List getParentLinks() { List parents = new ArrayList(); for(Component component : layout.getChildren()) { @@ -202,20 +224,63 @@ public class BreadCrumb extends Div implements EventListener { return parents; } + /** + * add links to other tabs at the same level + * @param links + */ public void addLinks(LinkedHashMap links) { this.links = links; final Label pathLabel = (Label) layout.getChildren().get(layout.getChildren().size()-2); - pathLabel.setStyle("cursor: pointer; font-weight: bold"); + pathLabel.setStyle("cursor: pointer; font-weight: bold; padding-right: 10px;"); EventListener listener = new EventListener() { @Override public void onEvent(Event event) throws Exception { - if (linkPopup != null ) { - if (linkPopup.getPage() != null && linkPopup.isVisible()) { - return; + if (linkPopup != null && linkPopup.getPage() != null && linkPopup.isVisible()) { + if (event.getName().equals(Events.ON_MOUSE_OUT)) { + linkPopup.setAttribute(ON_MOUSE_OUT_ECHO_EVENT, Boolean.TRUE); + StringBuilder script = new StringBuilder("setTimeout(function(){var w=zk('#") + .append(BreadCrumb.this.getUuid()).append("').$();") + .append("var e=new zk.Event(w, '") + .append(ON_MOUSE_OUT_ECHO_EVENT) + .append("', null, {toServer:true});") + .append("zAu.send(e);},500)"); + final AuScript response = new AuScript(script.toString()); + Clients.response(response); } + return; } - linkPopup = new Menupopup(); + if (event.getName().equals(Events.ON_CLICK)) { + if (linkPopup != null && linkPopup.getPage() != null) + linkPopup.detach(); + linkPopup = new Menupopup(); + showLinksMenu(pathLabel); + } else if (event.getName().equals(Events.ON_MOUSE_OVER)) { + if (linkPopup == null || !linkPopup.isVisible()) { + if (linkPopup != null && linkPopup.getPage() != null) + linkPopup.detach(); + linkPopup = new Menupopup(); + StringBuilder script = new StringBuilder("setTimeout(function(){var w=zk('#") + .append(event.getTarget().getUuid()).append("').$();") + .append("var e=new zk.Event(w, '") + .append(ON_MOUSE_OVER_ECHO_EVENT) + .append("', null, {toServer:true});") + .append("zAu.send(e);},500)"); + AuScript response = new AuScript(script.toString()); + Clients.response(response); + } + } else if (event.getName().equals(Events.ON_MOUSE_OUT)) { + if (linkPopup != null && linkPopup.getPage() == null) { + linkPopup = null; + } + } else if (event.getName().equals(ON_MOUSE_OVER_ECHO_EVENT)) { + if (linkPopup != null && linkPopup.getPage() == null) { + showLinksMenu(pathLabel); + } + } + } + + private void showLinksMenu(final Label pathLabel) { for(Map.Entryentry : BreadCrumb.this.links.entrySet()) { final Menuitem item = new Menuitem(); item.setLabel(entry.getValue()); @@ -223,12 +288,41 @@ public class BreadCrumb extends Div implements EventListener { item.addEventListener(Events.ON_CLICK, BreadCrumb.this); linkPopup.appendChild(item); } + + StringBuilder script = new StringBuilder("setTimeout(function(){var w=zk('#") + .append(BreadCrumb.this.getUuid()).append("').$();") + .append("var e=new zk.Event(w, '") + .append(ON_MOUSE_OUT_ECHO_EVENT) + .append("', null, {toServer:true});") + .append("zAu.send(e);},500)"); + final AuScript response = new AuScript(script.toString()); + linkPopup.addEventListener(Events.ON_MOUSE_OUT, new EventListener() { + @Override + public void onEvent(Event event) throws Exception { + if (linkPopup != null) { + linkPopup.setAttribute(ON_MOUSE_OUT_ECHO_EVENT, Boolean.TRUE); + Clients.response(response); + } + } + }); + linkPopup.addEventListener(Events.ON_MOUSE_OVER, new EventListener() { + + @Override + public void onEvent(Event event) throws Exception { + if (linkPopup != null && linkPopup.isVisible()) { + linkPopup.removeAttribute(ON_MOUSE_OUT_ECHO_EVENT); + } + } + }); linkPopup.setPage(pathLabel.getPage()); - linkPopup.open(pathLabel); + linkPopup.open(pathLabel); } }; pathLabel.addEventListener(Events.ON_CLICK, listener); pathLabel.addEventListener(Events.ON_MOUSE_OVER, listener); + pathLabel.addEventListener(Events.ON_MOUSE_OUT, listener); + pathLabel.addEventListener(ON_MOUSE_OVER_ECHO_EVENT, listener); + ZkCssHelper.appendStyle(pathLabel, "background: transparent url('images/downarrow.png') no-repeat right center"); } @Override @@ -241,10 +335,9 @@ public class BreadCrumb extends Div implements EventListener { String title = Msg.getMsg(Env.getCtx(), "Who") + m_text; new WRecordInfo (title, m_dse); - }else if(event.getTarget() instanceof RecordLink){ + } else if(event.getTarget() instanceof RecordLink){ doZoom((RecordLink)event.getTarget()); - } - else if (event.getTarget().getParent() == messageContainer) { + } else if (event.getTarget().getParent() == messageContainer) { showPopup(); } else if (event.getTarget() == btnFirst) { if (toolbarListener != null) @@ -258,23 +351,42 @@ public class BreadCrumb extends Div implements EventListener { } else if (event.getTarget() == btnLast) { if (toolbarListener != null) toolbarListener.onLast(); + } else if (event.getName().equals(ON_MOUSE_OUT_ECHO_EVENT)) { + if (linkPopup != null && linkPopup.getPage() != null && linkPopup.isVisible() + && linkPopup.getAttribute(ON_MOUSE_OUT_ECHO_EVENT) != null) { + linkPopup.detach(); + linkPopup = null; + } } else { Events.sendEvent(this, event); } } + /** + * remove all links + */ public void reset() { - layout.getChildren().clear(); - layout.appendChild(toolbarContainer); - this.links = null; + if (layout.getChildren().size() == 0 || layout.getChildren().size() > 1) { + layout.getChildren().clear(); + layout.appendChild(toolbarContainer); + this.links = null; + } } + /** + * enable/disable first record and previous record toolbar button + * @param enabled + */ public void enableFirstNavigation(boolean enabled) { this.btnFirst.setDisabled(!enabled); this.btnPrevious.setDisabled(!enabled); } + /** + * enable or disable the next record and last record toolbar button + * @param enabled + */ public void enableLastNavigation(boolean enabled) { this.btnLast.setDisabled(!enabled); @@ -505,7 +617,6 @@ public class BreadCrumb extends Div implements EventListener { msgPopup.setContentStyle("overflow: auto"); msgPopup.setWidth("500px"); msgPopup.appendChild(msgPopupCnt); -// msgPopup.setPage(SessionManager.getAppDesktop().getComponent().getPage()); msgPopup.setShadow(true); msgPopupCaption = new Caption(); msgPopup.appendChild(msgPopupCaption); @@ -524,10 +635,17 @@ public class BreadCrumb extends Div implements EventListener { linkPopup.detach(); } + /** + * + * @param visible + */ public void setNavigationToolbarVisibility(boolean visible) { toolbarContainer.setVisible(visible); } + /** + * @return true if there are one or more parent link + */ public boolean hasParentLink() { for(Component c : layout.getChildren()) { if (c instanceof BreadCrumbLink) { @@ -537,6 +655,10 @@ public class BreadCrumb extends Div implements EventListener { return false; } + /** + * + * @return process logs + */ public ProcessInfoLog[] getPLogs() { return pInfoLogs; } diff --git a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/adwindow/CompositeADTabbox.java b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/adwindow/CompositeADTabbox.java index 3065122612..6e94047fd2 100644 --- a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/adwindow/CompositeADTabbox.java +++ b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/adwindow/CompositeADTabbox.java @@ -21,6 +21,7 @@ import java.util.ArrayList; import java.util.Collections; import java.util.LinkedHashMap; import java.util.List; +import java.util.logging.Level; import org.adempiere.util.Callback; import org.adempiere.webui.component.ADTabListModel; @@ -54,10 +55,11 @@ import org.zkoss.zul.Vlayout; */ public class CompositeADTabbox extends AbstractADTabbox { + private static final String ON_POST_TAB_SELECTION_CHANGED_EVENT = "onPostTabSelectionChanged"; + public static final String ON_SELECTION_CHANGED_EVENT = "onSelectionChanged"; /** Logger */ - @SuppressWarnings("unused") private static CLogger log = CLogger.getCLogger (CompositeADTabbox.class); private List tabLabelList = new ArrayList(); @@ -66,17 +68,17 @@ public class CompositeADTabbox extends AbstractADTabbox private Vlayout layout; - protected DetailPane detailPane; - private EventListener selectionListener; private IADTabpanel headerTab; private int selectedIndex = 0; - public CompositeADTabbox() - { - detailPane = new DetailPane(); + public CompositeADTabbox(){ + } + + protected DetailPane createDetailPane() { + DetailPane detailPane = new DetailPane(); detailPane.setEventListener(new EventListener() { @Override @@ -84,8 +86,8 @@ public class CompositeADTabbox extends AbstractADTabbox if (DetailPane.ON_EDIT_EVENT.equals(event.getName())) { if (headerTab.getGridTab().isNew()) return; - final int row = detailPane.getSelectedADTabpanel() != null - ? detailPane.getSelectedADTabpanel().getGridTab().getCurrentRow() + final int row = getSelectedDetailADTabpanel() != null + ? getSelectedDetailADTabpanel().getGridTab().getCurrentRow() : 0; final boolean formView = event.getData() != null ? (Boolean)event.getData() : true; adWindowPanel.saveAndNavigate(new Callback() { @@ -99,8 +101,8 @@ public class CompositeADTabbox extends AbstractADTabbox else if (DetailPane.ON_NEW_EVENT.equals(event.getName())) { if (headerTab.getGridTab().isNew()) return; - final int row = detailPane.getSelectedADTabpanel() != null - ? detailPane.getSelectedADTabpanel().getGridTab().getCurrentRow() + final int row = getSelectedDetailADTabpanel() != null + ? getSelectedDetailADTabpanel().getGridTab().getCurrentRow() : 0; adWindowPanel.saveAndNavigate(new Callback() { @Override @@ -116,7 +118,7 @@ public class CompositeADTabbox extends AbstractADTabbox else if (DetailPane.ON_DELETE_EVENT.equals(event.getName())) { if (headerTab.getGridTab().isNew()) return; - final IADTabpanel tabPanel = detailPane.getSelectedADTabpanel(); + final IADTabpanel tabPanel = getSelectedDetailADTabpanel(); if (tabPanel != null && tabPanel.getGridTab().getRowCount() > 0 && tabPanel.getGridTab().getCurrentRow() >= 0) { FDialog.ask(tabPanel.getGridTab().getWindowNo(), null, "DeleteRecord?", new Callback() { @@ -134,12 +136,14 @@ public class CompositeADTabbox extends AbstractADTabbox } } } - }); + }); + + return detailPane; } - + protected void onEditDetail(int row, boolean formView) { int oldIndex = selectedIndex; - IADTabpanel selectedPanel = detailPane.getSelectedADTabpanel(); + IADTabpanel selectedPanel = getSelectedDetailADTabpanel(); if (selectedPanel == null) return; int newIndex = selectedPanel.getTabNo(); @@ -168,6 +172,13 @@ public class CompositeADTabbox extends AbstractADTabbox } else { layout.setPage(page); } + + layout.addEventListener(ON_POST_TAB_SELECTION_CHANGED_EVENT, new EventListener() { + @Override + public void onEvent(Event event) throws Exception { + onPostTabSelectionChanged(); + } + }); BreadCrumb breadCrumb = getBreadCrumb(); breadCrumb.addEventListener(Events.ON_CLICK, new EventListener() { @@ -210,12 +221,10 @@ public class CompositeADTabbox extends AbstractADTabbox return; IADTabpanel tabPanel = (IADTabpanel) event.getTarget(); - if (tabPanel == headerTab) { + if (tabPanel != headerTab) { if (b != null && b.booleanValue()) { - activateDetailADTabpanel(); - } - } else { - onActivateDetail(tabPanel); + onActivateDetail(tabPanel); + } } } }); @@ -225,26 +234,26 @@ public class CompositeADTabbox extends AbstractADTabbox public void onEvent(Event event) throws Exception { final IADTabpanel tabPanel = (IADTabpanel) event.getTarget(); int oldIndex = (Integer) event.getData(); - if (oldIndex != detailPane.getSelectedIndex()) { - IADTabpanel prevTabPanel = detailPane.getADTabpanel(oldIndex); + if (oldIndex != headerTab.getDetailPane().getSelectedIndex()) { + IADTabpanel prevTabPanel = headerTab.getDetailPane().getADTabpanel(oldIndex); if (prevTabPanel != null && prevTabPanel.needSave(true, true)) { - final int newIndex = detailPane.getSelectedIndex(); - detailPane.setSelectedIndex(oldIndex); + final int newIndex = headerTab.getDetailPane().getSelectedIndex(); + headerTab.getDetailPane().setSelectedIndex(oldIndex); adWindowPanel.saveAndNavigate(new Callback() { @Override public void onCallback(Boolean result) { if (result) { - detailPane.setSelectedIndex(newIndex); - onActivateDetail(tabPanel); + headerTab.getDetailPane().setSelectedIndex(newIndex); + tabPanel.activate(true); } } }); } else { - detailPane.setSelectedIndex(detailPane.getSelectedIndex()); - onActivateDetail(tabPanel); + headerTab.getDetailPane().setSelectedIndex(headerTab.getDetailPane().getSelectedIndex()); + tabPanel.activate(true); } } else { - onActivateDetail(tabPanel); + tabPanel.activate(true); } } }); @@ -259,7 +268,8 @@ public class CompositeADTabbox extends AbstractADTabbox if (detailPanel != null) { detailPanel.setDetailPaneMode(true); } - detailPane.setVflex("true"); + if (headerTab.getDetailPane() != null) + headerTab.getDetailPane().setVflex("true"); } } }); @@ -272,7 +282,7 @@ public class CompositeADTabbox extends AbstractADTabbox if (tabPanel == headerTab) { adWindowPanel.onToggle(); } else { - detailPane.onEdit(true); + headerTab.getDetailPane().onEdit(true); } } @@ -295,19 +305,18 @@ public class CompositeADTabbox extends AbstractADTabbox headerTab = tabPanel; updateBreadCrumb(); } else if (tabLabel.tabLevel <= 1) { - if (detailPane.getParent() == null) { - ADTabpanel adtabpanel = (ADTabpanel) headerTab; - adtabpanel.addDetails(detailPane); + if (headerTab.getDetailPane() == null) { + headerTab.setDetailPane(createDetailPane()); } else tabPanel.setVisible(false); - detailPane.setHflex("1"); - detailPane.addADTabpanel(tabPanel, tabLabel); + headerTab.getDetailPane().setHflex("1"); + headerTab.getDetailPane().addADTabpanel(tabPanel, tabLabel); tabPanel.setDetailPaneMode(true); - detailPane.setVflex("true"); + headerTab.getDetailPane().setVflex("true"); } else { - detailPane.addADTabpanel(tabPanel, tabLabel, false); + headerTab.getDetailPane().addADTabpanel(tabPanel, tabLabel, false); tabPanel.setDetailPaneMode(true); - detailPane.setVflex("true"); + headerTab.getDetailPane().setVflex("true"); } HtmlBasedComponent htmlComponent = (HtmlBasedComponent) tabPanel; htmlComponent.setVflex("1"); @@ -316,39 +325,33 @@ public class CompositeADTabbox extends AbstractADTabbox tabPanel.getGridTab().addDataStatusListener(new SyncDataStatusListener(tabPanel)); } - private void activateDetailADTabpanel() { - if (detailPane != null && detailPane.getParent() != null) { - IADTabpanel tabPanel = detailPane.getSelectedADTabpanel(); - if (tabPanel != null) { - tabPanel.activate(true); - if (!tabPanel.isGridView()) { - tabPanel.switchRowPresentation(); - } - } + private void activateDetailIfVisible() { + if (headerTab instanceof ADTabpanel) { + ((ADTabpanel)headerTab).activateDetailIfVisible(); } } @Override protected void updateTabState() { - if (detailPane != null && detailPane.getTabcount() > 0) + if (isDetailPaneLoaded()) { - for(int i = 0; i < detailPane.getTabcount(); i++) + for(int i = 0; i < headerTab.getDetailPane().getTabcount(); i++) { - IADTabpanel adtab = detailPane.getADTabpanel(i); + IADTabpanel adtab = headerTab.getDetailPane().getADTabpanel(i); if (adtab.getDisplayLogic() != null && adtab.getDisplayLogic().trim().length() > 0) { if (!Evaluator.evaluateLogic(headerTab, adtab.getDisplayLogic())) { - detailPane.setTabVisibility(i, false); + headerTab.getDetailPane().setTabVisibility(i, false); } else { - detailPane.setTabVisibility(i, true); + headerTab.getDetailPane().setTabVisibility(i, true); } } } - int selected = detailPane.getSelectedIndex(); - if (detailPane.getADTabpanel(selected) == null || !detailPane.isTabVisible(selected)) { - for(int i = 0; i < detailPane.getTabcount(); i++) { + int selected = headerTab.getDetailPane().getSelectedIndex(); + if (headerTab.getDetailPane().getADTabpanel(selected) == null || !headerTab.getDetailPane().isTabVisible(selected)) { + for(int i = 0; i < headerTab.getDetailPane().getTabcount(); i++) { if (selected == i) continue; - if (detailPane.isTabVisible(i)) { - detailPane.setSelectedIndex(i); + if (headerTab.getDetailPane().isTabVisible(i)) { + headerTab.getDetailPane().setSelectedIndex(i); break; } } @@ -387,46 +390,68 @@ public class CompositeADTabbox extends AbstractADTabbox layout.getChildren().clear(); layout.appendChild(headerTab); - detailPane.detach(); - detailPane.reset(); - - int currentLevel = headerTab.getTabLevel(); - for (int i = selectedIndex + 1; i< tabPanelList.size(); i++) { - IADTabpanel tabPanel = tabPanelList.get(i); - int tabLevel = tabPanel.getTabLevel(); - ADTabListModel.ADTabLabel tabLabel = tabLabelList.get(i); - if ((tabLevel - currentLevel) == 1 || (tabLevel == 0 && currentLevel == 0)) { - if (tabPanel.isActive() && !tabPanel.isGridView()) { - tabPanel.switchRowPresentation(); - } - if (tabPanel.getParent() != null) tabPanel.detach(); - detailPane.addADTabpanel(tabPanel, tabLabel); - tabPanel.setDetailPaneMode(true); - } else if (tabLevel > currentLevel ){ - detailPane.addADTabpanel(tabPanel, tabLabel, false); - tabPanel.setDetailPaneMode(true); - } else { - break; - } - } - - if (detailPane.getTabcount() > 0 && !headerTab.getGridTab().isSortTab()) { - ADTabpanel adtabpanel = (ADTabpanel) headerTab; - adtabpanel.addDetails(detailPane); - detailPane.setVflex("true"); - detailPane.setSelectedIndex(0); - activateDetailADTabpanel(); - } - + //set state headerTab.setDetailPaneMode(false); + getBreadCrumb().getFirstChild().setVisible(false); - updateBreadCrumb(); + Events.echoEvent(new Event(ON_POST_TAB_SELECTION_CHANGED_EVENT, layout)); } - private void updateBreadCrumb() { + private void onPostTabSelectionChanged() { + if (headerTab instanceof ADTabpanel) { + DetailPane detailPane = headerTab.getDetailPane(); + if (detailPane == null) { + detailPane = createDetailPane(); + } + + int tabIndex = -1; + int currentLevel = headerTab.getTabLevel(); + for (int i = selectedIndex + 1; i< tabPanelList.size(); i++) { + IADTabpanel tabPanel = tabPanelList.get(i); + int tabLevel = tabPanel.getTabLevel(); + ADTabListModel.ADTabLabel tabLabel = tabLabelList.get(i); + if ((tabLevel - currentLevel) == 1 || (tabLevel == 0 && currentLevel == 0)) { + if (tabPanel.isActivated() && !tabPanel.isGridView()) { + tabPanel.switchRowPresentation(); + } + if (tabPanel.getParent() != null) tabPanel.detach(); + tabIndex++; + detailPane.setADTabpanel(tabIndex, tabPanel, tabLabel); + tabPanel.setDetailPaneMode(true); + } else if (tabLevel > currentLevel ){ + tabIndex++; + detailPane.setADTabpanel(tabIndex, tabPanel, tabLabel, false); + tabPanel.setDetailPaneMode(true); + } else { + break; + } + } + + if (detailPane.getTabcount() > 0 && !headerTab.getGridTab().isSortTab()) { + detailPane.setVflex("true"); + detailPane.setSelectedIndex(0); + if (headerTab.getDetailPane() == null) { + headerTab.setDetailPane(detailPane); + } + activateDetailIfVisible(); + } + } + + updateBreadCrumb(); + getBreadCrumb().getFirstChild().setVisible(true); + + updateTabState(); + + ADWindow adwindow = ADWindow.findADWindow(layout); + if (adwindow != null) { + adwindow.getADWindowContent().getToolbar().enableTabNavigation(getBreadCrumb().hasParentLink(), + headerTab.getDetailPane() != null && headerTab.getDetailPane().getTabcount() > 0); + } + } + + private void updateBreadCrumb() { BreadCrumb breadCrumb = getBreadCrumb(); breadCrumb.reset(); - if (selectedIndex > 0) { List parents = new ArrayList(); List parentIndex = new ArrayList(); @@ -510,14 +535,15 @@ public class CompositeADTabbox extends AbstractADTabbox Execution execution = Executions.getCurrent(); if (execution == null) return; - if (tabPanel == headerTab && detailPane.getPage() != null && e.getChangedColumn() == -1) { + if (tabPanel == headerTab && e.getChangedColumn() == -1 + && isDetailActivated()) { ArrayList parentColumnNames = new ArrayList(); GridField[] parentFields = headerTab.getGridTab().getFields(); for (GridField parentField : parentFields) { parentColumnNames.add(parentField.getColumnName()); } - IADTabpanel detailTab = detailPane.getSelectedADTabpanel(); + IADTabpanel detailTab = getSelectedDetailADTabpanel(); if (detailTab != null) { //check data action String uuid = (String) execution.getAttribute(CompositeADTabbox.class.getName()+".dataAction"); @@ -540,7 +566,7 @@ public class CompositeADTabbox extends AbstractADTabbox detailTab.activate(true); detailTab.setDetailPaneMode(true); } - detailPane.setVflex("true"); + headerTab.getDetailPane().setVflex("true"); } } @@ -548,13 +574,24 @@ public class CompositeADTabbox extends AbstractADTabbox @Override public void onDetailRecord() { - if (detailPane != null && detailPane.getSelectedADTabpanel() != null) { + if (headerTab.getDetailPane() != null && getSelectedDetailADTabpanel() != null) { try { - detailPane.onEdit(false); - } catch (Exception e) {} + headerTab.getDetailPane().onEdit(false); + } catch (Exception e) { + log.log(Level.SEVERE, e.getLocalizedMessage(), e); + } } } + public boolean isDetailActivated() { + if (headerTab instanceof ADTabpanel) { + ADTabpanel atp = (ADTabpanel) headerTab; + return atp.hasDetailTabs() && getSelectedDetailADTabpanel() != null && + getSelectedDetailADTabpanel().isActivated(); + } + return false; + } + @Override public boolean isSortTab() { return headerTab != null ? headerTab.getGridTab().isSortTab() : false; @@ -562,8 +599,8 @@ public class CompositeADTabbox extends AbstractADTabbox @Override public IADTabpanel getSelectedDetailADTabpanel() { - if (detailPane != null && detailPane.getParent() != null) { - return detailPane.getSelectedADTabpanel(); + if (headerTab instanceof ADTabpanel && ((ADTabpanel)headerTab).hasDetailTabs()) { + return headerTab.getDetailPane().getSelectedADTabpanel(); } return null; } @@ -612,7 +649,7 @@ public class CompositeADTabbox extends AbstractADTabbox @Override public void setDetailPaneStatusMessage(String status, boolean error) { - detailPane.setStatusMessage(status, error); + headerTab.getDetailPane().setStatusMessage(status, error); } @Override @@ -636,9 +673,9 @@ public class CompositeADTabbox extends AbstractADTabbox tabPanel.switchRowPresentation(); } tabPanel.setDetailPaneMode(true); - detailPane.setVflex("true"); + headerTab.getDetailPane().setVflex("true"); if (tabPanel instanceof ADSortTab) { - detailPane.updateToolbar(false, true); + headerTab.getDetailPane().updateToolbar(false, true); } else { tabPanel.dynamicDisplay(0); } @@ -648,7 +685,7 @@ public class CompositeADTabbox extends AbstractADTabbox String msg = CLogger.retrieveErrorString(null); if (msg != null) { - detailPane.setStatusMessage(Msg.getMsg(Env.getCtx(), msg), true); + headerTab.getDetailPane().setStatusMessage(Msg.getMsg(Env.getCtx(), msg), true); } //other error will be catch in the dataStatusChanged event } @@ -656,24 +693,36 @@ public class CompositeADTabbox extends AbstractADTabbox @Override public void updateDetailPaneToolbar(boolean changed, boolean readOnly) { if (headerTab.getGridTab().isNew() || headerTab.getGridTab().getRowCount() == 0) - detailPane.disableToolbar(); + headerTab.getDetailPane().disableToolbar(); else - detailPane.updateToolbar(changed, readOnly); + headerTab.getDetailPane().updateToolbar(changed, readOnly); } @Override - public void setDetailpaneSelection(int tabIndex, int currentRow) { - if (detailPane.getTabcount() > 0) { - for(int i = 0; i < detailPane.getTabcount(); i++) { - IADTabpanel adtab = detailPane.getADTabpanel(i); - int index = adtab.getTabNo(); - if (index == tabIndex) { - if (!detailPane.isTabVisible(i) || !detailPane.isTabEnabled(i)) { + public boolean isDetailPaneLoaded() { + if (headerTab.getDetailPane() == null || headerTab.getDetailPane().getTabcount() == 0) + return false; + for(int i = 0; i < headerTab.getDetailPane().getTabcount(); i++) { + if (headerTab.getDetailPane().getADTabpanel(i) == null) + return false; + } + return true; + } + + @Override + public void setDetailPaneSelectedTab(int adTabNo, int currentRow) { + if (headerTab instanceof ADTabpanel && ((ADTabpanel) headerTab).hasDetailTabs()) { + for(int i = 0; i < headerTab.getDetailPane().getTabcount(); i++) { + IADTabpanel adtab = headerTab.getDetailPane().getADTabpanel(i); + if (adtab == null) continue; + int tabNo = adtab.getTabNo(); + if (tabNo == adTabNo) { + if (!headerTab.getDetailPane().isTabVisible(i) || !headerTab.getDetailPane().isTabEnabled(i)) { return; } - if (i != detailPane.getSelectedIndex()) { - detailPane.setSelectedIndex(i); - detailPane.fireActivateDetailEvent(); + if (i != headerTab.getDetailPane().getSelectedIndex()) { + headerTab.getDetailPane().setSelectedIndex(i); + headerTab.getDetailPane().fireActivateDetailEvent(); } if (adtab.getGridTab().getCurrentRow() != currentRow) adtab.getGridTab().setCurrentRow(currentRow, true); diff --git a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/adwindow/DetailPane.java b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/adwindow/DetailPane.java index c3d04b92be..c8798b88f0 100644 --- a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/adwindow/DetailPane.java +++ b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/adwindow/DetailPane.java @@ -114,15 +114,25 @@ public class DetailPane extends Panel implements EventListener, IdSpace { setId("detailPane"); } + /** + * @return selected tab index + */ public int getSelectedIndex() { return tabbox.getSelectedIndex(); } + /** + * set selected tab index + * @param curTabIndex + */ public void setSelectedIndex(int curTabIndex) { tabbox.setSelectedIndex(curTabIndex); prevSelectedIndex = curTabIndex; } + /** + * @return number of tabs + */ public int getTabcount() { int count = 0; Tabs tabs = tabbox.getTabs(); @@ -131,18 +141,64 @@ public class DetailPane extends Panel implements EventListener, IdSpace { return count; } + /** + * undo last tab selection + */ public void undoLastTabSelection() { tabbox.setSelectedIndex(prevSelectedIndex); } + /** + * redraw tabbox + */ public void refresh() { tabbox.invalidate(); } + /** + * replace of add + * @param index + * @param tabPanel + * @param tabLabel + */ + public void setADTabpanel(int index, IADTabpanel tabPanel, ADTabLabel tabLabel) { + if (index < getTabcount()) { + tabbox.getTabpanel(index).appendChild(tabPanel); + } else { + addADTabpanel(tabPanel, tabLabel); + } + } + + /** + * replace or add + * @param index + * @param tabPanel + * @param tabLabel + * @param enabled + */ + public void setADTabpanel(int index, IADTabpanel tabPanel, ADTabLabel tabLabel, boolean enabled) { + if (index < getTabcount()) { + tabbox.getTabpanel(index).appendChild(tabPanel); + } else { + addADTabpanel(tabPanel, tabLabel, enabled); + } + } + + /** + * + * @param tabPanel + * @param tabLabel + */ public void addADTabpanel(IADTabpanel tabPanel, ADTabLabel tabLabel) { addADTabpanel(tabPanel, tabLabel, true); } + /** + * + * @param tabPanel + * @param tabLabel + * @param enabled + */ public void addADTabpanel(IADTabpanel tabPanel, ADTabLabel tabLabel, boolean enabled) { Tabs tabs = tabbox.getTabs(); if (tabs == null) { @@ -280,10 +336,17 @@ public class DetailPane extends Panel implements EventListener, IdSpace { LayoutUtils.openPopupWindow(button, popup, "after_start"); } + /** + * + * @param listener + */ public void setEventListener(EventListener listener) { eventListener = listener; } + /** + * remove all tabs and tabpanels + */ public void reset() { if (tabbox.getTabs() != null) { tabbox.getTabs().getChildren().clear(); @@ -294,6 +357,10 @@ public class DetailPane extends Panel implements EventListener, IdSpace { } + /** + * @param index + * @return adtabpanel at index + */ public IADTabpanel getADTabpanel(int index) { if (index < 0 || index >= tabbox.getTabpanels().getChildren().size()) return null; @@ -306,6 +373,10 @@ public class DetailPane extends Panel implements EventListener, IdSpace { return null; } + /** + * + * @return selected adtabpanel + */ public IADTabpanel getSelectedADTabpanel() { org.zkoss.zul.Tabpanel selectedPanel = tabbox.getSelectedPanel(); if (selectedPanel != null) { @@ -317,6 +388,11 @@ public class DetailPane extends Panel implements EventListener, IdSpace { return null; } + /** + * + * @param status + * @param error + */ public void setStatusMessage(String status, boolean error) { IADTabpanel tabPanel = getSelectedADTabpanel(); if (tabPanel == null) return; @@ -404,7 +480,8 @@ public class DetailPane extends Panel implements EventListener, IdSpace { createPopupContent(status); showPopup(error, messageContainer); } else if (event.getName().equals(ADTabpanel.ON_DYNAMIC_DISPLAY_EVENT)) { - updateProcessToolbar(); + if (LayoutUtils.isReallyVisible(this)) + updateProcessToolbar(); } else if (event.getName().equals(LayoutUtils.ON_REDRAW_EVENT)) { ExecutionCtrl ctrl = (ExecutionCtrl) Executions.getCurrent(); Event evt = ctrl.getNextEvent(); @@ -463,6 +540,11 @@ public class DetailPane extends Panel implements EventListener, IdSpace { tabbox.setVflex(flex); } + /** + * update toolbar button state + * @param changed + * @param readOnly + */ public void updateToolbar(boolean changed, boolean readOnly) { int index = getSelectedIndex(); if (index < 0 || index >= getTabcount()) return; @@ -503,6 +585,7 @@ public class DetailPane extends Panel implements EventListener, IdSpace { Toolbar toolbar = (Toolbar) tabpanel.getFirstChild(); IADTabpanel adtab = getADTabpanel(index); + if (adtab == null) return; for(Component c : toolbar.getChildren()) { if (c instanceof ToolBarButton) { @@ -519,11 +602,19 @@ public class DetailPane extends Panel implements EventListener, IdSpace { } } + /** + * Edit current record + * @param formView + * @throws Exception + */ public void onEdit(boolean formView) throws Exception { Event openEvent = new Event(ON_EDIT_EVENT, DetailPane.this, Boolean.valueOf(formView)); eventListener.onEvent(openEvent); } + /** + * fire the on activate detail event + */ public void fireActivateDetailEvent() { int index = tabbox.getSelectedIndex(); IADTabpanel tabPanel = (IADTabpanel) tabbox.getTabpanel(index).getChildren().get(1); @@ -531,11 +622,15 @@ public class DetailPane extends Panel implements EventListener, IdSpace { Events.sendEvent(activateEvent); } - public void setTabVisibility(int i, boolean visible) { - if (i < 0 || tabbox.getTabs() == null || i >= tabbox.getTabs().getChildren().size()) + /** + * @param tabIndex + * @param visible + */ + public void setTabVisibility(int tabIndex, boolean visible) { + if (tabIndex < 0 || tabbox.getTabs() == null || tabIndex >= tabbox.getTabs().getChildren().size()) return; - Tab tab = (Tab) tabbox.getTabs().getChildren().get(i); + Tab tab = (Tab) tabbox.getTabs().getChildren().get(tabIndex); tab.setVisible(visible); if (tab.isSelected()) { tab.setSelected(false); @@ -545,29 +640,46 @@ public class DetailPane extends Panel implements EventListener, IdSpace { } } - public boolean isTabVisible(int i) { - if (i < 0 || tabbox.getTabs() == null || i >= tabbox.getTabs().getChildren().size()) + /** + * + * @param tabIndex + * @return true if tab at tabIndex is visible + */ + public boolean isTabVisible(int tabIndex) { + if (tabIndex < 0 || tabbox.getTabs() == null || tabIndex >= tabbox.getTabs().getChildren().size()) return false; - return tabbox.getTabs().getChildren().get(i).isVisible(); + return tabbox.getTabs().getChildren().get(tabIndex).isVisible(); } - public boolean isTabEnabled(int i) { - if (i < 0 || tabbox.getTabs() == null || i >= tabbox.getTabs().getChildren().size()) + /** + * @param tabIndex + * @return true if tab at tabIndex is enable + */ + public boolean isTabEnabled(int tabIndex) { + if (tabIndex < 0 || tabbox.getTabs() == null || tabIndex >= tabbox.getTabs().getChildren().size()) return false; - Tab tab = (Tab) tabbox.getTabs().getChildren().get(i); + Tab tab = (Tab) tabbox.getTabs().getChildren().get(tabIndex); return !tab.isDisabled(); } - public void setTabEnabled(int i, boolean enabled) { - if (i < 0 || tabbox.getTabs() == null || i >= tabbox.getTabs().getChildren().size()) + /** + * + * @param tabIndex + * @param enabled + */ + public void setTabEnabled(int tabIndex, boolean enabled) { + if (tabIndex < 0 || tabbox.getTabs() == null || tabIndex >= tabbox.getTabs().getChildren().size()) return; - Tab tab = (Tab) tabbox.getTabs().getChildren().get(i); + Tab tab = (Tab) tabbox.getTabs().getChildren().get(tabIndex); tab.setDisabled(!enabled); } + /** + * disable toolbar + */ public void disableToolbar() { int index = getSelectedIndex(); if (index < 0 || index >= getTabcount()) return; diff --git a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/adwindow/IADTabbox.java b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/adwindow/IADTabbox.java index c98d589062..519aa569ec 100644 --- a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/adwindow/IADTabbox.java +++ b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/adwindow/IADTabbox.java @@ -109,25 +109,72 @@ public interface IADTabbox extends UIPart { */ public void setADWindowPanel(AbstractADWindowContent abstractADWindowPanel); + /** + * drill down to the current selected adtabpanel + */ public void onDetailRecord(); + /** + * @return true if current header tab is a sort tab + */ public boolean isSortTab(); + /** + * @param rowChange + * @param onlyRealChange + * @return true if there are changes pending to be save + */ public boolean needSave(boolean rowChange, boolean onlyRealChange); + /** + * ignore all pending changes + */ public void dataIgnore(); + /** + * @return selected header grid tab + */ public GridTab getSelectedGridTab(); + /** + * + * @param onSaveEvent + * @return true if save is successfull + */ public boolean dataSave(boolean onSaveEvent); + /** + * + * @param status + * @param error + */ public void setDetailPaneStatusMessage(String status, boolean error); + /** + * @return the currently selected detail adtabpanel + */ IADTabpanel getSelectedDetailADTabpanel(); + /** + * @return dirty adtabpanel that need save ( if any ) + */ IADTabpanel getDirtyADTabpanel(); + /** + * + * @param changed + * @param readOnly + */ public void updateDetailPaneToolbar(boolean changed, boolean readOnly); - public void setDetailpaneSelection(int tabIndex, int currentRow); + /** + * @param tabIndex + * @param currentRow + */ + public void setDetailPaneSelectedTab(int tabIndex, int currentRow); + + /** + * @return true if all the tabs of detail pane have been linked up with adtabpanel + */ + boolean isDetailPaneLoaded(); } diff --git a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/adwindow/IADTabpanel.java b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/adwindow/IADTabpanel.java index cc2242bf16..aa1efe2c12 100644 --- a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/adwindow/IADTabpanel.java +++ b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/adwindow/IADTabpanel.java @@ -120,19 +120,64 @@ public interface IADTabpanel extends Component, Evaluatee { */ public boolean isGridView(); - public boolean isActive(); + /** + * @return true if the panel have been activated + */ + public boolean isActivated(); + /** + * + * @param detailMode + */ public void setDetailPaneMode(boolean detailMode); + /** + * + * @return true if the panel is in detailpane node + */ public boolean isDetailPaneMode(); + /** + * + * @return gridview instance + */ public abstract GridView getGridView(); + /** + * + * @param rowChange + * @param onlyRealChange + * @return true if there are pending changes + */ public boolean needSave(boolean rowChange, boolean onlyRealChange); + /** + * @param onSaveEvent + * @return true if the save operation completed successfully + */ public boolean dataSave(boolean onSaveEvent); + /** + * + * @param tabNo + */ public void setTabNo(int tabNo); - public int getTabNo(); + /** + * + * @return tab no ( ad_tab.tabno ) + */ + public int getTabNo(); + + /** + * + * @param detailPane + */ + public void setDetailPane(DetailPane detailPane); + + /** + * + * @return detailpane + */ + public DetailPane getDetailPane(); } diff --git a/org.adempiere.ui.zk/theme/default/css/theme.css.dsp b/org.adempiere.ui.zk/theme/default/css/theme.css.dsp index a4c04a101a..1016ee24d2 100644 --- a/org.adempiere.ui.zk/theme/default/css/theme.css.dsp +++ b/org.adempiere.ui.zk/theme/default/css/theme.css.dsp @@ -488,6 +488,7 @@ div.wc-modal, div.wc-modal-none, div.wc-highlighted, div.wc-highlighted-none { border: none; width: 100%; height: 100%; + background-color: #F9F9F9; } .adwindow-layout .z-center { @@ -577,10 +578,12 @@ div.wc-modal, div.wc-modal-none, div.wc-highlighted, div.wc-highlighted-none { .adwindow-gridview-detail + .z-south-splt { border-top: 1px solid #C5C5C5; + border-bottom: 1px solid #C5C5C5; } .adwindow-gridview-detail .z-south-body { padding-top: 1px; + background-color: #D3D3D3; } .adwindow-gridview-borderlayout { @@ -807,6 +810,12 @@ tr.z-group { font-family: Helvetica,Arial,sans-serif; } +.z-group td.z-group-inner { + overflow: hidden !important; + border-bottom: 1px solid #CFCFCF !important; + border-top: 1px solid #CFCFCF !important; +} + <%-- Tablet --%> .tablet-scrolling { -webkit-overflow-scrolling: touch; @@ -1043,12 +1052,6 @@ tbody.z-grid-empty-body td { width: 85% !important; } -.z-group td.z-group-inner { - overflow: hidden !important; - border-bottom: 1px solid #CFCFCF !important; - border-top: 1px solid #CFCFCF !important; -} - .embedded-dialog { position: absolute; }