/******************************************************************************
* 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.grid;
import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Event;
import java.awt.event.KeyEvent;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import javax.swing.BorderFactory;
import javax.swing.Box;
import javax.swing.JPanel;
import javax.swing.JSeparator;
import javax.swing.KeyStroke;
import net.miginfocom.layout.BoundSize;
import net.miginfocom.layout.ComponentWrapper;
import net.miginfocom.layout.ConstraintParser;
import net.miginfocom.layout.LC;
import net.miginfocom.layout.LayoutCallback;
import net.miginfocom.layout.UnitValue;
import net.miginfocom.swing.MigLayout;
import net.miginfocom.swing.SwingComponentWrapper;
import net.miginfocom.swing.SwingContainerWrapper;
import org.adempiere.plaf.AdempierePLAF;
import org.compiere.apps.APanel;
import org.compiere.grid.ed.VButton;
import org.compiere.grid.ed.VCheckBox;
import org.compiere.grid.ed.VEditor;
import org.compiere.grid.ed.VEditorFactory;
import org.compiere.grid.ed.VLine;
import org.compiere.model.GridField;
import org.compiere.model.X_AD_FieldGroup;
import org.compiere.swing.CLabel;
import org.compiere.swing.CPanel;
import org.compiere.swing.CTabbedPane;
import org.compiere.swing.CollapsiblePanel;
import org.compiere.util.CLogger;
import org.compiere.util.DB;
import org.compiere.util.DisplayType;
import org.compiere.util.Language;
import org.compiere.util.Util;
import org.jdesktop.swingx.JXCollapsiblePane;
import org.jdesktop.swingx.border.DropShadowBorder;
import org.omidp.util.LocaleUtil;
import com.lowagie.text.Font;
/**
* Single Row Panel.
* Called from GridController
*
* Uses MigLayout
*
* @author Jorg Janke
* @version $Id: VPanel.java,v 1.3 2006/07/30 00:51:28 jjanke Exp $
* @contributor Victor Perez , e-Evolution.SC FR [ 1757088 ]
* Carlos Ruiz - globalqss / Fix bug 2307133 - Swing client hiding fields incorrectly
*/
public final class VPanel extends CTabbedPane
{
/**
*
*/
private static final long serialVersionUID = 8227080366665213627L;
private int m_WindowNo;
// show layout grid
private boolean debug = false;
private String defaultLayoutConstraints;
private int labelMinWidth = 0;
private int fieldMinWidth = 0;
public VPanel(String Name)
{
this(Name, 0);
}
/**
*
* @param Name
* @param WindowNo
*/
public VPanel(String Name, int WindowNo)
{
m_WindowNo = WindowNo;
defaultLayoutConstraints = "hidemode 3"; // hidden fields take no part in grid
defaultLayoutConstraints += debug ? ", debug" : "";
//[ 1757088 ]
m_main.setName(Name);
String constraints = defaultLayoutConstraints;
MigLayout mainLayout = new MigLayout(constraints);
mainLayout.addLayoutCallback(callback);
m_main.setLayout(mainLayout);
//omidp
LocaleUtil.applyComponentOrientation(m_main);
m_tablist.put("main", m_main);
this.setBorder(marginBorder);
CPanel dummy = new CPanel();
dummy.setLayout(new BorderLayout());
dummy.add(m_main, BorderLayout.NORTH);
dummy.setName(m_main.getName());
this.add(dummy);
} // VPanel
/** Orientation */
private final boolean m_leftToRight = Language.getLoginLanguage().isLeftToRight();
/** Previous Field Group Header */
private String m_oldFieldGroup = null;
/** Previous Field Group Type */
private String m_oldFieldGroupType = null;
//[ 1757088 ]
private java.util.Hashtable<String, JPanel> m_tablist = new java.util.Hashtable<String, JPanel>();
private java.util.Hashtable<Integer, CollapsiblePanel> m_tabincludelist = new java.util.Hashtable<Integer, CollapsiblePanel>();
private CPanel m_main = new CPanel(org.compiere.plaf.CompiereColor.getDefaultBackground());
private DropShadowBorder marginBorder = new DropShadowBorder();
/**
* Use layout callback to implement size groups across panels
* and to shrink hidden components vertically.
*/
private LayoutCallback callback = new LayoutCallback() {
// Set the min and preferred sizes to match the largest component
public BoundSize[] getSize(ComponentWrapper comp)
{
if (comp.getComponent() instanceof CLabel) {
Component c = (Component) comp.getComponent();
int w = labelMinWidth;
int h = c.getPreferredSize().height;
BoundSize width = ConstraintParser.parseBoundSize(w + ":" + w, false, true); // min:pref
BoundSize height = ConstraintParser.parseBoundSize(h + ":" + h, false, false);
return new BoundSize[] {width, height};
}
else
{
Component c = (Component) comp.getComponent();
int w = fieldMinWidth;
int h = c.getPreferredSize().height;
BoundSize width = ConstraintParser.parseBoundSize(w + ":" + w, false, true);
BoundSize height = ConstraintParser.parseBoundSize(h + ":" + h, false, false);
return new BoundSize[] {width, height};
}
}//
public void correctBounds(ComponentWrapper comp)
{
}
};
private VEditor prevEditor = null;
private GridField prevField = null;
private boolean wrap = false;
/** Logger */
private static CLogger log = CLogger.getCLogger (VPanel.class);
/**
* Set Field Mnemonic
* @param mField field
*/
public void setMnemonic (GridField mField)
{
if (mField.isCreateMnemonic())
return;
String text = mField.getHeader();
int pos = text.indexOf('&');
if (pos != -1 && text.length() > pos) // We have a mnemonic - creates Ctrl_Shift_
{
char mnemonic = text.toUpperCase().charAt(pos+1);
if (mnemonic != ' ')
{
if (!m_mnemonics.contains(mnemonic))
{
mField.setMnemonic(mnemonic);
m_mnemonics.add(mnemonic);
}
else
log.warning(mField.getColumnName()
+ " - Conflict - Already exists: " + mnemonic + " (" + text + ")");
}
}
} // setMnemonic
/**
* Add Field and Label to buffer and push buffered field to Panel
* @param editor editor
* @param mField field model
*/
public void addFieldBuffered (VEditor editor, GridField mField)
{
wrap = mField != null ? !mField.isSameLine() : false;
if ( prevEditor != null && prevField != null)
addField(prevEditor, prevField);
prevEditor = editor;
prevField = mField;
}
/**
* Add the previous Field and Label to Panel
* @param editor editor
* @param mField field model
* @param wrap move to next line after this field
*/
public void addField (VEditor editor, GridField mField)
{
//[ 1757088 ]
int AD_Tab_ID = mField.getIncluded_Tab_ID();
if(AD_Tab_ID != 0 )
{
CollapsiblePanel m_tab = new CollapsiblePanel("");
m_tabincludelist.put(AD_Tab_ID, m_tab);
m_main.add(m_tab,"newline, spanx, growx");
m_tab.setName("IncludedTab#"+AD_Tab_ID);
if (includedTabList.containsKey(AD_Tab_ID))
{
includeTab(includedTabList.get(AD_Tab_ID));
}
return;
}
CLabel label = VEditorFactory.getLabel(mField);
if (label == null && editor == null)
return;
boolean sameLine = mField.isSameLine();
//[ 1757088 ] // sets top
String fieldGroup = mField.getFieldGroup();
String fieldGroupType = mField.getFieldGroupType();
if (Util.isEmpty(fieldGroup))
{
fieldGroup = m_oldFieldGroup;
fieldGroupType = m_oldFieldGroupType;
}
if (addGroup(fieldGroup, fieldGroupType)) // sets top
{
if (X_AD_FieldGroup.FIELDGROUPTYPE_Collapse.equals(fieldGroupType))
{
CollapsiblePanel panel = (CollapsiblePanel) m_tablist.get(fieldGroup);
LocaleUtil.applyComponentOrientation(panel);
panel.getCollapsiblePane().setCollapsed(mField.getIsCollapsedByDefault());
}
sameLine = false;
}
else
{
fieldGroup = m_oldFieldGroup;
fieldGroupType = m_oldFieldGroupType;
}
// label constraints
String constraints = "align trailing";
if ( !sameLine )
{
constraints += ", newline";
}
// *** The Label ***
if ( label == null)
{
label = new CLabel("");
label.setName(mField.getColumnName());
}
//
if (mField.isCreateMnemonic())
setMnemonic(label, mField.getMnemonic());
if (fieldGroupType.equals(X_AD_FieldGroup.FIELDGROUPTYPE_Tab))
{
CPanel m_tab = (CPanel)m_tablist.get(fieldGroup);
LocaleUtil.applyComponentOrientation(m_tab);
m_tab.add(label, constraints);
}
else if (fieldGroupType.equals(X_AD_FieldGroup.FIELDGROUPTYPE_Collapse))
{
CollapsiblePanel m_tab = (CollapsiblePanel) m_tablist.get(fieldGroup);
LocaleUtil.applyComponentOrientation(m_tab);
m_tab.getCollapsiblePane().getContentPane().add(label, constraints);
}
else // Label
{
m_main.add(label, constraints);
}
int currentWidth = label.getPreferredSize().width;
labelMinWidth = currentWidth > labelMinWidth ? currentWidth : labelMinWidth;
// *** The Field ***
if (editor != null)
{
Component field = (Component)editor;
// field constraints
// long fields span all remaining columns
constraints = "growx, pushx";
if ( mField.isLongField() )
{
constraints += mField.isLongField() ? ",spanx" : "";
}
if ( wrap )
{
// Vertical Spacing in Swing Client
// - https://sourceforge.net/tracker/?func=detail&aid=3073406&group_id=176962&atid=883808
constraints += ", wrap 0";
}
// Add Field
//[ 1757088 ]
if (fieldGroupType.equals(X_AD_FieldGroup.FIELDGROUPTYPE_Tab))
{
CPanel m_tab = (CPanel)m_tablist.get(fieldGroup);
m_tab.add(field, constraints);
}
else if (fieldGroupType.equals(X_AD_FieldGroup.FIELDGROUPTYPE_Collapse))
{
CollapsiblePanel m_tab = (CollapsiblePanel) m_tablist.get(fieldGroup);
Component comp = (Component) editor;
comp.setMinimumSize(comp.getPreferredSize());
if (mField.getDisplayType() == DisplayType.Text
|| mField.getDisplayType() == DisplayType.Memo
||mField.getDisplayType() == DisplayType.TextLong )
{
Component component = (Component) editor;
component.setMinimumSize(component.getPreferredSize());
}
if (!mField.isLongField())
fieldMinWidth = field.getPreferredSize().width > fieldMinWidth ? field.getPreferredSize().width : fieldMinWidth;
m_tab.getCollapsiblePane().getContentPane().add(field, constraints);
}
else // Label or null
{
m_main.add(field, constraints);
if (!mField.isLongField())
fieldMinWidth = field.getPreferredSize().width > fieldMinWidth ? field.getPreferredSize().width : fieldMinWidth;
}
// Link Label to Field
if (label != null)
label.setLabelFor(field);
else if (mField.isCreateMnemonic())
setMnemonic(editor, mField.getMnemonic());
}
} // addField
/**
* Add Group
* @param fieldGroup field group
* @param fieldGroupType
* @return true if group added
*/
private boolean addGroup(String fieldGroup, String fieldGroupType)
{
// First time - add top
if (m_oldFieldGroup == null)
{
m_oldFieldGroup = "";
m_oldFieldGroupType = "";
}
if (fieldGroup == null || fieldGroup.length() == 0 || fieldGroup.equals(m_oldFieldGroup))
return false;
//[ 1757088 ]
if (m_tablist.get(fieldGroup) != null)
{
return false;
}
//[ 1757088 ]
if (fieldGroupType.equals(X_AD_FieldGroup.FIELDGROUPTYPE_Tab))
{
CPanel m_tab = new CPanel();
LocaleUtil.applyComponentOrientation(m_tab);
m_tab.setBackground(AdempierePLAF.getFormBackground());
String tpConstraints = defaultLayoutConstraints ;
MigLayout layout = new MigLayout(tpConstraints);
layout.addLayoutCallback(callback);
m_tab.setLayout(layout);
m_tab.setName(fieldGroup);
CPanel dummy = new CPanel();
LocaleUtil.applyComponentOrientation(dummy);
dummy.setLayout(new BorderLayout());
dummy.add(m_tab, BorderLayout.NORTH);
dummy.setName(m_tab.getName());
dummy.setBorder(BorderFactory.createEmptyBorder(10,12,0,12));
this.add(dummy);
m_tablist.put(fieldGroup, m_tab);
}
else if (fieldGroupType.equals(X_AD_FieldGroup.FIELDGROUPTYPE_Collapse))
{
CollapsiblePanel collapsibleSection = new CollapsiblePanel(fieldGroup);
JXCollapsiblePane m_tab = collapsibleSection.getCollapsiblePane();
LocaleUtil.applyComponentOrientation(m_tab);
m_tab.setAnimated(false);
m_tab.getContentPane().setBackground(AdempierePLAF.getFormBackground());
String cpConstraints = defaultLayoutConstraints;
// 0 inset left and right as this is a nested panel
// 0 inset top because of the struts added below
cpConstraints += ", ins 0 0 n 0";
MigLayout layout = null;
if(LocaleUtil.isIranLocale()){
LC lc = new LC();
lc.setLeftToRight(false);
//TODO : high expansion in y
layout = new MigLayout(lc);
}else{
layout = new MigLayout(cpConstraints);
}
layout.addLayoutCallback(callback);
collapsibleSection.setName(fieldGroup);
m_main.add(collapsibleSection, "newline, spanx, growx");
m_tab.setLayout(layout);
/* for compatibility with old layout, force collapsible field groups
* to have a minimum of two columns by inserting invisible components
*/
Component strut1 = Box.createVerticalStrut(1);
strut1.setName("vstrut1"+fieldGroup);
Component strut2 = Box.createVerticalStrut(1);
strut2.setName("vstrut2"+fieldGroup);
m_tab.add(new CLabel(""), "gap 0 0 0 0");
m_tab.add(strut1, "pushx, growx, gap 0 0 0 0");
m_tab.add(new CLabel(""), "");
m_tab.add(strut2, "pushx, growx, gap 0 0 0 0, wrap");
m_tablist.put(fieldGroup, collapsibleSection);
}
else // Label or null
{
CLabel label = new CLabel(fieldGroup, CLabel.LEADING);
label.setFont(AdempierePLAF.getFont_Label().deriveFont(Font.BOLDITALIC, AdempierePLAF.getFont_Label().getSize2D()));
m_main.add(label, "newline, alignx leading");
m_main.add(new JSeparator(), "newline, spanx, growx");
// reset
}
m_oldFieldGroup = fieldGroup;
m_oldFieldGroupType = fieldGroupType;
return true;
} // addGroup
/**
* Set Mnemonic for Label CTRL_SHIFT_x
* @param label label
* @param predefinedMnemonic predefined Mnemonic
*/
private void setMnemonic (CLabel label, char predefinedMnemonic)
{
String text = label.getText();
int pos = text.indexOf('&');
if (pos != -1 && predefinedMnemonic != 0)
{
text = text.substring(0, pos) + text.substring(pos+1);
label.setText(text);
label.setSavedMnemonic(predefinedMnemonic);
m_fields.add(label);
log.finest(predefinedMnemonic + " - " + label.getName());
}
else
{
char mnemonic = getMnemonic(text, label);
if (mnemonic != 0)
label.setSavedMnemonic(mnemonic);
// label.setDisplayedMnemonic(mnemonic);
}
} // setMnemonic
/**
* Set Mnemonic for Check Box or Button
* @param editor check box or button - other ignored
* @param predefinedMnemonic predefined Mnemonic
*/
private void setMnemonic (VEditor editor, char predefinedMnemonic)
{
if (editor instanceof VCheckBox)
{
VCheckBox cb = (VCheckBox)editor;
String text = cb.getText();
int pos = text.indexOf('&');
if (pos != -1 && predefinedMnemonic != 0)
{
text = text.substring(0, pos) + text.substring(pos+1);
cb.setText(text);
cb.setSavedMnemonic(predefinedMnemonic);
m_fields.add(cb);
log.finest(predefinedMnemonic + " - " + cb.getName());
}
else
{
char mnemonic = getMnemonic(text, cb);
if (mnemonic != 0)
cb.setSavedMnemonic(mnemonic);
// cb.setMnemonic(mnemonic);
}
}
// Button
else if (editor instanceof VButton)
{
VButton b = (VButton)editor;
String text = b.getText();
int pos = text.indexOf('&');
if (pos != -1 && predefinedMnemonic != 0)
{
text = text.substring(0, pos) + text.substring(pos+1);
b.setText(text);
b.setSavedMnemonic(predefinedMnemonic);
m_fields.add(b);
log.finest(predefinedMnemonic + " - " + b.getName());
}
else if (b.getColumnName().equals("DocAction"))
{
b.getInputMap(WHEN_IN_FOCUSED_WINDOW)
.put(KeyStroke.getKeyStroke(KeyEvent.VK_F4, Event.SHIFT_MASK, false), "pressed");
b.getInputMap(WHEN_IN_FOCUSED_WINDOW)
.put(KeyStroke.getKeyStroke(KeyEvent.VK_F4, Event.SHIFT_MASK, true), "released");
// Util.printActionInputMap(b);
}
else if (b.getColumnName().equals("Posted"))
{
b.getInputMap(WHEN_IN_FOCUSED_WINDOW)
.put(KeyStroke.getKeyStroke(KeyEvent.VK_F5, Event.SHIFT_MASK, false), "pressed");
b.getInputMap(WHEN_IN_FOCUSED_WINDOW)
.put(KeyStroke.getKeyStroke(KeyEvent.VK_F5, Event.SHIFT_MASK, true), "released");
// Util.printActionInputMap(b);
}
else
{
char mnemonic = getMnemonic(text, b);
if (mnemonic != 0)
b.setSavedMnemonic(mnemonic);
}
}
} // setMnemonic
/**
* Get Mnemonic from text
* @param text text
* @param source component
* @return Mnemonic or 0 if not unique
*/
private char getMnemonic (String text, Component source)
{
if (text == null || text.length() == 0)
return 0;
String oText = text;
text = text.trim().toUpperCase();
char mnemonic = text.charAt(0);
if (m_mnemonics.contains(mnemonic))
{
mnemonic = 0;
// Beginning new word
int index = text.indexOf(' ');
while (index != -1 && text.length() > index)
{
char c = text.charAt(index+1);
if (Character.isLetterOrDigit(c) && !m_mnemonics.contains(c))
{
mnemonic = c;
break;
}
index = text.indexOf(' ', index+1);
}
// Any character
if (mnemonic == 0)
{
for (int i = 1; i < text.length(); i++)
{
char c = text.charAt(i);
if (Character.isLetterOrDigit(c) && !m_mnemonics.contains(c))
{
mnemonic = c;
break;
}
}
}
// Nothing found
if (mnemonic == 0)
{
log.finest("None for: " + oText);
return 0; // if first char would be returned, the first occurrence is invalid.
}
}
m_mnemonics.add(mnemonic);
m_fields.add(source);
log.finest(mnemonic + " - " + source.getName());
return mnemonic;
} // getMnemonic
/** Used Mnemonics */
private ArrayList<Character> m_mnemonics = new ArrayList<Character>(30);
/** Mnemonic Fields */
private ArrayList<Component> m_fields = new ArrayList<Component>(30);
private HashMap<Integer, GridController> includedTabList = new HashMap<Integer, GridController>();
/**
* Set Window level Mnemonics
* @param set true if set otherwise unregister
*/
public void setMnemonics (boolean set)
{
int size = m_fields.size();
for (int i = 0; i < size; i++)
{
Component c = m_fields.get(i);
if (c instanceof CLabel)
{
CLabel l = (CLabel)c;
if (set)
l.setDisplayedMnemonic(l.getSavedMnemonic());
else
l.setDisplayedMnemonic(0);
}
else if (c instanceof VCheckBox)
{
VCheckBox cb = (VCheckBox)c;
if (set)
cb.setMnemonic(cb.getSavedMnemonic());
else
cb.setMnemonic(0);
}
else if (c instanceof VButton)
{
VButton b = (VButton)c;
if (set)
b.setMnemonic(b.getSavedMnemonic());
else
b.setMnemonic(0);
}
}
} // setMnemonics
/**************************************************************************
* Set Background to AD_Color_ID (nop)
* @param AD_Color_ID Color
*/
public void setBackground (int AD_Color_ID)
{
} // setBackground
private void findChildComponents(CPanel container, List<Component> list)
{
Component[] comp = container.getComponents();
for (int c = 0; c < comp.length; c++)
{
list.add(comp[c]);
if ( comp [c] instanceof CollapsiblePanel)
{
CollapsiblePanel collapsiblePanel = (CollapsiblePanel)comp [c];
Component[] nestedComps = collapsiblePanel.getCollapsiblePane()
.getContentPane().getComponents();
for (int y = 0; y < nestedComps.length; y++)
{
if ( nestedComps [y] instanceof CPanel)
{
CPanel nestedPanel = (CPanel)nestedComps[y];
Component[] nestedPanelComps = nestedPanel.getComponents();
for (int x = 0; x < nestedPanelComps.length; x++)
{
list.add(nestedPanelComps[x]);
}
}
else
{
list.add(nestedComps[y]);
}
}
} else if (comp[c] instanceof CPanel)
{
findChildComponents((CPanel)comp[c], list);
}
}
}
//[ 1757088 ]
public Component[] getComponentsRecursive()
{
java.util.ArrayList<Component> list = new java.util.ArrayList<Component>();
for (int i = 0; i < this.getTabCount(); i++)
{
list.add(this.getComponentAt(i));
if (this.getComponentAt(i) instanceof CPanel)
{
CPanel panel = (CPanel)this.getComponentAt(i);
findChildComponents(panel, list);
}
}
Component[] result = new Component[list.size ()];
list.toArray (result);
return result;
}
/**
*
* @param detail
*/
public void includeTab(GridController detail) {
CollapsiblePanel section = m_tabincludelist.get(detail.getMTab().getAD_Tab_ID());
if(section != null)
{
APanel panel = new APanel(detail, m_WindowNo);
LocaleUtil.applyComponentOrientation(panel);
String name = detail.getMTab().getName() + "";
section.setTitle(name);
section.getCollapsiblePane().getContentPane().setLayout(new BorderLayout());
section.getCollapsiblePane().getContentPane().add(panel, BorderLayout.CENTER);
}
//this can be call before addField
if (!includedTabList.containsKey(detail.getMTab().getAD_Tab_ID()))
includedTabList.put(detail.getMTab().getAD_Tab_ID(), detail);
}
} // VPanel