/****************************************************************************** * 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