/******************************************************************************
 * 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 ]                  *
 *****************************************************************************/
package org.compiere.apps;

import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Cursor;
import java.awt.Dimension;
import java.awt.Event;
import java.awt.FlowLayout;
import java.awt.Image;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.InputEvent;
import java.awt.event.KeyEvent;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Properties;
import java.util.TreeMap;
import java.util.Vector;
import java.util.logging.Level;

import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.BorderFactory;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JList;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTabbedPane;
import javax.swing.JTable;
import javax.swing.JToolBar;
import javax.swing.KeyStroke;
import javax.swing.ListSelectionModel;
import javax.swing.SwingUtilities;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;

import org.compiere.apps.form.FormFrame;
import org.compiere.apps.search.Find;
import org.compiere.grid.APanelTab;
import org.compiere.grid.GridController;
import org.compiere.grid.GridSynchronizer;
import org.compiere.grid.ICreateFrom;
import org.compiere.grid.RecordAccessDialog;
import org.compiere.grid.VCreateFromFactory;
import org.compiere.grid.VOnlyCurrentDays;
import org.compiere.grid.VPayment;
import org.compiere.grid.VSortTab;
import org.compiere.grid.VTabbedPane;
import org.compiere.grid.ed.VButton;
import org.compiere.grid.ed.VDocAction;
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.GridWindowVO;
import org.compiere.model.GridWorkbench;
import org.compiere.model.Lookup;
import org.compiere.model.MLookupFactory;
import org.compiere.model.MProcess;
import org.compiere.model.MQuery;
import org.compiere.model.MRole;
import org.compiere.model.MUser;
import org.compiere.model.MWindow;
import org.compiere.plaf.CompiereColor;
import org.compiere.print.AReport;
import org.compiere.process.DocAction;
import org.compiere.process.ProcessInfo;
import org.compiere.process.ProcessInfoUtil;
import org.compiere.swing.CPanel;
import org.compiere.util.ASyncProcess;
import org.compiere.util.CLogMgt;
import org.compiere.util.CLogger;
import org.compiere.util.DB;
import org.compiere.util.Env;
import org.compiere.util.Language;
import org.compiere.util.Msg;
import org.compiere.util.Util;

/**
 *	Main Panel of application window.
 *  <pre>
 *  Structure:
 *      (MenuBar) -> to be added to owning window
 *		northPanel  (ToolBar)
 *		tabPanel
 *		southPanel  (StatusBar)
 *  </pre>
 *
 * 	@author 	Jorg Janke
 * 	@version 	$Id: APanel.java,v 1.4 2006/07/30 00:51:27 jjanke Exp $
 *
 *  Colin Rooney 2007/03/20 RFE#1670185 & related BUG#1684142 - Extend Sec to Info Queries
 *  @contributor Victor Perez , e-Evolution.SC FR [ 1757088 ]
 *  @contributor fer_luck@centuryon.com , FR [ 1757088 ]
 *  @author Teo Sarca, SC ARHIPAC SERVICE SRL
 *				<li>BF [ 1824621 ] History button can't be canceled
 *				<li>BF [ 1941271 ] VTreePanel is modifying even if is save wasn't successfull
 *				<li>FR [ 1943731 ] Window data export functionality
 *				<li>FR [ 1974354 ] VCreateFrom.create should be more flexible
 * 				<li>BF [ 1996056 ] Report error message is not displayed
 * 				<li>BF [ 1998575 ] Document Print is discarding any error
 *  @author Teo Sarca, teo.sarca@gmail.com
 *  			<li>BF [ 2876892 ] Save included tab before calling button action
 *  				https://sourceforge.net/tracker/?func=detail&aid=2876892&group_id=176962&atid=879332
 *  @author victor.perez@e-evolution.com
 *  @see FR [ 1966328 ] New Window Info to MRP and CRP into View http://sourceforge.net/tracker/index.php?func=detail&aid=1966328&group_id=176962&atid=879335
 *  @autor tobi42, metas GmBH
 *  			<li>BF [ 2799362 ] You can press New button a lot of times
 *  @author Cristina Ghita, www.arhipac.ro
 *  @see FR [ 2877111 ] See identifiers columns when delete records https://sourceforge.net/tracker/?func=detail&atid=879335&aid=2877111&group_id=176962
 *
 * 	@author hengsin, hengsin.low@idalica.com
 *  @see FR [2887701] https://sourceforge.net/tracker/?func=detail&atid=879335&aid=2887701&group_id=176962
 *  @sponsor www.metas.de
 */
public final class APanel extends CPanel
	implements DataStatusListener, ChangeListener, ActionListener, ASyncProcess
{
	/**
	 *
	 */
	private static final long serialVersionUID = 6066778919781303581L;

	private boolean isNested = false;

	/**
	 * Constructs a new instance.
	 * Need to call initPanel for dynamic initialization
	 */
	//FR [ 1757088 ]
	public APanel(GridController gc, int windowNo){
		super();
		isNested = true;
		m_ctx = Env.getCtx();
		try{
			m_curGC = gc;
			gc.addDataStatusListener(this);
			m_curTab = gc.getMTab();

			Component tabElement = null;
			tabElement = gc;
			VTabbedPane tabPane = new VTabbedPane(false);
			tabPane.addTab(m_curTab.getName().toString(), m_curTab, tabElement);
			m_curWinTab = tabPane;
			m_curWindowNo = windowNo;
			jbInit();
			initSwitchLineAction();
		}
		catch(Exception e){
			log.log(Level.SEVERE, "", e);
		}

		createMenu();

		MRole role = MRole.getDefault();
		m_curGC.query (m_onlyCurrentRows, m_onlyCurrentDays, role.getMaxQueryRecords());
		m_curTab.navigateCurrent();     //  updates counter
		m_curGC.dynamicDisplay(0);
	}

	public APanel(AWindow window)
	{
		super();
		m_window = window;

		m_ctx = Env.getCtx();
		//
		try
		{
			jbInit();
		}
		catch (Exception e)
		{
			log.log(Level.SEVERE, "", e);
		}
		createMenu();
	}	//	APanel

	/**	Logger			*/
	private static CLogger log = CLogger.getCLogger(APanel.class);

	private AWindow m_window;
	private boolean isCancel = false; //Goodwill

	/**
	 *	Dispose
	 */
	public void dispose()
	{
	//	log.config("");
		//  ignore changes
		m_disposing = true;
		//
		if (m_curAPanelTab != null)
		{
			m_curAPanelTab.unregisterPanel();
			m_curAPanelTab = null;
		}
		//  close panels
		tabPanel.dispose(this);
		tabPanel = null;
		//  All Workbenches
		for (int i = 0; i < m_mWorkbench.getWindowCount(); i++)
		{
			m_curWindowNo = m_mWorkbench.getWindowNo(i);
			log.info("#" + m_curWindowNo);
			Env.setAutoCommit(m_ctx, m_curWindowNo, false);
			m_mWorkbench.dispose(i);
			Env.clearWinContext(m_ctx, m_curWindowNo);
		}   //  all Workbenchens

		//  Get rid of remaining model
		if (m_mWorkbench != null)
			m_mWorkbench.dispose();
		m_mWorkbench = null;
		//  MenuBar
		if (menuBar != null)
			menuBar.removeAll();
		menuBar = null;
		//  ToolBar
		if (toolBar != null)
			toolBar.removeAll();
		toolBar = null;
		//  Prepare GC
		this.removeAll();
	}	//	dispose

	/**
	 * The Layout.
	 */
	private BorderLayout mainLayout = new BorderLayout();
	private VTabbedPane tabPanel = new VTabbedPane(true);
	private StatusBar statusBar = new StatusBar();
	private CPanel northPanel = new CPanel();
	private JToolBar toolBar = new JToolBar();
	private JMenuBar menuBar = new JMenuBar();
	private FlowLayout northLayout = new FlowLayout();

	/**
	 * Initializes the state of this instance.
	 * @throws Exception
	 */
	private void jbInit() throws Exception
	{
		this.setLocale(Language.getLoginLanguage().getLocale());
		this.setLayout(mainLayout);

		//	tabPanel
		mainLayout.setHgap(2);
		mainLayout.setVgap(2);
		if (isNested)
			this.add(m_curGC, BorderLayout.CENTER);
		else
		{
			CPanel dummy = new CPanel();
			dummy.setLayout(new BorderLayout());
			dummy.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 2));
			dummy.add(tabPanel, BorderLayout.CENTER);
			this.add(dummy, BorderLayout.CENTER);
		}
		//	southPanel
		this.add(statusBar, BorderLayout.SOUTH);
		//	northPanel
		this.add(northPanel, BorderLayout.NORTH);
		northPanel.setLayout(northLayout);
		northLayout.setAlignment(FlowLayout.LEFT);
		toolBar.putClientProperty("JToolBar.isRollover", Boolean.TRUE);
		toolBar.setBorderPainted(false);
		toolBar.setFloatable(false); // teo_sarca, [ 1666591 ] Toolbar should not be floatable
		northPanel.add(toolBar, null);
	}	//	jbInit

	private AppsAction 		aPrevious, aNext, aParent, aDetail, aFirst, aLast,
							aNew, aCopy, aDelete, aPrint, aPrintPreview,
							aExport = null,
							aRefresh, aHistory, aAttachment, aChat, aMulti, aFind,
							aWorkflow, aZoomAcross, aRequest, aWinSize, aArchive;
	/** Ignore Button		*/
	public AppsAction		aIgnore;
	/** Save Button			*/
	public AppsAction		aSave;
	/** Private Lock Button	*/
	public AppsAction		aLock;
	//	Local (added to toolbar)
	private AppsAction	    aReport, aEnd, aHome, aHelp, aProduct, aLogout,
							aAccount, aCalculator, aCalendar, aEditor, aPreference, aScript,
							aOnline, aMailSupport, aAbout, aPrintScr, aScrShot, aExit, aBPartner,
							aDeleteSelection, aShowAllWindow;

	private SwitchAction aSwitchLinesDownAction, aSwitchLinesUpAction;

	private WindowMenu m_WindowMenu;
	/**************************************************************************
	 *	Create Menu and Toolbar and registers keyboard actions.
	 *  - started from constructor
	 */
	private void createMenu()
	{
		/**
		 *	Menu
		 */
	//	menuBar.setHelpMenu();
		//								File
		JMenu mFile = AEnv.getMenu("File");
		menuBar.add(mFile);
		aPrintScr =	addAction("PrintScreen",	mFile,	KeyStroke.getKeyStroke(KeyEvent.VK_PRINTSCREEN, 0), 	false);
		aScrShot =	addAction("ScreenShot",		mFile,	KeyStroke.getKeyStroke(KeyEvent.VK_PRINTSCREEN, Event.SHIFT_MASK), 	false);
		aReport = 	addAction("Report",			mFile, 	KeyStroke.getKeyStroke(KeyEvent.VK_F11, 0),	false);
		aPrint = 	addAction("Print",			mFile, 	KeyStroke.getKeyStroke(KeyEvent.VK_F12, 0),	false);
		aPrintPreview = addAction("PrintPreview",	mFile, KeyStroke.getKeyStroke(KeyEvent.VK_P, Event.SHIFT_MASK+Event.ALT_MASK), false);
		if (MRole.getDefault().isCanExport())
		{
			aExport = addAction("Export", mFile, null, false);
		}
		mFile.addSeparator();
		aEnd =	 	addAction("End",			mFile, 	KeyStroke.getKeyStroke(KeyEvent.VK_X, Event.ALT_MASK),	false);
		aLogout = 	addAction("Logout", 		mFile, 	KeyStroke.getKeyStroke(KeyEvent.VK_L, Event.SHIFT_MASK+Event.ALT_MASK), false);
		aExit =		addAction("Exit",			mFile, 	KeyStroke.getKeyStroke(KeyEvent.VK_X, Event.SHIFT_MASK+Event.ALT_MASK),	false);
		//								Edit
		JMenu mEdit = AEnv.getMenu("Edit");
		menuBar.add(mEdit);
		aNew = 		addAction("New", 			mEdit, 	KeyStroke.getKeyStroke(KeyEvent.VK_F2, 0), false);
		aSave = 	addAction("Save",			mEdit, 	KeyStroke.getKeyStroke(KeyEvent.VK_F4, 0),	false);
		mEdit.addSeparator();
		aCopy =		addAction("Copy", 			mEdit, 	KeyStroke.getKeyStroke(KeyEvent.VK_F2, Event.SHIFT_MASK),	false);
		aDelete = 	addAction("Delete",			mEdit, 	KeyStroke.getKeyStroke(KeyEvent.VK_F3, 0),	false);
		aDeleteSelection = addAction("DeleteSelection", mEdit, KeyStroke.getKeyStroke(KeyEvent.VK_D, Event.CTRL_MASK), false);
		aIgnore = 	addAction("Ignore",			mEdit, 	KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0),	false);
		aRefresh = 	addAction("Refresh",		mEdit, 	KeyStroke.getKeyStroke(KeyEvent.VK_F5, 0),	false);
		mEdit.addSeparator();
		aFind = 	addAction("Find",			mEdit, 	KeyStroke.getKeyStroke(KeyEvent.VK_F6, 0), true);	//	toggle
		if (m_isPersonalLock)
			aLock = addAction("Lock",			mEdit, 	null,	true);		//	toggle
		//								View
		JMenu mView = AEnv.getMenu("View");
		menuBar.add(mView);
		if (MRole.getDefault().isAllow_Info_Product())
		{
			aProduct =	addAction("InfoProduct",	mView, 	KeyStroke.getKeyStroke(KeyEvent.VK_I, Event.ALT_MASK),	false);
		}
		if (MRole.getDefault().isAllow_Info_BPartner())
		{
			aBPartner =	addAction("InfoBPartner",	mView, 	KeyStroke.getKeyStroke(KeyEvent.VK_I, Event.SHIFT_MASK+Event.ALT_MASK),	false);
		}
		if (MRole.getDefault().isShowAcct() && MRole.getDefault().isAllow_Info_Account())
		{
			aAccount =  addAction("InfoAccount",mView, 	KeyStroke.getKeyStroke(KeyEvent.VK_I, Event.ALT_MASK+Event.CTRL_MASK),	false);
		}
		if (MRole.getDefault().isAllow_Info_Schedule())
		{
			AEnv.addMenuItem("InfoSchedule", null, null, mView, this);
		}
		//FR [ 1966328 ]
		if (MRole.getDefault().isAllow_Info_MRP())
		{
			AEnv.addMenuItem("InfoMRP", "Info", null, mView, this);
		}
		if (MRole.getDefault().isAllow_Info_CRP())
		{
			AEnv.addMenuItem("InfoCRP", "Info", null, mView, this);
		}
		mView.addSeparator();
		if (MRole.getDefault().isAllow_Info_Order())
		{
			AEnv.addMenuItem("InfoOrder", "Info", null, mView, this);
		}
		if (MRole.getDefault().isAllow_Info_Invoice())
		{
			AEnv.addMenuItem("InfoInvoice", "Info", null, mView, this);
		}
		if (MRole.getDefault().isAllow_Info_InOut())
		{
			AEnv.addMenuItem("InfoInOut", "Info", null, mView, this);
		}
		if (MRole.getDefault().isAllow_Info_Payment())
		{
			AEnv.addMenuItem("InfoPayment", "Info", null, mView, this);
		}
		if (MRole.getDefault().isAllow_Info_CashJournal())
		{
			AEnv.addMenuItem("InfoCashLine", "Info", null, mView, this);
		}
		if (MRole.getDefault().isAllow_Info_Resource())
		{
			AEnv.addMenuItem("InfoAssignment", "Info", null, mView, this);
		}
		if (MRole.getDefault().isAllow_Info_Asset())
		{
			AEnv.addMenuItem("InfoAsset", "Info", null, mView, this);
		}



		mView.addSeparator();
		aAttachment = addAction("Attachment",	mView, 	KeyStroke.getKeyStroke(KeyEvent.VK_F7, 0),	true);		//	toggle
		aChat = addAction("Chat",				mView, 	null,	true);		//	toggle
		aHistory = 	addAction("History",		mView, 	KeyStroke.getKeyStroke(KeyEvent.VK_F9, 0),	true);		//	toggle
		mView.addSeparator();
		aMulti =	addAction("Multi",			mView, 	KeyStroke.getKeyStroke(KeyEvent.VK_F8, 0),	true);		//	toggle
		//								Go
		JMenu mGo = AEnv.getMenu("Go");
		menuBar.add(mGo);
		aFirst =	addAction("First", 			mGo, 	KeyStroke.getKeyStroke(KeyEvent.VK_PAGE_UP, Event.ALT_MASK),	false);
		aPrevious = addAction("Previous", 		mGo, 	KeyStroke.getKeyStroke(KeyEvent.VK_UP, Event.ALT_MASK),	false);
		aNext = 	addAction("Next", 			mGo, 	KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, Event.ALT_MASK),	false);
		aLast =		addAction("Last",	 		mGo, 	KeyStroke.getKeyStroke(KeyEvent.VK_PAGE_DOWN, Event.ALT_MASK),	false);
		mGo.addSeparator();
		aParent =	addAction("Parent", 		mGo, 	KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, Event.ALT_MASK),	false);
		aDetail =	addAction("Detail", 		mGo,	KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, Event.ALT_MASK),	false);
		mGo.addSeparator();
		aZoomAcross = addAction("ZoomAcross",	mGo, 	null,	false);
		aRequest =  addAction("Request",		mGo, 	null,	false);
		aArchive =  addAction("Archive",		mGo, 	null,	false);
		aHome =		addAction("Home", 			mGo,	null,	false);
		//								Tools
		JMenu mTools = AEnv.getMenu("Tools");
		menuBar.add(mTools);
		aCalculator = addAction("Calculator",	mTools, 	null,	false);
		aCalendar = addAction("Calendar",		mTools, 	null,	false);
		aEditor =	addAction("Editor",			mTools, 	null,	false);
		MUser user = MUser.get(Env.getCtx());
		if (user.isAdministrator())
			aScript = addAction("Script",	        mTools, 	null,	false);
		if ("Y".equals(Env.getContext(m_ctx, "#SysAdmin")))	//	set in DB.loginDB
			aWinSize = addAction("WinSize",     mTools, 	null,	false);
		if (AEnv.isWorkflowProcess())
			aWorkflow = addAction("WorkFlow",	mTools,		null,	false);
		if (MRole.getDefault().isShowPreference())
		{
			mTools.addSeparator();
			aPreference = addAction("Preference",	mTools, 	null,	false);
		}

		//Window
		AMenu aMenu = (AMenu)Env.getWindow(0);
		m_WindowMenu = new WindowMenu(aMenu.getWindowManager(), m_window);
		menuBar.add(m_WindowMenu);
		aShowAllWindow = addAction("ShowAllWindow", null, KeyStroke.getKeyStroke(KeyEvent.VK_W, KeyEvent.CTRL_MASK),	false);

		//								Help
		JMenu mHelp = AEnv.getMenu("Help");
		menuBar.add(mHelp);
		aHelp = 	addAction("Help",			mHelp, 	KeyStroke.getKeyStroke(KeyEvent.VK_F1, 0),	false);
		aOnline =	addAction("Online",			mHelp, 	null,	false);
		aMailSupport = addAction("EMailSupport",	mHelp,	null,	false);
		aAbout = 	addAction("About",			mHelp, 	null,	false);

		/**
		 *	ToolBar
		 */
		toolBar.add(aIgnore.getButton());		//	ESC
		toolBar.addSeparator();
		toolBar.add(aHelp.getButton());			//	F1
		toolBar.add(aNew.getButton());
		toolBar.add(aCopy.getButton());
		toolBar.add(aDelete.getButton());
		toolBar.add(aDeleteSelection.getButton());
		toolBar.add(aSave.getButton());
		toolBar.addSeparator();
		toolBar.add(aRefresh.getButton());      //  F5
		toolBar.add(aFind.getButton());
		toolBar.add(aAttachment.getButton());
		toolBar.add(aChat.getButton());
		toolBar.add(aMulti.getButton());
		toolBar.addSeparator();
		//FR [ 1757088 ]
		if((m_curGC == null) || (m_curGC != null && !m_curGC.isDetailGrid())){
			toolBar.add(aHistory.getButton());		//	F9
			toolBar.add(aHome.getButton()); //	F10 is Windows Menu Key
			toolBar.add(aParent.getButton());
			toolBar.add(aDetail.getButton());
			toolBar.addSeparator();
		}

		toolBar.add(aFirst.getButton());
		toolBar.add(aPrevious.getButton());
		toolBar.add(aNext.getButton());
		toolBar.add(aLast.getButton());
		toolBar.addSeparator();
		toolBar.add(aReport.getButton());
		toolBar.add(aArchive.getButton());
		toolBar.add(aPrintPreview.getButton());
		toolBar.add(aPrint.getButton());
		// FR [ 1757088 ]
		if((m_curGC == null) || (m_curGC != null && !m_curGC.isDetailGrid())){
			toolBar.addSeparator();
			if (m_isPersonalLock)
				toolBar.add(aLock.getButton());
			toolBar.add(aZoomAcross.getButton());
			if (aWorkflow != null)
				toolBar.add(aWorkflow.getButton());
			toolBar.add(aRequest.getButton());
			if (MRole.getDefault().isAllow_Info_Product())
			{
				toolBar.add(aProduct.getButton());
			}
			toolBar.addSeparator();
			toolBar.add(aEnd.getButton());
		}

		//
		if (CLogMgt.isLevelAll())
			Util.printActionInputMap(this);
	}	//	createMenu


	/**
	 *	Add (Toggle) Action to Toolbar and Menu
	 *  @param actionName action name
	 *  @param menu manu
	 *  @param accelerator accelerator
	 *  @param toggle toggle button
	 *  @return AppsAction
	 */
	private AppsAction addAction (String actionName, JMenu menu, KeyStroke accelerator, boolean toggle)
	{
		AppsAction action = new AppsAction(actionName, accelerator, toggle);
		if (menu != null)
			menu.add(action.getMenuItem());
		action.setDelegate(this);
	//	AbstractButton b = action.getButton();
	//	String s = null;
	//	if (b != null)
	//		s = b.getToolTipText();

		//	Key Strokes
		if (accelerator != null)
		{
			getInputMap(WHEN_IN_FOCUSED_WINDOW).put(accelerator, actionName);
			getActionMap().put(actionName, action);
		}
		//
		return action;
	}	//	addAction

	/**
	 *	Return MenuBar
	 *  @return JMenuBar
	 */
	public JMenuBar getMenuBar()
	{
		return menuBar;
	}	//	getMenuBar

	/**
	 *	Get Title of Window
	 *  @return String with Title
	 */
	public String getTitle()
	{
		if (m_mWorkbench != null && m_mWorkbench.getWindowCount() > 1)
		{
			StringBuffer sb = new StringBuffer();
			sb.append(m_mWorkbench.getName()).append("  ")
				.append(Env.getContext(m_ctx, "#AD_User_Name")).append("@")
				.append(Env.getContext(m_ctx, "#AD_Client_Name")).append(".")
				.append(Env.getContext(m_ctx, "#AD_Org_Name")).append(" [")
				.append(Env.getContext(m_ctx, "#DB_UID")).append("]");
			return sb.toString();
		}
		return Env.getHeader(m_ctx, m_curWindowNo);
	}	//	getTitle


	private Properties      m_ctx;

	/** Workbench Model                                 */
	private GridWorkbench	m_mWorkbench;
	/** Current MTab                                    */
	private GridTab			m_curTab;
	/** Current GridController                          */
	private GridController  m_curGC;
	/** Current Window Panel                            */
	private JTabbedPane     m_curWinTab = null;
	/** Current Window No                               */
	private int				m_curWindowNo;
	/** Current Window Panel Index                      */
	private int				m_curTabIndex = -1;
	/**	Current Tab Order								*/
	private APanelTab		m_curAPanelTab = null;

	/** Dispose active                                  */
	private boolean         m_disposing = false;
	/** Save Error Message indicator                    */
	private boolean         m_errorDisplayed = false;
	/** Only current row flag                           */
	private boolean			m_onlyCurrentRows = true;
	/** Number of days to show	0=all					*/
	private int				m_onlyCurrentDays = 0;
	/** Process Info                                    */
	private boolean         m_isLocked = false;
	/** Show Personal Lock								*/
	private boolean 		m_isPersonalLock = MRole.getDefault().isPersonalLock();
	/**	Last Modifier of Action Event					*/
	private int 			m_lastModifiers;

	private HashMap<Integer, GridController> includedMap;


	/**************************************************************************
	 *	Dynamic Panel Initialization - either single window or workbench.
	 *  <pre>
	 *  either
	 *  - Workbench tabPanel    (VTabbedPane)
	 *      - Tab               (GridController)
	 *  or
	 *  - Workbench tabPanel    (VTabbedPane)
	 *      - Window            (VTabbedPane)
	 *          - Tab           (GridController)
	 *  </pre>
	 *  tabPanel
	 *  @param AD_Workbench_ID  if > 0 this is a workbench, AD_Window_ID ignored
	 *  @param AD_Window_ID     if not a workbench, Window ID
	 *  @param query			if not a Workbench, Zoom Query - additional SQL where clause
	 *  @return true if Panel is initialized successfully
	 */
	public boolean initPanel (int AD_Workbench_ID, int AD_Window_ID, MQuery query)
	{
		log.info("WB=" + AD_Workbench_ID + ", Win=" + AD_Window_ID + ", Query=" + query);
		this.setName("APanel" + AD_Window_ID);

		//  Single Window
		if (AD_Workbench_ID == 0)
			m_mWorkbench = new GridWorkbench(m_ctx, AD_Window_ID);
		else
		//  Workbench
		{
		//	m_mWorkbench = new MWorkbench(m_ctx);
		//	if (!m_mWorkbench.initWorkbench (AD_Workbench_ID))
		//	{
		//		log.log(Level.SEVERE, "APanel.initWindow - No Workbench Model");
		//		return false;
		//	}
		//	tabPanel.setWorkbench(true);
		//	tabPanel.addChangeListener(this);
			ADialog.warn(0, this, "","Not implemented yet");
			return false;
		}

		Dimension windowSize = m_mWorkbench.getWindowSize();

		MQuery detailQuery = null;
		/**
		 *  WorkBench Loop
		 */
		for (int wb = 0; wb < m_mWorkbench.getWindowCount(); wb++)
		{
			//  Get/set WindowNo
			m_curWindowNo = Env.createWindowNo (this);			                //  Timing: ca. 1.5 sec
			m_mWorkbench.setWindowNo(wb, m_curWindowNo);
			//  Set AutoCommit for this Window
			Env.setAutoCommit(m_ctx, m_curWindowNo, Env.isAutoCommit(m_ctx));
			boolean autoNew = Env.isAutoNew(m_ctx);
			Env.setAutoNew(m_ctx, m_curWindowNo, autoNew);

			//  Workbench Window
			VTabbedPane window = null;
			//  just one window
			if (m_mWorkbench.getWindowCount() == 1)
			{
				window = tabPanel;
				window.setWorkbench(false);
			}
			else
			{
				VTabbedPane tp = new VTabbedPane(false);
				window = tp;
			}
			//  Window Init
			window.addChangeListener(this);

			/**
			 *  Init Model
			 */
			int wbType = m_mWorkbench.getWindowType(wb);

			/**
			 *  Window
			 */
			if (wbType == GridWorkbench.TYPE_WINDOW)
			{
				includedMap = new HashMap<Integer,GridController>(4);
				//
				GridWindowVO wVO = AEnv.getMWindowVO(m_curWindowNo, m_mWorkbench.getWindowID(wb), 0);
				if (wVO == null)
				{
					ADialog.error(0, null, "AccessTableNoView", "(No Window Model Info)");
					return false;
				}
				GridWindow mWindow = new GridWindow (wVO, true);			                //  Timing: ca. 0.3-1 sec
				//	Set SO/AutoNew for Window
				Env.setContext(m_ctx, m_curWindowNo, "IsSOTrx", mWindow.isSOTrx());
				if (!autoNew && mWindow.isTransaction())
					Env.setAutoNew(m_ctx, m_curWindowNo, true);
				m_mWorkbench.setMWindow(wb, mWindow);
				if (wb == 0)
					m_onlyCurrentRows = mWindow.isTransaction();	//	default = only current
				if (windowSize == null)
					windowSize = mWindow.getWindowSize();

				/**
				 *  Window Tabs
				 */
				int tabSize = mWindow.getTabCount();
				boolean goSingleRow = query != null;	//	Zoom Query
				for (int tab = 0; tab < tabSize; tab++)
				{
					boolean included = false;
					//  MTab
					if (tab == 0) mWindow.initTab(0);
					GridTab gTab = mWindow.getTab(tab);
					Env.setContext(m_ctx, m_curWindowNo, tab, GridTab.CTX_TabLevel, Integer.toString(gTab.getTabLevel()));
					//  Query first tab
					if (tab == 0)
					{
						//  initial user query for single workbench tab
						if (m_mWorkbench.getWindowCount() == 1)
						{
							if (query != null && query.getZoomTableName() != null && query.getZoomColumnName() != null
								&& query.getZoomValue() instanceof Integer && (Integer)query.getZoomValue() > 0)
					    	{
					    		if (!query.getZoomTableName().equalsIgnoreCase(gTab.getTableName()))
					    		{
					    			detailQuery = query;
					    			query = new MQuery();
					    			query.addRestriction("1=2");
					    		}
					    	}
							isCancel = false; //Goodwill
							query = initialQuery (query, gTab);
							if (isCancel) return false; //Cancel opening window
							if (query != null && query.getRecordCount() <= 1)
								goSingleRow = true;
						}
						else if (wb != 0)
						//  workbench dynamic query for dependent windows
						{
							query = m_mWorkbench.getQuery();
						}
						//	Set initial Query on first tab
						if (query != null)
						{
							m_onlyCurrentRows = false;  //  Query might involve history
							gTab.setQuery(query);
						}
						if (wb == 0)
							m_curTab = gTab;
					}	//	query on first tab

					Component tabElement = null;
					//  GridController
					if (gTab.isSortTab())
					{
						VSortTab st = new VSortTab(m_curWindowNo, gTab.getAD_Table_ID(),
							gTab.getAD_ColumnSortOrder_ID(), gTab.getAD_ColumnSortYesNo_ID());
						st.setTabLevel(gTab.getTabLevel());
						tabElement = st;
					}
					else	//	normal tab
					{
						GridController gc = new GridController();			        //  Timing: ca. .1 sec
						CompiereColor cc = mWindow.getColor();
						if (cc != null)
							gc.setBackgroundColor(cc);                  //  set color on Window level
						gc.initGrid(gTab, false, m_curWindowNo, this, mWindow, (tab != 0));  //  will set color on Tab level
																		//  Timing: ca. 6-7 sec for first .2 for next
						gc.addDataStatusListener(this);
						gc.registerESCAction(aIgnore);      //  register Escape Key
						//	Set First Tab
						if (wb == 0 && tab == 0)
						{
							m_curGC = gc;
							Dimension size = gc.getPreferredSize();     //  Screen Sizing
							size.width += 4;
							size.height += 4;
							gc.setPreferredSize(size);
						}
						tabElement = gc;
						//	If we have a zoom query, switch to single row
						if (tab == 0 && goSingleRow)
							gc.switchSingleRow();

                        // FR [ 1757088 ]
						GridField[] fields = gc.getMTab().getFields();
						int m_tab_id = 0;
						for(int f =0 ; f < fields.length ; f ++)
						{
							m_tab_id = fields[f].getIncluded_Tab_ID();
							if ( m_tab_id != 0)
							{
								includedMap.put(m_tab_id, gc);
							}
						}

						//	Is this tab included?
						if (includedMap.size() > 0)
						{
							GridController parent = (GridController)includedMap.get(new Integer(gTab.getAD_Tab_ID()));
							if (parent != null)
							{
								// FR [ 1757088 ]
								gc.removeDataStatusListener(this);
								GridSynchronizer synchronizer = new GridSynchronizer(mWindow, parent, gc);
								if (parent == m_curGC)
									synchronizer.activateChild();
								included = parent.includeTab(gc,this,synchronizer);
							}
						}
						initSwitchLineAction();
					}	//	normal tab

					if (!included)	//  Add to TabbedPane
					{
						StringBuffer tabName = new StringBuffer ();
						tabName.append ("<html>");
						if (gTab.isReadOnly())
							tabName.append("<i>");
						int pos = gTab.getName ().indexOf (" ");
						if (pos == -1)
							tabName.append (gTab.getName ()).append ("<br>&nbsp;");
						else
						{
							tabName.append (gTab.getName().substring (0, pos))
							  .append ("<br>")
							  .append (gTab.getName().substring(pos + 1));
						}
						if (gTab.isReadOnly())
							tabName.append("</i>");
						tabName.append ("</html>");
						//	Add Tab - sets ALT-<number> and Shift-ALT-<x>
						window.addTab (tabName.toString(), gTab, tabElement);
					}
				}   //  Tab Loop
			//  Tab background
			//	window.setBackgroundColor(new AdempiereColor(Color.magenta, Color.green));
			}   //  Type-MWindow

			//  Single Workbench Window Tab
			if (m_mWorkbench.getWindowCount() == 1)
			{
				window.setToolTipText(m_mWorkbench.getDescription(wb));
			}
			else
			//  Add Workbench Window Tab
			{
				tabPanel.addTab(m_mWorkbench.getName(wb), m_mWorkbench.getIcon(wb), window, m_mWorkbench.getDescription(wb));
			}
			//  Used for Env.getHeader
			Env.setContext(m_ctx, m_curWindowNo, "WindowName", m_mWorkbench.getName(wb));

		}   //  Workbench Loop

		//  stateChanged (<->) triggered
		toolBar.setName(getTitle());
		m_curTab.getTableModel().setChanged(false);
		//	Set Detail Button
		aDetail.setEnabled(0 != m_curWinTab.getTabCount()-1);

		//	Enable/Disable Tabs dynamically
		if (m_curWinTab instanceof VTabbedPane)
			((VTabbedPane)m_curWinTab).evaluate(null);
		//	Size
		if (windowSize != null)
			setPreferredSize(windowSize);
		else
			revalidate();

		if (detailQuery != null && zoomToDetailTab(detailQuery)) {
			return true;
		}

		Dimension size = getPreferredSize();
		log.info( "fini - " + size);
		m_curWinTab.requestFocusInWindow();
		return true;
	}	//	initPanel

	private boolean zoomToDetailTab(MQuery query) {
		if (query != null && query.getZoomTableName() != null && query.getZoomColumnName() != null)
    	{
    		GridTab gTab = m_mWorkbench.getMWindow(0).getTab(0);
    		if (!query.getZoomTableName().equalsIgnoreCase(gTab.getTableName()))
    		{
    			int tabSize = m_mWorkbench.getMWindow(0).getTabCount();

    	        for (int tab = 0; tab < tabSize; tab++)
    	        {
    	        	gTab = m_mWorkbench.getMWindow(0).getTab(tab);
    	        	if (gTab.isSortTab())
    	        		continue;

    	        	if (gTab.getTableName().equalsIgnoreCase(query.getZoomTableName()))
    	        	{
	        			if (doZoomToDetail(gTab, query, tab)) {
	        				return true;
	        			}
    	        	}
    	        }
    		}
    	}
        return false;
	}

	private boolean doZoomToDetail(GridTab gTab, MQuery query, int tabIndex) {
		GridField[] fields = gTab.getFields();
		for (GridField field : fields)
		{
			if (field.getColumnName().equalsIgnoreCase(query.getZoomColumnName()))
			{
				m_mWorkbench.getMWindow(0).initTab(tabIndex);
				int parentId = DB.getSQLValue(null, "SELECT " + gTab.getLinkColumnName() + " FROM " + gTab.getTableName() + " WHERE " + query.getWhereClause());
				if (parentId > 0)
				{
					Map<Integer, Object[]>parentMap = new TreeMap<Integer, Object[]>();
					int index = tabIndex;
					int oldpid = parentId;
					GridTab currentTab = gTab;
					while (index > 0)
					{
						index--;
						GridTab pTab = m_mWorkbench.getMWindow(0).getTab(index);
						if (pTab.getTabLevel() < currentTab.getTabLevel())
						{
							m_mWorkbench.getMWindow(0).initTab(index);
							if (index > 0)
							{
								if (pTab.getLinkColumnName() != null && pTab.getLinkColumnName().trim().length() > 0)
								{
									int pid = DB.getSQLValue(null, "SELECT " + pTab.getLinkColumnName() + " FROM " + pTab.getTableName() + " WHERE " + currentTab.getLinkColumnName() + " = ?", oldpid);
									if (pid > 0)
									{
										parentMap.put(index, new Object[]{currentTab.getLinkColumnName(), oldpid});
										oldpid = pid;
										currentTab = pTab;
									}
									else
									{
										parentMap.clear();
										break;
									}
								}
							}
							else
							{
								parentMap.put(index, new Object[]{currentTab.getLinkColumnName(), oldpid});
							}
						}
					}
					for(Map.Entry<Integer, Object[]> entry : parentMap.entrySet())
					{
						GridTab pTab = m_mWorkbench.getMWindow(0).getTab(entry.getKey());
						Object[] value = entry.getValue();
						MQuery pquery = new MQuery(pTab.getAD_Table_ID());
						pquery.addRestriction((String)value[0], "=", value[1]);
						pTab.setQuery(pquery);
						GridController gc = (GridController) tabPanel.getComponentAt(entry.getKey());
						gc.activate();
						gc.query(false, 0, 0);
					}


					MQuery targetQuery = new MQuery(gTab.getAD_Table_ID());
					targetQuery.addRestriction(gTab.getLinkColumnName(), "=", parentId);
					gTab.setQuery(targetQuery);
					GridController gc = null;
					if (!includedMap.containsKey(gTab.getAD_Tab_ID()))
					{
						int target = tabPanel.findTabindex(gTab);
						gc = (GridController) tabPanel.getComponentAt(target);
					}
					else
					{
						GridController parent = includedMap.get(gTab.getAD_Tab_ID());
						gc = parent.findChild(gTab);
					}
					gc.activate();
					gc.query(false, 0, 0);

					GridTable table = gTab.getTableModel();
    				int count = table.getRowCount();
    				for(int i = 0; i < count; i++)
    				{
    					int id = table.getKeyID(i);
    					if (id == ((Integer)query.getZoomValue()).intValue())
    					{
    						if (!includedMap.containsKey(gTab.getAD_Tab_ID()))
    						{
    							tabPanel.setSelectedIndex(tabPanel.findTabindex(gTab));
    						}
    						else
    						{
    							GridController parent = includedMap.get(gTab.getAD_Tab_ID());
    							int pindex = tabPanel.findTabindex(parent.getMTab());
    							if (pindex >= 0)
    								tabPanel.setSelectedIndex(pindex);
    						}
    						gTab.navigate(i);
    						return true;
    					}
    				}
				}
			}
		}
		return false;
	}

	/**
	 * 	Get Current Window No
	 *	@return win no
	 */
	public int getWindowNo()
	{
		return m_curWindowNo;
	}	//	getWindowNo

	/**
	 * 	Initial Query
	 *	@param query initial query
	 *	@param mTab tab
	 *	@return query or null
	 */
	private MQuery initialQuery (MQuery query, GridTab mTab)
	{
		MRole role = MRole.getDefault(m_ctx, false);
		//	We have a (Zoom) query
		if (query != null && query.isActive() && !role.isQueryMax(query.getRecordCount()))
			return query;
		//
		StringBuffer where = new StringBuffer(Env.parseContext(m_ctx, m_curWindowNo, mTab.getWhereExtended(), false));
		//	Query automatically if high volume and no query
		boolean require = mTab.isHighVolume();
		if (!require && !m_onlyCurrentRows)				//	No Trx Window
		{
			/*  Where Extended already appended above, check for variables */
			if (query != null)
			{
				String wh2 = query.getWhereClause();
				if (wh2.length() > 0)
				{
					if (where.length() > 0)
						where.append (" AND ");
					where.append(wh2);
				}
			}
			//
			StringBuffer sql = new StringBuffer("SELECT COUNT(*) FROM ")
				.append(mTab.getTableName());
			if (where.length() > 0)
				sql.append(" WHERE ").append(where);
			//	Does not consider security
			int no = DB.getSQLValue(null, sql.toString());
			//
			require = MRole.getDefault().isQueryRequire(no);
		}
		//	Show Query
		if (require)
		{
			GridField[] findFields = mTab.getFields();
			Find find = new Find (Env.getFrame(this), m_curWindowNo, mTab.getName(),
				mTab.getAD_Tab_ID(), mTab.getAD_Table_ID(), mTab.getTableName(),
				where.toString(), findFields, 10);	//	no query below 10
			query = find.getQuery();
			isCancel = (query == null);//Goodwill
			find.dispose();
			find = null;
		}
		return query;
	}	//	initialQuery


	/**
	 *  Get Window Index
	 *  @return Window Index
	 */
	private int getWindowIndex()
	{
		//  only one window
		if (m_mWorkbench.getWindowCount() == 1)
			return 0;
		//  workbench
		return tabPanel.getSelectedIndex();
	}   //  getWindowIndex

	/**
	 *  Is first Tab (on Window)
	 *  @return true if the panel displays the first tab
	 */
	private boolean isFirstTab()
	{
		return m_curWinTab.getSelectedIndex() == 0;
	}   //  isFirstTab

	/**
	 * 	Get Window Image
	 *	@return image or null
	 */
	public Image getImage()
	{
		return m_mWorkbench.getImage(getWindowIndex());
	}	//	getImage


	/**************************************************************************
	 *	Data Status Listener (row change)			^ | v
	 *  @param e event
	 */
	public void dataStatusChanged (DataStatusEvent e)
	{
		if (m_disposing)
			return;
		log.info(e.getMessage());
		String dbInfo = e.getMessage();
		if (m_curTab != null && m_curTab.isQueryActive())
			dbInfo = "[ " + dbInfo + " ]";
		statusBar.setStatusDB(dbInfo, e);
		if (!isNested)
			m_window.setTitle(getTitle());

		//	Set Message / Info
		if (e.getAD_Message() != null || e.getInfo() != null)
		{
			StringBuffer sb = new StringBuffer();
			String msg = e.getMessage();
			if (msg != null && msg.length() > 0)
				sb.append(Msg.getMsg(m_ctx, e.getAD_Message()));
			String info = e.getInfo();
			if (info != null && info.length() > 0)
			{
				if (sb.length() > 0 && !sb.toString().trim().endsWith(":"))
					sb.append(": ");
				sb.append(info);
			}
			if (sb.length() > 0)
			{
				int pos = sb.indexOf("\n");
				if (pos != -1)  // replace CR/NL
					sb.replace(pos, pos+1, " - ");
				setStatusLine (sb.toString (), e.isError ());
			}
		}

		//  Confirm Error
		if (e.isError() && !e.isConfirmed())
		{
			ADialog.error(m_curWindowNo, this, e.getAD_Message(), e.getInfo());
			e.setConfirmed(true);   //  show just once - if MTable.setCurrentRow is involved the status event is re-issued
			m_errorDisplayed = true;
		}
		//  Confirm Warning
		else if (e.isWarning() && !e.isConfirmed())
		{
			ADialog.warn(m_curWindowNo, this, e.getAD_Message(), e.getInfo());
			e.setConfirmed(true);   //  show just once - if MTable.setCurrentRow is involved the status event is re-issued
		}

		//	update Navigation
		boolean firstRow = e.isFirstRow();
		aFirst.setEnabled(!firstRow);
		aPrevious.setEnabled(!firstRow);
		boolean lastRow = e.isLastRow();
		aNext.setEnabled(!lastRow);
		aLast.setEnabled(!lastRow);

		//	update Change

		boolean changed = e.isChanged() || e.isInserting();
		int changedColumn = e.getChangedColumn();
		boolean inserting = e.isInserting();

		if(e.getAD_Message() != null && e.getAD_Message().equals("Saved"))
			changed = false;
		boolean readOnly = m_curTab.isReadOnly();
		boolean insertRecord = !readOnly;
		if (insertRecord)
			insertRecord = m_curTab.isInsertRecord();
		aNew.setEnabled(((inserting && changedColumn>0) || !inserting) && insertRecord);
		aCopy.setEnabled(!changed && insertRecord);
		aRefresh.setEnabled(!changed);
		aDelete.setEnabled(!changed && !readOnly);
		aDeleteSelection.setEnabled(!changed && !readOnly);
		//
		if (readOnly && m_curTab.isAlwaysUpdateField())
			readOnly = false;
		aIgnore.setEnabled(changed && !readOnly);
		aSave.setEnabled(changed && !readOnly);
		//
		//	No Rows
		if (e.getTotalRows() == 0 && insertRecord) {
			aNew.setEnabled(true);
			aDelete.setEnabled(false);
			aDeleteSelection.setEnabled(false);
		}

		//	Single-Multi
		aMulti.setPressed(!m_curGC.isSingleRow());

		//	History	(on first Tab only)
		if (isFirstTab())
			aHistory.setPressed(!m_curTab.isOnlyCurrentRows());

		//	Transaction info
		String trxInfo = m_curTab.getTrxInfo();
		if (trxInfo != null)
			statusBar.setInfo(trxInfo);

		//	Check Attachment
		boolean canHaveAttachment = m_curTab.canHaveAttachment();		//	not single _ID column
		//
		if (canHaveAttachment && e.isLoading() && m_curTab.getCurrentRow() > e.getLoadedRows())
			canHaveAttachment = false;
		if (canHaveAttachment && m_curTab.getRecord_ID() == -1)    //	No Key
			canHaveAttachment = false;
		if (canHaveAttachment)
		{
			aAttachment.setEnabled(true);
			aAttachment.setPressed(m_curTab.hasAttachment());
			aChat.setEnabled(true);
			aChat.setPressed(m_curTab.hasChat());
		}
		else
		{
			aAttachment.setEnabled(false);
			aChat.setEnabled(false);
		}
		//	Lock Indicator
		if (m_isPersonalLock)
			aLock.setPressed(m_curTab.isLocked());

		if (m_curWinTab instanceof VTabbedPane)
			((VTabbedPane)m_curWinTab).evaluate(e);
	//	log.info("- fini", e.getMessage());
	}	//	dataStatusChanged

	/**
	 *	Set Status Line to text
	 *  @param text clear text
	 *  @param error error flag
	 */
	public void setStatusLine (String text, boolean error)
	{
		log.fine(text);
		statusBar.setStatusLine(text, error);
	}	//	setStatusLine

	/**
	 *	Indicate Busy
	 *  @param busy busy
	 *  @param focus request focus
	 */
	private void setBusy (boolean busy, boolean focus)
	{
		m_isLocked = busy;
		//
		JFrame frame = Env.getFrame(this);
		if (frame == null)  //  during init
			return;
		if (frame instanceof AWindow)
			((AWindow)frame).setBusy(busy);
	//	String processing = Msg.getMsg(m_ctx, "Processing");
		if (busy)
		{
	//		setStatusLine(processing);
			this.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
			frame.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
		}
		else
		{
			this.setCursor(Cursor.getDefaultCursor());
			frame.setCursor(Cursor.getDefaultCursor());
			if (focus)
				m_curGC.requestFocus();
	//		if (statusBar.getStatusLine().equals(processing))
	//			statusBar.setStatusLine("");
		}
	}	//	set Busy


	/**************************************************************************
	 *	Change Listener - (tab change)			<->
	 *  @param e event
	 */
	public void stateChanged (ChangeEvent e)
	{
		if (m_disposing)
			return;
		log.info(e.toString());
		setBusy(true, true);

		VTabbedPane tp = (VTabbedPane)e.getSource();
		boolean back = false;
		boolean isAPanelTab = false;

		//int previousIndex = 0;

		//  Workbench Tab Change
		if (tp.isWorkbench())
		{
			int WBIndex = tabPanel.getSelectedIndex();
			m_curWindowNo = m_mWorkbench.getWindowNo(WBIndex);
			//  Window Change
			log.info("curWin=" + m_curWindowNo + " - Win=" + tp);
			if (tp.getSelectedComponent() instanceof JTabbedPane)
				m_curWinTab = (JTabbedPane)tp.getSelectedComponent();
			else
				throw new java.lang.IllegalArgumentException("Window does not contain Tabs");
			if (m_curWinTab.getSelectedComponent() instanceof GridController) {
				m_curGC = (GridController)m_curWinTab.getSelectedComponent();
				initSwitchLineAction();
			}
		//	else if (m_curWinTab.getSelectedComponent() instanceof APanelTab)
		//		isAPanelTab = true;
			else
				throw new java.lang.IllegalArgumentException("Window-Tab does not contain GridControler");
			//  change pointers
			m_curTabIndex = m_curWinTab.getSelectedIndex();
		}
		else
		{
			//  Just a Tab Change
			log.info("Tab=" + tp);
			m_curWinTab = tp;
			int tpIndex = m_curWinTab.getSelectedIndex();
			//	detect no tab change
			if (tpIndex == m_curTabIndex) return;
			back = tpIndex < m_curTabIndex;
			GridController gc = null;
			if (m_curWinTab.getSelectedComponent() instanceof GridController)
				gc = (GridController)m_curWinTab.getSelectedComponent();
			else if (m_curWinTab.getSelectedComponent() instanceof APanelTab)
				isAPanelTab = true;
			else
				throw new java.lang.IllegalArgumentException("Tab does not contain GridControler");
			//  Save old Tab
			if (m_curGC != null)
			{
				m_curGC.stopEditor(true);
				//  has anything changed?
				if (m_curTab.needSave(true, false))
				{   //  do we have real change
					if (m_curTab.needSave(true, true))
					{
						//	Automatic Save
						if (Env.isAutoCommit(m_ctx, m_curWindowNo))
						{
							if (!m_curTab.dataSave(true))
							{	//  there is a problem, so we go back
								showLastError();
								m_curWinTab.setSelectedIndex(m_curTabIndex);
								setBusy(false, true);
								return;
							}
						}
						//  explicitly ask when changing tabs
						else if (ADialog.ask(m_curWindowNo, this, "SaveChanges?", m_curTab.getCommitWarning()))
						{   //  yes we want to save
							if (!m_curTab.dataSave(true))
							{   //  there is a problem, so we go back
								showLastError();
								m_curWinTab.setSelectedIndex(m_curTabIndex);
								setBusy(false, true);
								return;
							}
						}
						else    //  Don't save
						{
							int newRecord= m_curTab.getTableModel().getNewRow();     //VOSS COM

							if( newRecord == -1)
								m_curTab.dataIgnore();
							else
							{
								m_curWinTab.setSelectedIndex(m_curTabIndex);
								setBusy(false, true);
								return;
							}
						}
		            }

					else    //  new record, but nothing changed
						m_curTab.dataIgnore();
				}   //  there is a change
			}
			if (m_curAPanelTab != null)
			{
				m_curAPanelTab.saveData();
				m_curAPanelTab.unregisterPanel();
				m_curAPanelTab = null;
			}

			//	new tab
		//	if (m_curTabIndex >= 0)
		//		m_curWinTab.setForegroundAt(m_curTabIndex, AdempierePLAF.getTextColor_Normal());
		//	m_curWinTab.setForegroundAt(tpIndex, AdempierePLAF.getTextColor_OK());
		//	previousIndex = m_curTabIndex;
			m_curTabIndex = tpIndex;
			if (!isAPanelTab) {
				m_curGC = gc;
				initSwitchLineAction();
			}
		}

		//	Sort Tab Handling
		if (isAPanelTab)
		{
			m_curAPanelTab = (APanelTab)m_curWinTab.getSelectedComponent();
			m_curAPanelTab.registerAPanel(this);
			m_curAPanelTab.loadData();
			// Consider that APanelTab (e.g. VSortTab) is not navigable - teo_sarca [ 1705444 ]
			aFirst.setEnabled(false);
			aPrevious.setEnabled(false);
			aNext.setEnabled(false);
			aLast.setEnabled(false);
		}
		else	//	Cur Tab Setting
		{
			int gwTabIndex = m_mWorkbench.getMWindow(0).getTabIndex(m_curGC.getMTab());
			//boolean needValidate = false;
			if (m_mWorkbench.getMWindow(0).isTabInitialized(gwTabIndex) == false)
			{
				m_mWorkbench.getMWindow(0).initTab(gwTabIndex);
				//needValidate = true;
			}
			m_curGC.activate();
			m_curTab = m_curGC.getMTab();

			//	Refresh only current row when tab is current
			if (back && m_curTab.isCurrent())
				m_curTab.dataRefresh();
			else	//	Requery & autoSize
			{
				MRole role = MRole.getDefault();
				m_curGC.query (m_onlyCurrentRows, m_onlyCurrentDays, role.getMaxQueryRecords());
				/*
				if (m_curGC.isNeedToSaveParent())
				{
					// there is a problem, so we go back
					ADialog.error(m_curWindowNo, this, "SaveParentFirst");
					m_curWinTab.setSelectedIndex(previousIndex);
					setBusy(false, true);
					return;
				}*/
			}
			//  Set initial record
			if (m_curTab.getRowCount() == 0)
			{
				//	Automatically create New Record, if none & tab not RO
				if (!m_curTab.isReadOnly()
					&& (Env.isAutoNew(m_ctx, m_curWindowNo) || m_curTab.isQueryNewRecord()))
				{
					log.config("No record - New - AutoNew=" + Env.isAutoNew(m_ctx, m_curWindowNo)
						+ " - QueryNew=" + m_curTab.isQueryNewRecord());
					m_curTab.dataNew(false);
				}
				else	//	No Records found
				{
					aSave.setEnabled(false);
					aDelete.setEnabled(false);
					aDeleteSelection.setEnabled(false);
				}
				m_curTab.navigateCurrent();     //  updates counter
				m_curGC.dynamicDisplay(0);
			}
			/*
			if (needValidate)
			{
				JFrame frame = Env.getFrame(APanel.this);
				if (frame != null)
				{
					//not sure why, the following lines is needed to make dynamic resize work
					//tested on jdk1.5, 1.6 using jgoodies look and feel
					frame.getPreferredSize();

					if (frame.getExtendedState() != JFrame.MAXIMIZED_BOTH)
					{
						frame.setMinimumSize(frame.getSize());
						revalidate();
						SwingUtilities.invokeLater(new Runnable() {

							public void run() {
								JFrame frame = Env.getFrame(APanel.this);
								frame.validate();
								AEnv.showCenterScreen(frame);
								frame.setMinimumSize(null);
							}

						});
					}
				}
			}*/
		//	else		##CHANGE
		//		m_curTab.navigateCurrent();
		}

		//	Update <-> Navigation
		aDetail.setEnabled(m_curTabIndex != m_curWinTab.getTabCount()-1);
		aParent.setEnabled(m_curTabIndex != 0 && m_curWinTab.getTabCount() > 1);

		//	History (on first tab only)
		if (m_mWorkbench.getMWindow(getWindowIndex()).isTransaction())
			aHistory.setEnabled(isFirstTab());
		else
		{
			aHistory.setPressed(false);
			aHistory.setEnabled(false);
		}
		//	Document Print
		aPrint.setEnabled(m_curTab.isPrinted());
		aPrintPreview.setEnabled(m_curTab.isPrinted());
		//	Query
		aFind.setPressed(m_curTab.isQueryActive());

		//	Order Tab
		if (isAPanelTab)
		{
			aMulti.setPressed(false);
			aMulti.setEnabled(false);
			aNew.setEnabled(false);
			aDelete.setEnabled(false);
			aDeleteSelection.setEnabled(false);
			aFind.setEnabled(false);
			aRefresh.setEnabled(false);
			aAttachment.setEnabled(false);
			aChat.setEnabled(false);
		}
		else	//	Grid Tab
		{
			aMulti.setEnabled(true);
			aMulti.setPressed(!m_curGC.isSingleRow());
			aFind.setEnabled(true);
			aRefresh.setEnabled(true);
			aAttachment.setEnabled(true);
			aChat.setEnabled(true);
		}
		//
		m_curWinTab.requestFocusInWindow();
		setBusy(false, true);
		log.config( "fini");
	}	//	stateChanged

	/**
	 *	Navigate to Detail Tab			->
	 */
	private void cmd_detail()
	{
		int index = m_curWinTab.getSelectedIndex();
		if (index == m_curWinTab.getTabCount()-1)
			return;
		//hengsin, bug [ 1637763 ]
		if (m_curWinTab instanceof VTabbedPane)
		{
			VTabbedPane tabPane = (VTabbedPane)m_curWinTab;
			index++;
			while ( index < tabPane.getTabCount() )
			{
				if (tabPane.isEnabledAt(index))
				{
					m_curGC.getTable().removeEditor();
					m_curGC.acceptEditorChanges();
					tabPane.setSelectedIndex(index);
					break;
				}
				else
					index++;
			}
		}
		else
		{
			m_curGC.getTable().removeEditor();
			m_curGC.acceptEditorChanges();
			m_curWinTab.setSelectedIndex(index+1);
		}

	}	//	navigateDetail

	/**
	 *	Navigate to Parent Tab			<-
	 */
	private void cmd_parent()
	{
		int index = m_curWinTab.getSelectedIndex();
		if (index == 0)
			return;
		//hengsin, bug [ 1637763 ]
		if (m_curWinTab instanceof VTabbedPane)
		{
			VTabbedPane tabPane = (VTabbedPane)m_curWinTab;
			index--;
			while ( index >= 0 )
			{
				if (tabPane.isEnabledAt(index))
				{
					m_curGC.getTable().removeEditor();
					m_curGC.acceptEditorChanges();
					tabPane.setSelectedIndex(index);
					break;
				}
				else
					index--;
			}
		}
		else
		{
			m_curGC.getTable().removeEditor();
			m_curGC.acceptEditorChanges();
			m_curWinTab.setSelectedIndex(index-1);
		}
	}	//	navigateParent


	/**************************************************************************
	 *	Action Listener
	 *  @param e event
	 */
	public void actionPerformed (ActionEvent e)
	{
		log.info(e.getActionCommand() + " - " + e.getModifiers());
		//	+ " - " + new Timestamp(e.getWhen()) + " " + isUILocked());
		if (m_disposing || isUILocked())
			return;

		m_lastModifiers = e.getModifiers();
		String cmd = e.getActionCommand();
		//	Do ScreenShot w/o busy
		if (cmd.equals("ScreenShot"))
		{
			AEnv.actionPerformed (e.getActionCommand(), m_curWindowNo, this);
			return;
		}

		//  Problem: doubleClick detection - can't disable button as clicking button may change button status
		if (!cmd.equals(aShowAllWindow.getName()))
			setBusy (true, true);
		//  Command Buttons
		if (e.getSource() instanceof VButton)
		{
			setStatusLine(processButtonCallout((VButton)e.getSource()), true);
			actionButton((VButton)e.getSource());
			setBusy(false, true);
			return;
		}

		try
		{
			//	File
			if (cmd.equals(aReport.getName()))
				cmd_report();
			else if (cmd.equals(aPrint.getName()))
				cmd_print();
			else if (cmd.equals(aPrintPreview.getName()))
				cmd_print(true);
			else if (aExport != null && cmd.equals(aExport.getName()))
				cmd_export();
			else if (cmd.equals(aEnd.getName()))
				cmd_end(false);
			else if (cmd.equals(aExit.getName()))
				cmd_end(true);
			//	Edit
			else if (cmd.equals(aNew.getName()))
				cmd_new(false);
			else if (cmd.equals(aSave.getName()))
				cmd_save(true);
			else if (cmd.equals(aCopy.getName()))
				cmd_new(true);
			else if (cmd.equals(aDelete.getName()))
				cmd_delete();
			else if (cmd.equals(aDeleteSelection.getName()))
				cmd_deleteSelection();
			else if (cmd.equals(aIgnore.getName()))
				cmd_ignore();
			else if (cmd.equals(aRefresh.getName()))
				cmd_refresh();
			else if (cmd.equals(aFind.getName()))
				cmd_find();
			else if (m_isPersonalLock && cmd.equals(aLock.getName()))
				cmd_lock();
			//	View
			else if (cmd.equals(aAttachment.getName()))
				cmd_attachment();
			else if (cmd.equals(aChat.getName()))
				cmd_chat();
			else if (cmd.equals(aHistory.getName()))
				cmd_history();
			else if (cmd.equals(aMulti.getName()))
				m_curGC.switchRowPresentation();
			//	Go
			else if (cmd.equals(aHome.getName())) {
				// show main menu - teo_sarca [ 1706409, 1707221 ]
				setBusy(false, false);
				AEnv.showWindow(Env.getWindow(0));
				return;
			}
			else if (cmd.equals(aFirst.getName()))
			{	/*cmd_save(false);*/
				m_curGC.getTable().removeEditor();
				m_curGC.acceptEditorChanges();
				m_curTab.navigate(0);
			}
			else if (cmd.equals(aSwitchLinesUpAction.getName()))
			{
				//up-key + shift
				m_curGC.getTable().removeEditor();
				m_curTab.switchRows(m_curTab.getCurrentRow(), m_curTab.getCurrentRow() - 1, m_curGC.getTable().getSortColumn(), m_curGC.getTable().isSortAscending());
				m_curGC.getTable().requestFocus();
			}
			else if (cmd.equals(aPrevious.getName()))
			{ /* cmd_save(false); */
				//up-image + shift
				m_curGC.getTable().removeEditor();
				m_curGC.acceptEditorChanges();
				if ((e.getModifiers() & ActionEvent.SHIFT_MASK) != 0) {
					m_curTab.switchRows(m_curTab.getCurrentRow(), m_curTab.getCurrentRow() - 1, m_curGC.getTable().getSortColumn(), m_curGC.getTable().isSortAscending());
				} else {
					m_curTab.navigateRelative(-1);
				}
			}
			else if (cmd.equals(aSwitchLinesDownAction.getName()))
			{
				//down-key + shift
				m_curGC.getTable().removeEditor();
				m_curTab.switchRows(m_curTab.getCurrentRow(), m_curTab.getCurrentRow() + 1, m_curGC.getTable().getSortColumn(), m_curGC.getTable().isSortAscending());
				m_curGC.getTable().requestFocus();
			}
			else if (cmd.equals(aNext.getName()))
			{ /* cmd_save(false); */
				//down-image + shift
				m_curGC.getTable().removeEditor();
				m_curGC.acceptEditorChanges();
				if ((e.getModifiers() & ActionEvent.SHIFT_MASK) != 0) {
					m_curTab.switchRows(m_curTab.getCurrentRow(), m_curTab.getCurrentRow() + 1, m_curGC.getTable().getSortColumn(), m_curGC.getTable().isSortAscending());
				} else {
					m_curTab.navigateRelative(+1);
				}
			}
			else if (cmd.equals(aLast.getName()))
			{	/*cmd_save(false);*/
				m_curGC.getTable().removeEditor();
				m_curGC.acceptEditorChanges();
				m_curTab.navigate(m_curTab.getRowCount()-1);
			}
			else if (cmd.equals(aParent.getName()))
				cmd_parent();
			else if (cmd.equals(aDetail.getName()))
				cmd_detail();
			else if (cmd.equals(aZoomAcross.getName()))
				cmd_zoomAcross();
			else if (cmd.equals(aRequest.getName()))
				cmd_request();
			else if (cmd.equals(aArchive.getName()))
				cmd_archive();
			//	Tools
			else if (aWorkflow != null && cmd.equals(aWorkflow.getName()))
			{
				if (m_curTab.getRecord_ID() <= 0)
					;
				else if (m_curTab.getTabNo() == 0 && m_mWorkbench.getMWindow(getWindowIndex()).isTransaction())
					AEnv.startWorkflowProcess(m_curTab.getAD_Table_ID(), m_curTab.getRecord_ID());
				else
					AEnv.startWorkflowProcess(m_curTab.getAD_Table_ID(), m_curTab.getRecord_ID());
			}
			else if (aWinSize != null && cmd.equals(aWinSize.getName()))
				cmd_winSize();
			//	Help
			else if (cmd.equals(aHelp.getName()))
				cmd_help();
			//  General Commands (Environment)
			else if (cmd.equals(aLogout.getName()))
				cmd_logout();
			else if (cmd.equals(aShowAllWindow.getName()))
				m_WindowMenu.expose();
			else if (!AEnv.actionPerformed (e.getActionCommand(), m_curWindowNo, this))
				log.log(Level.SEVERE, "No action for: " + cmd);
		}
		catch (Exception ex)
		{
			log.log(Level.SEVERE, cmd, ex);
			String msg = ex.getMessage();
			if (msg == null || msg.length() == 0)
				msg = ex.toString();
			msg = Msg.parseTranslation(m_ctx, msg);
			ADialog.error(m_curWindowNo, this, "Error", msg);
		}
		//
		m_curWinTab.requestFocusInWindow();
		setBusy(false, true);
	}	//	actionPerformed

	private void cmd_logout() {
		JFrame top = Env.getWindow(0);
		if (top instanceof AMenu) {
			((AMenu)top).logout();
		}

	}

	/**************************************************************************
	 *  Process Callout(s).
	 *  <p>
	 *  The Callout is in the string of
	 *  "class.method;class.method;"
	 * If there is no class name, i.e. only a method name, the class is regarded
	 * as CalloutSystem.
	 * The class needs to comply with the Interface Callout.
	 *
	 * @param field field
	 * @return error message or ""
	 * @see org.compiere.model.Callout
	 */
	private String processButtonCallout (VButton button)
	{
		GridField field = m_curTab.getField(button.getColumnName());
		return m_curTab.processCallout(field);
	}	//	processButtonCallout

	/**
	 *  Create New Record
	 *  @param copy true if current record is to be copied
	 */
	private void cmd_new (boolean copy)
	{
		log.config("copy=" + copy);
		if (!m_curTab.isInsertRecord())
		{
			log.warning("Insert Record disabled for Tab");
			return;
		}

		m_curGC.stopEditor(true);
		m_curGC.acceptEditorChanges();

		// BF [ 2799362 ] attempt to save if save action is enabled. Using
		// m_curTab.needSave instead might miss unfilled mandatory fields.
		if(aSave.isEnabled()){
			//	Automatic Save
			if (Env.isAutoCommit(m_ctx, m_curWindowNo))
			{
				if (!cmd_save(true))
				{
					return;
				}
			}
			//  explicitly ask when changing tabs
			else if (ADialog.ask(m_curWindowNo, this, "SaveChanges?", m_curTab.getCommitWarning()))
			{   //  yes we want to save
				if (!cmd_save(true))
				{
					return;
				}
			}
			else    //  Don't save
				m_curTab.dataIgnore();
		}
		if (copy && m_curTab.getCurrentRow() < 0)
			copy = false;
		m_curTab.dataNew (copy);
		m_curGC.dynamicDisplay(0);
	//	m_curTab.getTableModel().setChanged(false);
	}   //  cmd_new

	/**
	 *  Confirm & delete record
	 */
	private void cmd_delete()
	{
		if (m_curTab.isReadOnly())
			return;
		int keyID = m_curTab.getRecord_ID();
		if (ADialog.ask(m_curWindowNo, this, "DeleteRecord?"))
			if (m_curTab.dataDelete())
				m_curGC.rowChanged(false, keyID);
		m_curGC.dynamicDisplay(0);
	}   //  cmd_delete

	/**
	 * Show a list to select one or more items to delete.
	 */
	private void cmd_deleteSelection(){
		if (m_curTab.isReadOnly())
			return;
		//show table with deletion rows -> by identifiers columns
		JPanel messagePanel = new JPanel();
		JList list = new JList();
		JScrollPane scrollPane = new JScrollPane(list);
		Vector<String> data = new Vector<String>();
		// FR [ 2877111 ]
		final String keyColumnName = m_curTab.getKeyColumnName();
		String sql = null;
		if (! "".equals(keyColumnName)) {
			sql = MLookupFactory.getLookup_TableDirEmbed(Env.getLanguage(m_ctx), keyColumnName, "[?","?]")
			   .replace("[?.?]", "?");
		}
		int noOfRows = m_curTab.getRowCount();
		for(int i = 0; i < noOfRows; i++)
		{
			StringBuffer displayValue = new StringBuffer();
			if ("".equals(keyColumnName))
			{
				ArrayList<String> parentColumnNames = m_curTab.getParentColumnNames();
				for (Iterator<String> iter = parentColumnNames.iterator(); iter.hasNext();)
				{
					String columnName = iter.next();
					GridField field = m_curTab.getField(columnName);
					if(field.isLookup()){
						Lookup lookup = field.getLookup();
						if (lookup != null){
							displayValue = displayValue.append(lookup.getDisplay(m_curTab.getValue(i,columnName))).append(" | ");
						} else {
							displayValue = displayValue.append(m_curTab.getValue(i,columnName)).append(" | ");
						}
					} else {
						displayValue = displayValue.append(m_curTab.getValue(i,columnName)).append(" | ");
					}
				}
			} else {
				final int id = m_curTab.getKeyID(i);
				String value = DB.getSQLValueStringEx(null, sql, id);
				if (value != null)
					value = value.replace(" - ", " | ");
				displayValue.append(value);
				// Append ID
				if (displayValue.length() == 0 || CLogMgt.isLevelFine())
				{
					if (displayValue.length() > 0)
						displayValue.append(" | ");
					displayValue.append("<").append(id).append(">");
				}
			}
			//
			data.add(displayValue.toString());
		}
		// FR [ 2877111 ]
		list.setListData(data);

		list.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
		messagePanel.add(scrollPane);

		final JOptionPane pane = new JOptionPane(
				messagePanel, // message
				JOptionPane.QUESTION_MESSAGE, // messageType
				JOptionPane.OK_CANCEL_OPTION); // optionType
		final JDialog deleteDialog = pane.createDialog(this.getParent(), Msg.getMsg(m_ctx, "DeleteSelection"));
		deleteDialog.setVisible(true);
		Integer okCancel = (Integer) pane.getValue();
		if(okCancel != null && okCancel == JOptionPane.OK_OPTION)
		{
			log.fine("ok");
			Object[] selectedValues = list.getSelectedValues();
			for (int i = 0; i < selectedValues.length; i++)
			{
				log.fine(selectedValues[i].toString());
			}
			int[] indices = list.getSelectedIndices();
			Arrays.sort(indices);
			int offset = 0;
			for (int i = 0; i < indices.length; i++)
			{
				//m_curTab.setCurrentRow(indices[i]-offset);
				m_curTab.navigate(indices[i]-offset);
				int keyID = m_curTab.getRecord_ID();
				if (m_curTab.dataDelete())
				{
					m_curGC.rowChanged(false, keyID);
					offset++;
				}
			}
			m_curGC.dynamicDisplay(0);
		}
		else
		{
			log.fine("cancel");
		}
	}//cmd_deleteSelection

	/**
	 *  If required ask if you want to save and save it
	 *  @param manualCmd true if invoked manually (i.e. force)
	 *  @return true if saved
	 */
	public boolean cmd_save (boolean manualCmd)
	{
		if (m_curAPanelTab != null)
			manualCmd = false;
		log.config("Manual=" + manualCmd);
		m_errorDisplayed = false;
		m_curGC.stopEditor(true);
		m_curGC.acceptEditorChanges();

		if (m_curAPanelTab != null)
		{
			m_curAPanelTab.saveData();
			aSave.setEnabled(false);	//	set explicitly
		}

		if (m_curTab.getCommitWarning().length() > 0 && m_curTab.needSave(true, false))
			if (!ADialog.ask(m_curWindowNo, this, "SaveChanges?", m_curTab.getCommitWarning()))
				return false;

		//  manually initiated
		boolean retValue = m_curTab.dataSave(manualCmd);
		//   if there is no previous error
		if (manualCmd && !retValue && !m_errorDisplayed)
		{
			showLastError();
		}
		if (retValue)
			m_curGC.rowChanged(true, m_curTab.getRecord_ID());
		if (manualCmd) {
			m_curGC.dynamicDisplay(0);
			if (!isNested)
				m_window.setTitle(getTitle());
		}

		//BEGIN - [FR 1953734]
		if(m_curGC.isDetailGrid() && retValue){
			m_curGC.getGCParent().refreshMTab(m_curGC);
		}
		//END - [FR 1953734]

		return retValue;
	}   //  cmd_save

	private void showLastError() {
		String msg = CLogger.retrieveErrorString(null);
		if (msg != null)
			ADialog.error(m_curWindowNo, this, null, msg);
		else
			ADialog.error(m_curWindowNo, this, "SaveIgnored");
		setStatusLine(Msg.getMsg(m_ctx, "SaveIgnored"), true);
	}

	/**
	 *  Ignore
	 */
	private void cmd_ignore()
	{
		m_curGC.stopEditor(false);
		// Ignore changes in APanelTab (e.g. VSortTab) - teo_sarca [ 1705429 ]
		if (m_curAPanelTab != null)
		{
			m_curAPanelTab.loadData();
		}
		m_curTab.dataIgnore();
		m_curGC.dynamicDisplay(0);

	}   //  cmd_ignore

	/**
	 *  Refresh
	 */
	private void cmd_refresh()
	{
		cmd_save(false);
		m_curTab.dataRefreshAll();
		m_curGC.dynamicDisplay(0);
	}   //  cmd_refresh

	/**
	 *	Print standard Report
	 */
	private void cmd_report ()
	{
		log.info("");
		if (!MRole.getDefault().isCanReport(m_curTab.getAD_Table_ID()))
		{
			ADialog.error(m_curWindowNo, this, "AccessCannotReport");
			return;
		}

		cmd_save(false);

		//	Query
		MQuery query = new MQuery(m_curTab.getTableName());
		//	Link for detail records
		String queryColumn = m_curTab.getLinkColumnName();
		//	Current row otherwise
		if (queryColumn.length() == 0)
			queryColumn = m_curTab.getKeyColumnName();
		//	Find display
		String infoName = null;
		String infoDisplay = null;
		for (int i = 0; i < m_curTab.getFieldCount(); i++)
		{
			GridField field = m_curTab.getField(i);
			if (field.isKey())
				infoName = field.getHeader();
			if ((field.getColumnName().equals("Name") || field.getColumnName().equals("DocumentNo") )
				&& field.getValue() != null)
				infoDisplay = field.getValue().toString();
			if (infoName != null && infoDisplay != null)
				break;
		}
		if (queryColumn.length() != 0)
		{
			if (queryColumn.endsWith("_ID"))
				query.addRestriction(queryColumn, MQuery.EQUAL,
					new Integer(Env.getContextAsInt(m_ctx, m_curWindowNo, queryColumn)),
					infoName, infoDisplay);
			else
				query.addRestriction(queryColumn, MQuery.EQUAL,
					Env.getContext(m_ctx, m_curWindowNo, queryColumn),
					infoName, infoDisplay);
		}

		new AReport (m_curTab.getAD_Table_ID(), aReport.getButton(), query, this, m_curWindowNo, m_curTab.getWhereExtended());
	}	//	cmd_report


	/**
	 * 	Zoom Across Menu
	 */
	private void cmd_zoomAcross()
	{
		int record_ID = m_curTab.getRecord_ID();
		log.info("ID=" + record_ID);
		if (record_ID <= 0)
			return;

		//	Query
		MQuery query = new MQuery();
		//	Current row
		String link = m_curTab.getKeyColumnName();
		//	Link for detail records
		if (link.length() == 0)
			link = m_curTab.getLinkColumnName();
		if (link.length() != 0)
		{
			if (link.endsWith("_ID"))
				query.addRestriction(link, MQuery.EQUAL,
					new Integer(Env.getContextAsInt(m_ctx, m_curWindowNo, link)));
			else
				query.addRestriction(link, MQuery.EQUAL,
					Env.getContext(m_ctx, m_curWindowNo, link));
		}
		new AZoomAcross(aZoomAcross.getButton(), m_curTab.getTableName(),
				m_curTab.getAD_Window_ID(), query);
	}	//	cmd_zoom

	/**
	 * 	Open/View Request
	 */
	private void cmd_request()
	{
		int record_ID = m_curTab.getRecord_ID();
		log.info("ID=" + record_ID);
		if (record_ID <= 0)
			return;

		int AD_Table_ID = m_curTab.getAD_Table_ID();
		int C_BPartner_ID = 0;
		Object BPartner_ID = m_curTab.getValue("C_BPartner_ID");
		if (BPartner_ID != null)
			C_BPartner_ID = ((Integer)BPartner_ID).intValue();
		new ARequest (aRequest.getButton(), AD_Table_ID, record_ID, C_BPartner_ID);
	}	//	cmd_request

	/**
	 * 	Open/View Archive
	 */
	private void cmd_archive()
	{
		int record_ID = m_curTab.getRecord_ID();
		log.info("ID=" + record_ID);
		if (record_ID <= 0)
			return;

		int AD_Table_ID = m_curTab.getAD_Table_ID();
		new AArchive (aArchive.getButton(), AD_Table_ID, record_ID);
	}	//	cmd_archive

	/**
	 *	Print specific Report - or start default Report
	 */
	private void cmd_print()
	{
		cmd_print(false);
	}

	/**
	 *	Print specific Report - or start default Report
	 */
	private void cmd_print(boolean printPreview)
	{
		//	Get process defined for this tab
		int AD_Process_ID = m_curTab.getAD_Process_ID();
		log.info("ID=" + AD_Process_ID);

		//	No report defined
		if (AD_Process_ID == 0)
		{
			cmd_report();
			return;
		}

		cmd_save(false);
		//
		int table_ID = m_curTab.getAD_Table_ID();
		int record_ID = m_curTab.getRecord_ID();
		ProcessInfo pi = new ProcessInfo (getTitle(), AD_Process_ID, table_ID, record_ID);
		pi.setAD_User_ID (Env.getAD_User_ID(m_ctx));
		pi.setAD_Client_ID (Env.getAD_Client_ID(m_ctx));
		pi.setPrintPreview(printPreview);

		ProcessCtl.process(this, m_curWindowNo, pi, null); //  calls lockUI, unlockUI
		statusBar.setStatusLine(pi.getSummary(), pi.isError());
	}   //  cmd_print

	/**
	 *	Find - Set Query
	 */
	private void cmd_find()
	{
		if (m_curTab == null)
			return;
		cmd_save(false);
		//	Gets Fields from AD_Field_v
		GridField[] findFields = GridField.createFields(m_ctx, m_curWindowNo, 0, m_curTab.getAD_Tab_ID());
		Find find = new Find (Env.getFrame(this), m_curWindowNo, m_curTab.getName(),
			m_curTab.getAD_Tab_ID(), m_curTab.getAD_Table_ID(), m_curTab.getTableName(),
			m_curTab.getWhereExtended(), findFields, 1);
		MQuery query = find.getQuery();
		find.dispose();
		find = null;

		//	Confirmed query
		if (query != null)
		{
			m_onlyCurrentRows = false;      	//  search history too
			m_curTab.setQuery(query);
			m_curGC.query(m_onlyCurrentRows, m_onlyCurrentDays, 0);   //  autoSize
		}
		aFind.setPressed(m_curTab.isQueryActive());
	}	//	cmd_find

	/**
	 *	Attachment
	 */
	private void cmd_attachment()
	{
		int record_ID = m_curTab.getRecord_ID();
		log.info("Record_ID=" + record_ID);
		if (record_ID == -1)	//	No Key
		{
			aAttachment.setEnabled(false);
			return;
		}

	//	Attachment va =
		new Attachment (Env.getFrame(this), m_curWindowNo,
			m_curTab.getAD_AttachmentID(), m_curTab.getAD_Table_ID(), record_ID, null);
		//
		m_curTab.loadAttachments();				//	reload
		aAttachment.setPressed(m_curTab.hasAttachment());
	}	//	attachment

	/**
	 *	Chat
	 */
	private void cmd_chat()
	{
		int record_ID = m_curTab.getRecord_ID();
		log.info("Record_ID=" + record_ID);
		if (record_ID == -1)	//	No Key
		{
			aChat.setEnabled(false);
			return;
		}
		//	Find display
		String infoName = null;
		String infoDisplay = null;
		for (int i = 0; i < m_curTab.getFieldCount(); i++)
		{
			GridField field = m_curTab.getField(i);
			if (field.isKey())
				infoName = field.getHeader();
			if ((field.getColumnName().equals("Name") || field.getColumnName().equals("DocumentNo") )
				&& field.getValue() != null)
				infoDisplay = field.getValue().toString();
			if (infoName != null && infoDisplay != null)
				break;
		}
		String description = infoName + ": " + infoDisplay;
		//
	//	AChat va =
		new AChat (Env.getFrame(this), m_curWindowNo,
			m_curTab.getCM_ChatID(), m_curTab.getAD_Table_ID(), record_ID,
			description, null);
		//
		m_curTab.loadChats();				//	reload
		aChat.setPressed(m_curTab.hasChat());
	}	//	chat

	/**
	 *	Lock
	 */
	private void cmd_lock()
	{
		log.info("Modifiers=" + m_lastModifiers);
		if (!m_isPersonalLock)
			return;
		int record_ID = m_curTab.getRecord_ID();
		if (record_ID == -1)	//	No Key
			return;
		//	Control Pressed
		if ((m_lastModifiers & InputEvent.CTRL_MASK) != 0)
		{
			new RecordAccessDialog(Env.getFrame(this), m_curTab.getAD_Table_ID(), record_ID);
		}
		else
		{
			m_curTab.lock (Env.getCtx(), record_ID, aLock.getButton().isSelected());
			m_curTab.loadAttachments();			//	reload
		}
		aLock.setPressed(m_curTab.isLocked());
	}	//	lock

	/**
	 *	Toggle History
	 */
	private void cmd_history()
	{
		log.info("");
		if (m_mWorkbench.getMWindow(getWindowIndex()).isTransaction())
		{
			if (m_curTab.needSave(true, true) && !cmd_save(false))
				return;

			Point pt = new Point (0, aHistory.getButton().getBounds().height);
			SwingUtilities.convertPointToScreen(pt, aHistory.getButton());
			VOnlyCurrentDays ocd = new VOnlyCurrentDays(Env.getFrame(this), pt);
			if (!ocd.isCancel()) {
				m_onlyCurrentDays = ocd.getCurrentDays();
				if (m_onlyCurrentDays == 1)	//	Day
				{
					m_onlyCurrentRows = true;
					m_onlyCurrentDays = 0; 	//	no Created restriction
				}
				else
					m_onlyCurrentRows = false;
				//
				m_curTab.setQuery(null);	//	reset previous queries
				MRole role = MRole.getDefault();
				int maxRows = role.getMaxQueryRecords();
				//
				log.config("OnlyCurrent=" + m_onlyCurrentRows
					+ ", Days=" + m_onlyCurrentDays
					+ ", MaxRows=" + maxRows);
				m_curGC.query(m_onlyCurrentRows, m_onlyCurrentDays, maxRows );   //  autoSize
			}
			// Restore history button's pressed status
			else {
				if (isFirstTab())
					aHistory.setPressed(!m_curTab.isOnlyCurrentRows());
			}
		}
	}	//	cmd_history

	/**
	 *	Help
	 */
	private void cmd_help()
	{
		log.info("");
		Help hlp = new Help (Env.getFrame(this), this.getTitle(), m_mWorkbench.getMWindow(getWindowIndex()));
		hlp.setVisible(true);
	}	//	cmd_help

	/**
	 *  Close this screen - after save
	 *  @param exit ask if user wants to exit application
	 */
	private void cmd_end (boolean exit)
	{
		boolean exitSystem = false;
		if (!cmd_save(false))
			return;
		if (exit && ADialog.ask(m_curWindowNo, this, "ExitApplication?"))
			exitSystem = true;

		Env.getFrame(this).dispose();		//	calls this dispose

		if (exitSystem)
			AEnv.exit(0);
	}   //  cmd_end

	/**
	 * 	Set Window Size
	 */
	private void cmd_winSize()
	{
		Dimension size = getSize();
		if (!ADialog.ask(m_curWindowNo, this, "WinSizeSet",
			"x=" + size.width + " - y=" + size.height))
		{
			setPreferredSize(null);
			SwingUtilities.getWindowAncestor(this).pack();
			size = new Dimension (0,0);
		}
		//
		MWindow win = new MWindow(m_ctx, m_curTab.getAD_Window_ID(), null);
		win.setWindowSize(size);
		win.save();
	}	//	cmdWinSize

	private void cmd_export()
	{
		new AExport(this);
	}


	/**************************************************************************
	 *	Start Button Process
	 *  @param vButton button
	 */
	private void actionButton (VButton vButton)
	{
		log.info(vButton.toString());

		if (m_curTab.hasChangedCurrentTabAndParents()) {
			String msg = CLogger.retrieveErrorString("Please ReQuery Window");
			ADialog.error(m_curWindowNo, this, null, msg);
			return;
		}

		boolean startWOasking = false;
//		boolean batch = false;
		String col = vButton.getColumnName();

		//  Zoom
		if (col.equals("Record_ID"))
		{
			int AD_Table_ID = Env.getContextAsInt (m_ctx, m_curWindowNo, "AD_Table_ID");
			int Record_ID = Env.getContextAsInt (m_ctx, m_curWindowNo, "Record_ID");
			AEnv.zoom(AD_Table_ID, Record_ID);
			return;
		}   //  Zoom

		//  save first	---------------
		if (m_curTab.needSave(true, false))
			if (!cmd_save(true))
				return;
		// Save included tabs if necessary - teo_sarca BF [ 2876892 ]
		for (GridTab includedTab : m_curTab.getIncludedTabs())
		{
			if (includedTab.needSave(true, false))
				if(!includedTab.dataSave(true))
					return;
		}
		//
		int table_ID = m_curTab.getAD_Table_ID();
		//	Record_ID
		int record_ID = m_curTab.getRecord_ID();
		//	Record_ID - Language Handling
		if (record_ID == -1 && m_curTab.getKeyColumnName().equals("AD_Language"))
			record_ID = Env.getContextAsInt (m_ctx, m_curWindowNo, "AD_Language_ID");
		//	Record_ID - Change Log ID
		if (record_ID == -1
			&& (vButton.getProcess_ID() == 306 || vButton.getProcess_ID() == 307))
		{
			Integer id = (Integer)m_curTab.getValue("AD_ChangeLog_ID");
			record_ID = id.intValue();
		}
		//	Ensure it's saved
		if (record_ID == -1 && m_curTab.getKeyColumnName().endsWith("_ID"))
		{
			ADialog.error(m_curWindowNo, this, "SaveErrorRowNotFound");
			return;
		}

		boolean isProcessMandatory = false;

		//	Pop up Payment Rules
		if (col.equals("PaymentRule"))
		{
			VPayment vp = new VPayment(m_curWindowNo, m_curTab, vButton);
			if (vp.isInitOK())		//	may not be allowed
				vp.setVisible(true);
			vp.dispose();
			if (vp.needSave())
			{
				cmd_save(false);
				cmd_refresh();
			}
		}	//	PaymentRule

		//	Pop up Document Action (Workflow)
		else if (col.equals("DocAction"))
		{
			isProcessMandatory = true;
			VDocAction vda = new VDocAction(m_curWindowNo, m_curTab, vButton, record_ID);
			//	Something to select from?
			if (vda.getNumberOfOptions() == 0)
			{
				vda.dispose ();
				log.info("DocAction - No Options");
				return;
			}
			else
			{
				vda.setVisible(true);
				if (!vda.isStartProcess())
					return;
//				batch = vda.isBatch();
				startWOasking = true;
				vda.dispose();
			}
		}	//	DocAction

		//  Pop up Create From
		else if (col.equals("CreateFrom"))
		{
			// Run form only if the button has no process defined - teo_sarca [ 1974354 ]
			if (vButton.getProcess_ID() <= 0)
			{
				ICreateFrom cf = VCreateFromFactory.create(m_curTab);
				if(cf != null)
				{
					if(cf.isInitOK())
					{
						cf.showWindow();
						cf.closeWindow();
						m_curTab.dataRefresh();
					}
					else
						cf.closeWindow();
					return;
				}
				//	else may start process
			}
		}	//	CreateFrom

		//  Posting -----
		else if (col.equals("Posted") && MRole.getDefault().isShowAcct())
		{
			//  Check Doc Status
			String processed = Env.getContext(m_ctx, m_curWindowNo, "Processed");
			if (!processed.equals("Y"))
			{
				String docStatus = Env.getContext(m_ctx, m_curWindowNo, "DocStatus");
				if (DocAction.STATUS_Completed.equals(docStatus)
					|| DocAction.STATUS_Closed.equals(docStatus)
					|| DocAction.STATUS_Reversed.equals(docStatus)
					|| DocAction.STATUS_Voided.equals(docStatus))
					;
				else
				{
					ADialog.error(m_curWindowNo, this, "PostDocNotComplete");
					return;
				}
			}

			// try to get table and record id from context data (eg for unposted view)
			// otherwise use current table/record
			int tableId = Env.getContextAsInt(m_ctx, m_curWindowNo, "AD_Table_ID", true);
			int recordId = Env.getContextAsInt(m_ctx, m_curWindowNo, "Record_ID", true);
			if ( tableId == 0 || recordId == 0 )
			{
				tableId = m_curTab.getAD_Table_ID();
				recordId = m_curTab.getRecord_ID();
			}

			//  Check Post Status
			Object ps = m_curTab.getValue("Posted");
			if (ps != null && ps.equals("Y"))
			{
				new org.compiere.acct.AcctViewer (Env.getContextAsInt (m_ctx, m_curWindowNo, "AD_Client_ID"),
					tableId, recordId);
			}
			else
			{
				if (ADialog.ask(m_curWindowNo, this, "PostImmediate?"))
				{
					boolean force = ps != null && !ps.equals ("N");		//	force when problems
					String error = AEnv.postImmediate (m_curWindowNo, Env.getAD_Client_ID(m_ctx),
						tableId, recordId, force);
					if (error != null)
						ADialog.error(m_curWindowNo, this, "PostingError-N", error);
					cmd_refresh();
				}
			}
			return;
		}   //  Posted

		/**
		 *  Start Process ----
		 *  or invoke user form
		 */

		log.config("Process_ID=" + vButton.getProcess_ID() + ", Record_ID=" + record_ID);
		if (vButton.getProcess_ID() == 0)
		{
			if (isProcessMandatory)
			{
				ADialog.error(m_curWindowNo, this, null, Msg.parseTranslation(m_ctx, "@NotFound@ @AD_Process_ID@"));
			}
			return;
		}
		//	Save item changed
		if (m_curTab.needSave(true, false))
			if (!cmd_save(true))
				return;

		// call form
		MProcess pr = new MProcess(m_ctx, vButton.getProcess_ID(), null);
		int form_ID = pr.getAD_Form_ID();
		if (form_ID != 0 )
		{

			if (m_curTab.needSave(true, false))
				if (!cmd_save(true))
					return;

			FormFrame ff = new FormFrame(getGraphicsConfiguration());
			String title = vButton.getDescription();
			if (title == null || title.length() == 0)
				title = vButton.getName();
			ProcessInfo pi = new ProcessInfo (title, vButton.getProcess_ID(), table_ID, record_ID);
			pi.setAD_User_ID (Env.getAD_User_ID(m_ctx));
			pi.setAD_Client_ID (Env.getAD_Client_ID(m_ctx));
			ff.setProcessInfo(pi);
			ff.openForm(form_ID);
			ff.pack();
			AEnv.showCenterScreen(ff);
			return;
		}
		else {
			ProcessModalDialog dialog = new ProcessModalDialog(m_ctx, Env.getWindow(m_curWindowNo), Env.getHeader(m_ctx, m_curWindowNo),
					this, m_curWindowNo, vButton.getProcess_ID(), table_ID,
					record_ID, startWOasking);
			if (dialog.isValidDialog())
			{
				dialog.validate();
				dialog.pack();
				AEnv.showCenterWindow(Env.getWindow(m_curWindowNo), dialog);
			}
		}
	}	//	actionButton


	/**************************************************************************
	 *  Lock User Interface.
	 *  Called from the Worker before processing
	 *  @param pi process info
	 */
	public void lockUI (ProcessInfo pi)
	{
	//	log.fine("" + pi);
		setBusy(true, false);
	}   //  lockUI

	/**
	 *  Unlock User Interface.
	 *  Called from the Worker when processing is done
	 *  @param pi of execute ASync call
	 */
	public void unlockUI (ProcessInfo pi)
	{
	//	log.fine("" + pi);
		boolean notPrint = pi != null
			&& pi.getAD_Process_ID() != m_curTab.getAD_Process_ID()
			&& pi.isReportingProcess() == false;
		//
		setBusy(false, notPrint);
		//  Process Result
		if (notPrint)		//	refresh if not print
		{
			//	Refresh data
			m_curTab.dataRefresh();
			//	Timeout
			if (pi.isTimeout())		//	set temporarily to R/O
				Env.setContext(m_ctx, m_curWindowNo, "Processed", "Y");
			m_curGC.dynamicDisplay(0);
			//	Update Status Line
			setStatusLine(pi.getSummary(), pi.isError());
			if ( pi.isError() )
				ADialog.error(m_curWindowNo, this, null, pi.getSummary());
			//	Get Log Info
			ProcessInfoUtil.setLogFromDB(pi);
			String logInfo = pi.getLogInfo();
			if (logInfo.length() > 0)
				ADialog.info(m_curWindowNo, this, Env.getHeader(m_ctx, m_curWindowNo),
					pi.getTitle(), logInfo);	//	 clear text
		}
		else
		{
			//	Update Status Line
			setStatusLine(pi.getSummary(), pi.isError());
			if ( pi.isError() )
				ADialog.error(m_curWindowNo, this, null, pi.getSummary());
		}
	}   //  unlockUI

	/**
	 *  Is the UI locked (Internal method)
	 *  @return true, if UI is locked
	 */
	public boolean isUILocked()
	{
		return m_isLocked;
	}   //  isLoacked

	/**
	 *  Method to be executed async.
	 *  Called from the ASyncProcess worker
	 *  @param pi process info
	 */
	public void executeASync (ProcessInfo pi)
	{
		log.config("-");
	}   //  executeASync

	/**
	 * 	Get Current Tab
	 *	@return current tab
	 */
	public GridTab getCurrentTab()
	{
		return m_curTab;
	}	//	getCurrentTab

	/**
	 * Get the number of tabs in the panels JTabbedPane.
	 * @return no of tabs in the JTabbedPane of the panel
	 */
	public int noOfTabs() {
 		return m_curWinTab.getTabCount();
	}

	/**
	 * Get the selected tab index of the panels JTabbedPane.
	 * @return selected index of JTabbedPane
	 */
	public int getSelectedTabIndex() {
 		return m_curWinTab.getSelectedIndex();
	}

	/**
	 * Set the tab index of the panels JTabbedPane.
	 */
	public void setSelectedTabIndex(int index) {
 		m_curWinTab.setSelectedIndex(index);
	}

	/**
	 * Get the name of the selected tab in the panels JTabbedPane.
	 * @return name of selected tab
	 */
	public String getSelectedTabName() {
 		String title = m_curWinTab.getTitleAt(m_curWinTab.getSelectedIndex());
 		title = title.substring(title.indexOf("<html>")+6);
 		title = title.substring(0,title.indexOf('<'));
 		return title;
	}

	/**
	 *  String representation
	 *  @return String representation
	 */
	public String toString()
	{
		String s = "APanel[curWindowNo=" + m_curWindowNo;
		if (m_mWorkbench != null)
			s += ",WB=" + m_mWorkbench.toString();
		s += "]";
		return s;
	}   //  toString

	/**
	 * Simple action class for the resort of tablelines (switch line no). Delegates actionPerformed
	 * to APanel.
	 *
	 * @author Karsten Thiemann, kthiemann@adempiere.org
	 *
	 */
	class SwitchAction extends AbstractAction {
		/**
		 *
		 */
		private static final long serialVersionUID = 3837712049468116744L;

		/** the action listener - APanel */
		private ActionListener al;

		/** action name */
		private String name;

		/**
		 * Constructor.
		 * @param name
		 * @param accelerator
		 * @param al
		 */
		SwitchAction(String name, KeyStroke accelerator, ActionListener al) {
			super(name);
			putValue(Action.NAME, name); // Display
			putValue(Action.SHORT_DESCRIPTION, name); // Tooltip
			putValue(Action.ACCELERATOR_KEY, accelerator); // KeyStroke
			putValue(Action.ACTION_COMMAND_KEY, name); // ActionCammand
			this.al = al;
			this.name = name;
		}

		public void actionPerformed(ActionEvent e) {
			al.actionPerformed(e);
		} // actionPerformed

		public String getName() {
			return name;
		}
	}

	/**
	 * Removes the default KeyStroke action for the up/down keys and adds switch
	 * line actions.
	 */
	private void initSwitchLineAction() {
		aSwitchLinesDownAction = new SwitchAction("switchLinesDown", KeyStroke.getKeyStroke(
				KeyEvent.VK_DOWN, Event.SHIFT_MASK), this);
		aSwitchLinesUpAction = new SwitchAction("switchLinesUp", KeyStroke.getKeyStroke(
				KeyEvent.VK_UP, Event.SHIFT_MASK), this);

		JTable table = m_curGC.getTable();
		table.getInputMap(CPanel.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(
				KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, Event.SHIFT_MASK), "none");
		table.getInputMap(CPanel.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(
				KeyStroke.getKeyStroke(KeyEvent.VK_UP, Event.SHIFT_MASK), "none");
		table.getInputMap(CPanel.WHEN_FOCUSED).put(
				KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, Event.SHIFT_MASK), "none");
		table.getInputMap(CPanel.WHEN_FOCUSED).put(
				KeyStroke.getKeyStroke(KeyEvent.VK_UP, Event.SHIFT_MASK), "none");

		getInputMap(CPanel.WHEN_IN_FOCUSED_WINDOW).put(
				KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, Event.SHIFT_MASK),
				aSwitchLinesDownAction.getName());
		getActionMap().put(aSwitchLinesDownAction.getName(), aSwitchLinesDownAction);
		getInputMap(CPanel.WHEN_IN_FOCUSED_WINDOW).put(
				KeyStroke.getKeyStroke(KeyEvent.VK_UP, Event.SHIFT_MASK),
				aSwitchLinesUpAction.getName());
		getActionMap().put(aSwitchLinesUpAction.getName(), aSwitchLinesUpAction);
	}

	public boolean isNested() {
		return isNested;
	}

}	//	APanel
