/******************************************************************************
* 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.panel;
import java.awt.event.MouseEvent;
import java.io.Serializable;
import java.math.BigDecimal;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Objects;
import java.util.Set;
import java.util.Vector;
import java.util.logging.Level;
import org.adempiere.exceptions.AdempiereException;
import org.adempiere.model.IInfoColumn;
import org.adempiere.model.MInfoProcess;
import org.adempiere.webui.AdempiereWebUI;
import org.adempiere.webui.ClientInfo;
import org.adempiere.webui.LayoutUtils;
import org.adempiere.webui.apps.AEnv;
import org.adempiere.webui.apps.ProcessModalDialog;
import org.adempiere.webui.apps.WProcessCtl;
import org.adempiere.webui.component.Button;
import org.adempiere.webui.component.Combobox;
import org.adempiere.webui.component.ConfirmPanel;
import org.adempiere.webui.component.ListModelTable;
import org.adempiere.webui.component.Mask;
import org.adempiere.webui.component.ProcessInfoDialog;
import org.adempiere.webui.component.WListItemRenderer;
import org.adempiere.webui.component.WListbox;
import org.adempiere.webui.component.Window;
import org.adempiere.webui.desktop.IDesktop;
import org.adempiere.webui.editor.WEditor;
import org.adempiere.webui.event.DialogEvents;
import org.adempiere.webui.event.ValueChangeEvent;
import org.adempiere.webui.event.ValueChangeListener;
import org.adempiere.webui.event.WTableModelEvent;
import org.adempiere.webui.event.WTableModelListener;
import org.adempiere.webui.factory.InfoManager;
import org.adempiere.webui.info.InfoWindow;
import org.adempiere.webui.part.ITabOnSelectHandler;
import org.adempiere.webui.part.WindowContainer;
import org.adempiere.webui.session.SessionManager;
import org.adempiere.webui.util.ZKUpdateUtil;
import org.adempiere.webui.window.Dialog;
import org.compiere.minigrid.ColumnInfo;
import org.compiere.minigrid.IDColumn;
import org.compiere.minigrid.UUIDColumn;
import org.compiere.model.AccessSqlParser.TableInfo;
import org.compiere.model.GridField;
import org.compiere.model.InfoColumnVO;
import org.compiere.model.InfoRelatedVO;
import org.compiere.model.MInfoColumn;
import org.compiere.model.MInfoWindow;
import org.compiere.model.MPInstance;
import org.compiere.model.MProcess;
import org.compiere.model.MRefTable;
import org.compiere.model.MRole;
import org.compiere.model.MStatusLine;
import org.compiere.model.MSysConfig;
import org.compiere.model.MTable;
import org.compiere.model.X_AD_CtxHelp;
import org.compiere.process.ProcessInfo;
import org.compiere.process.ProcessInfoLog;
import org.compiere.process.ProcessInfoUtil;
import org.compiere.util.CLogger;
import org.compiere.util.DB;
import org.compiere.util.DisplayType;
import org.compiere.util.Env;
import org.compiere.util.KeyNamePair;
import org.compiere.util.Msg;
import org.compiere.util.NamePair;
import org.compiere.util.Trx;
import org.compiere.util.Util;
import org.compiere.util.ValueNamePair;
import org.zkoss.zk.au.out.AuEcho;
import org.zkoss.zk.ui.Component;
import org.zkoss.zk.ui.Executions;
import org.zkoss.zk.ui.HtmlBasedComponent;
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.KeyEvent;
import org.zkoss.zk.ui.event.SelectEvent;
import org.zkoss.zk.ui.util.Clients;
import org.zkoss.zul.Comboitem;
import org.zkoss.zul.Listhead;
import org.zkoss.zul.Listheader;
import org.zkoss.zul.Listitem;
import org.zkoss.zul.Menuitem;
import org.zkoss.zul.Paging;
import org.zkoss.zul.event.PagingEvent;
import org.zkoss.zul.event.ZulEvents;
import org.zkoss.zul.ext.Sortable;
/**
* Search dialog that works in two mode.
* Lookup mode: Search and return selection to lookup field.
* Window mode: Search and view search results. Optional support for execution of process.
*
* @author Sendy Yagambrum
* @author Elaine
* @contributor red1 IDEMPIERE-1711 with final review by HengSin
*/
public abstract class InfoPanel extends Window implements EventListener, WTableModelListener, Sortable, IHelpContext
{
/**
* generated serial id
*/
private static final long serialVersionUID = 8253708190979803268L;
protected static final String ON_USER_QUERY_ATTR = "ON_USER_QUERY";
protected static final String INFO_QUERY_TIME_OUT_ERROR = "InfoQueryTimeOutError";
protected static final String COLUMN_VISIBLE_ORIGINAL = "column.visible.original";
protected static final String ROW_CTX_VARIABLE_PREFIX = "_IWInfo_";
protected static final String ROW_ID_CTX_VARIABLE_NAME = "_IWInfoIDs_Selected";
private final static int DEFAULT_PAGE_SIZE = 100;
private final static int DEFAULT_PAGE_PRELOAD = 4;
protected List btProcessList = new ArrayList();
/** Column:WEditor */
protected Map editorMap = new HashMap();
protected final static String PROCESS_ID_KEY = "processId";
protected final static String ON_RUN_PROCESS = "onRunProcess";
protected final static String ON_SELECT_ALL_RECORDS = "onSelectAllRecords";
// attribute key of info process
protected final static String ATT_INFO_PROCESS_KEY = "INFO_PROCESS";
protected int pageSize;
/** KeyNamePair/ValueNamePair:[Column:Value] */
public LinkedHashMap> m_values = null;
protected InfoRelatedVO[] relatedInfoList;
// for test disable load all record when num of record < 1000
protected boolean isIgnoreCacheAll = true;
// Num of page preload, default is 2 page before current and 2 page after current
protected int numPagePreLoad = MSysConfig.getIntValue(MSysConfig.ZK_INFO_NUM_PAGE_PRELOAD, DEFAULT_PAGE_PRELOAD);
// max end index is integer.max_value - 1, not integer.max_value.
protected int extra_max_row = 1;
/**
* MInfoColumn with isKey = true. In case no column has
* isKey = true, keyColumnOfView will be null and we use {@link #p_keyColumn}
*/
protected MInfoColumn keyColumnOfView = null;
/**
* index of {@link #keyColumnOfView} in data model, set when prepare listbox
*/
protected int indexKeyOfView = -1;
protected boolean isIDColumnKeyOfView = false;
protected boolean hasRightQuickEntry = true;
protected boolean isHasNextPage = false;
/**
* Value of Key Column:Values of Row.
* Store selected rows.
* If there's no key column of view, use value of {@link #p_keyColumn}.
* Zk6.x listview deosn't send event when user click header checkbox to select all rows,
* so we can't manage selectedRecord all the time.
* Each time change page, we will update this list with current selected records of current page
* by calling function {@link #updateListSelected()}.
* When move to zk7, enough to just handle onclick only.
* Because of the issue above, don't use recordSelectedData directly, call
* {@link #getSelectedRowInfo()} instead.
*/
protected Map> recordSelectedData = new HashMap>();
/**
* When re-query but don't want to clear selected record (example after run process),
* set this flag to true to run sync selected record. See also
* {@link #syncSelectedAfterRequery()}
*/
protected boolean isRequeryByRunSuccessProcess = false;
/**
*
* @param WindowNo
* @param tableName
* @param keyColumn
* @param value
* @param multiSelection
* @param whereClause
* @return {@link InfoPanel}
*/
public static InfoPanel create (int WindowNo,
String tableName, String keyColumn, String value,
boolean multiSelection, String whereClause)
{
return InfoManager.create(WindowNo, tableName, keyColumn, value, multiSelection, whereClause, true);
}
/**
* Show panel based on tablename (non modal and not lookup)
* @param tableName
*/
public static void showPanel (String tableName)
{
InfoPanel info = InfoManager.create(0, tableName, tableName + "_ID", "", false, "", false);
info.setAttribute(Window.MODE_KEY, Window.MODE_EMBEDDED);
AEnv.showWindow(info);
info.setFocus(true);
} // showPanel
/** Window Width */
static final int INFO_WIDTH = 800;
/** true for lookup mode */
protected boolean m_lookup;
/** AD_InfoWindow_ID */
protected int m_infoWindowID;
private boolean m_closeAfterExecutionOfProcess = false;
private Button btnSelectAll;
private Button btnDeSelectAll;
/** true if {@link #p_WindowNo} is not from caller/parent window */
private boolean registerWindowNo = false;
/**
* @param WindowNo WindowNo
* @param tableName tableName
* @param keyColumn keyColumn
* @param multipleSelection
* @param whereClause whereClause
*/
protected InfoPanel (int WindowNo,
String tableName, String keyColumn,boolean multipleSelection,
String whereClause)
{
this(WindowNo, tableName, keyColumn, multipleSelection, whereClause, true);
}
/**
*
* @param WindowNo
* @param tableName
* @param keyColumn
* @param multipleSelection
* @param whereClause
* @param lookup
*/
protected InfoPanel (int WindowNo,
String tableName, String keyColumn,boolean multipleSelection,
String whereClause, boolean lookup){
this(WindowNo, tableName, keyColumn, multipleSelection, whereClause,
lookup, 0);
}
/**
* @param WindowNo
* @param tableName
* @param keyColumn
* @param multipleSelection
* @param whereClause
* @param lookup
* @param ADInfoWindowID
*/
protected InfoPanel (int WindowNo,
String tableName, String keyColumn,boolean multipleSelection,
String whereClause, boolean lookup, int ADInfoWindowID)
{
this(WindowNo, tableName, keyColumn, multipleSelection,
whereClause, lookup, ADInfoWindowID, null);
}
/**
* @param WindowNo WindowNo
* @param tableName tableName
* @param keyColumn keyColumn
* @param multipleSelection
* @param whereClause whereClause
* @param lookup
* @param ADInfoWindowID
* @param queryValue
*/
protected InfoPanel (int WindowNo,
String tableName, String keyColumn,boolean multipleSelection,
String whereClause, boolean lookup, int ADInfoWindowID, String queryValue)
{
if (WindowNo <= 0) {
p_WindowNo = SessionManager.getAppDesktop().registerWindow(this);
registerWindowNo = true;
} else {
p_WindowNo = WindowNo;
}
if (log.isLoggable(Level.INFO))
log.info("WinNo=" + WindowNo + " " + whereClause);
p_tableName = tableName;
this.m_infoWindowID = ADInfoWindowID;
p_keyColumn = keyColumn;
this.queryValue = queryValue;
if (queryValue != null && queryValue.trim().length() > 0)
{
parseQueryValue();
}
setMultipleSelection(multipleSelection);
m_lookup = lookup;
loadInfoWindowData();
if (whereClause == null || whereClause.indexOf('@') == -1)
p_whereClause = whereClause == null ? "" : whereClause;
else
{
p_whereClause = Env.parseContext(Env.getCtx(), p_WindowNo, whereClause, false, false);
if (p_whereClause.length() == 0)
log.log(Level.SEVERE, "Cannot parse context= " + whereClause);
}
pageSize = MSysConfig.getIntValue(MSysConfig.ZK_PAGING_SIZE, DEFAULT_PAGE_SIZE, Env.getAD_Client_ID(Env.getCtx()));
if (infoWindow != null && infoWindow.getPagingSize() > 0)
pageSize = infoWindow.getPagingSize();
init();
this.setAttribute(ITabOnSelectHandler.ATTRIBUTE_KEY, new ITabOnSelectHandler() {
public void onSelect() {
scrollToSelectedRow();
}
});
setWidgetAttribute(AdempiereWebUI.WIDGET_INSTANCE_NAME, "infopanel");
addEventListener(WindowContainer.ON_WINDOW_CONTAINER_SELECTION_CHANGED_EVENT, this);
addEventListener(ON_RUN_PROCESS, this);
addEventListener(ON_SELECT_ALL_RECORDS, this);
addEventListener(Events.ON_CLOSE, this);
setAttribute(IDesktop.WINDOWNO_ATTRIBUTE, p_WindowNo); // for closing the window with shortcut
} // InfoPanel
/**
* parse query value from calling input element
*/
protected void parseQueryValue() {
if (Util.isEmpty(queryValue, true))
return;
int start = queryValue.indexOf("?autocomplete={");
if (start > 0 && queryValue.endsWith("}")) {
this.isAutoComplete = true;
this.numPagePreLoad = 1;
String optionInput = queryValue.substring(start+"?autocomplete={".length(), queryValue.length()-1);
queryValue = queryValue.substring(0, start);
String[] options = optionInput.split("[,]");
for(String option : options) {
String[] pair = option.trim().split("[:]");
if (pair.length != 2)
continue;
if (pair[0].equalsIgnoreCase("timeout")) {
try {
int t = Integer.parseInt(pair[1]);
if (t > 0)
setFixedQueryTimeout(t);
} catch (Exception e) {}
} else if (pair[0].equalsIgnoreCase("pagesize")) {
try {
int t = Integer.parseInt(pair[1]);
if (t > 0)
this.pageSize = t;
} catch (Exception e) {}
} else if (pair[0].equalsIgnoreCase("searchcolumn")) {
this.autoCompleteSearchColumn = pair[1];
}
}
}
}
/**
* set fixed query timeout value, overwrite the value from sysconfig
* @param timeout
*/
public void setFixedQueryTimeout(int timeout) {
this.queryTimeout = timeout;
useQueryTimeoutFromSysConfig = false;
}
/**
* Layout window
*/
private void init()
{
if (isLookup())
{
setAttribute(Window.MODE_KEY, Window.MODE_HIGHLIGHTED);
setBorder("normal");
setClosable(true);
int height = ClientInfo.get().desktopHeight;
int width = ClientInfo.get().desktopWidth;
if (width <= ClientInfo.MEDIUM_WIDTH)
{
ZKUpdateUtil.setWidth(this, "100%");
ZKUpdateUtil.setHeight(this, "100%");
}
else
{
height = height * 85 / 100;
width = width * 80 / 100;
ZKUpdateUtil.setWidth(this, width + "px");
ZKUpdateUtil.setHeight(this, height + "px");
}
this.setContentStyle("overflow: auto");
}
else
{
setAttribute(Window.MODE_KEY, Window.MODE_EMBEDDED);
setBorder("none");
ZKUpdateUtil.setWidth(this, "100%");
ZKUpdateUtil.setHeight(this, "100%");
}
confirmPanel = new ConfirmPanel(true, true, true, true, true, true); // Elaine 2008/12/16
confirmPanel.addComponentsLeft(confirmPanel.createButton(ConfirmPanel.A_NEW));
btnSelectAll = confirmPanel.createButton("SelectAll");
confirmPanel.addComponentsLeft(btnSelectAll);
btnSelectAll.setEnabled(false);
btnSelectAll.setVisible(p_multipleSelection);
btnDeSelectAll = confirmPanel.createButton("DeSelectAll");
confirmPanel.addComponentsLeft(btnDeSelectAll);
btnDeSelectAll.setEnabled(false);
btnDeSelectAll.setVisible(p_multipleSelection);
confirmPanel.addActionListener(Events.ON_CLICK, this);
ZKUpdateUtil.setHflex(confirmPanel, "1");
if (ClientInfo.isMobile())
{
if (ClientInfo.maxWidth(ClientInfo.SMALL_WIDTH) || ClientInfo.maxHeight(ClientInfo.SMALL_HEIGHT))
{
confirmPanel.useSmallButtonClassForSmallScreen();
}
}
confirmPanel.getButton(ConfirmPanel.A_CUSTOMIZE).setVisible(hasCustomize());
confirmPanel.getButton(ConfirmPanel.A_HISTORY).setVisible(hasHistory());
confirmPanel.getButton(ConfirmPanel.A_ZOOM).setVisible(hasZoom());
confirmPanel.getButton(ConfirmPanel.A_NEW).setVisible(hasNew());
//
if (!isLookup())
{
confirmPanel.getButton(ConfirmPanel.A_OK).setVisible(false);
}
this.setSizable(true);
this.setMaximizable(true);
this.addEventListener(Events.ON_OK, this);
if (isLookup())
addEventListener(Events.ON_CANCEL, this);
contentPanel.setOddRowSclass(null);
contentPanel.setWidgetAttribute(AdempiereWebUI.WIDGET_INSTANCE_NAME, "infoListbox");
contentPanel.addEventListener("onAfterRender", this);
contentPanel.setSclass("z-word-nowrap");
this.setSclass("info-panel");
} // init
protected ConfirmPanel confirmPanel;
/** Lookup mode: parent window number. Non lookup mode: register desktop tab number for this window */
protected int p_WindowNo;
/** Table Name */
protected String p_tableName;
/** Key Column Name */
protected String p_keyColumn;
/** Enable more than one selection */
protected boolean p_multipleSelection;
/** Initial WHERE Clause */
protected String p_whereClause = "";
protected StatusBarPanel statusBar = new StatusBarPanel();
/** */
private List line;
private boolean m_ok = false;
/** Cancel pressed - need to differentiate between OK - Cancel - Exit */
private boolean m_cancel = false;
/** Result IDs */
private ArrayList m_results = new ArrayList(3);
/** Model of {@link #contentPanel} */
private ListModelTable model;
/** Layout of {@link #contentPanel} */
protected ColumnInfo[] p_layout;
/** Main SQL Statement */
protected String m_sqlMain;
/** Count SQL Statement */
protected String m_sqlCount;
/** Order By Clause */
protected String m_sqlOrder;
private String m_sqlUserOrder;
protected int indexOrderColumn = -1;
/** sql column name of infocolumn (can be alias) */
protected String sqlOrderColumn;
protected Boolean isColumnSortAscending = null;
/**ValueChange listeners */
private ArrayList listeners = new ArrayList();
/** Loading success indicator */
protected boolean p_loadedOK = false;
/** SO Zoom Window */
private int m_SO_Window_ID = -1;
/** PO Zoom Window */
private int m_PO_Window_ID = -1;
protected MInfoWindow infoWindow;
/** Logger */
protected transient CLogger log = CLogger.getCLogger(getClass());
protected WListbox contentPanel = new WListbox();
protected Paging paging;
protected int pageNo;
protected int m_count;
private int cacheStart;
private int cacheEnd;
private boolean m_useDatabasePaging = false;
// in case double click to item. this store clicked item (maybe it's un-select item)
private int m_lastSelectedIndex = -1;
protected GridField m_gridfield;
/**
* If false, use saved where clause.
* IDEMPIERE-1979
*/
protected boolean isQueryByUser = false;
/** true for auto complete call from lookup field */
protected boolean isAutoComplete = false;
protected int queryTimeout = 0;
protected boolean useQueryTimeoutFromSysConfig = true;
/** column name for auto complete call */
protected String autoCompleteSearchColumn = null;
protected String queryValue;
/**
* saved where clause of previous query
*/
protected String prevWhereClause = null;
/**
* saved value of previous query parameters
*/
protected List prevParameterValues = null;
protected List prevQueryOperators = null;
protected List prevRefParmeterEditor = null;
private static final String[] lISTENER_EVENTS = {};
/**
* Info processes of this infoWindow
*/
protected MInfoProcess [] infoProcessList;
/**
* flag detect exists info process
*/
protected boolean haveProcess = false;
/**
* Info process with style = button
*/
protected List infoProcessBtList;
/**
* Info process with style = drop down list
*/
protected List infoProcessDropList;
/**
* Info process with style = menu
*/
protected List infoProcessMenuList;
/**
* saved selected id and viewID
*/
protected Collection m_viewIDMap = new ArrayList ();
/**
* AD_InfoColumn_ID:Index Column Sequence
* Store index of infoColumn that have been added.Index increase from 0.
*/
protected Map columnDataIndex = new HashMap ();
/**
* After loading of first record, set this to false.
* When need to update {@link #columnDataIndex}, set this to true.
*/
protected boolean isMustUpdateColumnIndex = true;
/**
* Number of index column that have been added to {@link #columnDataIndex}.
*/
protected int indexColumnCount = 0;
/**
* Before the start of reading of a row, reset this list.
* After reading of each column, store id of infoColumn to this list to prevent duplicate.
*/
protected List lsReadedColumn = new ArrayList ();
/**
* IDEMPIERE-1334
* button and combobox when layout process button as dropdown list
*/
protected Button btCbbProcess;
protected Combobox cbbProcess;
protected Button btMenuProcess;
/**
* SysConfig USE_ESC_FOR_TAB_CLOSING
*/
private boolean isUseEscForTabClosing = MSysConfig.getBooleanValue(MSysConfig.USE_ESC_FOR_TAB_CLOSING, false, Env.getAD_Client_ID(Env.getCtx()));
/**
* Contains the keys of the selected rows in the order of selection
*/
protected ArrayList m_rowSelectionOrder = new ArrayList();
/**
* Number of selected rows
*/
protected int m_selectedCount = 0;
/**
* Parameter Name:Value
* Values that will be put into the context on re-query
*/
protected HashMap paraCtxValues = new HashMap();
/**
* Loaded correctly
* @return true if loaded OK
*/
public boolean loadedOK()
{
return p_loadedOK;
} // loadedOK
/**
* Set Status Line
* @param text text
* @param error error
*/
public void setStatusLine (String text, boolean error)
{
statusBar.setStatusLine(text, error);
} // setStatusLine
/**
* Set status text for DB
* @param text text
*/
public void setStatusDB (String text)
{
statusBar.setStatusDB(text);
} // setStatusDB
/**
* Set status text for selected rows
*/
public void setStatusSelected ()
{
int selectedCount = p_multipleSelection ? recordSelectedData.size() : 0;
for (int rowIndex = 0; rowIndex < contentPanel.getModel().getRowCount(); rowIndex++){
Object keyCandidate = getColumnValue(rowIndex);
@SuppressWarnings("unchecked")
List candidateRecord = (List)contentPanel.getModel().get(rowIndex);
if (contentPanel.getModel().isSelected(candidateRecord)){
if(!p_multipleSelection) {
selectedCount++;
break;
}
else if (!recordSelectedData.containsKey(keyCandidate)){
selectedCount++;
}
}else if (p_multipleSelection){
if (recordSelectedData.containsKey(keyCandidate)){// unselected record
selectedCount--;
}
}
}
m_selectedCount = selectedCount;
String msg = Msg.getMsg(Env.getCtx(), "IWStatusSelected", new Object [] {String.valueOf(selectedCount)});
statusBar.setSelectedRowNumber(msg);
btnSelectAll.setEnabled(m_count > 0 && selectedCount != m_count);
btnDeSelectAll.setEnabled(selectedCount > 0);
} // setStatusSelected
/**
* set up list box and construct sql clause
* @param layout
* @param from
* @param where
* @param orderBy
*/
protected void prepareTable (ColumnInfo[] layout,
String from,
String where,
String orderBy)
{
String sql =contentPanel.prepareTable(layout, from,
where,p_multipleSelection,
getTableName(),false);
if (infoWindow != null)
contentPanel.setwListBoxName("AD_InfoWindow_UU|"+ infoWindow.getAD_InfoWindow_UU() );
else
contentPanel.setwListBoxName("AD_InfoPanel|"+ from );
p_layout = contentPanel.getLayout();
m_sqlMain = sql;
m_sqlCount = "SELECT COUNT(*) FROM " + from + " WHERE " + where;
//
m_sqlOrder = "";
if (orderBy != null && orderBy.trim().length() > 0)
m_sqlOrder = " ORDER BY " + orderBy;
} // prepareTable
/**
* @return true if number of page will be determined through record count.
*/
protected boolean isLoadPageNumber(){
return infoWindow == null || infoWindow.isLoadPageNum();
}
/**
* Execute Query
*/
protected void executeQuery()
{
saveWlistBoxColumnWidth(this.getFirstChild());
line = new ArrayList();
setCacheStart(-1);
cacheEnd = -1;
if (isLoadPageNumber())
testCount();
else
m_count = Integer.MAX_VALUE;
if (m_count > 0)
{
m_useDatabasePaging = isIgnoreCacheAll || (m_count > 1000);
if (m_useDatabasePaging)
{
return ;
}
else
{
readLine(0, -1);
}
}
}
/**
* Read row from result set
* @param rs
* @throws SQLException
*/
private void readData(ResultSet rs) throws SQLException {
int colOffset = 1; // columns start with 1
List data = new ArrayList();
for (int col = 0; col < p_layout.length; col++)
{
Object value = null;
Class> c = p_layout[col].getColClass();
int colIndex = col + colOffset;
if (c == IDColumn.class)
value = new IDColumn(rs.getInt(colIndex));
else if (c == UUIDColumn.class)
value = new UUIDColumn(rs.getString(colIndex));
else if (c == Boolean.class)
value = Boolean.valueOf("Y".equals(rs.getString(colIndex)));
else if (c == Timestamp.class)
value = rs.getTimestamp(colIndex);
else if (c == BigDecimal.class)
value = rs.getBigDecimal(colIndex);
else if (c == Double.class)
value = Double.valueOf(rs.getDouble(colIndex));
else if (c == Integer.class)
value = Integer.valueOf(rs.getInt(colIndex));
else if (c == KeyNamePair.class)
{
if (p_layout[col].isKeyPairCol())
{
String display = rs.getString(colIndex);
int key = rs.getInt(colIndex+1);
if (! rs.wasNull()) {
value = new KeyNamePair(key, display);
}
colOffset++;
}
else
{
int key = rs.getInt(colIndex);
if (! rs.wasNull()) {
WEditor editor = editorMap.get(p_layout[col].getColSQL());
if (editor != null)
{
editor.setValue(key);
value = new KeyNamePair(key, editor.getDisplayTextForGridView(key));
}
else
{
value = new KeyNamePair(key, Integer.toString(key));
}
}
}
}
else if (c == ValueNamePair.class)
{
String key = rs.getString(colIndex);
WEditor editor = editorMap.get(p_layout[col].getColSQL());
if (editor != null)
{
value = new ValueNamePair(key, editor.getDisplayTextForGridView(key));
}
else
{
value = new ValueNamePair(key, key);
}
}
else
{
value = rs.getString(colIndex);
if (! rs.wasNull()) {
WEditor editor = editorMap.get(p_layout[col].getColSQL());
if (editor != null && editor.getGridField() != null && editor.getGridField().isLookup())
{
editor.setValue(value);
value = editor.getDisplay();
}
}
}
data.add(value);
}
line.add(data);
appendDataForViewID(rs, data, lsReadedColumn);
appendDataForParentLink(rs, data, lsReadedColumn);
appendDataForKeyView (rs, data, lsReadedColumn);
}
/**
* Append viewID column in infoProcessList to data.
* When override {@link #readData(ResultSet)}, should include call to this method.
* IDEMPIERE-1970
* @param rs result set to read data
* @param data data line to append
* @param listReadedColumn list of columns appended
* @throws SQLException
*/
protected void appendDataForViewID(ResultSet rs, List data, List listReadedColumn) throws SQLException {
appendInfoColumnData(rs, data, infoProcessList, listReadedColumn);
}
/**
* Append id column in related info list to data.
* When override {@link #readData(ResultSet)}, should include call to this method.
* IDEMPIERE-2152
* @param rs
* @param data
* @param listReadedColumn
* @throws SQLException
*/
protected void appendDataForParentLink(ResultSet rs, List data, List listReadedColumn) throws SQLException {
appendInfoColumnData(rs, data, relatedInfoList, listReadedColumn);
}
/**
* Append {@link #keyColumnOfView} to data.
* When override {@link #readData(ResultSet)}, should include call to this method.
* IDEMPIERE-1970
* @param rs record set to read data
* @param data data line to append
* @param listReadedColumn list column is appended
* @throws SQLException
*/
protected void appendDataForKeyView(ResultSet rs, List data, List listReadedColumn) throws SQLException {
if (isNeedAppendKeyViewData())
appendInfoColumnData(rs, data, new IInfoColumn [] {keyColumnOfView}, listReadedColumn);
}
/**
* Append value of infoColumn in listModelHaveInfoColumn to data.
* @param rs record set to read data
* @param data data line to append
* @param listModelHasInfoColumn
* @param listReadedColumn list of columns appended
* @throws SQLException
*/
protected void appendInfoColumnData(ResultSet rs, List data, IInfoColumn [] listModelHasInfoColumn, List listReadedColumn) throws SQLException {
if (listModelHasInfoColumn == null || listModelHasInfoColumn.length == 0) {
return;
}
// get InfoColumn from each modelHaveInfoColumn, read it form resultSet by name and append to data line
for (IInfoColumn modelHasInfoColumn : listModelHasInfoColumn){
// have no InfoColumn or this column is readed, read next column
if (modelHasInfoColumn.getInfoColumnID() <= 0 || listReadedColumn.contains(modelHasInfoColumn.getInfoColumnID()))
continue;
MInfoColumn infoColumnApp = (MInfoColumn) modelHasInfoColumn.getAD_InfoColumn();
InfoColumnVO infoColumnAppend = new InfoColumnVO(Env.getCtx(), infoColumnApp);
Object appendData = null;
try {
if (DisplayType.isID(infoColumnAppend.getAD_Reference_ID())) {
appendData = rs.getInt(infoColumnAppend.getColumnName());
} else if (DisplayType.isDate(infoColumnAppend.getAD_Reference_ID())) {
appendData = rs.getTimestamp(infoColumnAppend.getColumnName());
} else if (DisplayType.isNumeric(infoColumnAppend.getAD_Reference_ID())) {
appendData = rs.getBigDecimal(infoColumnAppend.getColumnName());
} else {
appendData = rs.getString(infoColumnAppend.getColumnName());
}
} catch (SQLException e) {
appendData = null;
}
if (rs.wasNull()) {
appendData = null;
}
data.add(appendData);
// when need update append column index, just update it.
if (isMustUpdateColumnIndex && !columnDataIndex.containsKey(modelHasInfoColumn.getInfoColumnID())){
columnDataIndex.put(modelHasInfoColumn.getInfoColumnID(), indexColumnCount);
indexColumnCount++;
}
// mark this column is readed
listReadedColumn.add(modelHasInfoColumn.getInfoColumnID());
}
}
/**
* render list box items
*/
protected void renderItems()
{
if (m_count > 0)
{
if (m_count > pageSize)
{
if (paging == null)
{
paging = new Paging();
paging.setPageSize(pageSize);
paging.setTotalSize(m_count);
paging.setDetailed(true);
paging.addEventListener(ZulEvents.ON_PAGING, this);
}
else
{
paging.setTotalSize(m_count);
paging.setActivePage(0);
paging.setVisible(true);
}
List subList = readLine(0, pageSize);
model = new ListModelTable(subList);
model.setSorter(this);
model.addTableModelListener(this);
model.setMultiple(p_multipleSelection);
contentPanel.setData(model, null);
pageNo = 0;
}
else
{
if (paging != null)
{
paging.setTotalSize(m_count);
paging.setActivePage(0);
pageNo = 0;
}
model = new ListModelTable(readLine(0, -1));
model.setSorter(this);
model.addTableModelListener(this);
model.setMultiple(p_multipleSelection);
contentPanel.setData(model, null);
}
}
else
{
if (paging != null)
{
paging.setTotalSize(m_count);
paging.setActivePage(0);
pageNo = 0;
}
model = new ListModelTable(new ArrayList());
model.setSorter(this);
model.addTableModelListener(this);
model.setMultiple(p_multipleSelection);
contentPanel.setData(model, null);
contentPanel.renderCustomHeaderWidth();
}
autoHideEmptyColumns();
restoreSelectedInPage();
updateStatusBar (m_count);
setStatusSelected ();
setFocusToContentPanel();
addDoubleClickListener();
if (paging != null && paging.getParent() == null)
insertPagingComponent();
Mask mask = getMaskObj();
if (mask == null || mask.getParent() == null)
this.invalidate();
}
/**
* auto hide empty columns
*/
protected void autoHideEmptyColumns() {
String attr = contentPanel.getUuid()+".autoHideEmptyColumns";
if (Executions.getCurrent().getAttribute(attr) != null) {
return;
} else {
Executions.getCurrent().setAttribute(attr, Boolean.TRUE);
}
Listhead columns = contentPanel.getListhead();
List columnList = columns.getChildren();
int rowCount = model.getSize();
for(Listheader column : columnList) {
if (!isAutoHideEmptyColumns()) {
if (!column.isVisible()) {
Object attrValue = column.getAttribute(COLUMN_VISIBLE_ORIGINAL);
if (attrValue != null && attrValue instanceof Boolean) {
Boolean b = (Boolean) attrValue;
if (b.booleanValue())
column.setVisible(true);
}
}
continue;
}
boolean hideColumn = false;
if (rowCount > 0) {
hideColumn = true;
for (int i = 0; i < rowCount; i++) {
Object value = model.getDataAt(i, column.getColumnIndex());
String display = value != null ? value.toString() : "";
if (!Util.isEmpty(display, true)) {
hideColumn = false;
break;
}
}
}
if (hideColumn && column.isVisible()) {
column.setVisible(false);
column.setAttribute(COLUMN_VISIBLE_ORIGINAL, Boolean.TRUE);
} else if (!hideColumn && !column.isVisible()) {
Object attrValue = column.getAttribute(COLUMN_VISIBLE_ORIGINAL);
if (attrValue != null && attrValue instanceof Boolean) {
Boolean b = (Boolean) attrValue;
if (b.booleanValue())
column.setVisible(true);
}
}
}
}
/**
*
* @return true if info window should auto hide empty columns
*/
protected boolean isAutoHideEmptyColumns() {
return MSysConfig.getBooleanValue(MSysConfig.ZK_INFO_AUTO_HIDE_EMPTY_COLUMNS, false, Env.getAD_Client_ID(Env.getCtx()));
}
/**
* update info window status text
* @param no
*/
protected void updateStatusBar (int no){
setStatusLine((no == Integer.MAX_VALUE?"?":Integer.toString(no)) + " " + Msg.getMsg(Env.getCtx(), "SearchRows_EnterQuery"), false);
setStatusDB(no == Integer.MAX_VALUE?"?":Integer.toString(no));
}
/**
* Read rows from start to end
* @param start
* @param end
* @return rows read
*/
private List readLine(int start, int end) {
if (useQueryTimeoutFromSysConfig)
queryTimeout = MSysConfig.getIntValue(MSysConfig.ZK_INFO_QUERY_TIME_OUT, 0, Env.getAD_Client_ID(Env.getCtx()));
//cacheStart & cacheEnd - 1 based index, start & end - 0 based index
if (getCacheStart() >= 1 && cacheEnd > getCacheStart())
{
if (m_useDatabasePaging)
{
if (start+1 >= getCacheStart() && end+1 <= cacheEnd)
{
return end == -1 ? line : getSubList(start-getCacheStart()+1, end-getCacheStart()+1, line);
}
}
else
{
if (end > cacheEnd || end <= 0)
{
end = cacheEnd;
}
return getSubList (start, end, line);
}
}
setCacheStart(getOverIntValue((long)start + 1 - (pageSize * numPagePreLoad)));
if (getCacheStart() <= 0)
setCacheStart(1);
if (end == -1)
{
cacheEnd = m_count;
}
else
{
cacheEnd = getOverIntValue(end + 1 + (pageSize * numPagePreLoad));
if (cacheEnd > m_count)
cacheEnd = m_count;
}
line = new ArrayList();
PreparedStatement m_pstmt = null;
ResultSet m_rs = null;
String dataSql = null;
long startTime = System.currentTimeMillis();
//
dataSql = buildDataSQL(start, end);
isHasNextPage = false;
if (log.isLoggable(Level.FINER))
log.finer(dataSql);
Trx trx = null;
try
{
//https://jdbc.postgresql.org/documentation/head/query.html#query-with-cursor
String trxName = Trx.createTrxName("InfoPanelLoad:");
trx = Trx.get(trxName, true);
trx.setDisplayName(getClass().getName()+"_readLine");
m_pstmt = DB.prepareStatement(dataSql, trxName);
if (queryTimeout > 0)
m_pstmt.setQueryTimeout(queryTimeout);
setParameters (m_pstmt, false); // no count
if (log.isLoggable(Level.FINE))
log.fine("Start query - " + (System.currentTimeMillis()-startTime) + "ms");
m_pstmt.setFetchSize(100);
m_rs = m_pstmt.executeQuery();
if (log.isLoggable(Level.FINE))
log.fine("End query - " + (System.currentTimeMillis()-startTime) + "ms");
//skips the row that we dont need if we can't use native db paging
if (end > start && m_useDatabasePaging && !DB.getDatabase().isPagingSupported())
{
for (int i = 0; i < getCacheStart() - 1; i++)
{
if (!m_rs.next())
break;
}
}
int rowPointer = getCacheStart()-1;
while (m_rs.next())
{
rowPointer++;
// reset list column readed to start new round
lsReadedColumn.clear();
readData(m_rs);
// just set column index only one time.
isMustUpdateColumnIndex = false;
//check now of rows loaded, break if we hit the suppose end
if (m_useDatabasePaging && rowPointer >= cacheEnd)
{
isHasNextPage = true;
break;
}
}
}
catch (SQLException e)
{
if (DB.getDatabase().isQueryTimeout(e))
{
if (log.isLoggable(Level.INFO))
log.log(Level.INFO, dataSql, e);
Dialog.error(p_WindowNo, INFO_QUERY_TIME_OUT_ERROR);
}
else
{
log.log(Level.SEVERE, dataSql, e);
Dialog.error(p_WindowNo, "DBExecuteError", e.getMessage());
}
}
finally
{
DB.close(m_rs, m_pstmt);
trx.close();
}
if (end > cacheEnd || end <= 0)
{
end = cacheEnd;
}
validateEndPage ();
if (end == -1)
{
return line;
}
else
{
int fromIndex = start-getCacheStart()+1;
int toIndex = end-getCacheStart()+1;
return getSubList(fromIndex, toIndex, line);
}
}
/**
* After query from database, validate paging (when paging is dynamic, ie isLoadPageNumber()==false)
* If end page include in cache, calculate total record read.
* If current page is out of cache, process query count to detect end page.
*/
protected void validateEndPage (){
if (paging == null || isLoadPageNumber())
return;
if (!isHasNextPage){
int extraPage = ((line.size() % pageSize > 0)?1:0);
int pageInCache = line.size() / pageSize + extraPage;
if (pageInCache == 0 || pageInCache <= numPagePreLoad){
// selected page is out of cache
testCount();
extraPage = ((m_count % pageSize > 0)?1:0);
pageInCache = m_count / pageSize + extraPage;
// this one will set current page to end page
paging.setTotalSize(m_count);
Event pagingEvent = new PagingEvent("onPaging", paging, paging.getPageCount() - 1);
Events.postEvent(pagingEvent);
}else if (pageInCache > numPagePreLoad){
// current page isn't end page. but page in cache has end page.
int prePage = pageNo - numPagePreLoad;
int readTotalRecord = (prePage > 0?prePage:0) * pageSize + line.size();
paging.setTotalSize(readTotalRecord);
m_count = readTotalRecord;
}
updateStatusBar (m_count);
}
}
/**
* fromIndex and toIndex is calculate base on result from {@link #testCount()}.
* For example after testCount, the calculated number of page is 6 page.
* When user navigate to page 4. something change in database and we just get 5 page with current query parameter.
* So when user navigate to page 6. user will face with index issue (out of index or start index > end index).
* This function include fix for it.
* @param fromIndex
* @param toIndex
* @param line
* @return sub list from line
*/
protected List getSubList (int fromIndex, int toIndex, List line){
if (toIndex > line.size())
toIndex = line.size();
if (fromIndex >= line.size())
fromIndex = line.size();
// case line.size = 0
if (fromIndex < 0)
fromIndex = 0;
return line.subList(fromIndex, toIndex);
}
/**
* When calculating value at bound of integer datatype, sometime value will overflow.
* This function calculate with higher data type to avoid the overflow issue.
* @param value
* @return int
*/
protected int getOverIntValue (long value){
return getOverIntValue (value, 0);
}
/**
* see {@link #getOverIntValue(long)}. when value >= max_value, set it near or equal to max_value.
* @param value
* @param extra extra value to minus from max_value
* @return int
*/
protected int getOverIntValue (long value, int extra){
if (value >= Integer.MAX_VALUE)
value = Integer.MAX_VALUE - extra;
return (int)value;
}
/**
* build sql clause with paging
* @param start
* @param end
* @return sql clause
*/
protected String buildDataSQL(int start, int end) {
String dataSql;
String dynWhere = getSQLWhere(); // includes first AND
StringBuilder sql = new StringBuilder (m_sqlMain);
if (dynWhere.length() > 0) {
if(sql.toString().trim().endsWith("WHERE")) {
dynWhere = dynWhere.replaceFirst("AND", " ");
}
sql.append(dynWhere);
}
if (sql.toString().trim().endsWith("WHERE")) {
int index = sql.lastIndexOf(" WHERE");
sql.delete(index, sql.length());
}
sql.append(getUserOrderClause());
dataSql = Msg.parseTranslation(Env.getCtx(), sql.toString()); // Variables
dataSql = MRole.getDefault().addAccessSQL(dataSql, getTableName(),
MRole.SQL_FULLYQUALIFIED, MRole.SQL_RO);
if (end > start && m_useDatabasePaging && DB.getDatabase().isPagingSupported())
{
dataSql = DB.getDatabase().addPagingSQL(dataSql, getCacheStart(), cacheEnd);
}
return dataSql;
}
/**
* column index of grid isn't fix, it can change by display logic of column each time after loading of data
* {@link InfoWindow#prepareTable(ColumnInfo[], String, String, String)},
* so need to validate it by comparing the sql of current sort column
*/
protected void validateOrderIndex() {
if (indexOrderColumn > 0 && (indexOrderColumn + 1 > p_layout.length || !p_layout[indexOrderColumn].getColSQL().trim().equals(sqlOrderColumn))) {
// try to find out new index of ordered column, in case has other column is hide or display
for (int testIndex = 0; testIndex < p_layout.length; testIndex++) {
if (p_layout[testIndex].getColSQL().trim().equals(sqlOrderColumn) || p_layout[testIndex].getDisplayColumn().equals(sqlOrderColumn)) {
indexOrderColumn = testIndex;
break;
}
}
// index still incorrect and can't find out new index (ordered column become hide column)
if (indexOrderColumn > 0 && (indexOrderColumn + 1 > p_layout.length
|| (!p_layout[indexOrderColumn].getColSQL().trim().equals(sqlOrderColumn) && !p_layout[indexOrderColumn].getDisplayColumn().equals(sqlOrderColumn)))) {
indexOrderColumn = -1;
sqlOrderColumn = null;
m_sqlUserOrder = null;
}
}
}
/**
* build order clause of current sort column, and save it to m_sqlUserOrder
* @return order clause
*/
protected String getUserOrderClause() {
validateOrderIndex();
if (indexOrderColumn < 0) {
return m_sqlOrder;
}
if (m_sqlUserOrder == null) {
m_sqlUserOrder = getUserOrderClause (indexOrderColumn);
}
return m_sqlUserOrder;
}
/**
* Build order clause of given column index.
* If call init list will raise NPE.
* @param col
* @return order clause
*/
protected String getUserOrderClause(int col) {
ColumnInfo orderColumnInfo = p_layout[col];
String displayColumn = orderColumnInfo.getDisplayColumn();
String colsql = !Util.isEmpty(displayColumn) ? displayColumn : p_layout[col].getColSQL().trim();
colsql = getSelectForOrderBy(colsql);
if(!Util.isEmpty(displayColumn) && (DisplayType.isLookup(orderColumnInfo.getAD_Reference_ID()) || DisplayType.isChosenMultipleSelection(orderColumnInfo.getAD_Reference_ID()))) {
String from = getFromForOrderBy(orderColumnInfo, displayColumn);
String where = getWhereForOrderBy(orderColumnInfo);
return String.format(" ORDER BY (SELECT %s FROM %s WHERE %s) %s ", colsql, from, where, isColumnSortAscending? "" : "DESC");
}
else {
return String.format(" ORDER BY %s %s ", colsql, isColumnSortAscending? "" : "DESC");
}
}
/**
* Get SQL column clause for ORDER BY
* @param colsql
* @return sql column clause
*/
private String getSelectForOrderBy(String colsql) {
int lastSpaceIdx = colsql.lastIndexOf(" ");
if (lastSpaceIdx > 0)
{
String tmp = colsql.substring(0, lastSpaceIdx).trim();
char last = tmp.charAt(tmp.length() - 1);
String alias = colsql.substring(lastSpaceIdx).trim();
boolean hasAlias = alias.matches("^[a-zA-Z_][a-zA-Z0-9_]*$"); // valid SQL alias - starts with letter then digits, letters, underscore
if (tmp.toLowerCase().endsWith("as") && hasAlias)
{
colsql = alias;
}
else if (!(last == '*' || last == '-' || last == '+' || last == '/' || last == '>' || last == '<' || last == '='))
{
if (alias.startsWith("\"") && alias.endsWith("\""))
{
colsql = alias;
}
else
{
if (hasAlias)
{
colsql = alias;
}
}
}
}
return colsql;
}
/**
* Get SQL FROM clause for ORDER BY (table and join)
* @param orderColumnInfo
* @param displayColumn
* @return String FROM clause
*/
private String getFromForOrderBy(ColumnInfo orderColumnInfo, String displayColumn) {
String fromClause = "";
MTable table = getTable(orderColumnInfo.getAD_Reference_Value_ID(), orderColumnInfo.getColumnName());
String tableName = table.getTableName();
if(table != null)
fromClause += tableName;
// join translation table
if(displayColumn.contains(table.getTableName()+"_Trl")) {
String tableNameTrl = tableName+"_Trl";
MTable tableTrl = MTable.get(Env.getCtx(), tableNameTrl);
String sqlSelect = orderColumnInfo.getSelectClause();
String[] keyCols = tableTrl.getKeyColumns();
fromClause += " JOIN " + tableNameTrl + " ON (";
for(int i = 0; i < keyCols.length; i++) {
String keyCol = keyCols[i];
if(i > 0)
fromClause += " AND ";
fromClause += tableNameTrl + "." + keyCol + " = ";
if("AD_Language".equalsIgnoreCase(keyCol))
fromClause += " '" + Env.getAD_Language(Env.getCtx()) + "' ";
else
fromClause += sqlSelect;
}
fromClause += ") ";
}
return fromClause;
}
/**
* Get WHERE clause for ORDER BY
* @param orderColumnInfo
* @return String WHERE clause
*/
private String getWhereForOrderBy(ColumnInfo orderColumnInfo) {
MTable table = getTable(orderColumnInfo.getAD_Reference_Value_ID(), orderColumnInfo.getColumnName());
String tableName = table.getTableName();
String keyCol = table.getKeyColumns()[0];
String sqlSelect = orderColumnInfo.getSelectClause();
String whereClause = "";
whereClause += tableName + "." + keyCol + " = " + sqlSelect;
return whereClause;
}
/**
* Add current info panel instance as double click and on select listener of {@link #contentPanel}.
*/
private void addDoubleClickListener() {
Iterator> i = contentPanel.getEventListeners(Events.ON_DOUBLE_CLICK).iterator();
while (i.hasNext()) {
if (i.next() == this)
return;
}
contentPanel.addEventListener(Events.ON_DOUBLE_CLICK, this);
contentPanel.addEventListener(Events.ON_SELECT, this);
}
/**
* Get alias of the table, or the table name
* @return String alias
*/
public String getAlias(String tableName) {
if(Util.isEmpty(tableName))
return "";
String alias = tableName;
for(TableInfo tableInfo : infoWindow.getTableInfos()) {
if(tableName.equalsIgnoreCase(tableInfo.getTableName()))
alias = !Util.isEmpty(tableInfo.getSynonym()) ? tableInfo.getSynonym() : tableName;
}
return alias;
}
/**
* add paging component for list box
*/
protected void insertPagingComponent() {
contentPanel.getParent().insertBefore(paging, contentPanel.getNextSibling());
}
/**
*
* @param p_layout
* @return column headers
*/
public Vector getColumnHeader(ColumnInfo[] p_layout)
{
Vector columnHeader = new Vector();
for (ColumnInfo info: p_layout)
{
columnHeader.add(info.getColHeader());
}
return columnHeader;
}
/**
* Test Row Count
* @return true if display
*/
protected boolean testCount()
{
if (useQueryTimeoutFromSysConfig)
queryTimeout = MSysConfig.getIntValue(MSysConfig.ZK_INFO_QUERY_TIME_OUT, 0, Env.getAD_Client_ID(Env.getCtx()));
long start = System.currentTimeMillis();
String dynWhere = getSQLWhere(); // includes first AND
StringBuilder sql = new StringBuilder (m_sqlCount);
if (dynWhere.length() > 0) {
if(sql.toString().trim().endsWith("WHERE")) {
dynWhere = dynWhere.replaceFirst("AND", " ");
}
sql.append(dynWhere);
}
String countSql = Msg.parseTranslation(Env.getCtx(), sql.toString()); // Variables
if (countSql.trim().endsWith("WHERE")) {
countSql = countSql.trim();
countSql = countSql.substring(0, countSql.length() - 5);
}
countSql = MRole.getDefault().addAccessSQL (countSql, getTableName(),
MRole.SQL_FULLYQUALIFIED, MRole.SQL_RO);
if (log.isLoggable(Level.FINER))
log.finer(countSql);
m_count = -1;
PreparedStatement pstmt = null;
ResultSet rs = null;
try
{
pstmt = DB.prepareStatement(countSql, null);
if (queryTimeout > 0)
pstmt.setQueryTimeout(queryTimeout);
setParameters (pstmt, true);
rs = pstmt.executeQuery();
if (rs.next())
m_count = rs.getInt(1);
}
catch (Exception e)
{
if (e instanceof SQLException && DB.getDatabase().isQueryTimeout((SQLException) e))
{
if (log.isLoggable(Level.INFO))
log.log(Level.INFO, countSql, e);
Dialog.error(p_WindowNo, INFO_QUERY_TIME_OUT_ERROR);
}
else
{
log.log(Level.SEVERE, countSql, e);
Dialog.error(p_WindowNo, "DBExecuteError", e.getMessage());
}
m_count = -2;
}
finally
{
DB.close(rs, pstmt);
rs = null;
pstmt = null;
}
if (log.isLoggable(Level.FINE))
log.fine("#" + m_count + " - " + (System.currentTimeMillis()-start) + "ms");
return true;
} // testCount
/**
* Save Selection - Called by dispose
*/
protected void saveSelection ()
{
// Already disposed
if (contentPanel == null)
return;
if (log.isLoggable(Level.CONFIG)) log.config( "OK=" + m_ok);
// clear prev selected result
m_results.clear();
if (!m_ok) // did not press OK
{
contentPanel = null;
return;
}
// Multi Selection
if (p_multipleSelection)
{
m_results.addAll(getSelectedRowKeys());
}
else // singleSelection
{
Serializable data = getSelectedRowKey();
if (data != null)
m_results.add(data);
}
if (log.isLoggable(Level.CONFIG)) log.config(getSelectedSQL());
// Save Settings of detail info screens
saveSelectionDetail();
} // saveSelection
/**
* Get the key of currently selected row
* @return selected key
*/
protected T getSelectedRowKey()
{
T key = contentPanel.getSelectedRowKey();
return key;
} // getSelectedRowKey
/**
* Get the integer key of currently selected row
* @param tableId
* @return selected key
*/
protected Integer getIntSelectedRowKey(int tableId)
{
Object key = getSelectedRowKey();
if (key == null)
return Integer.valueOf(-1);
if (key instanceof Integer)
return (Integer) key;
MTable table = MTable.get(tableId);
table.getPOByUU((String) key, null);
return Integer.valueOf(table.get_ID());
} // getIntSelectedRowKey
/**
* Get the keys of selected rows
* @return selected IDs or UUIDs
*/
@SuppressWarnings("unchecked")
protected List getSelectedRowKeys()
{
List selectedDataList = new ArrayList<>();
for (Map.Entry> rowInfo : getSelectedRowInfo().entrySet()) {
if(rowInfo.getValue().get(0) instanceof IDColumn idColumn)
selectedDataList.add((T)idColumn.getRecord_ID());
else if(rowInfo.getValue().get(0) instanceof UUIDColumn uuidColumn)
selectedDataList.add((T)uuidColumn.getRecord_UU());
}
return selectedDataList;
} // getSelectedRowKeys
/**
* Get selected Keys as Collection
* @deprecated use getSaveKeys
* @return selected keys (Integers)
*/
public Collection getSelectedKeysCollection()
{
m_ok = true;
saveSelection();
if (!m_ok || m_results.size() == 0)
return null;
return m_results;
}
/**
* Add view id column (infoColumnId) to {@link #m_viewIDMap}.
* @param infoColumnId view id column
*/
public Collection getSaveKeys (int infoColumnId){
// clear result from previous call
m_viewIDMap.clear();
if (p_multipleSelection)
{
Map > selectedRow = getSelectedRowInfo();
for (Entry> selectedInfo : selectedRow.entrySet())
{
// get key data column
Object keyData = selectedInfo.getKey();
if (infoColumnId > 0){
// has viewID, get it
int dataIndex = columnDataIndex.get(infoColumnId) + p_layout.length;
// get row data from model
Object viewIDValue = selectedInfo.getValue().get(dataIndex);
if (keyData instanceof String)
m_viewIDMap.add (new ValueNamePair((String) keyData, viewIDValue == null?null:viewIDValue.toString()));
else
m_viewIDMap.add (new KeyNamePair((Integer) keyData, viewIDValue == null?null:viewIDValue.toString()));
}else{
// no viewID, set viewID value to null
if (keyData instanceof String)
m_viewIDMap.add (new ValueNamePair((String) keyData, null));
else
m_viewIDMap.add (new KeyNamePair((Integer) keyData, null));
}
}
return m_viewIDMap;
}else{
// should never reach here, when have process, p_multipleSelection is always true
return null;
}
}
/**
* need override at infoWindow to check isDisplay
* @return true if need to append {@link #keyColumnOfView} to {@link #columnDataIndex}
*/
protected boolean isNeedAppendKeyViewData (){
return false;
}
/**
* Check type of object is IDColumn
* @param keyData
* @param isCheckNull when true, raise exception when data is null
* @return true if keyData is instanceof IDColumn
*/
protected boolean isIDColumn(Object keyData, boolean isCheckNull){
if (isCheckNull && keyData == null){
AdempiereException ex = getKeyNullException();
log.severe(ex.getMessage());
throw ex;
}
if (keyData != null && keyData instanceof IDColumn){
return true;
}
return false;
}
/**
* call {@link #isIDColumn(Object, boolean)} without null check
* @param keyData
* @return true if keyData is instanceof IDColumn
*/
protected boolean isIDColumn(Object keyData){
return isIDColumn(keyData, false);
}
/**
* Get all selected record of current page and update to {@link #recordSelectedData}.
* Remove unselected record and add new selected record.
* We maintain value of key, and extra value append by {@link #appendInfoColumnData(ResultSet, List, IInfoColumn[], List)}
*/
protected void updateListSelected (){
for (int rowIndex = 0; rowIndex < contentPanel.getModel().getRowCount(); rowIndex++){
Object keyCandidate = getColumnValue(rowIndex);
@SuppressWarnings("unchecked")
List candidateRecord = (List)contentPanel.getModel().get(rowIndex);
int ri = rowIndex;
if (contentPanel.getModel().isSelected(candidateRecord) || Arrays.stream(contentPanel.getSelectedIndices()).anyMatch(si -> si==ri)){
recordSelectedData.put(keyCandidate, candidateRecord);// add or update selected record info
}else{
if (recordSelectedData.containsKey(keyCandidate)){// unselected record
List recordSelected = recordSelectedData.get(keyCandidate);
IDColumn idcSel = null;
UUIDColumn uucSel = null;
if (recordSelected.get(0) instanceof IDColumn) {
idcSel = (IDColumn) recordSelected.get(0);
} else if (recordSelected.get(0) instanceof UUIDColumn) {
uucSel = (UUIDColumn) recordSelected.get(0);
}
IDColumn idcCan = null;
UUIDColumn uucCan = null;
if (candidateRecord.get(0) instanceof IDColumn) {
idcCan = (IDColumn) candidateRecord.get(0);
} else if (candidateRecord.get(0) instanceof UUIDColumn) {
uucCan = (UUIDColumn) candidateRecord.get(0);
}
if ( (idcSel != null && idcCan != null && idcSel.getRecord_ID().equals(idcCan.getRecord_ID()))
|| (uucSel != null && uucCan != null && uucSel.getRecord_UU().equals(uucCan.getRecord_UU())) ) {
recordSelected.set(0, candidateRecord.get(0)); // set same ID/UUID Column for comparison
}
if (recordSelected.equals(candidateRecord)) {
recordSelectedData.remove(keyCandidate);
}
}
}
}
}
/**
* get column index of keyView
* @return index of key column
*/
protected int getIndexKeyColumnOfView (){
if (keyColumnOfView == null){
return contentPanel.getKeyColumnIndex();
}else if (isNeedAppendKeyViewData()){
return columnDataIndex.get(keyColumnOfView.getInfoColumnID()) + p_layout.length;
}else{
return indexKeyOfView;
}
}
/**
* go through all data record, in case key value is in {@link #recordSelectedData}, mark it as selected record
*/
protected void restoreSelectedInPage (){
if (!p_multipleSelection)
return;
Collection lsSelectionRecord = new ArrayList();
for (int rowIndex = 0; rowIndex < contentPanel.getModel().getRowCount(); rowIndex++){
Object keyViewValue = getColumnValue(rowIndex);
if (recordSelectedData.containsKey(keyViewValue)){
// TODO: maybe add logic to check value of current record (focus only to viewKeys value) is same as value save in lsSelectedKeyValue
// because record can change by other user
Object row = contentPanel.getModel().get(rowIndex);
if(onRestoreSelectedItemIndexInPage(keyViewValue, rowIndex, row)) // F3P: provide an hook for operations on restored index
lsSelectionRecord.add(row);
}
}
contentPanel.getModel().setSelection(lsSelectionRecord);
}
/**
* Hook to intercept 'restore selection' actions
*
* @param keyViewValue row view key
* @param rowIndex row index
* @param row row
* @return false to skip restore selection
*/
public boolean onRestoreSelectedItemIndexInPage(Object keyViewValue, int rowIndex, Object row)
{
return true;
}
protected AdempiereException getKeyNullException (){
String errorMessage = String.format("has null value at column %1$s use as key of view in info window %2$s",
keyColumnOfView == null ? p_keyColumn : keyColumnOfView, infoWindow.getName());
return new AdempiereException(errorMessage);
}
/**
* Get keyView value at rowIndex.
* Exception is raise if value is null.
* @param rowIndex
* @return value of key column
*/
protected Object getColumnValue (int rowIndex){
int keyIndex = getIndexKeyColumnOfView();
Object keyValue = null;
// get row data from model
Object keyColumValue = contentPanel.getModel().getDataAt(rowIndex, keyIndex);
// throw exception when value is null
if (keyColumValue == null){
AdempiereException ex = getKeyNullException();
log.severe(ex.getMessage());
throw ex;
}
// IDColumn is recreate after change page, because use value of IDColumn
if (keyColumValue != null) {
if (keyColumValue instanceof IDColumn) {
keyColumValue = ((IDColumn)keyColumValue).getRecord_ID();
} else if (keyColumValue instanceof UUIDColumn) {
keyColumValue = ((UUIDColumn)keyColumValue).getRecord_UU();
}
}
if (keyColumValue instanceof Integer) {
keyValue = (Integer)keyColumValue;
} else if (keyColumValue instanceof String) {
keyValue = (String)keyColumValue;
} else {
String msg = "keyView column must be integer or string";
AdempiereException ex = new AdempiereException (msg);
log.severe(msg);
throw ex;
}
return keyValue;
}
/**
* Maintain selected record after re-query (for example after success run of a process).
* We must sync selected row, because some selected row maybe missing after re-query (change by process and no longer match with current query).
* TODO:rewrite, current code just reset isRequeryByRunSuccessProcess flag and didn't do anything else.
*/
protected void syncSelectedAfterRequery (){
if (isRequeryByRunSuccessProcess){
isRequeryByRunSuccessProcess = false;
//TODO:it's hard to ensure in case use keyViewId we can re-sync. some issue:
// + after RunSuccessProcess maybe key of record is change.
// + after RunSuccessProcess maybe value of viewID change.
// + after RunSuccessProcess maybe some record is out of query result
// + when load many page, sync at one time effect to performance
// maybe make two list, just sync for first page, old list use for reference,
// when user change page will use it for restore selected record, synced record will copy to new list
}
}
/**
* Update {@link #recordSelectedData}
* @return {@link #recordSelectedData} after update
*/
public Map> getSelectedRowInfo (){
updateListSelected();
return recordSelectedData;
}
/**
* Get selected Keys
* @return selected keys (Integers)
*/
public Object[] getSelectedKeys()
{
if (!m_ok || m_results.size() == 0)
return null;
return m_results.toArray(new Object[0]);
} // getSelectedKeys;
/**
* Get (first) selected Key
* @return selected key
*/
public Object getSelectedKey()
{
if (!m_ok || m_results.size() == 0)
return null;
return m_results.get(0);
} // getSelectedKey
/**
* Is cancelled?
* - if pressed Cancel = true
* - if pressed OK or window closed = false
* @return true if cancelled
*/
public boolean isCancelled()
{
return m_cancel;
} // isCancelled
/**
* Get where clause for (first) selected key
* @return WHERE Clause
*/
public String getSelectedSQL()
{
// No results
Object[] keys = getSelectedKeys();
if (keys == null || keys.length == 0)
{
if (log.isLoggable(Level.CONFIG)) log.config("No Results - OK="
+ m_ok + ", Cancel=" + m_cancel);
return "";
}
//
StringBuilder sb = new StringBuilder(getKeyColumn());
if (keys.length > 1)
sb.append(" IN (");
else
sb.append("=");
// Add elements
for (int i = 0; i < keys.length; i++)
{
if (getKeyColumn().endsWith("_ID"))
sb.append(keys[i].toString()).append(",");
else
sb.append("'").append(keys[i].toString()).append("',");
}
sb.replace(sb.length()-1, sb.length(), "");
if (keys.length > 1)
sb.append(")");
return sb.toString();
} // getSelectedSQL;
/**
* query ADInfoWindow from ADInfoWindowID
*/
protected void loadInfoWindowData (){}
/**
* Get Table name Synonym
* @return table name
*/
protected String getTableName()
{
return p_tableName;
} // getTableName
/**
* Get Key Column Name
* @return column name
*/
protected String getKeyColumn()
{
return p_keyColumn;
} // getKeyColumn
/**
* @return list of events
*/
public String[] getEvents()
{
return InfoPanel.lISTENER_EVENTS;
}
/**
* enable all control button or disable all depends on whether there are selected records.
*/
protected void enableButtons (){
boolean enable = (contentPanel.getSelectedCount() > 0 || getSelectedRowInfo().size() > 0);
enableButtons(enable);
}
// Elaine 2008/11/28
/**
* enable or disable all control button
* Enable OK, History, Zoom if row/s selected
* ---
* Changes: Changed the logic for accommodating multiple selection
* author ashley
*/
protected void enableButtons (boolean enable)
{
confirmPanel.getOKButton().setEnabled(enable); //red1 allow Process for 1 or more records
if (hasHistory())
confirmPanel.getButton(ConfirmPanel.A_HISTORY).setEnabled(enable);
if (hasZoom())
confirmPanel.getButton(ConfirmPanel.A_ZOOM).setEnabled(!enable?enable : (contentPanel.getSelectedCount() == 1) ); //red1 only zoom for single record
if (hasProcess())
confirmPanel.getButton(ConfirmPanel.A_PROCESS).setEnabled(enable);
// IDEMPIERE-1334 start
for (Button btProcess : btProcessList){
btProcess.setEnabled(enable);
}
if (btCbbProcess != null){
btCbbProcess.setEnabled(enable);
}
if (btMenuProcess != null){
btMenuProcess.setEnabled(enable);
}
if (cbbProcess != null){
cbbProcess.setEnabled(enable);
}
// IDEMPIERE-1334 end
} // enableButtons
//
/**
* Get dynamic WHERE part of SQL.
* When override this method, please take isQueryByUser and prevWhereClause into consideration.
* @return WHERE clause
*/
protected abstract String getSQLWhere();
/**
* Set Parameters for Query.
* When override this method, please take isQueryByUser and prevWhereClause into consideration.
* @param pstmt statement
* @param forCount for counting records
* @throws SQLException
*/
protected abstract void setParameters (PreparedStatement pstmt, boolean forCount)
throws SQLException;
/**
* Show history dialog
*/
protected void showHistory() {}
/**
* Has History (default false).
* Override this and {@link #showHistory()} method to add history dialog support.
* @return true if it has history (default false)
*/
protected boolean hasHistory() {return false;}
/**
* @return true if support running of process.
*/
protected boolean hasProcess() {return false;}
/**
* Show Customize dialog
*/
protected void customize() {}
/**
* Has Customize dialog (default false).
* Override this and {@link #customize()} method to add customize dialog support.
* @return true if it has customize (default false)
*/
protected boolean hasCustomize() {return false;}
/**
* Has Zoom support (default false)
* @return true if it has zoom support (default false)
*/
protected boolean hasZoom() {return false;}
/**
* Support create new record (default false)
* @return true if support create new record
*/
protected boolean hasNew() {return false;}
/**
* Save Selection Details.
* This method is call when user close an info window.
* By default, infoWindow will set value of all column of current selected record to environment context with {@link Env#TAB_INFO}.
* Sub class can override this method to do more.
*/
protected void saveSelectionDetail() {}
/**
* Get Zoom Window
* @param tableName table name
* @param isSOTrx sales trx
* @return AD_Window_ID
*/
protected int getAD_Window_ID (String tableName, boolean isSOTrx)
{
if (!isSOTrx && m_PO_Window_ID > 0)
return m_PO_Window_ID;
if (m_SO_Window_ID > 0)
return m_SO_Window_ID;
//
MTable table = MTable.get(Env.getCtx(), tableName);
if (table != null)
{
m_SO_Window_ID = table.getAD_Window_ID();
m_PO_Window_ID = table.getPO_Window_ID();
}
//
if (!isSOTrx && m_PO_Window_ID > 0)
return m_PO_Window_ID;
return m_SO_Window_ID;
} // getAD_Window_ID
@Override
public void onEvent(Event event)
{
if (event == null){
return;
}
if (event.getTarget().equals(confirmPanel.getButton(ConfirmPanel.A_OK)))
{
onOk();
}
else if (event.getTarget() == contentPanel && event.getName().equals(Events.ON_SELECT))
{
setStatusSelected ();
SelectEvent, ?> selectEvent = (SelectEvent, ?>) event;
if (selectEvent.getReference() != null && selectEvent.getReference() instanceof Listitem)
{
Listitem m_lastOnSelectItem = (Listitem) selectEvent.getReference();
m_lastSelectedIndex = m_lastOnSelectItem.getIndex();
}
enableButtons();
if(!isLookup()) {
updateRowSelectionOrder();
updateContext(false);
}
}else if (event.getTarget() == contentPanel && event.getName().equals("onAfterRender")){
//IDEMPIERE-1334 at this event selected item from listBox and model is sync
enableButtons();
setFocusToContentPanel();
}
else if (event.getTarget() == contentPanel && event.getName().equals(Events.ON_DOUBLE_CLICK))
{
if (event.getClass().equals(MouseEvent.class)){
return;
}
if (contentPanel.isMultiple() && m_lastSelectedIndex >= 0 && isLookup()) {
contentPanel.setSelectedIndex(m_lastSelectedIndex);
model.clearSelection();
List lsSelectedItem = new ArrayList();
lsSelectedItem.add(model.getElementAt(m_lastSelectedIndex));
model.setSelection(lsSelectedItem);
int m_keyColumnIndex = contentPanel.getKeyColumnIndex();
for (int i = 0; i < contentPanel.getRowCount(); i++) {
// Find the IDColumn Key
Object data = contentPanel.getModel().getValueAt(i, m_keyColumnIndex);
if (data instanceof IDColumn) {
IDColumn dataColumn = (IDColumn) data;
if (i == m_lastSelectedIndex) {
dataColumn.setSelected(true);
}
else {
dataColumn.setSelected(false);
}
}
}
}
if (isLookup() || hasZoom())
{
onDoubleClick();
contentPanel.repaint();
m_lastSelectedIndex = -1;
}
}
else if (event.getTarget().equals(confirmPanel.getButton(ConfirmPanel.A_REFRESH)))
{
recordSelectedData.clear();
setStatusSelected();
onUserQuery();
}
else if (event.getTarget().equals(confirmPanel.getButton(ConfirmPanel.A_CANCEL)))
{
onCancel();
}
else if (event.getTarget().equals(confirmPanel.getButton(ConfirmPanel.A_RESET))) {
recordSelectedData.clear();
resetParameters ();
}
else if (event.getTarget().equals(confirmPanel.getButton(ConfirmPanel.A_HISTORY)))
{
if (!contentPanel.getChildren().isEmpty() && contentPanel.getSelectedRowKey()!=null)
{
showHistory();
}
}
else if (event.getTarget().equals(confirmPanel.getButton(ConfirmPanel.A_CUSTOMIZE)))
{
if (!contentPanel.getChildren().isEmpty() && contentPanel.getSelectedRowKey()!=null)
{
customize();
}
}
//
else if (event.getTarget().equals(confirmPanel.getButton(ConfirmPanel.A_ZOOM)))
{
if (!contentPanel.getChildren().isEmpty() && contentPanel.getSelectedRowKey()!=null)
{
zoom();
if (isLookup())
this.detach();
}
}
else if (event.getTarget().equals(confirmPanel.getButton(ConfirmPanel.A_NEW)))
{
newRecordAction ();
}
else if (event.getTarget().equals(btnSelectAll))
{
Clients.showBusy(Msg.getMsg(Env.getCtx(), "Processing"));
Events.echoEvent(ON_SELECT_ALL_RECORDS, this, null);
}
else if (ON_SELECT_ALL_RECORDS.equals(event.getName()))
{
selectAllRecords();
if(!isLookup()) {
updateRowSelectionOrder();
updateContext(false);
}
}
else if (event.getTarget().equals(btnDeSelectAll))
{
deSelectAllRecords();
if(!isLookup()) {
updateRowSelectionOrder();
updateContext(false);
}
}
// IDEMPIERE-1334 handle event click into process button start
else if (ON_RUN_PROCESS.equals(event.getName())){
// hand echo event after click button process
runProcess((Integer)event.getData());
}else if (Events.ON_CLICK.equals(event.getName()) &&
event.getTarget() != null && event.getTarget() instanceof Menuitem &&
event.getTarget().getAttribute(PROCESS_ID_KEY) != null){
// handle event when click to menu item of info process
preRunProcess((Integer)event.getTarget().getAttribute(PROCESS_ID_KEY));
}
else if (event.getTarget().equals(btCbbProcess)){
// click bt process in case display drop down list
Comboitem cbbSelectedItem = cbbProcess.getSelectedItem();
if (cbbSelectedItem == null || cbbSelectedItem.getValue() == null){
// do nothing when no process is selected
return;
}
MProcess selectedProcess = (MProcess)cbbSelectedItem.getValue();
preRunProcess(selectedProcess.getAD_Process_ID());
}
else if (btProcessList.contains(event.getTarget())){
// click bt process in case display button list
Button btProcess = (Button)event.getTarget();
Integer processId = (Integer)btProcess.getAttribute(PROCESS_ID_KEY);
preRunProcess (processId);
}
// IDEMPIERE-1334 handle event click into process button end
else if (event.getTarget() == paging)
{
updateListSelected();
int pgNo = paging.getActivePage();
if (pgNo == paging.getPageCount()-1 && !isLoadPageNumber()) {
testCount();
paging.setTotalSize(m_count);
pgNo = paging.getActivePage();
}
if (pageNo != pgNo)
{
contentPanel.clearSelection();
pageNo = pgNo;
int start = pageNo * pageSize;
int end = getOverIntValue ((long)start + pageSize, extra_max_row);
if (end >= m_count)
end = m_count;
List subList = readLine(start, end);
model = new ListModelTable(subList);
model.setSorter(this);
model.addTableModelListener(this);
model.setMultiple(p_multipleSelection);
contentPanel.setData(model, null);
restoreSelectedInPage();
//contentPanel.setSelectedIndex(0);
}
autoHideEmptyColumns();
}
else if (event.getName().equals(Events.ON_CHANGE))
{
}
else if (event.getName().equals(WindowContainer.ON_WINDOW_CONTAINER_SELECTION_CHANGED_EVENT))
{
if (infoWindow != null)
SessionManager.getAppDesktop().updateHelpContext(X_AD_CtxHelp.CTXTYPE_Info, infoWindow.getAD_InfoWindow_ID(), this);
else
SessionManager.getAppDesktop().updateHelpContext(X_AD_CtxHelp.CTXTYPE_Home, 0);
}
else if (event.getName().equals(Events.ON_CTRL_KEY))
{
KeyEvent keyEvent = (KeyEvent) event;
if (LayoutUtils.isReallyVisible(this))
this.onCtrlKeyEvent(keyEvent);
}else if (event.getName().equals(Events.ON_OK)){// on ok when focus at non parameter component. example grid result
if (m_lookup && contentPanel.getSelectedIndex() >= 0){
// do nothing when parameter not change and at window mode, or at dialog mode but select no record
onOk();
}
else if (m_infoWindowID == 0 && event.getTarget() instanceof InfoGeneralPanel) {
onUserQuery();
}
}else if (event.getName().equals(Events.ON_CANCEL) || (event.getTarget().equals(this) && event.getName().equals(Events.ON_CLOSE))){
onCancel();
}
else if(IDesktop.ON_CLOSE_WINDOW_SHORTCUT_EVENT.equals(event.getName())) {
IDesktop desktop = SessionManager.getAppDesktop();
if (p_WindowNo > 0 && desktop.isCloseTabWithShortcut())
desktop.closeWindow(p_WindowNo);
else
desktop.setCloseTabWithShortcut(true);
}
//when user push enter keyboard at input parameter field
else
{
// onUserQuery(); // captured now on control key
}
} // onEvent
/**
* handle cancel event
*/
protected void onCancel() {
// do not allow to close tab for Events.ON_CTRL_KEY event
if(isUseEscForTabClosing)
SessionManager.getAppDesktop().setCloseTabWithShortcut(false);
m_cancel = true;
dispose(false);
}
public static final int VK_ENTER = '\r';
public static final int VK_ESCAPE = 0x1B;
/**
* Handle ON_CTRL_KEY event
* @param keyEvent
*/
private void onCtrlKeyEvent(KeyEvent keyEvent) {
if (keyEvent.isAltKey() && !keyEvent.isCtrlKey() && keyEvent.isShiftKey()) { // Shift+Alt
if (keyEvent.getKeyCode() == KeyEvent.DOWN) { // Shift+Alt+Down
// navigate to results
if (contentPanel.getRowCount() > 0) {
contentPanel.setFocus(true);
contentPanel.setSelectedIndex(0);
}
}
} else if (keyEvent.getKeyCode() == VK_ENTER) { // Enter
// do nothing, let on_ok at infoWindo do, at this is too soon to get value from control, it's not bind
} else if ((keyEvent.isAltKey() && keyEvent.getKeyCode() == 0x58) // Alt-X
|| (keyEvent.getKeyCode() == 0x1B && isUseEscForTabClosing)) { // ESC
keyEvent.stopPropagation();
Events.echoEvent(new Event(IDesktop.ON_CLOSE_WINDOW_SHORTCUT_EVENT, this));
}
}
/**
* Call query when user click to query button enter in parameter field
*/
public void onUserQuery (){
recordSelectedData.clear();
if (Executions.getCurrent().getAttribute(ON_USER_QUERY_ATTR) != null)
return;
Executions.getCurrent().setAttribute(ON_USER_QUERY_ATTR, Boolean.TRUE);
if (validateParameters()){
showBusyDialog();
isQueryByUser = true;
Clients.response(new AuEcho(this, "onQueryCallback", null));
}
}
/**
* validate parameter before run query
* @return true if parameters pass validation
*/
public boolean validateParameters(){
return true;
}
/**
* Call after loading of parameter panel to set initial value. Can call to reset parameters.
*/
protected void initParameters() {
}
/**
* Update related info when selection in {@link #contentPanel} change.
*/
protected void updateSubcontent (){ updateSubcontent(-1);};
/**
* Update related info for a specific row. If targetRow < 0, update using selected row.
* @param targetRow
*/
protected void updateSubcontent (int targetRow){};
/**
* Reset parameter to default value or to empty value.
*/
protected void resetParameters() {
}
/**
* Before running of process
* @param processId
*/
protected void preRunProcess (Integer processId){
// disable all control button when run process
enableButtons(false);
// call run process in next request to disable all button control
Events.echoEvent(ON_RUN_PROCESS, this, processId);
}
/**
* Run a process.
* Before start process, save id of record selected.
* After run of process, show process message report result.
* @param processIdObj
*/
protected void runProcess (Object processIdObj){
final Integer processId = (Integer)processIdObj;
final MProcess m_process = MProcess.get(Env.getCtx(), processId);
final ProcessInfo m_pi = new ProcessInfo(m_process.getName(), processId);
m_pi.setAD_User_ID(Env.getAD_User_ID(Env.getCtx()));
m_pi.setAD_Client_ID(Env.getAD_Client_ID(Env.getCtx()));
MPInstance instance = new MPInstance(Env.getCtx(), processId, 0, 0, null);
instance.saveEx();
final int pInstanceID = instance.getAD_PInstance_ID();
// devCoffee - enable use of special forms from process related with info windows
m_pi.setAD_PInstance_ID(pInstanceID);
int adFormID = m_process.getAD_Form_ID();
if (adFormID != 0 )
{
String title = m_process.getName();
if (title == null || title.length() == 0)
title = m_process.getValue();
// store in T_Selection table selected rows for Execute Process that retrieves from T_Selection in code.
DB.createT_SelectionNewNP(pInstanceID, getSaveKeys(getInfoColumnIDFromProcess(processId)), null);
ADForm form = ADForm.openForm(adFormID, null, m_pi);
Mode mode = form.getWindowMode();
form.setAttribute(Window.MODE_KEY, form.getWindowMode());
form.setAttribute(Window.INSERT_POSITION_KEY, Window.INSERT_NEXT);
if (mode == Mode.HIGHLIGHTED || mode == Mode.MODAL) {
form.addEventListener(DialogEvents.ON_WINDOW_CLOSE, new EventListener() {
@Override
public void onEvent(Event event) throws Exception {
;
}
});
form.doHighlighted();
form.focus();
}
else {
form.addEventListener(DialogEvents.ON_WINDOW_CLOSE, new EventListener() {
@Override
public void onEvent(Event event) throws Exception {
updateListSelected();
recordSelectedData.clear();
Clients.response(new AuEcho(InfoPanel.this, "onQueryCallback", null));
onUserQuery();
}
});
SessionManager.getAppDesktop().showWindow(form);
}
return;
}
// Execute Process
m_pi.setAD_PInstance_ID(pInstanceID);
m_pi.setAD_InfoWindow_ID(infoWindow.getAD_InfoWindow_ID());
//let process end with message and re-query
WProcessCtl.process(p_WindowNo, m_pi, (Trx)null, new EventListener() {
@Override
public void onEvent(Event event) throws Exception {
ProcessModalDialog processModalDialog = (ProcessModalDialog)event.getTarget();
if (DialogEvents.ON_BEFORE_RUN_PROCESS.equals(event.getName())){
updateListSelected();
// store in T_Selection table selected rows for Execute Process that retrieves from T_Selection in code.
DB.createT_SelectionNewNP(pInstanceID, getSaveKeys(getInfoColumnIDFromProcess(processModalDialog.getAD_Process_ID())),
null);
saveResultSelection(getInfoColumnIDFromProcess(processModalDialog.getAD_Process_ID()));
createT_Selection_InfoWindow(pInstanceID);
recordSelectedData.clear();
}else if (ProcessModalDialog.ON_WINDOW_CLOSE.equals(event.getName())){
if (getDesktop() == null)
return;
if (processModalDialog.isCancel()){
//clear back
m_results.clear();
// enable or disable control button rely selected record status
enableButtons();
}else if (m_pi.isError()){
ProcessInfoDialog.showProcessInfo(m_pi, p_WindowNo, InfoPanel.this, true);
// enable or disable control button rely selected record status
enableButtons();
}else if (!m_pi.isError()){
if (isCloseAfterExecutionOfProcess()) {
ProcessInfoUtil.setLogFromDB(m_pi);
ProcessInfoLog[] logs = m_pi.getLogs();
if (logs != null && logs.length > 0) {
ProcessInfoDialog dialog = ProcessInfoDialog.showProcessInfo(m_pi, p_WindowNo, InfoPanel.this, false);
dialog.addEventListener(DialogEvents.ON_WINDOW_CLOSE, e -> InfoPanel.this.detach());
} else {
detach();
}
} else {
ProcessInfoDialog.showProcessInfo(m_pi, p_WindowNo, InfoPanel.this, true);
isRequeryByRunSuccessProcess = true;
Clients.response(new AuEcho(InfoPanel.this, "onQueryCallback", null));
}
}
recordSelectedData.clear();
}
}
});
}
/**
* Save selected rows to {@link #m_values}
* @param infoColumnId AD_InfoProcess.AD_InfoColumn_ID. Use as key column if > 0
*/
protected void saveResultSelection(int infoColumnId) {
int m_keyColumnIndex = contentPanel.getKeyColumnIndex();
if (m_keyColumnIndex == -1) {
return;
}
m_values = new LinkedHashMap>();
if (p_multipleSelection) {
Map > selectedRow = getSelectedRowInfo();
// for selected rows
for (Entry> selectedInfo : selectedRow.entrySet())
{
// get key and viewID
Object keyData = selectedInfo.getKey();
NamePair kp = null;
if (infoColumnId > 0){
int dataIndex = columnDataIndex.get(infoColumnId) + p_layout.length;
Object viewIDValue = selectedInfo.getValue().get(dataIndex);
if (keyData instanceof String)
kp = new ValueNamePair((String) keyData, viewIDValue == null ? null : viewIDValue.toString());
else
kp = new KeyNamePair((Integer) keyData, viewIDValue == null ? null : viewIDValue.toString());
}else{
if (keyData instanceof String)
kp = new ValueNamePair((String) keyData, null);
else
kp = new KeyNamePair((Integer) keyData, null);
}
// get Data
LinkedHashMap values = new LinkedHashMap();
for(int col = 0 ; col < p_layout.length; col ++)
{
// layout has same columns as selectedInfo
values.put(p_layout[col].getColumnName(), selectedInfo.getValue().get(col));
}
if(values.size() > 0)
m_values.put(kp, values);
}
}
} // saveResultSelection
/**
* Insert selected rows to T_Selection_InfoWindow
* @param AD_PInstance_ID
*/
public void createT_Selection_InfoWindow(int AD_PInstance_ID)
{
MTable table = MTable.get(infoWindow.getAD_Table_ID());
StringBuilder insert = new StringBuilder();
insert.append("INSERT INTO T_Selection_InfoWindow (AD_PINSTANCE_ID, ");
if (table != null && table.isUUIDKeyTable())
insert.append("T_SELECTION_UU");
else
insert.append("T_SELECTION_ID");
insert.append(", COLUMNNAME , VALUE_STRING, VALUE_NUMBER , VALUE_DATE ) VALUES(?,?,?,?,?,?) ");
for (Entry> records : m_values.entrySet()) {
//set Record ID
LinkedHashMap fields = records.getValue();
for(Entry field : fields.entrySet())
{
List parameters = new ArrayList();
parameters.add(AD_PInstance_ID);
Object key = records.getKey();
if(key instanceof KeyNamePair)
{
KeyNamePair knp = (KeyNamePair)key;
parameters.add(knp.getKey());
}
else if(key instanceof ValueNamePair)
{
ValueNamePair vnp = (ValueNamePair)key;
parameters.add(vnp.getValue());
}
else
{
parameters.add(key);
}
parameters.add(field.getKey());
Object data = field.getValue();
// set Values
if (data instanceof IDColumn)
{
IDColumn id = (IDColumn) data;
parameters.add(null);
parameters.add(id.getRecord_ID());
parameters.add(null);
}
else if (data instanceof UUIDColumn)
{
UUIDColumn id = (UUIDColumn) data;
parameters.add(id.getRecord_UU());
parameters.add(null);
parameters.add(null);
}
else if (data instanceof String)
{
parameters.add(data);
parameters.add(null);
parameters.add(null);
}
else if (data instanceof BigDecimal || data instanceof Integer || data instanceof Double)
{
parameters.add(null);
if(data instanceof Double)
{
BigDecimal value = BigDecimal.valueOf((Double)data);
parameters.add(value);
}
else
parameters.add(data);
parameters.add(null);
}
else if (data instanceof Integer)
{
parameters.add(null);
parameters.add((Integer)data);
parameters.add(null);
}
else if (data instanceof Timestamp || data instanceof Date)
{
parameters.add(null);
parameters.add(null);
if(data instanceof Date)
{
Timestamp value = new Timestamp(((Date)data).getTime());
parameters.add(value);
}
else
parameters.add(data);
}
else if(data instanceof KeyNamePair)
{
KeyNamePair knpData = (KeyNamePair)data;
parameters.add(null);
parameters.add(knpData.getKey());
parameters.add(null);
}
else if(data instanceof ValueNamePair)
{
ValueNamePair vnp = (ValueNamePair)data;
parameters.add(vnp.getValue());
parameters.add(null);
parameters.add(null);
}
else
{
parameters.add(data);
parameters.add(null);
parameters.add(null);
}
DB.executeUpdateEx(insert.toString(),parameters.toArray() , null);
}
}
} // createT_Selection_InfoWindow
/**
* Get InfoColumnID of infoProcess
* @param processId
* @return AD_InfoColumn_ID, -1 if processId not in {@link #infoProcessList}
*/
protected int getInfoColumnIDFromProcess (int processId){
for (int i = 0; i < infoProcessList.length; i++){
if (infoProcessList[i].getAD_Process_ID() == processId){
return infoProcessList[i].getAD_InfoColumn_ID();
}
}
return -1;
}
private void showBusyDialog() {
Clients.showBusy(this, Msg.getMsg(Env.getCtx(), "Processing"));
}
private void hideBusyDialog() {
Clients.clearBusy(this);
}
/**
* Set sort direction indicator
*/
protected void correctHeaderOrderIndicator() {
Listhead listHead = contentPanel.getListHead();
if (listHead != null) {
List> headers = listHead.getChildren();
for(Object obj : headers)
{
Listheader header = (Listheader) obj;
if (header.getColumnIndex() == indexOrderColumn)
header.setSortDirection(isColumnSortAscending?"ascending":"descending");
else
header.setSortDirection("natural");
}
}
}
/**
* handle echo from query event
* @param event null to indicate reset instead of echo from query event
*/
public void onQueryCallback(Event event)
{
try
{
// event == null mean direct call from reset button
if (event == null)
m_count = 0;
else
executeQuery();
renderItems();
correctHeaderOrderIndicator();
// IDEMPIERE-1334 after refresh, restore prev selected item start
// just evaluate display logic of process button when requery by use click requery button
if (isQueryByUser){
bindInfoProcess();
// reset selected list
recordSelectedData.clear();
isRequeryByRunSuccessProcess = false;
}
if (isRequeryByRunSuccessProcess){
syncSelectedAfterRequery();
restoreSelectedInPage();
}
// IDEMPIERE-1334 after refresh, restore prev selected item end
updateSubcontent ();
}
finally
{
isQueryByUser = false;
hideBusyDialog();
}
if(!isLookup()) {
updateRowSelectionOrder();
updateContext(true);
}
}
/**
* Evaluate display logic of processes.
* Implemented by sub class.
*/
protected void bindInfoProcess (){}
/**
* handle ok event
*/
protected void onOk()
{
if (!contentPanel.getChildren().isEmpty() && getSelectedRowInfo().size() > 0)
{
dispose(true);
}
}
/**
* handle double click on row event
*/
protected void onDoubleClick()
{
if (isLookup())
{
dispose(true);
}
else
{
zoom();
}
}
@Override
public void tableChanged(WTableModelEvent event)
{
enableButtons();
}
/**
* zoom to record
*/
public void zoom()
{
Object recordId = contentPanel.getSelectedRowKey();
// prevent NPE when double click is raise but no recore is selected
if (recordId == null)
return;
if (listeners != null && listeners.size() > 0)
{
ValueChangeEvent event = new ValueChangeEvent(this,"zoom",
contentPanel.getSelectedRowKey(),contentPanel.getSelectedRowKey());
fireValueChange(event);
}
else
{
int AD_Table_ID = infoWindow != null ? infoWindow.getAD_Table_ID() : -1;
if (AD_Table_ID <= 0)
{
if (p_keyColumn.endsWith("_ID") || p_keyColumn.endsWith("_UU"))
{
AD_Table_ID = MTable.getTable_ID(p_keyColumn.substring(0, p_keyColumn.length() - 3));
}
}
if (AD_Table_ID > 0) {
if (recordId instanceof String)
AEnv.zoomUU(AD_Table_ID, (String) recordId);
else
AEnv.zoom(AD_Table_ID, (Integer) recordId);
}
}
}
/**
* Select all records from all pages
*/
private void selectAllRecords() {
// select all
try {
if (paging != null) {
int currentPage = paging.getActivePage();
int pgCnt = paging.getPageCount();
for (int pgNo = 0; pgNo <= pgCnt-1; pgNo++) {
if (pgNo == currentPage)
continue; // will be done at the end
setAndLoadActivePage(pgNo);
}
setAndLoadActivePage(currentPage);
} else {
addAllCurrentContentPanelToSelected();
}
restoreSelectedInPage();
setStatusSelected();
btnSelectAll.setEnabled(false);
btnDeSelectAll.setEnabled(true);
} finally {
Clients.clearBusy();
}
}
/**
* Set and load pgNo as active page
* @param pgNo
*/
private void setAndLoadActivePage(int pgNo) {
paging.setActivePage(pgNo);
contentPanel.clearSelection();
pageNo = pgNo;
int start = pageNo * pageSize;
int end = getOverIntValue ((long)start + pageSize, extra_max_row);
if (end >= m_count)
end = m_count;
List subList = readLine(start, end);
model = new ListModelTable(subList);
model.setSorter(this);
model.addTableModelListener(this);
model.setMultiple(p_multipleSelection);
contentPanel.setData(model, null);
addAllCurrentContentPanelToSelected();
}
/**
* Add all the records from current content panel to selected records
*/
private void addAllCurrentContentPanelToSelected() {
for (int rowIndex = 0; rowIndex < contentPanel.getModel().getRowCount(); rowIndex++){
Object keyCandidate = getColumnValue(rowIndex);
@SuppressWarnings("unchecked")
List candidateRecord = (List)contentPanel.getModel().get(rowIndex);
if (!recordSelectedData.containsKey(keyCandidate)) {
recordSelectedData.put(keyCandidate, candidateRecord);
}
}
}
/**
* De-Select all records from all pages
*/
private void deSelectAllRecords() {
// unselect all
recordSelectedData.clear();
restoreSelectedInPage();
setStatusSelected();
btnSelectAll.setEnabled(true);
btnDeSelectAll.setEnabled(false);
}
/**
* Handle user click on new record button.
*/
protected void newRecordAction (){}
/**
*
* @param listener
*/
public void addValueChangeListener(ValueChangeListener listener)
{
if (listener == null)
{
return;
}
listeners.add(listener);
}
/**
* Fire ValueChangeEvent event.
* @param event
*/
public void fireValueChange(ValueChangeEvent event)
{
for (ValueChangeListener listener : listeners)
{
listener.valueChange(event);
}
}
/**
* Dispose and save Selection
* @param ok true if OK pressed
*/
public void dispose(boolean ok)
{
if (log.isLoggable(Level.CONFIG)) log.config("OK=" + ok);
m_ok = ok;
// End Worker
if (isLookup())
{
saveSelection();
}
if (Window.MODE_EMBEDDED.equals(getAttribute(Window.MODE_KEY)))
{
SessionManager.getAppDesktop().closeActiveWindow();
}
else
{
//detach if attach to page
if (getDesktop() != null)
{
//Workaround for detached HTML input element leak
if (getChildren().size() > 0) {
Component[] childs = getChildren().toArray(new Component[0]);
for(Component c : childs) {
AEnv.detachInputElement(c);
}
}
Executions.schedule(getDesktop(), e -> this.detach(), new Event("onAsyncDetach"));
}
}
} // dispose
/**
* Save width of columns
* @param comp
*/
private void saveWlistBoxColumnWidth(Component comp){
if(comp instanceof WListbox){
((WListbox)comp).saveColumnWidth();
}
List list = comp.getChildren();
for(Component child:list){
saveWlistBoxColumnWidth(child);
}
}
/**
* @param cmpr {@link WListItemRenderer.ColumnComparator}
* @param ascending
*/
public void sort(Comparator cmpr, boolean ascending) {
updateListSelected();
WListItemRenderer.ColumnComparator lsc = (WListItemRenderer.ColumnComparator) cmpr;
// keep column order
int col = lsc.getColumnIndex();
indexOrderColumn = col;
isColumnSortAscending = ascending;
String displayColumn = p_layout[col].getDisplayColumn();
sqlOrderColumn = !Util.isEmpty(displayColumn) ? displayColumn : p_layout[col].getColSQL().trim();
m_sqlUserOrder = null; // clear cache value
if (m_useDatabasePaging)
{
executeQuery();
}
else
{
Collections.sort(line, lsc);
}
renderItems();
}
/**
* Get table name from AD_Ref_Table of Column Name
* @param refValID
* @param columnName
* @return MTable[] tables
*/
private MTable getTable(int refValID, String columnName) {
if(refValID > 0) {
return MTable.get(Env.getCtx(), MRefTable.get(Env.getCtx(), refValID).getAD_Table_ID());
}
else if (columnName.endsWith("_ID") || columnName.endsWith("_UU")) {
return MTable.get(Env.getCtx(), columnName.substring(0, columnName.length() - 3));
}
else {
return null;
}
}
/**
*
* @return true if this is a lookup dialog
*/
public boolean isLookup()
{
return m_lookup;
}
/**
* scroll selected row into view (i.e make sure it is visible)
*/
public void scrollToSelectedRow()
{
if (contentPanel != null && contentPanel.getSelectedIndex() >= 0) {
Listitem selected = contentPanel.getItemAtIndex(contentPanel.getSelectedIndex());
if (selected != null) {
selected.focus();
}
}
}
@Override
public String getSortDirection(Comparator cmpr) {
return "natural";
}
/**
*
* @return window no
*/
public int getWindowNo() {
return p_WindowNo;
}
/**
*
* @return row count
*/
public int getRowCount() {
return contentPanel.getRowCount();
}
/**
*
* @return first row key/id
*/
public Object getFirstRowKey() {
return contentPanel.getFirstRowKey();
}
/**
*
* @param row
* @return row key/id
*/
public Object getRowKeyAt(int row) {
return contentPanel.getRowKeyAt(row);
}
/**
* @return the cacheStart
*/
protected int getCacheStart() {
return cacheStart;
}
/**
* @param cacheStart the cacheStart to set
*/
private void setCacheStart(int cacheStart) {
this.cacheStart = cacheStart;
}
/**
* @return the cacheEnd
*/
protected int getCacheEnd() {
return cacheEnd;
}
/**
*
* @return true if using database paging feature
*/
protected boolean isUseDatabasePaging() {
return m_useDatabasePaging;
}
@Override
public void onPageAttached(Page newpage, Page oldpage) {
super.onPageAttached(newpage, oldpage);
if (newpage != null && !isLookup()) {
if (infoWindow != null)
SessionManager.getAppDesktop().updateHelpContext(X_AD_CtxHelp.CTXTYPE_Info, infoWindow.getAD_InfoWindow_ID(), this);
else
SessionManager.getAppDesktop().updateHelpContext(X_AD_CtxHelp.CTXTYPE_Home, 0, this);
}
SessionManager.getSessionApplication().getKeylistener().addEventListener(Events.ON_CTRL_KEY, this);
addEventListener(IDesktop.ON_CLOSE_WINDOW_SHORTCUT_EVENT, this);
}
@Override
public void onPageDetached(Page page) {
super.onPageDetached(page);
try {
removeEventListener(IDesktop.ON_CLOSE_WINDOW_SHORTCUT_EVENT, this);
if (SessionManager.getSessionApplication() != null &&
SessionManager.getSessionApplication().getKeylistener() != null)
SessionManager.getSessionApplication().getKeylistener().removeEventListener(Events.ON_CTRL_KEY, this);
if (getFirstChild() != null)
saveWlistBoxColumnWidth(getFirstChild());
if (registerWindowNo && SessionManager.getAppDesktop() != null)
SessionManager.getAppDesktop().unregisterWindow(p_WindowNo);
} catch (Exception e){
log.log(Level.WARNING, e.getMessage(), e);
}
}
/**
* field call this info panel as search editor
* null in case info window open in stand-alone window (from menu, fav,...)
* @return {@link GridField}
*/
public GridField getGridfield() {
return m_gridfield;
}
/**
*
* @param m_gridfield
*/
public void setGridfield(GridField m_gridfield) {
this.m_gridfield = m_gridfield;
}
/**
*
* @return page size (number of rows per page)
*/
public int getPageSize() {
return pageSize;
}
/**
*
* @return true if dialog should auto close after successful execution of process
*/
public boolean isCloseAfterExecutionOfProcess() {
return m_closeAfterExecutionOfProcess;
}
/**
* Set whether dialog should auto close after successful execution of process
* @param closeAfterExecutionOfProcess
*/
public void setCloseAfterExecutionOfProcess(boolean closeAfterExecutionOfProcess) {
this.m_closeAfterExecutionOfProcess = closeAfterExecutionOfProcess;
}
/**
*
* @param multipleSelection
*/
public void setMultipleSelection(boolean multipleSelection) {
p_multipleSelection = multipleSelection;
if (btnSelectAll != null)
btnSelectAll.setVisible(multipleSelection);
if (btnDeSelectAll != null)
btnDeSelectAll.setVisible(multipleSelection);
}
/**
* Widget support
* Depending on Window/Tab returns widget lines info
* @return info
*/
public String getStatusLinesWidget() {
if(infoWindow == null)
return null;
MStatusLine[] wls = MStatusLine.getStatusLinesWidget(0, 0, 0, infoWindow.getAD_InfoWindow_ID());
if (wls != null && wls.length > 0)
{
StringBuilder lines = new StringBuilder();
for (MStatusLine wl : wls) {
String line = wl.parseLine(getWindowNo());
if (line != null) {
lines.append(line).append(" ");
}
}
if (lines.length() > 0)
return lines.toString();
}
return null;
} // getWidgetLines
/**
* Update row selection order
*/
protected void updateRowSelectionOrder() {
// check if any rows are found
if(contentPanel.getModel().size() <= 0) {
m_rowSelectionOrder.clear();
return;
}
// update selection
if(!p_multipleSelection && m_lastSelectedIndex >= 0) {
m_rowSelectionOrder.clear();
@SuppressWarnings("unchecked")
List lastSelectedRecord = (List)contentPanel.getModel().get(m_lastSelectedIndex);
Object key = lastSelectedRecord.get(0);
if(key instanceof IDColumn)
key = ((IDColumn)key).getRecord_ID();
m_rowSelectionOrder.add(key);
}
else {
// add selected rows
for(Map.Entry> entry : getSelectedRowInfo().entrySet()) {
List candidateRecord = entry.getValue();
// get row key
Object key = candidateRecord.get(0);
if(key instanceof IDColumn)
key = ((IDColumn)key).getRecord_ID();
//
if(!m_rowSelectionOrder.contains(key))
m_rowSelectionOrder.add(key);
}
// remove unselected rows
for(Iterator it = m_rowSelectionOrder.iterator(); it.hasNext();) {
if(!getSelectedRowInfo().containsKey(it.next()))
it.remove();
}
}
} // updateRowSelectionOrder
/**
* Put values from the selected row into the context
*/
protected void updateContext(boolean checkQueryCriteria) {
List lastSelectedRow = getLastSelectedRow();
if(checkQueryCriteria) {
// put parameter values into the context
for(Map.Entry e : paraCtxValues.entrySet()) {
String columnName = e.getKey();
Object value = e.getValue();
setContext(columnName, value);
}
}
// put the values of the last selected row into the context
for(int i = 0; i < p_layout.length; i++) {
String columnName = p_layout[i].getColumnName();
Object value = lastSelectedRow != null ? lastSelectedRow.get(i) : null;
setContext(ROW_CTX_VARIABLE_PREFIX + columnName, value);
}
// add selected IDs to the context
setContext(ROW_ID_CTX_VARIABLE_NAME, getSelectedIDsForCtx());
// update Quick Info widget
if (infoWindow != null)
SessionManager.getAppDesktop().updateHelpContext(X_AD_CtxHelp.CTXTYPE_Info, infoWindow.getAD_InfoWindow_ID(), this);
else
SessionManager.getAppDesktop().updateHelpContext(X_AD_CtxHelp.CTXTYPE_Home, 0, this);
} // updateContext
/**
* Set context
* @param columnName
* @param value
*/
protected void setContext(String columnName, Object value) {
if(value instanceof KeyNamePair)
value = ((KeyNamePair)value).getKey();
else if(value instanceof IDColumn)
value = ((IDColumn)value).getRecord_ID();
if (value == null) {
Env.setContext(Env.getCtx(), p_WindowNo, columnName, "");
} else if (value instanceof Boolean) {
Env.setContext(Env.getCtx(), p_WindowNo, columnName, (Boolean)value);
} else if (value instanceof Timestamp) {
Env.setContext(Env.getCtx(), p_WindowNo, columnName, (Timestamp)value);
} else {
Env.setContext(Env.getCtx(), p_WindowNo, columnName, value.toString());
}
}
/**
* Get a comma-separated string of selected IDs
* @return String ctx value
*/
protected String getSelectedIDsForCtx() {
String returnVal = null;
for(Object key : m_rowSelectionOrder) {
String selectedID = Objects.toString(key);
if(returnVal == null)
returnVal = selectedID;
else
returnVal += "," + selectedID;
}
return returnVal;
}
/**
* Get last selected row
* @return List
*/
protected List getLastSelectedRow() {
int index = m_rowSelectionOrder.size() - 1;
List lastSelectedRow = m_rowSelectionOrder.size() > 0 ? getSelectedRowInfo().get(m_rowSelectionOrder.get(index)) : null;
return lastSelectedRow;
}
/**
* Set focus to {@link #contentPanel}:
* - Single selection: auto select first item and set focus to it.
* - Multiple selection: set focus to first item.
*/
private void setFocusToContentPanel() {
if(contentPanel.getRowCount() > 0) {
if(p_multipleSelection) {
((HtmlBasedComponent)contentPanel.getItems().get(0)).focus();
}
else {
if(contentPanel.getSelectedItem() == null)
contentPanel.setSelectedIndex(0);
((HtmlBasedComponent)contentPanel.getSelectedItem()).focus();
contentPanel.getSelectedItem().setSelected(true);
Set selectionList = new LinkedHashSet<>();
selectionList.add(contentPanel.getSelectedItem());
Events.postEvent(new SelectEvent<>(Events.ON_SELECT, contentPanel, selectionList));
}
setStatusSelected ();
m_lastSelectedIndex = 0;
}
}
} // Info