/******************************************************************************
* Product: Adempiere ERP & CRM Smart Business Solution *
* Copyright (C) 1999-2006 ComPiere, Inc. 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 *
* ComPiere, Inc., 2620 Augustine Dr. #245, Santa Clara, CA 95054, USA *
* or via info@compiere.org or http://www.compiere.org/license.html *
* @contributor Victor Perez , e-Evolution.SC FR [ 1757088 ] *
* @contributor fer_luck @ centuryon *
*****************************************************************************/
package org.compiere.grid;
import java.awt.AWTEvent;
import java.awt.BorderLayout;
import java.awt.CardLayout;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.KeyboardFocusManager;
import java.awt.event.KeyEvent;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.beans.PropertyVetoException;
import java.beans.VetoableChangeListener;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Properties;
import java.util.Set;
import java.util.logging.Level;
import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JScrollPane;
import javax.swing.JSplitPane;
import javax.swing.KeyStroke;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableColumn;
import javax.swing.table.TableColumnModel;
import org.adempiere.plaf.AdempiereLookAndFeel;
import org.adempiere.plaf.AdempierePLAF;
import org.compiere.apps.ADialog;
import org.compiere.apps.APanel;
import org.compiere.apps.AppsAction;
import org.compiere.grid.ed.VCellEditor;
import org.compiere.grid.ed.VCellRenderer;
import org.compiere.grid.ed.VEditor;
import org.compiere.grid.ed.VEditorFactory;
import org.compiere.grid.ed.VHeaderRenderer;
import org.compiere.grid.ed.VManagedEditor;
import org.compiere.grid.ed.VRowIDEditor;
import org.compiere.grid.ed.VRowIDRenderer;
import org.compiere.grid.ed.VString;
import org.compiere.grid.tree.VTreePanel;
import org.compiere.model.DataStatusEvent;
import org.compiere.model.DataStatusListener;
import org.compiere.model.GridField;
import org.compiere.model.GridTab;
import org.compiere.model.GridTable;
import org.compiere.model.GridWindow;
import org.compiere.model.MTree;
import org.compiere.model.MTreeNode;
import org.compiere.swing.CPanel;
import org.compiere.swing.CScrollPane;
import org.compiere.swing.CollapsiblePanel;
import org.compiere.swing.TableCellNone;
import org.compiere.util.CLogMgt;
import org.compiere.util.CLogger;
import org.compiere.util.DisplayType;
import org.compiere.util.Env;
import org.compiere.util.Evaluatee;
import org.compiere.util.Trx;
import org.omidp.util.LocaleUtil;
/**
* The Grid Controller is the panel for single and multi-row presentation
* and links to the Model Tab.
*
* <pre>
* UI Structure:
* this (BorderLayout)
* splitPane (JSplitPane)
* left
* graphicPanel
* right
* cardPanel JPanel (CardLayout)
* srPane JSplitPane
* vPane JScrollPane
* vPanel VPanel (GridBagLayout)
* vIncludedGC GridController
* mrPane JScrollPane
* vTable VTable
*
* <B>DataBinding:<B>
* - MultiRow - is automatic between VTable and MTable
* - SingleRow
* - from VEditors via fireVetoableChange(m_columnName, null, getText());
* (vetoableChange)
* - to VEditors via updateSingleRow -> Editor.setValue(object)
*
* Event Chains
* -- Navigation --
* (VTable selection -> GridController.valueChanged)
* (APanel selection)
* + MTab.navivate
* + MTab.setCurrentRow
* + Update all MFields
* + MField.setValue
* + setContext
* + fire PropertyChange "Value"
* + VEditor.propertyChange
* + VEditor.setValue
* + MTab.fireProperyChange "CurrentRow"
* + VTable.propertyChange (setRowSelectionInterval)
* + GridController.valueChange
* + GridController.dynamicDisplay(complete)
* + MTab.fireDataStatusChanged
* + APanel.statusChanged
*
* -- ValueChanges --
* VEditor.fireVetoableChange
* + (VCellEditor.vetoableChange/getCellEditorValue) -- multi-row source
* + (GridController.vetoableChange) -- single-row source
* + MTable.setValueAt
* + MField.setValue
* + setContext
* + fire PropertyChange "Value"
* + VEditor.setValue
* + MTable.fireDataStatusChanged
* + MTab.dataStatusChanged
* + MTab.fireDataStatusChanged
* + APanel.statusChanged
* + GridController.dataStatusChanged
* + GridController.dynamicDisplay(selective)
* </pre>
* @author Jorg Janke
* @version $Id: GridController.java,v 1.8 2006/09/25 00:59:52 jjanke Exp $
*
* @author Teo Sarca - BF [ 1742159 ], BF [ 1707876 ]
* @contributor Victor Perez , e-Evolution.SC FR [ 1757088 ]
* @contributor fer_luck @ centuryon FR [ 1757088 ]
*/
public class GridController extends CPanel
implements DataStatusListener, ListSelectionListener, Evaluatee,
VetoableChangeListener, PropertyChangeListener, MouseListener
{
/**
*
*/
private static final long serialVersionUID = 7308782933999556880L;
/**
* Constructor - you need to call initGrid for instanciation
*/
public GridController()
{
try
{
jbInit();
}
catch(Exception e)
{
log.log(Level.SEVERE, "", e);
}
} // GridController
/**
* toString
* @return string representation
*/
public String toString()
{
return "GridController for " + m_mTab;
} // toString
/** Logger */
private static CLogger log = CLogger.getCLogger(GridController.class);
/**
* The Layout
*/
private BorderLayout mainLayout = new BorderLayout();
private JSplitPane splitPane = new JSplitPane();
private CPanel graphPanel = new CPanel();
private BorderLayout graphLayout = new BorderLayout();
private CPanel cardPanel = new CPanel();
private CardLayout cardLayout = new CardLayout();
//private JSplitPane srPane = new JSplitPane();
private JScrollPane vPane = new JScrollPane();
private CScrollPane mrPane = new CScrollPane();
private CPanel xPanel = new CPanel();
private BorderLayout xLayout = new BorderLayout();
private VTable vTable = new VTable();
//FR [ 1757088 ]
private VPanel vPanel = null;
private boolean detailGrid = false;
/**
* Static Layout init
* @throws Exception
*/
private void jbInit() throws Exception
{
this.setLayout(mainLayout);
this.add(splitPane, BorderLayout.CENTER);
splitPane.setOpaque(false);
graphPanel.setLayout(graphLayout);
//
splitPane.add(graphPanel, JSplitPane.LEFT);
splitPane.add(cardPanel, JSplitPane.RIGHT);
splitPane.setBorder(null);
splitPane.setName("gc_splitPane");
//This code added by Omid Pourhadi (Omidp)
LocaleUtil.applyComponentOrientation(graphPanel, splitPane, cardPanel, mrPane, xPanel, vPane);
//
cardPanel.setLayout(cardLayout);
cardPanel.add(vPane, "vPane"); // Sequence Important!
cardPanel.add(mrPane, "mrPane");
cardPanel.setBorder(null);
cardPanel.setName("gc_cardPanel");
// single row (w/o xPane it would be centered)
/*
srPane.setBorder(null);
srPane.setName("gc_srPane");
srPane.setOrientation(JSplitPane.VERTICAL_SPLIT);
srPane.add(vPane, JSplitPane.TOP);
srPane.setTopComponent(vPane);
srPane.setBottomComponent(null); // otherwise a button is created/displayed
*/
//FR [ 1757088 ] vPane.getViewport().add(xPanel, null);
//FR [ 1757088 ] xPanel.add(vPanel);
xPanel.setLayout(xLayout);
xPanel.setName("gc_xPanel");
xPanel.setBorder(BorderFactory.createEmptyBorder());
//xLayout.setAlignment(FlowLayout.LEFT);
xLayout.setHgap(0);
xLayout.setVgap(0);
// multi-row
mrPane.setBorder(null);
mrPane.getViewport().add(vTable, null);
mrPane.setName("gc_mrPane");
//
graphPanel.setBorder(null);
graphPanel.setName("gc_graphPanel");
//srPane.setDividerLocation(200);
vPane.setBorder(BorderFactory.createEmptyBorder());
} // jbInit
/**
* Displose
*/
public void dispose()
{
log.config( "(" + m_mTab.toString() + ")");
// clear info
stopEditor(false);
if (m_mTab.isLoadComplete())
{
if (m_mTab.needSave(true, false))
m_mTab.dataIgnore();
}
//FR [ 1757088 ] vIncludedGC = null;
// Listeners
if (m_mTab.isLoadComplete())
{
m_mTab.getTableModel().removeDataStatusListener(this);
m_mTab.getTableModel().removeVetoableChangeListener(this);
}
vTable.getSelectionModel().removeListSelectionListener(this);
m_mTab.removePropertyChangeListener(vTable);
// editors
Component[] comp = vPanel.getComponentsRecursive();
for (int i = 0; i < comp.length; i++)
{
if (comp[i] instanceof VEditor)
{
VEditor vEditor = (VEditor)comp[i];
vEditor.removeVetoableChangeListener(this);
String columnName = comp[i].getName();
GridField mField = m_mTab.getField(columnName);
if (mField != null)
mField.removePropertyChangeListener(vEditor);
vEditor.dispose();
}
}
/** @todo Remove APanel Button listeners */
vTable.removeAll();
vTable.setModel(new DefaultTableModel()); // remove reference
vTable = null;
vPanel.removeAll();
vPanel = null;
//srPane.removeAll();
//srPane = null;
splitPane.removeAll();
splitPane = null;
m_mTab = null;
m_tree = null;
this.removeAll();
} // dispose
/** Model Tab */
private GridTab m_mTab = null;
/** Window */
private int m_WindowNo;
/** Only Multi-Row exist */
private boolean m_onlyMultiRow = false;
/** Single/Multi Row indicator */
private boolean m_singleRow = true;
/** Veto Active */
private boolean m_vetoActive = false;
/** Tree Panel (optional) */
private VTreePanel m_tree;
private APanel m_aPanel;
private boolean init;
private ArrayList<GridSynchronizer> synchronizerList = new ArrayList<GridSynchronizer>();
public boolean initGrid (GridTab mTab, boolean onlyMultiRow,
int WindowNo, APanel aPanel, GridWindow mWindow)
{
return initGrid(mTab, onlyMultiRow, WindowNo, aPanel, mWindow, false);
}
/**************************************************************************
* Init Grid.
* <pre>
* - Map table to model
* - Update (multi-row) table info with renderers/editors
* - build single-row panel
* - initialize display
* </pre>
* @param mTab tab
* @param onlyMultiRow only table
* @param WindowNo window no
* @param aPanel optional Application Panel for adding button listeners
* @param mWindow parent Window Model
* @return true if initialized
*/
public boolean initGrid (GridTab mTab, boolean onlyMultiRow,
int WindowNo, APanel aPanel, GridWindow mWindow, boolean lazy)
{
log.config( "(" + mTab.toString() + ")");
m_mTab = mTab;
m_WindowNo = WindowNo;
m_onlyMultiRow = onlyMultiRow;
m_aPanel = aPanel;
setName("GC-" + mTab);
//FR [ 1757088 ]
vPanel = new VPanel(mTab.getName(), m_WindowNo);
vPanel.putClientProperty(AdempiereLookAndFeel.HIDE_IF_ONE_TAB, Boolean.TRUE);
if (this.isDetailGrid())
{
vPanel.setBorder(BorderFactory.createLineBorder(AdempierePLAF.getPrimary2()));
}
vPane.getViewport().add(xPanel, null);
xPanel.add(vPanel, BorderLayout.CENTER);
setTabLevel(m_mTab.getTabLevel());
if (!lazy)
init();
else
{
//Load tab meta data, needed for includeTab to work
m_mTab.initTab(false);
}
// log.config( "GridController.dynInit (" + mTab.toString() + ") - fini");
return true;
} // initGrid
private void init()
{
//FIXME: cause bug in sorting
LocaleUtil.applyComponentOrientation(vTable);
// Set up Multi Row Table
vTable.setModel(m_mTab.getTableModel());
// Update Table Info -------------------------------------------------
int size = setupVTable (m_aPanel, m_mTab, vTable);
// Set Color on Tab Level
// this.setBackgroundColor (mTab.getColor());
// Single Row -------------------------------------------------------
if (!m_onlyMultiRow)
{
// Set Softcoded Mnemonic &x
for (int i = 0; i < size; i++)
{
GridField mField = m_mTab.getField(i);
if (mField.isDisplayed())
vPanel.setMnemonic(mField);
} // for all fields
// Add Fields
for (int i = 0; i < size; i++)
{
GridField mField = m_mTab.getField(i);
if (mField.isDisplayed())
{
VEditor vEditor = VEditorFactory.getEditor(m_mTab, mField, false);
if (vEditor == null)
{
log.warning("Editor not created for " + mField.getColumnName());
continue;
}
// MField => VEditor - New Field value to be updated to editor
mField.addPropertyChangeListener(vEditor);
// VEditor => this - New Editor value to be updated here (MTable)
vEditor.addVetoableChangeListener(this);
// Add to VPanel
vPanel.addFieldBuffered(vEditor, mField);
// APanel Listen to buttons
if (mField.getDisplayType() == DisplayType.Button && m_aPanel != null)
((JButton)vEditor).addActionListener (m_aPanel);
}
} // for all fields
vPanel.addFieldBuffered(null, null); // flush the last one through
// No Included Grid Controller
/*
srPane.setResizeWeight(1); // top part gets all
srPane.setDividerSize (0);
srPane.setDividerLocation (9999);
*/
// Use SR to size MR
mrPane.setPreferredSize(vPanel.getPreferredSize());
} // Single-Row
// Tree Graphics Layout
int AD_Tree_ID = 0;
if (m_mTab.isTreeTab())
AD_Tree_ID = MTree.getDefaultAD_Tree_ID (
Env.getAD_Client_ID(Env.getCtx()), m_mTab.getKeyColumnName());
if (m_mTab.isTreeTab() && AD_Tree_ID != 0)
{
m_tree = new VTreePanel(m_WindowNo, false, true);
if (m_mTab.getTabNo() == 0) // initialize other tabs later
m_tree.initTree(AD_Tree_ID);
m_tree.addPropertyChangeListener(VTreePanel.NODE_SELECTION, this);
graphPanel.add(m_tree, BorderLayout.CENTER);
splitPane.setDividerLocation(250);
// splitPane.resetToPreferredSizes();
}
else // No Graphics - hide
{
graphPanel.setPreferredSize(new Dimension(0,0));
splitPane.setDividerSize(0);
splitPane.setDividerLocation(0);
}
// Receive DataStatusChanged info from MTab
m_mTab.addDataStatusListener(this);
// Receive vetoableChange info from MTable when saving
m_mTab.getTableModel().addVetoableChangeListener(this);
// Selection Listener -> valueChanged
vTable.getSelectionModel().addListSelectionListener(this);
// Navigation (RowChanged)
m_mTab.addPropertyChangeListener(vTable);
// Update UI
vTable.autoSize(true);
// Set initial presentation
if (m_onlyMultiRow || !m_mTab.isSingleRow())
switchMultiRow();
else
switchSingleRow();
init = true;
}
/**
*
* @return boolean
*/
public boolean isInit()
{
return init;
}
/**
* Include Tab
* @param gc grid controller to add
* @return GridSynchronizer
*/
//FR [ 1757088 ]
public boolean includeTab (GridController gc , APanel aPanel, GridSynchronizer sync)
{
GridController detail = gc;
detail.setDetailGrid(true);
detail.addMouseListener(detail);
detail.enableEvents(AWTEvent.HIERARCHY_EVENT_MASK + AWTEvent.MOUSE_EVENT_MASK);
vPanel.includeTab(detail);
//BEGIN - [FR 1953734]
gc.setGCParent(this);
//END - [FR 1953734]
gc.getGCParent().setPreferredSize(vPanel.getPreferredSize());
synchronizerList.add(sync);
return true;
} // IncludeTab
//FR [ 1757088 ]
public void setDetailGrid(boolean value){
detailGrid = value;
if (detailGrid && vPanel != null)
vPanel.setBorder(BorderFactory.createLineBorder(AdempierePLAF.getPrimary2()));
}
public boolean isDetailGrid(){
return detailGrid;
}
/**
* Get Title
* @return title
*/
public String getTitle ()
{
return m_mTab.getName();
} // getTitle
/**
* Setup Multi-Row Table (add fields)
* @param aPanel Panel
* @param mTab Model Tab
* @param table JTable
* @return size
*/
private int setupVTable (APanel aPanel, GridTab mTab, VTable table)
{
if (!mTab.isDisplayed())
return 0;
int size = mTab.getFieldCount ();
TableColumnModel tcm = table.getColumnModel();
if (size != tcm.getColumnCount())
throw new IllegalStateException("TableColumn Size <> TableModel");
for (int i = 0; i < size; i++)
{
GridField mField = mTab.getField (i);
TableColumn tc = tcm.getColumn(i);
tc.setMinWidth(30);
// FR 3051618 - Hide in list view
if (mField.isHideInListView()) {
vTable.setColumnVisibility(tc, false);
}
if (mField.getColumnName().equals(tc.getIdentifier().toString()))
{
//don't show included tab field in grid
if (mField.getIncluded_Tab_ID() > 0)
{
TableCellNone tcn = new TableCellNone(mField.getColumnName());
tc.setCellRenderer (tcn);
tc.setCellEditor (tcn);
tc.setHeaderValue (null);
tc.setMinWidth (0);
tc.setMaxWidth (0);
tc.setPreferredWidth (0);
}
else if (mField.getDisplayType () == DisplayType.RowID)
{
tc.setCellRenderer (new VRowIDRenderer (false));
tc.setCellEditor (new VRowIDEditor (false));
tc.setHeaderValue ("");
tc.setMaxWidth (2);
}
else
{
// need to set CellEditor explicitly as default editor based on class causes problem (YesNo-> Boolean)
if (mField.isDisplayed ())
{
tc.setCellRenderer (new VCellRenderer (mField));
VCellEditor ce = new VCellEditor (mField);
tc.setCellEditor (ce);
//
tc.setHeaderValue (mField.getHeader ());
tc.setPreferredWidth (Math.max (mField.getDisplayLength (), 30));
tc.setHeaderRenderer (new VHeaderRenderer(mField));
// Enable Button actions in grid
if (mField.getDisplayType () == DisplayType.Button)
{
ce.setActionListener(aPanel);
}
}
else // column not displayed
{
TableCellNone tcn = new TableCellNone(mField.getColumnName());
tc.setCellRenderer (tcn);
tc.setCellEditor (tcn);
tc.setHeaderValue (null);
tc.setMinWidth (0);
tc.setMaxWidth (0);
tc.setPreferredWidth (0);
}
}
} // found field
else
log.log(Level.SEVERE, "TableColumn " + tc.getIdentifier ()
+ " <> MField " + mField.getColumnName() + mField.getHeader());
} // for all fields
return size;
} // setupVTable
/**
* Activate Grid Controller.
* Called by APanel when GridController is displayed (foreground)
*/
public void activate ()
{
if (!init) init();
// Tree to be initiated on second/.. tab
if (m_mTab.isTreeTab() && m_mTab.getTabNo() != 0)
{
String keyColumnName = m_mTab.getKeyColumnName();
String treeName = "AD_Tree_ID";
if (keyColumnName.startsWith("CM"))
{
if (keyColumnName.equals("CM_Container_ID"))
treeName = "AD_TreeCMC_ID";
else if (keyColumnName.equals("CM_CStage_ID"))
treeName = "AD_TreeCMS_ID";
else if (keyColumnName.equals("CM_Template_ID"))
treeName = "AD_TreeCMT_ID";
else if (keyColumnName.equals("CM_Media_ID"))
treeName = "AD_TreeCMM_ID";
}
int AD_Tree_ID = Env.getContextAsInt (Env.getCtx(), m_WindowNo, treeName, true);
log.config(keyColumnName + " -> " + treeName + " = " + AD_Tree_ID);
if (AD_Tree_ID == 0)
AD_Tree_ID = MTree.getDefaultAD_Tree_ID (
Env.getAD_Client_ID(Env.getCtx()), m_mTab.getKeyColumnName());
if (m_tree != null)
m_tree.initTree (AD_Tree_ID);
}
activateChilds();
} // activate
/**
* activate child grid controller ( included tab )
*/
private void activateChilds()
{
for (GridSynchronizer s : synchronizerList )
{
s.activateChild();
}
}
public GridController findChild(GridTab gTab)
{
GridController gc = null;
for (GridSynchronizer s : synchronizerList )
{
if (s.getChild().getMTab().equals(gTab))
{
gc = s.getChild();
break;
}
}
return gc;
}
/**
* Register ESC Actions
* - overwrite VTable's Keystrokes assignment for ESC
* @param aIgnore ignore
*/
public void registerESCAction (AppsAction aIgnore)
{
int c = VTable.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT;
vTable.getInputMap(c).put(KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), aIgnore.getName());
vTable.getActionMap().put(aIgnore.getName(), aIgnore);
// AEnv.printActionInputMap(vTable);
} // registerESCAction
/**
* Query Tab and resize Table
* (called from APanel)
* @param onlyCurrentRows only current rows
* @param onlyCurrentDays how many days back
* @param maxRows maximim rows or 0 for all
*/
public void query (boolean onlyCurrentRows, int onlyCurrentDays, int maxRows)
{
// start loading while building screen
m_mTab.query(onlyCurrentRows, onlyCurrentDays, maxRows);
// Update UI
if (!isSingleRow())
vTable.autoSize(true);
activateChilds();
} // query
/*
public boolean isNeedToSaveParent()
{
return m_mTab.isNeedToSaveParent();
}*/
/**************************************************************************
* Switch from single to multi & vice versa
*/
public void switchRowPresentation()
{
stopEditor(true);
if (m_singleRow)
switchMultiRow();
else
switchSingleRow();
} // switchRowPresentation
/**
* Switch to SingleRow Presentation
*/
public void switchSingleRow()
{
if (m_onlyMultiRow)
return;
cardLayout.first(cardPanel);
m_singleRow = true;
dynamicDisplay(0);
// vPanel.requestFocus();
} // switchSingleRow
/**
* Switch to MultiRow Presentation
*/
public void switchMultiRow()
{
cardLayout.last(cardPanel);
m_singleRow = false;
vTable.autoSize(true); // resizes
// vTable.requestFocus();
} // switchSingleRow
/**
* Is Single Row presentation
* @return true if Single Row is displayed
*/
public boolean isSingleRow()
{
return m_singleRow;
} // isSingleRow
/**************************************************************************
* Remove Listener - pass on to MTab
* @param l listener
*/
public synchronized void removeDataStatusListener(DataStatusListener l)
{
m_mTab.removeDataStatusListener(l);
} // removeDataStatusListener
/**
* Add Data Status Listener - pass on to MTab
* @param l listener
*/
public synchronized void addDataStatusListener(DataStatusListener l)
{
m_mTab.addDataStatusListener(l);
}
/**
* Data Status Listener - for MTab events.
* <p>
* Callouts are processed here for GUI changes
* - same as in MTab.setValue for batch changes
* <p>
* calls dynamicDisplay
* @param e event
*/
public void dataStatusChanged(DataStatusEvent e)
{
// if (e.getChangedColumn() == 0)
// return;
int col = e.getChangedColumn();
log.config("(" + m_mTab + ") Col=" + col + ": " + e.toString());
// Process Callout
GridField mField = m_mTab.getField(col);
if (mField != null
&& (mField.getCallout().length() > 0 || m_mTab.hasDependants(mField.getColumnName())))
{
String msg = m_mTab.processFieldChange(mField); // Dependencies & Callout
if (msg.length() > 0)
ADialog.error(m_WindowNo, this, msg);
}
//if (col >= 0)
dynamicDisplay(col);
} // dataStatusChanged
/**************************************************************************
* List Selection Listener (VTable) - row changed
* @param e event
*/
public void valueChanged(ListSelectionEvent e)
{
// no rows
if (m_mTab.getRowCount() == 0)
return;
// vTable.stopEditor(graphPanel);
int rowTable = vTable.getSelectedRow();
int rowCurrent = m_mTab.getCurrentRow();
log.config("(" + m_mTab.toString() + ") Row in Table=" + rowTable + ", in Model=" + rowCurrent);
/* BT [ 1972495 ] Multirow Automatic New Record loses context
// FR [ 1757088 ]
if(rowCurrent + 1 == vTable.getRowCount() && !isSingleRow() && Env.isAutoNew(Env.getCtx()) && m_mTab.getRecord_ID() != -1)
{
//stopEditor(true);
vTable.getSelectionModel().removeListSelectionListener(this);
m_mTab.dataNew(false);
dynamicDisplay(0);
vTable.getSelectionModel().addListSelectionListener(this);
return;
} */
if (rowTable == -1) // nothing selected
{
if (rowCurrent >= 0)
{
vTable.setRowSelectionInterval(rowCurrent, rowCurrent); // causes this method to be called again
return;
}
}
else
{
if (rowTable != rowCurrent) {
//make sure table selection is consistent with model
int t = m_mTab.navigate(rowTable);
if (t != rowTable) {
rowTable = t;
vTable.setRowSelectionInterval(rowTable, rowTable);
}
}
dynamicDisplay(0);
}
// TreeNavigation - Synchronize -- select node in tree
if (m_tree != null)
m_tree.setSelectedNode (m_mTab.getRecord_ID()); // ignores new (-1)
// log.config( "GridController.valueChanged (" + m_mTab.toString() + ") - fini",
// "Row in Table=" + rowTable + ", in Model=" + rowCurrent);
} // valueChanged
/**
* PropertyChange Listener - Tree Panel - node selection
* @param e event
*/
public void propertyChange(PropertyChangeEvent e)
{
// System.out.println("propertyChange");
// System.out.println(e);
if (e == null)
return;
Object value = e.getNewValue();
if (value == null)
return;
log.config(e.getPropertyName() + "=" + value
+ " - " + value.getClass().toString());
if (!(value instanceof MTreeNode))
return;
// We Have a TreeNode
int nodeID = ((MTreeNode)value).getNode_ID();
// root of tree selected - ignore
//if (nodeID == 0)
//return;
// Search all rows for mode id
int size = m_mTab.getRowCount();
int row = -1;
for (int i = 0; i < size; i++)
{
if (m_mTab.getKeyID(i) == nodeID)
{
row = i;
break;
}
}
if (row == -1)
{
if (nodeID > 0)
log.log(Level.WARNING, "Tab does not have ID with Node_ID=" + nodeID);
return;
}
// Navigate to node row
m_mTab.navigate(row);
} // propertyChange
/**
* Dynamic Display.
* - Single Row Screen layout and update of dynamic Lookups
* <p>
* Single Row layout:
* the components's name is the ColumnName; if it matches, the
* MField.isDisplayed(true) is used to determine if it is visible
* if the component is a VEditor, setEnabled is set from the MField
* <p>
* Multi Row layout is not changed:
* VCellRenderer calls JTable.isCellEditable -> checks MField.isEditable (Active, isDisplayed)
* VCellEditor.isCellEditable calls MField.isEditable(true) <br>
* If a column is not displayed, the width is set to 0 in dynInit
* <p>
* Dynamic update of data is handeled in VLookup.focusGained/Lost.
* When focus is gained the model is temporarily updated with the
* specific validated data, if lost, it is switched back to the
* unvalidated data (i.e. everything). This allows that the display
* methods have a lookup to display. <br>
* Here: if the changed field has dependents and the dependent
* is a Lookup and this lookup has a dynamic dependence of the changed field,
* the value of that field is set to null (in MTab.processDependencies -
* otherwise it would show an invalid value).
* As Editors listen for value changed of their MField, the display is updated.
* <p>
* Called from GridController.valueChanged/dataStatusChanged, APane;.stateChanged/unlock/cmd_...
* @param col selective column number or 0 if all
*/
public void dynamicDisplay (int col)
{
// log.config( "GridController.dynamicDisplay (" + m_mTab.toString() + ") SingleRow=" + isSingleRow() + ", OnlyMultiRow=" + m_onlyMultiRow);
// Don't update if multi-row
if (!isSingleRow() || m_onlyMultiRow)
return;
if (!m_mTab.isOpen())
return;
// Selective
if (col > 0)
{
GridField changedField = m_mTab.getField(col);
String columnName = changedField.getColumnName();
ArrayList<GridField> dependants = m_mTab.getDependantFields(columnName);
log.config("(" + m_mTab.toString() + ") "
+ columnName + " - Dependents=" + dependants.size());
// No Dependents and no Callout - Set just Background
if (dependants.size() == 0 && changedField.getCallout().length() > 0)
{
Component[] comp = vPanel.getComponentsRecursive();
for (int i = 0; i < comp.length; i++)
{
if (columnName.equals(comp[i].getName ()) && comp[i] instanceof VEditor)
{
VEditor ve = (VEditor)comp[i];
boolean manMissing = false;
boolean noValue = changedField.getValue() == null || changedField.getValue().toString().length() == 0;
if (noValue && changedField.isEditable(true) && changedField.isMandatory(true)) // check context
manMissing = true;
ve.setBackground(manMissing || changedField.isError());
break;
}
}
return;
}
} // selective
// complete single row re-display
boolean noData = m_mTab.getRowCount() == 0;
log.config(m_mTab.toString() + " - Rows=" + m_mTab.getRowCount());
// All Components in vPanel (Single Row)
Set<String> hiddens = new HashSet<String>();
Component[] comps = vPanel.getComponentsRecursive();
for (int i = 0; i < comps.length; i++)
{
Component comp = comps[i];
String columnName = comp.getName();
if (columnName != null && columnName.length() > 0)
{
GridField mField = m_mTab.getField(columnName);
if (mField != null)
{
if (mField.isDisplayed(true)) // check context
{
if (!comp.isVisible())
comp.setVisible(true); // visibility
/**
* Feature Request [1707462]
* Enable runtime change of VFormat
* @author fer_luck
*/
if (comp instanceof VString){
VString vs = (VString)comp;
if ((vs.getVFormat() != null && vs.getVFormat().length() > 0 && mField.getVFormat() == null)
|| (vs.getVFormat() == null && mField.getVFormat() != null && mField.getVFormat().length() > 0)
|| (vs.getVFormat() != null && mField.getVFormat() != null && !vs.getVFormat().equals(mField.getVFormat()))) {
vs.setVFormat(mField.getVFormat());
}
}
//End Feature Request [1707462]
if (comp instanceof VEditor)
{
VEditor ve = (VEditor)comp;
if (noData)
ve.setReadWrite(false);
else
{
boolean rw = mField.isEditable(true); // r/w - check Context
ve.setReadWrite(rw);
// log.log(Level.FINEST, "RW=" + rw + " " + mField);
boolean manMissing = false;
// least expensive operations first // missing mandatory
if (rw && mField.getValue() == null && mField.isMandatory(true)) // check context
manMissing = true;
ve.setBackground(manMissing || mField.isError());
}
}
}
else
{
if (comp.isVisible())
comp.setVisible(false);
hiddens.add(columnName);
}
}
}
} // all components
// hide empty field group based on the environment
for (int i = 0; i < comps.length; i++) {
Component comp = comps[i];
if (comp instanceof CollapsiblePanel)
{
if (comp.getName() == null || comp.getName().startsWith("IncludedTab#"))
continue;
else
{
boolean hasVisible = false;
Component[] childs = ((CollapsiblePanel)comp).getCollapsiblePane().getContentPane().getComponents();
for (int j = 0; j < childs.length; j++) {
if (childs[j].isVisible())
{
String columnName = childs[j].getName();
if (columnName != null && columnName.length() > 0)
{
GridField mField = m_mTab.getField(columnName);
if (mField != null)
{
hasVisible = true;
break;
}
}
}
}
if (comp.isVisible() != hasVisible)
comp.setVisible(hasVisible);
}
}
}
//
log.config(m_mTab.toString() + " - fini - "
+ (col <= 0 ? "complete" : "seletive"));
} // dynamicDisplay
/**
* Row Changed - synchronize with Tree
*
* @param save true the row was saved (changed/added), false if the row was deleted
* @param keyID the ID of the row changed
*/
public void rowChanged (boolean save, int keyID)
{
if (m_tree == null || keyID <= 0)
return;
String name = (String)m_mTab.getValue("Name");
String description = (String)m_mTab.getValue("Description");
Boolean IsSummary = (Boolean)m_mTab.getValue("IsSummary");
boolean summary = IsSummary != null && IsSummary.booleanValue();
String imageIndicator = (String)m_mTab.getValue("Action"); // Menu - Action
//
m_tree.nodeChanged(save, keyID, name, description,
summary, imageIndicator);
} // rowChanged
/**************************************************************************
* Save Multiple records - Clone a record and assign new values to each
* clone for a specific column.
* @param ctx context
* @param tableName Table Name
* @param columnName Column for which value need to be changed
* @param recordId Record to clone
* @param values Values to be assigned to clones for the specified column
* @param trxName Transaction
* @throws Exception If error is occured when loading the PO or saving clones
*
* @author ashley
*/
protected void saveMultipleRecords(Properties ctx, String tableName,
String columnName, int recordId, Integer[] values,
String trxName) throws Exception
{
if (values == null)
{
return ;
}
int oldRow = m_mTab.getCurrentRow();
GridField lineField = m_mTab.getField("Line");
for (int i = 0; i < values.length; i++)
{
if (!m_mTab.dataNew(true))
{
throw new IllegalStateException("Could not clone tab");
}
m_mTab.setValue(columnName, values[i]);
if (lineField != null)
{
m_mTab.setValue(lineField, 0);
}
if (!m_mTab.dataSave(false))
{
throw new IllegalStateException("Could not update tab");
}
m_mTab.setCurrentRow(oldRow);
}
}
/**************************************************************************
* Vetoable Change Listener.
* Called from VEditor
* <pre>
* - for Save Confirmation dialog
* - for Single Row from VEditor: Update MTable
* </pre>
* @param e event
* @throws PropertyVetoException
*/
public void vetoableChange(PropertyChangeEvent e) throws PropertyVetoException
{
if (m_mTab.isProcessed() || !m_mTab.isActive()) // only active records
{
Object source = e.getSource();
if (source instanceof VEditor)
{
if (!((VEditor)source).isReadWrite())
{
log.config("(" + m_mTab.toString() + ") " + e.getPropertyName());
return;
}
}
else
{
log.config("(" + m_mTab.toString() + ") " + e.getPropertyName());
return;
}
} // processed
log.config("(" + m_mTab.toString() + ") "
+ e.getPropertyName() + "=" + e.getNewValue() + " (" + e.getOldValue() + ") "
+ (e.getOldValue() == null ? "" : e.getOldValue().getClass().getName()));
// Save Confirmation dialog MTable-RowSave
if (e.getPropertyName().equals(GridTable.PROPERTY))
{
// throw new PropertyVetoException will call this listener again to revert to old value
if (m_vetoActive)
{
//ignore
m_vetoActive = false;
return;
}
if (!Env.isAutoCommit(Env.getCtx(), m_WindowNo) || m_mTab.getCommitWarning().length() > 0)
{
if (!ADialog.ask(m_WindowNo, this, "SaveChanges?", m_mTab.getCommitWarning()))
{
m_vetoActive = true;
throw new PropertyVetoException ("UserDeniedSave", e);
}
}
return;
} // saveConfirmation
// Get Row/Col Info
GridTable mTable = m_mTab.getTableModel();
int row = m_mTab.getCurrentRow();
int col = mTable.findColumn(e.getPropertyName());
//
if (e.getNewValue() == null && e.getOldValue() != null
&& e.getOldValue().toString().length() > 0) // some editors return "" instead of null
mTable.setChanged (true);
else
{
// mTable.setValueAt (e.getNewValue(), row, col, true);
/*
* Changes: Added the logic below to handle multiple values for a single field
* due to multiple selection in Lookup (Info).
* @author ashley
*/
Object newValue = e.getNewValue();
Integer newValues[] = null;
if (newValue instanceof Integer[])
{
newValues = ((Integer[])newValue);
newValue = newValues[0];
if (newValues.length > 1)
{
Integer valuesCopy[] = new Integer[newValues.length - 1];
System.arraycopy(newValues, 1, valuesCopy, 0, valuesCopy.length);
newValues = valuesCopy;
}
else
{
newValues = null;
}
}
else if (newValue instanceof Object[])
{
log.severe("Multiple values can only be processed for IDs (Integer)");
throw new PropertyVetoException("Multiple Selection values not available for this field", e);
}
mTable.setValueAt (newValue, row, col); // -> dataStatusChanged -> dynamicDisplay
// Force Callout
if (e.getPropertyName().equals("S_ResourceAssignment_ID"))
{
GridField mField = m_mTab.getField(col);
if (mField != null && mField.getCallout().length() > 0)
m_mTab.processFieldChange(mField); // Dependencies & Callout
}
if (newValues != null && newValues.length > 0)
{
// Save data, since record need to be used for generating clones.
if (!m_mTab.dataSave(false))
{
throw new PropertyVetoException("SaveError", e);
}
// Retrieve the current record ID
int recordId = m_mTab.getKeyID(m_mTab.getCurrentRow());
Trx trx = Trx.get(Trx.createTrxName(), true);
trx.start();
try
{
saveMultipleRecords(Env.getCtx(), mTable.getTableName(), e.getPropertyName(), recordId, newValues, trx.getTrxName());
trx.commit();
m_mTab.dataRefreshAll();
}
catch(Exception ex)
{
trx.rollback();
log.severe(ex.getMessage());
throw new PropertyVetoException("SaveError", e);
}
finally
{
trx.close();
}
}
}
// log.config( "GridController.vetoableChange (" + m_mTab.toString() + ") - fini", e.getPropertyName() + "=" + e.getNewValue());
} // vetoableChange
/**************************************************************************
* Get Model Tab
* @return Model Tab
*/
public GridTab getMTab()
{
return m_mTab;
} // getMTab
/**
* Get Display Logic
* @return Display Logic
*/
public String getDisplayLogic()
{
return m_mTab.getDisplayLogic();
} // getDisplayLogic
/**
* Get VTable
* @return VTable
*/
public VTable getTable()
{
return vTable;
} // getTable
/**
* Set Window level Mnemonics
* @param set true if set otherwise unregiser
*/
public void setMnemonics (boolean set)
{
if (vPanel != null)
vPanel.setMnemonics(set);
} // setMnemonics
/**
* Stop Table & SR Editors and move focus to graphPanel
* @param saveValue save value
*/
public void stopEditor (boolean saveValue)
{
log.config("(" + m_mTab.toString() + ") TableEditing=" + vTable.isEditing());
// MultiRow - remove editors
vTable.stopEditor(saveValue);
// SingleRow - stop editors by changing focus
if (m_singleRow)
vPanel.transferFocus();
// graphPanel.requestFocus();
//
// log.config( "GridController.stopEditor (" + m_mTab.toString() + ") - fini",
// "Editing=" + vTable.isEditing());
} // stopEditors
/**
* Mouse Clicked
* @param e event
*/
public void mouseClicked(MouseEvent e)
{
if (CLogMgt.isLevelFinest())
log.finest("" + this + " - " + e);
}
/**
* Mouse Pressed
* @param e event
*/
public void mousePressed(MouseEvent e)
{
if (CLogMgt.isLevelFinest())
log.finest("" + this + " - " + e);
}
/**
* Mouse Released
* @param e event
*/
public void mouseReleased(MouseEvent e)
{
if (CLogMgt.isLevelFinest())
log.finest("" + this + " - " + e);
}
/**
* Mouse Entered
* @param e event
*/
public void mouseEntered(MouseEvent e)
{
if (CLogMgt.isLevelFinest())
log.finest("" + this + " - " + e);
}
/**
* Mouse Exited
* @param e event
*/
public void mouseExited(MouseEvent e)
{
if (CLogMgt.isLevelFinest())
log.finest("" + this + " - " + e);
}
/**
* Get Variable Value
* @param variableName name
* @return value
*/
public String get_ValueAsString (String variableName)
{
return Env.getContext(Env.getCtx(), m_WindowNo, variableName);
} // get_ValueAsString
/**
* Is controller data not stale
* @return boolean
*/
public boolean isCurrent()
{
return m_mTab != null ? m_mTab.isCurrent() : false;
}
//FR [ 1757088 ]
public VPanel getvPanel()
{
return vPanel;
}
//BEGIN - [FR 1953734]
GridController m_Parent;
public void setGCParent(GridController gc){
m_Parent = gc;
}
public GridController getGCParent(){
return m_Parent;
}
public void refreshMTab(GridController includedTab){
int m_CurrentRowBeforeSave = includedTab.m_mTab.getCurrentRow();
m_mTab.dataRefresh(m_mTab.getCurrentRow());
includedTab.m_mTab.setCurrentRow(m_CurrentRowBeforeSave);
}
//END - [FR 1953734]
/**
* Accept pending editor changes.
*/
public void acceptEditorChanges()
{
if (isSingleRow())
{
Component c = KeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusOwner();
if (c != null && this.isAncestorOf(c))
{
Component t = c;
while (t != null && t != this)
{
if (t instanceof VManagedEditor)
{
((VManagedEditor)t).commitChanges();
return;
}
t = t.getParent();
}
}
}
}
} // GridController