/******************************************************************************
* 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