/******************************************************************************
* Product: Posterita Ajax UI *
* Copyright (C) 2007 Posterita Ltd. All Rights Reserved. *
* This program is free software; you can redistribute it and/or modify it *
* under the terms version 2 of the GNU General Public License as published *
* by the Free Software Foundation. This program is distributed in the hope *
* that it will be useful, but WITHOUT ANY WARRANTY; without even the implied *
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. *
* See the GNU General Public License for more details. *
* You should have received a copy of the GNU General Public License along *
* with this program; if not, write to the Free Software Foundation, Inc., *
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. *
* For the text or an alternative of this public license, you may reach us *
* Posterita Ltd., 3, Draper Avenue, Quatre Bornes, Mauritius *
* or via info@posterita.org or http://www.posterita.org/ *
*****************************************************************************/
package org.adempiere.webui.adwindow;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.adempiere.webui.panel.CustomizeGridViewPanel;
import org.adempiere.webui.part.AbstractUIPart;
import org.compiere.model.DataStatusEvent;
import org.compiere.model.GridField;
import org.compiere.model.GridTab;
import org.compiere.model.MTable;
import org.compiere.util.CLogger;
import org.compiere.util.Env;
import org.compiere.util.Evaluator;
import org.compiere.util.Util;
/**
* Abstract base class for header+details AD_Tabs UI for AD_Window.
* @author Ashley G Ramdass
* @author Low Heng Sin
* @date Feb 25, 2007
* @version $Revision: 0.10 $
*/
public abstract class AbstractADTabbox extends AbstractUIPart implements IADTabbox
{
/** Logger **/
private static final CLogger log = CLogger.getCLogger (AbstractADTabbox.class);
/** List of variables/columnName that's reference by one or more gridTab logic expression **/
private ArrayList m_dependents = new ArrayList();
/** List of {@link IADTabpanel} instance manage by this AbstractADTabbox instance **/
protected List tabPanelList = new ArrayList();
/** Parent part, the content part of AD Window **/
protected AbstractADWindowContent adWindowPanel;
/**
* default constructor
*/
public AbstractADTabbox()
{
}
/**
* Add new tab(AD_Tab).
* Delegate to {@link #doAddTab(GridTab, IADTabpanel)}
* @param gTab grid tab model
* @param tabPanel
*/
public void addTab(GridTab gTab, IADTabpanel tabPanel)
{
tabPanelList.add(tabPanel);
ArrayList dependents = gTab.getDependentOn();
for (int i = 0; i < dependents.size(); i++)
{
String name = dependents.get(i);
if (!m_dependents.contains(name))
{
m_dependents.add(name);
}
}
doAddTab(gTab, tabPanel);
}// addTab
/**
* Handle add new tab to UI.
* Override to implement add new tab to UI.
* @param tab
* @param tabPanel
*/
protected abstract void doAddTab(GridTab tab, IADTabpanel tabPanel);
/**
* @param index of tab panel
* @return true if enable, false otherwise
*/
public boolean isEnabledAt(int index)
{
return true;
}// isEnabledAt
/**
* Evaluate display logic
* @param tabPanel
* @return true if visible, false otherwise
*/
private boolean isDisplay(IADTabpanel tabPanel)
{
String logic = tabPanel.getDisplayLogic();
if (logic != null && logic.length() > 0)
{
boolean display = Evaluator.evaluateLogic(tabPanel, logic);
if (!display)
{
log.info("Not displayed - " + logic);
return false;
}
}
return true;
}
/**
* Change selected tab index from oldIndex to newIndex.
* Delegate to {@link #doTabSelectionChanged(int, int)}.
* @param oldIndex
* @param newIndex
* @return true if successfully switch to newIndex
*/
@Override
public boolean updateSelectedIndex(int oldIndex, int newIndex)
{
IADTabpanel newTab = tabPanelList.get(newIndex);
if (!isDisplay(newTab))
{
return false;
}
boolean canJump = true;
if (newIndex != oldIndex)
{
canJump = canNavigateTo(oldIndex, newIndex, true);
if (canJump)
{
prepareContext(newIndex, newTab);
doTabSelectionChanged(oldIndex, newIndex);
}
}
return canJump;
}
/**
* Prepare environment context for newTab.
* @param newIndex
* @param newTab
*/
private void prepareContext(int newIndex, IADTabpanel newTab) {
//update context
if (newTab != null)
{
List parents = new ArrayList();
//get parent list, always include first tab (0)
if (newIndex > 0)
{
int currentLevel = newTab.getTabLevel();
for (int i = newIndex - 1; i > 0; i--)
{
IADTabpanel adtab = tabPanelList.get(i);
if (adtab.getGridTab() == null) continue;
if (adtab instanceof CustomizeGridViewPanel) continue;
if (adtab.getTabLevel() < currentLevel)
{
parents.add(i);
currentLevel = adtab.getTabLevel();
}
}
parents.add(0);
Collections.reverse(parents);
}
else
{
parents.add(0);
}
//clear context
for (int i = 0; i < tabPanelList.size(); i++)
{
IADTabpanel adtab = tabPanelList.get(i);
if (adtab.getGridTab() == null) continue;
if (adtab instanceof CustomizeGridViewPanel) continue;
GridField[] fields = adtab.getGridTab().getFields();
for (GridField gf : fields)
{
Env.setContext(Env.getCtx(), gf.getWindowNo(), gf.getColumnName(), "");
}
}
//add parent value to context
for(int i : parents)
{
IADTabpanel adtab = tabPanelList.get(i);
GridField[] fields = adtab.getGridTab().getFields();
for (GridField gf : fields)
{
gf.updateContext();
}
}
}
}
/**
* Handle tab selection change event.
* Override to update UI for tab selection change.
* @param oldIndex
* @param newIndex
*/
protected abstract void doTabSelectionChanged(int oldIndex, int newIndex);
/**
* Evaluate display logic
* @param index
* @return true if visible, false otherwise
*/
public boolean isDisplay(int index) {
if (index >= tabPanelList.size())
return false;
IADTabpanel newTab = tabPanelList.get(index);
if (newTab instanceof ADTabpanel)
{
if (!isDisplay(newTab))
{
return false;
}
}
return true;
}
/**
* Delegate to {@link #canNavigateTo(int, int, boolean)}
* @param fromIndex
* @param toIndex
* @return true if can change selected tab from fromIndex to toIndex
*/
@Override
public boolean canNavigateTo(int fromIndex, int toIndex) {
return canNavigateTo(fromIndex, toIndex, false);
}
/**
*
* @param fromIndex
* @param toIndex
* @param checkRecordID true to validate fromIndex has a valid record id
* @return true if can change selected tab from fromIndex to toIndex
*/
public boolean canNavigateTo(int fromIndex, int toIndex, boolean checkRecordID) {
IADTabpanel newTab = tabPanelList.get(toIndex);
if (newTab instanceof ADTabpanel)
{
if (!isDisplay(newTab))
{
return false;
}
}
boolean canJump = true;
if (toIndex != fromIndex)
{
IADTabpanel oldTabpanel = fromIndex >= 0 ? tabPanelList.get(fromIndex) : null;
if (oldTabpanel != null)
{
if (newTab.getTabLevel() > oldTabpanel.getTabLevel())
{
int currentLevel = newTab.getTabLevel();
for (int i = toIndex - 1; i >= 0; i--)
{
IADTabpanel tabPanel = tabPanelList.get(i);
if (tabPanel.getTabLevel() < currentLevel)
{
if (!tabPanel.isCurrent())
{
canJump = false;
break;
}
currentLevel = tabPanel.getTabLevel();
}
}
if (canJump && checkRecordID ) {
int zeroValid = (MTable.isZeroIDTable(oldTabpanel.getTableName()) ? 1 : 0);
if (oldTabpanel.getRecord_ID() + zeroValid <= 0)
canJump = false;
// IDEMPIERE-651 Allow navigating to a detail when parent doesn't have ID
if (! canJump && (Util.isEmpty(oldTabpanel.getGridTab().getKeyColumnName()) || oldTabpanel.getGridTab().getKeyColumnName().endsWith("_UU")))
canJump = true;
}
}
}
}
return canJump;
}
/**
* Get break crumb path
* @return full path
*/
public String getPath() {
StringBuilder path = new StringBuilder();
int s = this.getSelectedIndex();
if (s <= 0 ) s = 0;
IADTabpanel p = tabPanelList.get(s);
for (int i = 0; i <= s; i++) {
String n = null;
if (i == s)
n = p.getTitle();
else {
IADTabpanel t = tabPanelList.get(i);
if (t.getTabLevel() < p.getTabLevel())
n = t.getTitle();
}
if (n != null) {
if (path.length() > 0) {
path.append(" > ");
}
path.append(n);
}
}
return path.toString();
}
/**
* Handle DataStatusEvent.
* Delegate to {@link #updateTabState()}.
* @param e event
*/
public void evaluate (DataStatusEvent e)
{
boolean process = (e == null);
String columnName = null;
if (!process)
{
columnName = e.getColumnName();
if (columnName != null)
process = m_dependents.contains(columnName);
else
process = true;
}
if (process)
{
updateTabState();
}
} // evaluate
/**
* Update UI state of tab (visibility, activation and if need invalidate)
*/
protected abstract void updateTabState();
/**
* @return the number of tab panels present
*/
public int getTabCount()
{
return tabPanelList.size();
}
/**
* @param index
* @return {@link IADTabpanel}
*/
public IADTabpanel getADTabpanel(int index)
{
try
{
IADTabpanel tabPanel = tabPanelList.get(index);
return tabPanel;
}
catch (Exception ex)
{
throw new IndexOutOfBoundsException(ex.getMessage());
}
}
/**
* Set newIndex as selected tab
* Delegate to {@link #updateSelectedIndex(int, int)}
* @param newIndex
*/
@Override
public void setSelectedIndex(int newIndex) {
int oldIndex = getSelectedIndex();
updateSelectedIndex(oldIndex, newIndex);
}
/**
* @param abstractADWindowPanel
*/
public void setADWindowPanel(AbstractADWindowContent abstractADWindowPanel) {
this.adWindowPanel = abstractADWindowPanel;
}
}