/*
Copyright (c) 2003-2008 ITerative Consulting Pty Ltd. All Rights Reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted
provided that the following conditions are met:
o Redistributions of source code must retain the above copyright notice, this list of conditions and
the following disclaimer.
o Redistributions in binary form must reproduce the above copyright notice, this list of conditions
and the following disclaimer in the documentation and/or other materials provided with the distribution.
o This jcTOOL Helper Class software, whether in binary or source form may not be used within,
or to derive, any other product without the specific prior written permission of the copyright holder
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package DisplayProject.table;
import java.awt.Component;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.Graphics2D;
import java.awt.GridBagConstraints;
import java.awt.image.BufferedImage;
import javax.swing.JCheckBox;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JScrollBar;
import javax.swing.JScrollPane;
import javax.swing.ListSelectionModel;
import javax.swing.table.TableCellEditor;
import DisplayProject.PaletteList;
import DisplayProject.RadioList;
import DisplayProject.TableJButton;
import DisplayProject.actions.ElementList;
import DisplayProject.actions.Parent;
import DisplayProject.actions.ScrollBar;
import DisplayProject.binding.BindingManager;
import DisplayProject.controls.ArrayField;
import DisplayProject.controls.AutoResizingComboBox;
import DisplayProject.controls.FillInField;
import DisplayProject.controls.OutlineField;
import DisplayProject.controls.ScrollList;
/**
* Helper class for static generic methods for both the editor and renderer.
*
* Written by Craig Mitchell
* @since 07/09/2007
*/
public class ArrayFieldCellHelper {
public static final String CLIENT_PROPERTY_ARRAY_FIELD = "ArrayField"; // CraigM: 28/03/2008
private static final String CLIENT_PROPERTY_ARRAY_COLUMN = "ArrayFieldColumn"; // CraigM: 28/03/2008
private static final String CLIENT_PROPERTY_ARRAY_IS_IN_GRID = "ArrayFieldInGrid"; // CraigM:18/10/2008
private static final String CLIENT_PROPERTY_BINDING_MANAGER = "ArrayFieldBindingManager"; // TF:15/10/2008
private static JFrame frameForBufferedImage;
private ArrayFieldCellHelper() {
}
public static int getGBCFill(Component pComp) {
if (pComp instanceof OutlineField) {
return GridBagConstraints.BOTH;
}
// BT: 20/11/2007 Added TableJButton condition to format buttons in arrays within JScrollPanes
else if (pComp instanceof RadioList || pComp instanceof JCheckBox || pComp instanceof TableJButton) {
return GridBagConstraints.NONE;
//PM:5/5/08 this is a possible solution for NVP-26 but is not general
// } else if ((pComp instanceof DataField) && (WidthPolicy.get((JComponent)pComp) != Constants.SP_TO_PARENT)){
// return GridBagConstraints.NONE;
} else {
return GridBagConstraints.HORIZONTAL;
}
}
private static Object getArrayFieldProperty(Component pComp, String pProperty) {
if (pComp != null) {
if (pComp instanceof FillInField.BorderedComboBoxEditor.Editor) {
FillInField fif = ((FillInField.BorderedComboBoxEditor.Editor)pComp).getFillInField();
return fif.getClientProperty(pProperty);
}
else if (pComp instanceof JComponent) {
return ((JComponent)pComp).getClientProperty(pProperty);
}
}
return null;
}
public static int getArrayFieldColumn(Component pComp) {
Object _Result = ArrayFieldCellHelper.getArrayFieldProperty(pComp, CLIENT_PROPERTY_ARRAY_COLUMN);
if (_Result instanceof Integer) {
return (Integer)_Result;
}
return -1;
}
/**
* @return the ArrayField that pComp is contained in (null if none).
*/
public static ArrayField getArrayField(Component pComp) {
// Component comp = pComp;
// TF:6 Nov 2008:Removed these lines because they break the array field tabbing! They conflict
// with the ForteKeyboardFocusManager where it looks to see if the parent of the currently
// selected component is a grid field (which it will be, given that the ArrayFieldCellEditor.EditorLayoutPanel
// components are all grid fields) and then tests to see if the array field associated with
// the parent is non-null. WIth these 3 lines in place, the parent will be non-null, so it
// will try to move the component within the grid, and end up focussing the grid field.
// if (comp instanceof ArrayFieldCellEditor.EditorLayoutPanel){//PM:5 Nov 2008: handle the editor panel
// comp = ((ArrayFieldCellEditor.EditorLayoutPanel)comp).getEditorComponent();
// }
Object _Result = ArrayFieldCellHelper.getArrayFieldProperty(pComp, CLIENT_PROPERTY_ARRAY_FIELD);
if (_Result instanceof ArrayField) {
return (ArrayField)_Result;
}
return null;
}
/**
* Return the real widget that is mapped inside an array field. For example, a data field that is a child of the
* array field will be defined as the column
* @param field
* @return Returns an {@link ArrayFieldCellEditor}
*/
public static Component getArrayEditorComponent(ArrayField field) {
return getArrayEditorComponent(field, true);
}
public static Component getArrayEditorComponent(ArrayField field, boolean usePostingComponent) {
Component currentEditor = field.getEditorComponent();
// TF:12/11/07:Null check
if (currentEditor == null) {
return null;
}
if (currentEditor instanceof ArrayFieldCellEditor.EditorLayoutPanel) {
currentEditor = ((ArrayFieldCellEditor.EditorLayoutPanel)currentEditor).getEditorComponent();
if (currentEditor instanceof ArrayTableComponentCorrector) {
if (usePostingComponent) {
currentEditor = ((ArrayTableComponentCorrector)currentEditor).correctPostingComponent();
}
else {
currentEditor = ((ArrayTableComponentCorrector)currentEditor).correctSubscriptionComponent();
}
}
}
if (((JComponent)currentEditor).getClientProperty(CLIENT_PROPERTY_ARRAY_FIELD) == null) {
if (currentEditor instanceof JScrollPane) {
JScrollPane scrollPane = (JScrollPane)currentEditor;
currentEditor = scrollPane.getViewport().getView();
}
}
return currentEditor;
}
/**
* Return the real widget at a particular row and column.
*
* @param pAF
* @param pRow
* @param pCol
*
* @since 29/01/2008
*/
public static Component getArrayEditorComponent(ArrayField pAF, int pRow, int pCol) {
TableCellEditor currentEditor = pAF.getCellEditor(pRow, pCol);
if (currentEditor instanceof ArrayFieldCellEditor) {
currentEditor = ((ArrayFieldCellEditor)currentEditor).getEditor();
}
if (currentEditor == null) {
return null;
}
Object o = null;
try {
o = pAF.getValueAt(pRow, pCol);
}
catch (Exception e) {
// Leave the value as null
}
return currentEditor.getTableCellEditorComponent(pAF, o, false, pRow, pCol);
}
public static void setUpCellEditor(JComponent pEditorComponent, ArrayField pAF, int pColumnNumber) {
pEditorComponent.putClientProperty(CLIENT_PROPERTY_ARRAY_FIELD, pAF);
pEditorComponent.putClientProperty(CLIENT_PROPERTY_ARRAY_COLUMN, new Integer(pColumnNumber));
}
/**
* The cell editors component gets marked with some information about the ArrayField they are in.
* Written by CraigM.
*
* @param pEditorComponent The component that is contained in the cell editor.
* @param pAF The ArrayField the component is contained in.
* @param pColumnNumber The column the component is assigned to in the ArrayField.
* @param pIsContainedInGrid Set to true if pEditorComponent is inside a grid field, inside the array field.
*
* @since 28/03/2008
*/
public static void setUpCellEditor(JComponent pEditorComponent, ArrayField pAF, int pColumnNumber, boolean pIsContainedInGrid) {
ArrayFieldCellHelper.setUpCellEditor(pEditorComponent, pAF, pColumnNumber);
if (pIsContainedInGrid) {
pEditorComponent.putClientProperty(CLIENT_PROPERTY_ARRAY_IS_IN_GRID, new Boolean(true)); // CraigM:18/10/2008
}
}
/**
* Set the passed binding manager as the binding manager to use for this component. This is
* typically used for the top level component mapped to a compound widget in an array
* @param component
* @param mgr
*/
public static void setArrayColumnBindingManager(JComponent component, BindingManager mgr) {
component.putClientProperty(CLIENT_PROPERTY_BINDING_MANAGER, mgr);
}
/**
* Return the binding manager previously associated with the passed component. This method
* can return <tt>null</tt> if no binding managers have been previously associated.
* @param pColumnTemplate
* @return
*/
public static BindingManager getArrayColumnBindingManager(Component pColumnTemplate) {
if (pColumnTemplate instanceof JComponent) {
return (BindingManager)((JComponent)pColumnTemplate).getClientProperty(CLIENT_PROPERTY_BINDING_MANAGER);
}
return null;
}
public static void cloneComponentArrayParts(JComponent origComponent, JComponent newComponent) {
setUpCellRenderer(origComponent, newComponent);
if (ArrayFieldCellHelper.getArrayField(origComponent)!= null) {
BindingManager origBindingMgr = ArrayFieldCellHelper.getArrayColumnBindingManager(origComponent);
if (origBindingMgr != null) {
// This is the top level binder, we need to make a new binding manager here
BindingManager newBindingMgr = new BindingManager(origBindingMgr.getMappedObject());
ArrayFieldCellHelper.setArrayColumnBindingManager(newComponent, newBindingMgr);
}
else {
// This is a component inside an array field, but either not a top level component, or not
// bound. Scan up the hierarchy to find a parent that has a binding manager
BindingManager topLevelManager = null;
Container c = Parent.get(newComponent);
while (c != null) {
BindingManager thisMgr = getArrayColumnBindingManager(c);
if (thisMgr != null) {
topLevelManager = thisMgr;
}
c = Parent.get(c);
}
// Now we've got the top level manager, we need to bind our component to it.
if (topLevelManager != null) {
if (newComponent instanceof AutoResizingComboBox) {
AutoResizingComboBox box = (AutoResizingComboBox)newComponent;
topLevelManager.bindComponent(box, BindingManager.getDataObjectPath(origComponent), ElementList.get(box));
}
else if (newComponent instanceof PaletteList) {
PaletteList list = (PaletteList)newComponent;
topLevelManager.bindComponent(list, BindingManager.getDataObjectPath(origComponent), ElementList.get(list));
}
else if (newComponent instanceof RadioList) {
RadioList list = (RadioList)newComponent;
topLevelManager.bindComponent(list, BindingManager.getDataObjectPath(origComponent), ElementList.get(list));
}
else if (newComponent instanceof JScrollBar) {
JScrollBar bar = (JScrollBar)newComponent;
topLevelManager.bindComponent(bar, BindingManager.getDataObjectPath(origComponent),
ScrollBar.getMinimumValue(bar),
ScrollBar.getMaximumValue(bar),
ScrollBar.getViewSize(bar));
}
else if (newComponent instanceof ScrollList) {
ScrollList sl = (ScrollList)newComponent;
ScrollList orig = (ScrollList)origComponent;
topLevelManager.bindComponent(sl, BindingManager.getDataObjectPath(origComponent),
ElementList.get(orig),
orig.getSelectionModel().getSelectionMode() == ListSelectionModel.SINGLE_SELECTION);
}
else {
topLevelManager.bindComponent(newComponent, BindingManager.getDataObjectPath(origComponent));
}
}
}
}
}
/**
* The cell renderers use a cloned widget which needs some things to be set up.
* Written by CraigM.
*
* @param pOriginalComponent the reference to the actual component
* @param pRendererComponent the cloned component
*
* @since 28/03/2008
*/
public static void setUpCellRenderer(JComponent pOriginalComponent, JComponent pRendererComponent) {
pRendererComponent.putClientProperty(CLIENT_PROPERTY_ARRAY_FIELD, ArrayFieldCellHelper.getArrayField(pOriginalComponent));
pRendererComponent.putClientProperty(CLIENT_PROPERTY_ARRAY_COLUMN, ArrayFieldCellHelper.getArrayFieldColumn(pOriginalComponent));
if (ArrayFieldCellHelper.isInGridFieldInArrayField(pOriginalComponent)) {
pRendererComponent.putClientProperty(CLIENT_PROPERTY_ARRAY_IS_IN_GRID, new Boolean(true));
}
}
/**
* @param comp
* @return true if comp is contained in a GridField inside the ArrayField (does not include
* the GridField (EditorLayoutPanel) created in ArrayFieldCellEditor or in ArrayFieldCellRenderer.
*/
public static boolean isInGridFieldInArrayField(Component comp) {
if (comp instanceof JComponent && ((JComponent)comp).getClientProperty(CLIENT_PROPERTY_ARRAY_IS_IN_GRID) != null) {
return true;
}
return false;
}
/**
* Determine if the passed component is contained within an array field (ie is a column template). If the passed
* component is <tt>null</tt> then <tt>false</tt> is returned
* @param comp
* @return
*/
public static boolean isInArrayField(Component comp) {
if (comp instanceof JComponent && ((JComponent)comp).getClientProperty(CLIENT_PROPERTY_ARRAY_FIELD) != null) {
return true;
}
return false;
}
/**
* Create a buffered image for comp. Used to draw empty rows in the ArrayField. CraigM:20/01/2009.
*
* @param comp
* @param width
* @param height
* @return
*/
public static BufferedImage createBufferedImage(Component comp, int width, int height) {
if (ArrayFieldCellHelper.frameForBufferedImage == null) {
ArrayFieldCellHelper.frameForBufferedImage = new JFrame();
ArrayFieldCellHelper.frameForBufferedImage.setUndecorated(true);
}
ArrayFieldCellHelper.frameForBufferedImage.getContentPane().removeAll();
ArrayFieldCellHelper.frameForBufferedImage.getContentPane().add(comp);
ArrayFieldCellHelper.frameForBufferedImage.setPreferredSize(new Dimension(width, height));
ArrayFieldCellHelper.frameForBufferedImage.setMinimumSize(new Dimension(width, height));
ArrayFieldCellHelper.frameForBufferedImage.setSize(new Dimension(width, height));
ArrayFieldCellHelper.frameForBufferedImage.pack();
BufferedImage img = new BufferedImage(Math.max(width,1), Math.max(height,1), BufferedImage.TYPE_INT_RGB);
Graphics2D g = img.createGraphics();
comp.paint(g);
g.dispose();
return img;
}
}