/****************************************************************************** * Product: Posterita Ajax UI * * Copyright (C) 2007 Posterita Ltd. All Rights Reserved. * * This program is free software; you can redistribute it and/or modify it * * under the terms version 2 of the GNU General Public License as published * * by the Free Software Foundation. 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., * * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. * * For the text or an alternative of this public license, you may reach us * * Posterita Ltd., 3, Draper Avenue, Quatre Bornes, Mauritius * * or via info@posterita.org or http://www.posterita.org/ * *****************************************************************************/ package org.adempiere.webui.adwindow; import java.math.RoundingMode; import java.text.DecimalFormat; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.logging.Level; import org.adempiere.base.Core; import org.adempiere.exceptions.AdempiereException; import org.adempiere.util.Callback; import org.adempiere.webui.AdempiereIdGenerator; import org.adempiere.webui.AdempiereWebUI; import org.adempiere.webui.ClientInfo; import org.adempiere.webui.LayoutUtils; import org.adempiere.webui.apps.CalloutDialog; import org.adempiere.webui.component.Borderlayout; import org.adempiere.webui.component.Column; import org.adempiere.webui.component.Columns; import org.adempiere.webui.component.EditorBox; import org.adempiere.webui.component.Grid; import org.adempiere.webui.component.Group; import org.adempiere.webui.component.Label; import org.adempiere.webui.component.NumberBox; import org.adempiere.webui.component.Row; import org.adempiere.webui.component.Rows; import org.adempiere.webui.component.SimpleTreeModel; import org.adempiere.webui.component.Tab; import org.adempiere.webui.component.Tabbox; import org.adempiere.webui.component.Tabpanel; import org.adempiere.webui.component.Urlbox; import org.adempiere.webui.editor.IZoomableEditor; import org.adempiere.webui.editor.WButtonEditor; import org.adempiere.webui.editor.WEditor; import org.adempiere.webui.editor.WEditorPopupMenu; import org.adempiere.webui.editor.WImageEditor; import org.adempiere.webui.editor.WPaymentEditor; import org.adempiere.webui.editor.WebEditorFactory; import org.adempiere.webui.event.ContextMenuListener; import org.adempiere.webui.panel.HelpController; import org.adempiere.webui.session.SessionManager; import org.adempiere.webui.theme.ThemeManager; import org.adempiere.webui.util.GridTabDataBinder; import org.adempiere.webui.util.TreeUtils; import org.adempiere.webui.util.ZKUpdateUtil; import org.adempiere.webui.window.Dialog; 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.MColumn; import org.compiere.model.MPreference; import org.compiere.model.MRole; import org.compiere.model.MStyle; import org.compiere.model.MSysConfig; import org.compiere.model.MTab; 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.PO; import org.compiere.model.Query; import org.compiere.model.SystemProperties; import org.compiere.model.X_AD_FieldGroup; import org.compiere.model.X_AD_ToolBarButton; import org.compiere.util.CCache; import org.compiere.util.CLogger; import org.compiere.util.DefaultEvaluatee; import org.compiere.util.DisplayType; import org.compiere.util.Env; import org.compiere.util.Evaluatee; import org.compiere.util.Msg; import org.compiere.util.Util; import org.zkoss.zk.au.out.AuFocus; import org.zkoss.zk.au.out.AuScript; import org.zkoss.zk.ui.Component; import org.zkoss.zk.ui.Executions; import org.zkoss.zk.ui.HtmlBasedComponent; import org.zkoss.zk.ui.IdSpace; import org.zkoss.zk.ui.Page; 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; import org.zkoss.zul.Center; import org.zkoss.zul.DefaultTreeNode; import org.zkoss.zul.Div; import org.zkoss.zul.RowRenderer; import org.zkoss.zul.Separator; import org.zkoss.zul.South; import org.zkoss.zul.Space; import org.zkoss.zul.Tabpanels; import org.zkoss.zul.Tabs; import org.zkoss.zul.Toolbar; import org.zkoss.zul.TreeModel; import org.zkoss.zul.Treeitem; import org.zkoss.zul.Vlayout; import org.zkoss.zul.West; import org.zkoss.zul.impl.XulElement; /** * UI for an AD_Tab content (AD_Tab + AD_Fields). * * @author Ashley G Ramdass * @date Feb 25, 2007 * @version $Revision: 0.10 $ * * @author Low Heng Sin */ public class ADTabpanel extends Div implements Evaluatee, EventListener, DataStatusListener, IADTabpanel, IdSpace, IFieldEditorContainer { //css for slide animation private static final String SLIDE_LEFT_IN_CSS = "slide-left-in"; private static final String SLIDE_LEFT_OUT_CSS = "slide-left-out"; private static final String SLIDE_RIGHT_IN_CSS = "slide-right-in"; private static final String SLIDE_RIGHT_OUT_CSS = "slide-right-out"; /** * generated serial id */ private static final long serialVersionUID = -5335610241895151024L; /** event to save open/close state of detail pane as user preference for ad window **/ private static final String ON_SAVE_OPEN_PREFERENCE_EVENT = "onSaveOpenPreference"; /** post init event for tab panel **/ public static final String ON_POST_INIT_EVENT = "onPostInit"; /** event after tab panel had switch presentation between form and list view **/ public static final String ON_SWITCH_VIEW_EVENT = "onSwitchView"; /** Event after execution of {@link #dynamicDisplay(int)} **/ public static final String ON_DYNAMIC_DISPLAY_EVENT = "onDynamicDisplay"; /** defer event to set selected tree node **/ private static final String ON_DEFER_SET_SELECTED_NODE = "onDeferSetSelectedNode"; /** ADTabpanel attribute to prevent ON_DEFER_SET_SELECTED_NODE event posted twice within 1 execution **/ private static final String ON_DEFER_SET_SELECTED_NODE_ATTR = "onDeferSetSelectedNode.Event.Posted"; private static final CLogger logger; static { logger = CLogger.getCLogger(ADTabpanel.class); } private GridTab gridTab; private GridWindow gridWindow; /** AD Window content part that own this ADTabpanel instance **/ private AbstractADWindowContent windowPanel; private int windowNo; /** form view for center of {@link #formContainer} **/ private Grid form; /** field editors **/ private ArrayList editors = new ArrayList(); /** components for field editors **/ private ArrayList editorComps = new ArrayList(); /** editor toolbar buttons**/ private ArrayList toolbarButtonEditors = new ArrayList(); /** toolbar buttons for AD_ToolBarButton **/ private ArrayList toolbarProcessButtons = new ArrayList(); /** true if UI have been created for form and list **/ private boolean uiCreated = false; /** list view for center of {@link #formContainer} **/ private GridView listPanel; /** content rows for group **/ private Map> fieldGroupContents; /** header row for group **/ private Map> fieldGroupHeaders; /** tabs for group (for tab type field group) **/ private Map> fieldGroupTabHeaders; /** all rows for current group (regardless of field group type) **/ private ArrayList rowList; /** all collapsible groups **/ protected List allCollapsibleGroups; /** main layout for header (center), tree (west) and detail pane (south) **/ private Borderlayout formContainer = null; /** Tree panel for west of {@link #formContainer} **/ private ADTreePanel treePanel = null; /** Sync field editor changes to GridField **/ private GridTabDataBinder dataBinder; /** true if tab have been activated **/ protected boolean activated = false; /** * current group for collapsible type field group */ private Group currentGroup; /** Panel for child tabs, south of {@link #formContainer} **/ private DetailPane detailPane; /** true if this ADTabpanel instance is own by detail pane **/ private boolean detailPaneMode; /** tab no within an AD Window (sequence start from 0) **/ private int tabNo; /** Default focus field */ private WEditor defaultFocusField = null; /** number of columns for {@link #form} **/ private int numberOfFormColumns; /** event to toggle between form and list view **/ public static final String ON_TOGGLE_EVENT = "onToggle"; /** default width for west tree panel **/ private static final String DEFAULT_PANEL_WIDTH = "300px"; private static CCache quickFormCache = new CCache(null, "QuickForm", 20, false); /** Tab Box for Tab Type Field Groups */ private Tabbox tabbox = new Tabbox(); /** List of Grid/Form for tab type field group */ private List tabGroupForms; /** Current Rows for tab type field group */ private Rows currentTabGroupRows; /** Event for south of {@link #formContainer} **/ private static enum SouthEvent { SLIDE(), OPEN(), CLOSE(); private SouthEvent() {} } /** * default constructor */ public ADTabpanel() { init(); } /** * init components and event listeners */ private void init() { initComponents(); addEventListener(ON_DEFER_SET_SELECTED_NODE, this); addEventListener(WPaymentEditor.ON_SAVE_PAYMENT, this); addEventListener(ON_ACTIVATE_EVENT, new EventListener() { @Override public void onEvent(Event event) throws Exception { removeAttribute(ATTR_ON_ACTIVATE_POSTED); } }); addEventListener(ON_POST_INIT_EVENT, this); addEventListener(ON_SAVE_OPEN_PREFERENCE_EVENT, this); if (ClientInfo.isMobile()) ClientInfo.onClientInfo(this, this::onClientInfo); } /** * Create new {@link #form} and {@link #listPanel} instance. */ private void initComponents() { LayoutUtils.addSclass("adtab-content", this); ZKUpdateUtil.setWidth(this, "100%"); form = new Grid(); ZKUpdateUtil.setHflex(form, "1"); ZKUpdateUtil.setHeight(form, null); form.setVflex(false); form.setSclass("grid-layout adwindow-form"); form.setWidgetAttribute(AdempiereWebUI.WIDGET_INSTANCE_NAME, "form"); //swipe listener for mobile if (ClientInfo.isMobile()) { form.addEventListener("onSwipeRight", e -> { if (windowPanel != null && windowPanel.getBreadCrumb() != null && windowPanel.getBreadCrumb().isPreviousEnabled()) { windowPanel.saveAndNavigate(b -> { if (b) { LayoutUtils.addSclass(SLIDE_RIGHT_OUT_CSS, form); windowPanel.onPrevious(); } }); } }); form.addEventListener("onSwipeLeft", e -> { if (windowPanel != null && windowPanel.getBreadCrumb() != null && windowPanel.getBreadCrumb().isNextEnabled()) { windowPanel.saveAndNavigate(b -> { if (b) { LayoutUtils.addSclass(SLIDE_LEFT_OUT_CSS, form); windowPanel.onNext(); } }); } }); } listPanel = new GridView(); if( "Y".equals(Env.getContext(Env.getCtx(), "P|ToggleOnDoubleClick")) ) listPanel.getListbox().addEventListener(Events.ON_DOUBLE_CLICK, this); } /** * Setup client side form swipe listener for mobile. * Send onSwipeRight and onSwipeLeft event to server. */ private void setupFormSwipeListener() { String uuid = form.getUuid(); StringBuilder script = new StringBuilder("(function(){let w=zk.Widget.$('") .append(uuid) .append("');"); script.append("jq(w).on('touchstart', function(e) {let w=zk.Widget.$(this);w._touchstart=e;});"); script.append("jq(w).on('touchmove', function(e) {let w=zk.Widget.$(this);w._touchmove=e;});"); script.append("jq(w).on('touchend', function(e) {let w=zk.Widget.$(this);let ts = w._touchstart; let tl = w._touchmove;" + "w._touchstart=null;w._touchmove=null;" + "if (ts && tl) {" + "if (ts.originalEvent) ts = ts.originalEvent;" + "if (tl.originalEvent) tl = tl.originalEvent;" + "if (ts.changedTouches && ts.changedTouches.length==1 && tl.changedTouches && tl.changedTouches.length==1) {" + "let diff=(tl.timeStamp-ts.timeStamp)/1000;if (diff > 1) return;" + "let diffx=tl.changedTouches[0].pageX-ts.changedTouches[0].pageX;" + "let diffy=tl.changedTouches[0].pageY-ts.changedTouches[0].pageY;" + "if (Math.abs(diffx) >= 100 && Math.abs(diffy) < 80) {" + "if (diffx > 0) {let event = new zk.Event(w, 'onSwipeRight', null, {toServer: true});zAu.send(event);} " + "else {let event = new zk.Event(w, 'onSwipeLeft', null, {toServer: true});zAu.send(event);}" + "}" + "}" + "}" + "});"); script.append("})()"); Clients.response(new AuScript(script.toString())); } @Override public void setDetailPane(DetailPane component) { detailPane = component; Borderlayout borderLayout = formContainer; South south = borderLayout.getSouth(); if (south == null) { south = new South(); LayoutUtils.addSlideSclass(south); borderLayout.appendChild(south); south.addEventListener(Events.ON_OPEN, this); south.addEventListener(Events.ON_SLIDE, this); } south.appendChild(component); south.setVisible(true); south.setCollapsible(true); south.setSplittable(true); south.setOpen(isOpenDetailPane()); south.setSclass("adwindow-gridview-detail"); if (!south.isOpen()) LayoutUtils.addSclass("slide", south); String height = heigthDetailPane(); if (! Util.isEmpty(height)) { try { ClientInfo browserInfo = SessionManager.getAppDesktop().getClientInfo(); int browserHeight = browserInfo.desktopHeight; int prefHeight = Integer.valueOf(height.replace("px", "")); int topmarginpx = MSysConfig.getIntValue(MSysConfig.TOP_MARGIN_PIXELS_FOR_HEADER, 222); int maxHeight = browserHeight - topmarginpx; if (prefHeight <= maxHeight) { height = Integer.toString(prefHeight) + "px"; ZKUpdateUtil.setHeight(formContainer.getSouth(), height); } } catch (Exception e) { // just ignore, exception is harmless here, consequence is just not setting height so it will assume the default of theme } } } @Override public DetailPane getDetailPane() { return detailPane; } /** * init tab panel layout ({@link #formContainer} and listeners * @param winPanel * @param gridTab */ public void init(AbstractADWindowContent winPanel, GridTab gridTab) { this.gridWindow = gridTab.getGridWindow(); this.windowNo = gridWindow.getWindowNo(); this.gridTab = gridTab; // callout dialog ask for input - devCoffee #3390 gridTab.setCalloutUI(new CalloutDialog(Executions.getCurrent().getDesktop(), windowNo)); this.windowPanel = winPanel; gridTab.addDataStatusListener(this); this.dataBinder = new GridTabDataBinder(gridTab); this.getChildren().clear(); if (SystemProperties.isZkUnitTest()) setId(AdempiereIdGenerator.escapeId(gridTab.getName())); int AD_Tree_ID = 0; if (gridTab.isTreeTab()) AD_Tree_ID = MTree.getDefaultAD_Tree_ID ( Env.getAD_Client_ID(Env.getCtx()), gridTab.getKeyColumnName()); if (gridTab.isTreeTab() && AD_Tree_ID != 0) { Borderlayout layout = new Borderlayout(); layout.setParent(this); layout.setSclass("adtab-form-borderlayout"); if (ClientInfo.isMobile()) LayoutUtils.addSclass("mobile", layout); treePanel = new ADTreePanel(windowNo, gridTab.getTabNo()); West west = new West(); west.appendChild(treePanel); ZKUpdateUtil.setWidth(west, widthTreePanel()); west.setCollapsible(true); west.setSplittable(true); west.setAutoscroll(true); layout.appendChild(west); LayoutUtils.addSlideSclass(west); if (isMobile()) { west.setOpen(false); LayoutUtils.addSclass("slide", west); } Center center = new Center(); Vlayout div = new Vlayout(); div.appendChild(form); center.appendChild(div); ZKUpdateUtil.setVflex(div, "1"); ZKUpdateUtil.setHflex(div, "1"); div.setSclass("adtab-form"); div.setStyle("overflow-y: visible;"); div.setSpacing("0px"); layout.appendChild(center); formContainer = layout; treePanel.getTree().addEventListener(Events.ON_SELECT, this); } else { Vlayout div = new Vlayout(); div.setSclass("adtab-form"); div.appendChild(form); div.setStyle("overflow-y: visible;"); ZKUpdateUtil.setVflex(div, "1"); ZKUpdateUtil.setWidth(div, "100%"); div.setSpacing("0px"); Borderlayout layout = new Borderlayout(); layout.setParent(this); layout.setSclass("adtab-form-borderlayout"); if (ClientInfo.isMobile()) LayoutUtils.addSclass("mobile", layout); Center center = new Center(); layout.appendChild(center); center.appendChild(div); formContainer = layout; } form.getParent().appendChild(tabbox); setGroupTabboxVisibility(); ZKUpdateUtil.setWidth(tabbox, "100%"); tabbox.setStyle("margin: 20px 0px 20px 0px; padding: 0px 20px 0px 20px; "); if (ClientInfo.isMobile()) { tabbox.setStyle(""); tabbox.setMold("accordion"); } form.getParent().appendChild(listPanel); listPanel.setVisible(false); listPanel.setWindowNo(windowNo); listPanel.setADWindowPanel(winPanel); } /** * Create UI for AD_Fields */ @Override public void createUI() { createUI(false); } /** * Create UI for AD_Fields * @param update true if it is update instead of create new */ protected void createUI(boolean update) { if (update) { if (!uiCreated) return; } else { if (uiCreated) return; uiCreated = true; } fieldGroupContents = new HashMap>(); fieldGroupHeaders = new HashMap>(); allCollapsibleGroups = new ArrayList(); tabGroupForms = new ArrayList(); fieldGroupTabHeaders = new HashMap>(); int numCols=gridTab.getNumColumns(); if (numCols <= 0) { numCols=6; } //adapt layout for phone and tablet int diff = 0; if (isMobile()) { if (ClientInfo.maxWidth(ClientInfo.EXTRA_SMALL_WIDTH-1)) { if (numCols > 3) { diff = numCols - 3; numCols=3; } } else if (ClientInfo.maxWidth(ClientInfo.MEDIUM_WIDTH-1)) { if (numCols > 6) { diff = numCols - 6; numCols=6; } } } this.numberOfFormColumns = numCols; if (update) form.getColumns().detach(); // set size in percentage per column leaving a MARGIN on right Columns columns = new Columns(); form.appendChild(columns); double equalWidth = 95.00d / numCols; DecimalFormat decimalFormat = new DecimalFormat("0.00"); decimalFormat.setRoundingMode(RoundingMode.DOWN); String columnWidth = decimalFormat.format(equalWidth); for (int h=0;h 0) row.appendCellChild(createSpacer(), numCols - actualxpos + 1); if (currentTabGroupRows != null) { currentTabGroupRows.appendChild(row); } else { row.setGroup(currentGroup); rows.appendChild(row); } if (rowList != null) rowList.add(row); List headerRows = new ArrayList(); fieldGroupHeaders.put(fieldGroup, headerRows); rowList = new ArrayList(); fieldGroupContents.put(fieldGroup, rowList); if (X_AD_FieldGroup.FIELDGROUPTYPE_Label.equals(field.getFieldGroupType())) { row = new Row(); Label groupLabel = new Label(fieldGroup); row.appendCellChild(groupLabel, numCols); rows.appendChild(row); headerRows.add(row); row = new Row(); Separator separator = new Separator(); separator.setBar(true); row.appendCellChild(separator, numCols); rows.appendChild(row); headerRows.add(row); currentGroup = null; currentTabGroupRows = null; } else if(X_AD_FieldGroup.FIELDGROUPTYPE_Tab.equals(field.getFieldGroupType())) { // Create New Tab for FieldGroup List headerTabs = new ArrayList(); fieldGroupTabHeaders.put(fieldGroup, headerTabs); Tabs tabs = tabbox.getTabs(); if (tabs == null) { tabs = new Tabs(); tabbox.appendChild(tabs); setGroupTabboxVisibility(); } Tab tab = new Tab(fieldGroup); tabs.appendChild(tab); headerTabs.add(tab); Grid tabForm = new Grid(); tabGroupForms.add(tabForm); ZKUpdateUtil.setHflex(tabForm, "1"); ZKUpdateUtil.setHeight(tabForm, null); tabForm.setVflex(false); tabForm.setSclass("grid-layout adwindow-form"); Columns tabColumns = new Columns(); tabForm.appendChild(tabColumns); double tabEqualWidth = 95.5d / numCols; DecimalFormat tabDecimalFormat = new DecimalFormat("0.00"); decimalFormat.setRoundingMode(RoundingMode.DOWN); String tabColumnWidth = tabDecimalFormat.format(tabEqualWidth); for (int h=0;h numCols && diff > 0) { xpos = xpos - diff; if (xpos <= 0) xpos = 1; if (xpos == 1 && (field.getDisplayType() == DisplayType.YesNo || field.getDisplayType() == DisplayType.Button || field.isFieldOnly())) xpos = 2; } //normal field if (xpos <= actualxpos) { // Fill right part of the row with spacers until number of columns if (numCols - actualxpos + 1 > 0) row.appendCellChild(createSpacer(), numCols - actualxpos + 1); // Tab Group vs Grid Group if (currentTabGroupRows != null) { currentTabGroupRows.appendChild(row); } else { row.setGroup(currentGroup); rows.appendChild(row); } if (rowList != null) rowList.add(row); row=new Row(); actualxpos = 0; } // Fill left part of the field if (xpos-1 - actualxpos > 0) row.appendCellChild(createSpacer(), xpos-1 - actualxpos); boolean paintLabel = ! (field.getDisplayType() == DisplayType.Button || field.getDisplayType() == DisplayType.YesNo || field.isFieldOnly()); // Adjust column spam to the remain columns size int remainCols = numCols - actualxpos; if (columnSpan > remainCols) columnSpan = remainCols-1 > 0 ? remainCols-1 : 1; if (field.isHeading()) actualxpos = xpos; else actualxpos = xpos + columnSpan-1 + (paintLabel ? 1 : 0); if (! field.isHeading()) { WEditor editor = update ? findEditor(field) : WebEditorFactory.getEditor(gridTab, field, false); if (editor != null) // Not heading { if (!update) { editor.getComponent().setWidgetOverride("fieldHeader", HelpController.escapeJavascriptContent(field.getHeader())); editor.getComponent().setWidgetOverride("fieldDescription", HelpController.escapeJavascriptContent(field.getDescription())); editor.getComponent().setWidgetOverride("fieldHelp", HelpController.escapeJavascriptContent(field.getHelp())); editor.getComponent().setWidgetListener("onFocus", "zWatch.fire('onFieldTooltip', this, null, this.fieldHeader(), this.fieldDescription(), this.fieldHelp());"); editor.setGridTab(this.getGridTab()); field.addPropertyChangeListener(editor); editors.add(editor); editorComps.add(editor.getComponent()); } if (paintLabel) { Div div = new Div(); div.setSclass("form-label"); Label label = editor.getLabel(); div.appendChild(label); if (label.getDecorator() != null) div.appendChild(label.getDecorator()); row.appendCellChild(div,1); } row.appendCellChild(editor.getComponent(), columnSpan ); //to support float/absolute editor row.getLastCell().setStyle("position: relative; overflow: visible;"); if (!update) { if (editor instanceof WButtonEditor) { if (windowPanel != null) ((WButtonEditor)editor).addActionListener(windowPanel); } else { editor.addValueChangeListener(dataBinder); } } // Default Focus if (defaultFocusField == null && field.isDefaultFocus()) defaultFocusField = editor; if (!update) { //stretch component to fill grid cell editor.fillHorizontal(); Component fellow = editor.getComponent().getFellowIfAny(field.getColumnName()); if (fellow == null) { editor.getComponent().setId(field.getColumnName()); } //setup editor context menu WEditorPopupMenu popupMenu = editor.getPopupMenu(); if (popupMenu == null) { popupMenu = new WEditorPopupMenu(false, false, false, false, false, false, null); } if (popupMenu != null) { if (editor instanceof ContextMenuListener) popupMenu.addMenuListener((ContextMenuListener)editor); popupMenu.setId(field.getColumnName()+"-popup"); this.appendChild(popupMenu); if (!field.isFieldOnly()) { Label label = editor.getLabel(); if (ClientInfo.isMobile()) { WEditorPopupMenu finalPopupMenu = popupMenu; label.addEventListener(Events.ON_CLICK, evt-> finalPopupMenu.open(label, "after_start")); } else { if (popupMenu.isZoomEnabled() && editor instanceof IZoomableEditor) { label.addEventListener(Events.ON_CLICK, new ZoomListener((IZoomableEditor) editor)); } popupMenu.addContextElement(label); } } popupMenu.addSuggestion(field); if(!ClientInfo.isMobile()) { if (editor.getComponent() instanceof XulElement) { popupMenu.addContextElement((XulElement) editor.getComponent()); } } } } } } else // just heading { //display just a label if we are "heading only" Label label = new Label(field.getHeader()); Div div = new Div(); div.setSclass("form-label-heading"); if (field.getAD_LabelStyle_ID() > 0) { MStyle style = MStyle.get(Env.getCtx(), field.getAD_LabelStyle_ID()); String cssStyle = style.buildStyle(ThemeManager.getTheme(), new Evaluatee() { @Override public String get_ValueAsString(String variableName) { return field.get_ValueAsString(variableName); } }); if (cssStyle != null && cssStyle.startsWith(MStyle.SCLASS_PREFIX)) { String sclass = cssStyle.substring(MStyle.SCLASS_PREFIX.length()); div.setSclass(sclass); } else if (style != null && cssStyle.startsWith(MStyle.ZCLASS_PREFIX)) { String zclass = cssStyle.substring(MStyle.ZCLASS_PREFIX.length()); div.setZclass(zclass); } else { div.setStyle(cssStyle); } } row.appendCellChild(createSpacer()); div.appendChild(label); row.appendCellChild(div); } } if (numCols - actualxpos + 1 > 0) row.appendCellChild(createSpacer(), numCols - actualxpos + 1); // Tab Group vs Grid Group if (currentTabGroupRows != null) { currentTabGroupRows.appendChild(row); } else { row.setGroup(currentGroup); rows.appendChild(row); } if (rowList != null) rowList.add(row); if (!update) loadToolbarButtons(); //create tree if (!update && gridTab.isTreeTab() && treePanel != null) { int AD_Tree_ID = Env.getContextAsInt (Env.getCtx(), getWindowNo(), "AD_Tree_ID", true); int AD_Tree_ID_Default = MTree.getDefaultAD_Tree_ID (Env.getAD_Client_ID(Env.getCtx()), gridTab.getKeyColumnName()); if (AD_Tree_ID != 0) { treePanel.initTree(AD_Tree_ID, windowNo); Events.echoEvent(ON_DEFER_SET_SELECTED_NODE, this, null); } else if (AD_Tree_ID_Default != 0) { int linkColId = MTree.get(Env.getCtx(), AD_Tree_ID_Default, null).getParent_Column_ID(); String linkColName = null; int linkID = 0; if (linkColId > 0) { linkColName = MColumn.getColumnName(Env.getCtx(), linkColId); linkID = Env.getContextAsInt(Env.getCtx(), windowNo, linkColName, true); } treePanel.initTree(AD_Tree_ID_Default, windowNo, linkColName, linkID); Events.echoEvent(ON_DEFER_SET_SELECTED_NODE, this, null); } } if (!update && !gridTab.isSingleRow() && !isGridView()) switchRowPresentation(); } /** * @param field * @return {@link WEditor} or null if not found */ private WEditor findEditor(GridField field) { for(WEditor editor : editors) { if (editor.getGridField() == field) return editor; } return null; } /** * load toolbar buttons from AD_ToolBarButton */ private void loadToolbarButtons() { //get extra toolbar process buttons MToolBarButton[] mToolbarButtons = MToolBarButton.getProcessButtonOfTab(gridTab.getAD_Tab_ID(), null); for(MToolBarButton mToolbarButton : mToolbarButtons) { Boolean access = MRole.getDefault().getProcessAccess(mToolbarButton.getAD_Process_ID()); if (access != null && access.booleanValue()) { ToolbarProcessButton toolbarProcessButton = new ToolbarProcessButton(mToolbarButton, this, windowPanel, windowNo); toolbarProcessButtons.add(toolbarProcessButton); } } if (toolbarProcessButtons.size() > 0) { int ids[] = MToolBarButtonRestrict.getProcessButtonOfTab(Env.getCtx(), Env.getAD_Role_ID(Env.getCtx()), gridTab.getAD_Tab_ID(), null); if (ids != null && ids.length > 0) { for(int id : ids) { X_AD_ToolBarButton tbt = new X_AD_ToolBarButton(Env.getCtx(), id, null); for(ToolbarProcessButton btn : toolbarProcessButtons) { if (tbt.getComponentName().equals(btn.getColumnName())) { toolbarProcessButtons.remove(btn); break; } } } } } } private Component createSpacer() { return new Space(); } /** * Update state of fields (visibility, style, writable, etc) * @param col */ @Override public void dynamicDisplay (int col) { if (!gridTab.isOpen()) { return; } //css animation for slide if (form.getSclass() != null && form.getSclass().contains(SLIDE_RIGHT_OUT_CSS)) { Executions.schedule(getDesktop(), e -> { LayoutUtils.removeSclass(SLIDE_RIGHT_OUT_CSS, form); LayoutUtils.addSclass(SLIDE_RIGHT_IN_CSS, form); Executions.schedule(getDesktop(), e1 -> onAfterSlide(e1), new Event("onAfterSlide", form)); }, new Event("onAfterSlideRightOut", form)); } else if (form.getSclass() != null && form.getSclass().contains(SLIDE_LEFT_OUT_CSS)) { Executions.schedule(getDesktop(), e -> { LayoutUtils.removeSclass(SLIDE_LEFT_OUT_CSS, form); LayoutUtils.addSclass(SLIDE_LEFT_IN_CSS, form); Executions.schedule(getDesktop(), e1 -> onAfterSlide(e1), new Event("onAfterSlide", form)); }, new Event("onAfterSlideLeftOut", form)); } List collapsedGroups = new ArrayList(); for (Group group : allCollapsibleGroups) { if (! group.isOpen()) collapsedGroups.add(group); } // Selective if (col > 0) { GridField changedField = gridTab.getField(col); String columnName = changedField.getColumnName(); ArrayList dependants = gridTab.getDependantFields(columnName); if (logger.isLoggable(Level.CONFIG)) logger.config("(" + gridTab.toString() + ") " + columnName + " - Dependents=" + dependants.size()); if ( ! ( dependants.size() > 0 || changedField.getCallout().length() > 0 || Core.findCallout(gridTab.getTableName(), columnName).size() > 0)) { for (WEditor comp : editors) { comp.updateStyle(); } return; } } boolean noData = gridTab.getRowCount() == 0; if (logger.isLoggable(Level.CONFIG)) logger.config(gridTab.toString() + " - Rows=" + gridTab.getRowCount()); for (WEditor comp : editors) { GridField mField = comp.getGridField(); if (mField != null) { if (mField.isDisplayed(true)) // check context { if (!comp.isVisible()) { comp.setVisible(true); // visibility } if (noData) { comp.setReadWrite(false); } else { boolean rw = mField.isEditable(true); // r/w - check Context if (rw && !comp.isReadWrite()) // IDEMPIERE-3421 - if it was read-only the list can contain direct values mField.refreshLookup(); comp.setReadWrite(rw); comp.setMandatory(mField.isMandatory(true)); // check context comp.dynamicDisplay(); } } else if (comp.isVisible()) { comp.setVisible(false); } } comp.updateStyle(); } // all components //hide row if all editor within the row is invisible List rows = form.getRows().getChildren(); for (Component comp : rows) { if (comp instanceof Row) { Row row = (Row) comp; boolean visible = false; boolean editorRow = false; for (Component cellComponent : row.getChildren()) { Component component = cellComponent.getFirstChild(); if (editorComps.contains(component)) { editorRow = true; // open the group if there is a mandatory unfilled field WEditor editor = editors.get(editorComps.indexOf(component)); if (editor != null && row.getGroup() != null && ! row.getGroup().isOpen() && editor.isMandatoryStyle()) { row.getGroup().setOpen(true); if (collapsedGroups.contains(row.getGroup())) { collapsedGroups.remove(row.getGroup()); } } if (component.isVisible()) { visible = true; break; } } } if (editorRow && (row.isVisible() != visible)) { row.setAttribute(Group.GROUP_ROW_VISIBLE_KEY, visible ? "true" : "false"); row.setVisible(visible); } } } //hide row if all editor within the row is invisible in Tabbox grid for(Grid tabForm: tabGroupForms) { List tabrows = tabForm.getRows().getChildren(); for (Component comp : tabrows) { if (comp instanceof Row) { Row row = (Row) comp; boolean visible = false; boolean editorRow = false; for (Component cellComponent : row.getChildren()) { Component component = cellComponent.getFirstChild(); if (editorComps.contains(component)) { editorRow = true; if (component.isVisible()) { visible = true; break; } } } if (editorRow && (row.isVisible() != visible)) { row.setVisible(visible); } } } } //hide fieldgroup if all editor row within the fieldgroup is invisible for(Iterator>> i = fieldGroupHeaders.entrySet().iterator(); i.hasNext();) { Map.Entry> entry = i.next(); List contents = fieldGroupContents.get(entry.getKey()); boolean visible = false; for (Row row : contents) { if (row.isVisible()) { visible = true; break; } } List headers = entry.getValue(); for(org.zkoss.zul.Row row : headers) { if (row.isVisible() != visible) row.setVisible(visible); } } // Check Field Group Tabs and Hide if all rows are invisible Tab visibleTab = null; // Change Selected Tab which will become invisible to another Tab boolean isSelectedTabInvisible = false; for(Iterator>> i = fieldGroupTabHeaders.entrySet().iterator(); i.hasNext();) { Map.Entry> entry = i.next(); List contents = fieldGroupContents.get(entry.getKey()); boolean visible = false; for (Row row : contents) { if (row.isVisible()) { visible = true; break; } } List tabs = entry.getValue(); for(Tab tab : tabs) { if (tab.isVisible() != visible) { if(tab.isSelected() && !visible) isSelectedTabInvisible = true; tab.setVisible(visible); } if(tab.isVisible()) visibleTab = tab; } } if(isSelectedTabInvisible && visibleTab != null) { tabbox.setSelectedTab(visibleTab); } // collapse the groups closed for (Group group : collapsedGroups) { group.setOpen(false); } if (listPanel.isVisible()) { listPanel.dynamicDisplay(col); } for (ToolbarProcessButton btn : toolbarProcessButtons) { btn.dynamicDisplay(); btn.readOnlyLogic(); btn.pressedLogic(); } Events.sendEvent(this, new Event(ON_DYNAMIC_DISPLAY_EVENT, this)); echoDeferSetSelectedNodeEvent(); if (logger.isLoggable(Level.CONFIG)) logger.config(gridTab.toString() + " - fini - " + (col<=0 ? "complete" : "seletive")); } // dynamicDisplay private void onAfterSlide(Event e) { //delay to let animation complete try { Thread.sleep(500); } catch (InterruptedException e1) {} LayoutUtils.removeSclass(SLIDE_LEFT_IN_CSS, form); LayoutUtils.removeSclass(SLIDE_RIGHT_IN_CSS, form); } /** * echo set selected node event for tree */ private void echoDeferSetSelectedNodeEvent() { if (getAttribute(ON_DEFER_SET_SELECTED_NODE_ATTR) == null) { setAttribute(ON_DEFER_SET_SELECTED_NODE_ATTR, Boolean.TRUE); Events.echoEvent(ON_DEFER_SET_SELECTED_NODE, this, null); } } /** * @return display logic */ @Override public String getDisplayLogic() { return gridTab.getDisplayLogic(); } /** * @return title of tab */ @Override public String getTitle() { return gridTab.getName(); } // getTitle /** * @param variableName * @return value */ @Override public String get_ValueAsString(String variableName) { return new DefaultEvaluatee(getGridTab(), windowNo, tabNo).get_ValueAsString(Env.getCtx(), variableName); } // get_ValueAsString /** * @return The tab level of this Tabpanel */ @Override public int getTabLevel() { return gridTab.getTabLevel(); } /** * @return The tablename of this Tabpanel */ @Override public String getTableName() { return gridTab.getTableName(); } /** * @return The record ID of current row */ @Override public int getRecord_ID() { return gridTab.getRecord_ID(); } /** * Is panel need refresh * @return true if GridTab need refresh */ @Override public boolean isCurrent() { return gridTab != null ? gridTab.isCurrent() : false; } /** * * @return windowNo */ public int getWindowNo() { return windowNo; } /** * Retrieve from db. * Delegate to {@link GridTab#query(boolean)} */ @Override public void query() { boolean open = gridTab.isOpen(); gridTab.query(false); if (listPanel.isVisible() && !open) gridTab.getTableModel().fireTableDataChanged(); } /** * Retrieve from db * @param onlyCurrentRows True to show only unprocessed or the one updated within x days (default is 1 day before today) * @param onlyCurrentDays if > 0, filter records with created >= current_date - onlyCurrentDays * @param maxRows if > 0, maximum number of rows to load */ @Override public void query (boolean onlyCurrentRows, int onlyCurrentDays, int maxRows) { boolean open = gridTab.isOpen(); gridTab.query(onlyCurrentRows, onlyCurrentDays, maxRows); if (listPanel.isVisible() && !open) gridTab.getTableModel().fireTableDataChanged(); } /** * Reset detail data grid for new parent record that's not saved yet. * Delegate to {@link GridTab#resetDetailForNewParentRecord()}. */ @Override public void resetDetailForNewParentRecord () { boolean open = gridTab.isOpen(); if (open) { gridTab.resetDetailForNewParentRecord(); } else { gridTab.setCurrentRow(-1, true); } } /** * @return GridTab */ public GridTab getGridTab() { return gridTab; } /** * @return ADTreePanel */ public ADTreePanel getTreePanel() { return treePanel; } /** * @return master, detail or both */ public String getTreeDisplayedOn() { return gridTab.getTreeDisplayedOn(); } /** * Refresh current row */ @Override public void refresh() { gridTab.dataRefresh(); } /** * Activate/deactivate panel * @param activate */ @Override public void activate(boolean activate) { if (activate) { if (getAttribute(ATTR_ON_ACTIVATE_POSTED) != null) { return; } setAttribute(ATTR_ON_ACTIVATE_POSTED, Boolean.TRUE); } activated = activate; if (listPanel.isVisible()) { if (activate) listPanel.activate(gridTab); else listPanel.deactivate(); } else { if (activate) { formContainer.setVisible(activate); if (!isMobile() && !isDetailPaneMode()) focusToFirstEditor(); } } if (gridTab.getRecord_ID() > 0 && gridTab.isTreeTab() && treePanel != null) { echoDeferSetSelectedNodeEvent(); } Event event = new Event(ON_ACTIVATE_EVENT, this, activate); Events.postEvent(event); } /** * set focus to first active editor */ @Override public void focusToFirstEditor() { focusToFirstEditor(false); } /** * Delegate to {@link #focusToEditor(WEditor, boolean)} * @param checkCurrent */ public void focusToFirstEditor(boolean checkCurrent) { WEditor toFocus = null; if (defaultFocusField != null && defaultFocusField.isVisible() && defaultFocusField.isReadWrite() && defaultFocusField.getComponent().getParent() != null && !(defaultFocusField instanceof WImageEditor)) { toFocus = defaultFocusField; } else { for (WEditor editor : editors) { if (editor.isVisible() && editor.isReadWrite() && editor.getComponent().getParent() != null && !(editor instanceof WImageEditor)) { toFocus = editor; break; } } } if (toFocus != null) { focusToEditor(toFocus, checkCurrent); } } /** * @param event * @see EventListener#onEvent(Event) */ @SuppressWarnings("unchecked") @Override public void onEvent(Event event) { if (event.getTarget() == listPanel.getListbox()) { Events.sendEvent(this, new Event(ON_TOGGLE_EVENT, this)); } else if (treePanel != null && event.getTarget() == treePanel.getTree()) { Treeitem item = treePanel.getTree().getSelectedItem(); if (item != null && item.getValue() != null) navigateTo((DefaultTreeNode)item.getValue()); } else if (ON_DEFER_SET_SELECTED_NODE.equals(event.getName())) { removeAttribute(ON_DEFER_SET_SELECTED_NODE_ATTR); setSelectedNode(); } else if (WPaymentEditor.ON_SAVE_PAYMENT.equals(event.getName())) { windowPanel.onSavePayment(); } 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) { boolean openEvent = event instanceof OpenEvent; if (openEvent) { OpenEvent oe = (OpenEvent)event; onSouthEvent(oe.isOpen() ? SouthEvent.OPEN : SouthEvent.CLOSE); } else { onSouthEvent(SouthEvent.SLIDE); } } } 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=? AND AD_User_ID=? AND AD_Process_ID IS NULL AND PreferenceFor = 'W'", null); int userId = Env.getAD_User_ID(Env.getCtx()); MPreference preference = query.setOnlyActiveRecords(true) .setApplyAccessFilter(true) .setClient_ID() .setParameters(windowId, adTabId+"|DetailPane.IsOpen", userId) .first(); if (preference == null || preference.getAD_Preference_ID() <= 0) { preference = new MPreference(Env.getCtx(), 0, null); preference.setAD_Window_ID(windowId); preference.setAD_User_ID(userId); preference.setAttribute(adTabId+"|DetailPane.IsOpen"); } preference.setValue(value ? "Y" : "N"); preference.saveEx(); //update current context Env.getCtx().setProperty("P"+windowId+"|"+adTabId+"|DetailPane.IsOpen", value ? "Y" : "N"); } } } /** * set selected tree node for current row (if there's tree) */ public void setSelectedNode() { if (gridTab.getRecord_ID() >= 0 && gridTab.isTreeTab() && treePanel != null) { setSelectedNode(gridTab.getRecord_ID()); } } /** * handle open/close event of south panel (detail pane) * @param event */ private void onSouthEvent(SouthEvent event) { if (event == SouthEvent.OPEN || event == SouthEvent.CLOSE) { boolean open = event == SouthEvent.OPEN ? true : false; Events.echoEvent(ON_SAVE_OPEN_PREFERENCE_EVENT, this, open); if (!open) return; } if (detailPane.getParent() == null) { formContainer.appendSouth(detailPane); } IADTabpanel tabPanel = detailPane.getSelectedADTabpanel(); if (tabPanel != null) { if (!tabPanel.isActivated() || !detailPane.isVisible()) { if (!detailPane.isVisible()) detailPane.setVisible(true); tabPanel.activate(true); } else if (tabPanel.getGridView() != null){ tabPanel.getGridView().invalidateGridView(); } if (!tabPanel.isGridView()) { if (detailPane.getSelectedPanel().isToggleToFormView()) { detailPane.getSelectedPanel().afterToggle(); } else { tabPanel.switchRowPresentation(); } } } } /** * @return true if detail pane is open/visible */ private boolean isOpenDetailPane() { if (isMobile()) return false; 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; } /** * @return height of detail pane from user preference */ private String heigthDetailPane() { String height = null; int windowId = getGridTab().getAD_Window_ID(); int adTabId = getGridTab().getAD_Tab_ID(); if (windowId > 0 && adTabId > 0) { height = Env.getPreference(Env.getCtx(), windowId, adTabId+"|DetailPane.Height", false); } return height; } /** * @return width of tree panel from user preference (or default if no user preference) */ private String widthTreePanel() { String width = null; int windowId = getGridTab().getAD_Window_ID(); int adTabId = getGridTab().getAD_Tab_ID(); if (windowId > 0 && adTabId > 0) width = Env.getPreference(Env.getCtx(), windowId, adTabId+"|TreePanel.Width", false); return Util.isEmpty(width) ? DEFAULT_PANEL_WIDTH : width; } /** * Navigate to a tree node * @param value */ private void navigateTo(DefaultTreeNode value) { MTreeNode treeNode = value.getData(); // We Have a TreeNode int nodeID = treeNode.getNode_ID(); // Search all rows for mode id int size = gridTab.getRowCount(); int row = -1; for (int i = 0; i < size; i++) { if (gridTab.getKeyID(i) == nodeID) { row = i; break; } } if (row == -1) { if (nodeID > 0 && logger.isLoggable(Level.WARNING)) logger.log(Level.WARNING, "Tab does not have ID with Node_ID=" + nodeID); if (gridTab.getCurrentRow() >= 0) { gridTab.setCurrentRow(gridTab.getCurrentRow(), true); } throw new AdempiereException(Msg.getMsg(Env.getCtx(),"RecordIsNotInCurrentSearch")); } windowPanel.onTreeNavigate(gridTab, row); } /** * @param e * @see DataStatusListener#dataStatusChanged(DataStatusEvent) */ @Override public void dataStatusChanged(DataStatusEvent e) { //ignore background event if (Executions.getCurrent() == null || e.isInitEdit()) return; int col = e.getChangedColumn(); if (logger.isLoggable(Level.CONFIG)) logger.config("(" + gridTab + ") Col=" + col + ": " + e.toString()); // Process Callout GridField mField = gridTab.getField(col); if (mField != null && (mField.getCallout().length() > 0 || (Core.findCallout(gridTab.getTableName(), mField.getColumnName())).size()>0 || gridTab.hasDependants(mField.getColumnName()))) { // IDEMPIERE-4106 Refresh the list (lookup) on dependant fields was moved inside processFieldChange->processDependencies String msg = gridTab.processFieldChange(mField); // Dependencies & Callout if (msg.length() > 0) { Dialog.error(windowNo, msg); } } //update UI state if (!uiCreated) createUI(); dynamicDisplay(col); //sync tree if (treePanel != null) { if (getTreeDisplayedOn().equals(MTab.TREEDISPLAYEDON_MasterTab)) treePanel.getParent().setVisible(!isDetailPaneMode()); else if (getTreeDisplayedOn().equals(MTab.TREEDISPLAYEDON_DetailTab)) treePanel.getParent().setVisible(isDetailPaneMode()); if ("Deleted".equalsIgnoreCase(e.getAD_Message())) { if (e.Record_ID != null && e.Record_ID instanceof Integer && ((Integer)e.Record_ID != gridTab.getRecord_ID())) deleteNode((Integer)e.Record_ID); else setSelectedNode(gridTab.getRecord_ID()); } else if (!e.isInserting()) { boolean refresh=true; Treeitem item = treePanel.getTree().getSelectedItem(); SimpleTreeModel model = (SimpleTreeModel)(TreeModel) treePanel.getTree().getModel(); if (item != null && item.getValue() != null) { @SuppressWarnings("unchecked") MTreeNode treeNode = ((DefaultTreeNode) item.getValue()).getData(); if (treeNode.getNode_ID() == gridTab.getRecord_ID()){ setSelectedNode(gridTab.getRecord_ID()); refresh = false; } } // Remove the node if driven by value; will be re-added right after if ("Saved".equals(e.getAD_Message()) && model.find(null, gridTab.getRecord_ID())!=null && isTreeDrivenByValue()) model.removeNode(model.find(null, gridTab.getRecord_ID())); if ("Saved".equals(e.getAD_Message()) && model.find(null, gridTab.getRecord_ID())==null) { addNewNode(); if (isTreeDrivenByValue()) treePanel.prepareForRefresh(); } if ("Saved".equals(e.getAD_Message()) && model.find(null, gridTab.getRecord_ID()) != null && !isTreeDrivenByValue()) { DefaultTreeNode treeNode = model.find(null, gridTab.getRecord_ID()); if (treeNode != null) { // MTreeNode data = (MTreeNode) treeNode.getData(); String label = (isValueDisplayed() ? (gridTab.getValue("Value").toString() + " - ") : "") + gridTab.get_ValueAsString("Name"); if (!data.getName().equals(label)) { data.setName(label); treeNode.setData(data); } } } if (refresh) { int AD_Tree_ID = Env.getContextAsInt (Env.getCtx(), getWindowNo(), "AD_Tree_ID", true); if (AD_Tree_ID != 0) { if (treePanel.initTree(AD_Tree_ID, windowNo)) echoDeferSetSelectedNodeEvent(); else setSelectedNode(gridTab.getRecord_ID()); } else { AD_Tree_ID = MTree.getDefaultAD_Tree_ID (Env.getAD_Client_ID(Env.getCtx()), gridTab.getKeyColumnName()); treePanel.prepareForRefresh(); int linkColId = MTree.get(Env.getCtx(), AD_Tree_ID, null).getParent_Column_ID(); String linkColName = null; int linkID = 0; if (linkColId > 0) { linkColName = MColumn.getColumnName(Env.getCtx(), linkColId); linkID = Env.getContextAsInt(Env.getCtx(), windowNo, linkColName, true); } if (treePanel.initTree(AD_Tree_ID, windowNo, linkColName, linkID)) echoDeferSetSelectedNodeEvent(); else setSelectedNode(gridTab.getRecord_ID()); } } } else if (e.isInserting() && gridTab.getRecord_ID() < 0 && gridTab.getTabLevel() > 0 && gridTab.getParentTab() != null && gridTab.getParentTab().getValue("AD_Tree_ID") != null) { int AD_Tree_ID = Integer.parseInt(gridTab.getParentTab().getValue("AD_Tree_ID").toString()); treePanel.initTree(AD_Tree_ID, windowNo); } } //update list view if (listPanel.isVisible()) { listPanel.updateListIndex(); listPanel.dynamicDisplay(col); if (GridTable.DATA_REFRESH_MESSAGE.equals(e.getAD_Message()) || "Sorted".equals(e.getAD_Message())) { listPanel.invalidateGridView(); } } } /** * delete tree node by recordId * @param recordId */ private void deleteNode(int recordId) { if (recordId <= 0) return; SimpleTreeModel model = (SimpleTreeModel)(TreeModel) treePanel.getTree().getModel(); if (treePanel.getTree().getSelectedItem() != null) { DefaultTreeNode treeNode = treePanel.getTree().getSelectedItem().getValue(); MTreeNode data = (MTreeNode) treeNode.getData(); if (data.getNode_ID() == recordId) { model.removeNode(treeNode); return; } } DefaultTreeNode treeNode = model.find(null, recordId); if (treeNode != null) { model.removeNode(treeNode); } } /** * add new tree node for current row */ private void addNewNode() { if (gridTab.getRecord_ID() > 0) { String name = (String)gridTab.getValue("Name"); String description = (String)gridTab.getValue("Description"); boolean summary = gridTab.getValueAsBoolean("IsSummary"); String imageIndicator = (String)gridTab.getValue("Action"); // Menu - Action // SimpleTreeModel model = (SimpleTreeModel)(TreeModel) treePanel.getTree().getModel(); DefaultTreeNode treeNode = model.getRoot(); MTreeNode root = (MTreeNode) treeNode.getData(); int parentID = root.getNode_ID(); DefaultTreeNode parentNode = null; if (isTreeDrivenByValue()) { String value = gridTab.getValue("Value").toString(); parentID = PO.retrieveIdOfParentValue(value, getTableName(), Env.getAD_Client_ID(Env.getCtx()), null); parentNode = model.find(treeNode, parentID); if (isValueDisplayed()) { name = value + " - " + name; } } MTreeNode node = new MTreeNode (gridTab.getRecord_ID(), 0, name, description, parentID, summary, imageIndicator, false, null); DefaultTreeNode newNode = new DefaultTreeNode(node); if (isTreeDrivenByValue() && parentNode != null) { model.addNode(parentNode, newNode, 0); } else { model.addNode(newNode); } int[] path = model.getPath(newNode); Treeitem ti = treePanel.getTree().renderItemByPath(path); treePanel.getTree().setSelectedItem(ti); } } /** * set selected tree node by recordId * @param recordId */ private void setSelectedNode(int recordId) { if (recordId <= 0) return; //force on init render if (TreeUtils.isOnInitRenderPosted(treePanel.getTree()) || treePanel.getTree().getTreechildren() == null || treePanel.getTree().getTreechildren().getItemCount() == 0) { treePanel.getTree().onInitRender(); } SimpleTreeModel model = (SimpleTreeModel)(TreeModel) treePanel.getTree().getModel(); if (treePanel.getTree().getSelectedItem() != null) { Treeitem treeItem = treePanel.getTree().getSelectedItem(); if (!treeItem.isLoaded()){ return; } DefaultTreeNode treeNode = treeItem.getValue(); MTreeNode data = (MTreeNode) treeNode.getData(); if (data.getNode_ID() == recordId) { int[] path = model.getPath(treeNode); Treeitem ti = treePanel.getTree().renderItemByPath(path); if (ti.getPage() == null) { echoDeferSetSelectedNodeEvent(); } boolean changed = false; if (isValueDisplayed()) { String value = (String) gridTab.getValue("Value"); String name = (String) gridTab.getValue("Name"); String full = value + " - " + name; if (full != null && !full.equals(data.getName())) { data.setName(full); changed = true; } } else if (Env.isBaseLanguage(Env.getCtx(), "AD_Menu")) { String name = (String) gridTab.getValue("Name"); if (name != null && !name.equals(data.getName())) { data.setName(name); changed = true; } } Object summaryobj = gridTab.getValue("IsSummary"); boolean summary = false; if (summaryobj != null) { if (summaryobj instanceof Boolean) { summary = ((Boolean)summaryobj).booleanValue(); } else { summary = "Y".equals(summaryobj.toString()); } } if (summary != data.isSummary()) { data.setSummary(summary); changed = true; } if (changed) { treeNode.setData(data); } return; } } DefaultTreeNode treeNode = model.find(null, recordId); if (treeNode != null) { int[] path = model.getPath(treeNode); Treeitem ti = treePanel.getTree().renderItemByPath(path); treePanel.getTree().selectItem(ti); } else { addNewNode(); } } /** * Toggle between form and grid view */ public void switchRowPresentation() { if (form.isVisible()) { form.setVisible(false); ((HtmlBasedComponent)form.getParent()).setStyle(""); } else { form.setVisible(true); ((HtmlBasedComponent)form.getParent()).setStyle("overflow-y: visible;"); } setGroupTabboxVisibility(); listPanel.setVisible(!form.isVisible()); if (listPanel.isVisible()) { listPanel.refresh(gridTab); listPanel.scrollToCurrentRow(); listPanel.invalidate(); } else { listPanel.deactivate(); } Events.sendEvent(this, new Event(ON_SWITCH_VIEW_EVENT, this)); } static class ZoomListener implements EventListener { private IZoomableEditor zoomableEditor; ZoomListener(IZoomableEditor editor) { zoomableEditor = editor; } public void onEvent(Event event) throws Exception { if (Events.ON_CLICK.equals(event.getName())) { zoomableEditor.actionZoom(); } } } /** * @see IADTabpanel#afterSave(boolean) */ @Override public void afterSave(boolean onSaveEvent) { } @Override public void focus() { if (form.isVisible() && !isMobile()) this.focusToFirstEditor(true); else listPanel.focus(); } /** * * @param columnName */ public void setFocusToField(String columnName) { if (formContainer.isVisible()) { for (WEditor editor : editors) { if (columnName.equals(editor.getColumnName())) { Clients.response(new AuFocus(editor.getComponent())); break; } } } else { listPanel.setFocusToField(columnName); } } /** * @see IADTabpanel#onEnterKey() */ @Override public boolean onEnterKey() { if (listPanel.isVisible()) { return listPanel.onEnterKey(); } return false; } /** * @return true if list/grid view is visible */ @Override public boolean isGridView() { return listPanel.isVisible(); } /** * * @return {@link GridView} */ @Override public GridView getGridView() { return listPanel; } @Override public boolean isActivated() { return activated; } @Override public void setDetailPaneMode(boolean detailPaneMode) { if (this.detailPaneMode != detailPaneMode) { this.detailPaneMode = detailPaneMode; if (detailPaneMode) { detachDetailPane(); } else { attachDetailPane(); } ZKUpdateUtil.setVflex(this, "true"); listPanel.setDetailPaneMode(detailPaneMode, gridTab); } } /** * Show detail pane */ private void attachDetailPane() { if (formContainer.getSouth() != null) { formContainer.getSouth().setVisible(true); if (formContainer.getSouth().isOpen()) { if (detailPane != null) { if (detailPane.getParent() != formContainer.getSouth()) formContainer.appendSouth(detailPane); else detailPane.setVisible(true); } } } } /** * Hide detail pane */ private void detachDetailPane() { if (formContainer.getSouth() != null) { formContainer.getSouth().setVisible(false); if (detailPane != null && detailPane.getParent() != null) { detailPane.setVisible(false); } } } /** * Get all visible toolbar button editors * @return List